You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现非线性核卷积,无需形态学与霍夫变换检测黑白图像圆形

非形态学/霍夫变换的黑白虹膜图像圆形检测方案(自定义卷积实现)

需求背景

需要在不使用形态学操作或霍夫变换的前提下,检测黑白虹膜样本图像中的圆形,待检测样本示例如下:
示例图像
示例2
示例3
示例4

现有实现基础

当前计划通过自定义卷积核的卷积操作实现需求,现有卷积核构造代码如下:

radius = 2
element = np.zeros((2 * radius + 1, 2 * radius + 1))
cv2.circle(element, (radius, radius), radius,  1, 1)
cv2.circle(element, (radius, radius), radius+1,  1, 1)
cv2.circle(element, (radius, radius), radius+2,  1, 1)

该卷积核由多组同心圆构成,核示例如下:
卷积核示例

待实现的卷积特性

需要实现的效果:对于卷积核上从中心指向最大半径的同一条射线上的所有非零元素,同一条射线上只要存在至少1个位置和输入图像像素匹配(像素值非0),卷积结果仅累加1次响应值,无需按匹配像素的数量累加。

举个例子:假设同一射线上有2个卷积核激活位置,若图像对应两个位置像素值均为255,卷积结果加255而非500;若仅1个位置像素为255,同样加255;若两个位置均为0则加0。本质是用粗圆环做卷积核时,每个径向方向仅作为单个核元素生效,响应值等同于单个像素的响应。

核心问题

  • 如何判断卷积核中的像素是否处于同一条射线?
  • 如何实现上述特性的自定义卷积?

实现方案

1. 判断同射线的方法

以卷积核中心为坐标原点(0,0),对核上每个非零像素的坐标(x,y),有两种判断同射线的方案:
方案一:计算坐标的极角np.arctan2(y, x),将极角按精度要求做分桶,同一分桶内的像素即属于同一条射线;
方案二:对坐标偏移量(dx, dy)做最大公约数约分,约分后数值相同的坐标对属于同一条射线,比如坐标(1,2)、(2,4)约分后都是(1,2),属于同一条射线,该方案可避免浮点精度误差。

2. 自定义卷积的实现步骤

  1. 预处理卷积核:遍历所有非零位置,按极角/约分坐标分组,每组对应一条射线的所有位置
  2. 对输入图像做 padding 避免边缘信息丢失,再做滑窗遍历,每个滑窗位置对应卷积核覆盖的图像区域
  3. 每个滑窗位置的响应值计算:
    • 初始化响应值为0
    • 遍历所有射线分组:检查该分组下所有核位置对应的图像像素是否存在非0值
    • 若存在非0值,响应值累加单个像素的预设响应值,否则不累加
  4. 处理完成后得到响应图,响应峰值位置即为圆形的圆心,对应卷积核半径就是检测到的圆半径

简化实现代码

import numpy as np
import cv2

def generate_rays(kernel_size, center):
    cx, cy = center
    rays = {}
    for y in range(kernel_size[0]):
        for x in range(kernel_size[1]):
            if x == cx and y == cy:
                continue
            # 计算坐标相对于中心的偏移
            dx = x - cx
            dy = y - cy
            # 约分dx、dy作为射线唯一标识
            g = np.gcd(np.abs(dx), np.abs(dy))
            if g != 0:
                key = (dx//g, dy//g)
            else:
                key = (np.sign(dx), np.sign(dy))
            if key not in rays:
                rays[key] = []
            rays[key].append((y, x))
    return list(rays.values())

# 构造卷积核
radius = 2
kernel_size = 2 * radius + 1
center = (radius, radius)
element = np.zeros((kernel_size, kernel_size), dtype=np.uint8)
cv2.circle(element, center, radius, 1, 1)
cv2.circle(element, center, radius+1, 1, 1)
cv2.circle(element, center, radius+2, 1, 1)

# 生成仅包含核有效位置的射线分组
rays = generate_rays((kernel_size, kernel_size), center)
valid_rays = []
for ray in rays:
    valid_pos = [pos for pos in ray if element[pos[0], pos[1]] == 1]
    if len(valid_pos) > 0:
        valid_rays.append(valid_pos)

# 自定义卷积实现
def custom_conv(img, valid_rays, response_val=255):
    h, w = img.shape
    k_size = element.shape[0]
    pad = k_size // 2
    padded_img = np.pad(img, pad, mode='constant')
    res = np.zeros_like(img, dtype=np.float32)
    for i in range(h):
        for j in range(w):
            window = padded_img[i:i+k_size, j:j+k_size]
            score = 0
            for ray in valid_rays:
                # 检查该射线是否有任意位置匹配
                has_match = np.any(window[[p[0] for p in ray], [p[1] for p in ray]] > 0)
                if has_match:
                    score += response_val
            res[i, j] = score
    return res

内容的提问来源于stack exchange,提问作者Nourless

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 21:21:02