如何实现非线性核卷积,无需形态学与霍夫变换检测黑白图像圆形
非形态学/霍夫变换的黑白虹膜图像圆形检测方案(自定义卷积实现)
需求背景
需要在不使用形态学操作或霍夫变换的前提下,检测黑白虹膜样本图像中的圆形,待检测样本示例如下:



现有实现基础
当前计划通过自定义卷积核的卷积操作实现需求,现有卷积核构造代码如下:
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. 自定义卷积的实现步骤
- 预处理卷积核:遍历所有非零位置,按极角/约分坐标分组,每组对应一条射线的所有位置
- 对输入图像做 padding 避免边缘信息丢失,再做滑窗遍历,每个滑窗位置对应卷积核覆盖的图像区域
- 每个滑窗位置的响应值计算:
- 初始化响应值为0
- 遍历所有射线分组:检查该分组下所有核位置对应的图像像素是否存在非0值
- 若存在非0值,响应值累加单个像素的预设响应值,否则不累加
- 处理完成后得到响应图,响应峰值位置即为圆形的圆心,对应卷积核半径就是检测到的圆半径
简化实现代码
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
相关产品推荐
相关产品推荐

