无外设条件下的自动找眼V2

引: (2026-01-31周六)第一版的找眼算法差强人意,在鲁棒性和准确性上有待进一步提升,今天更新出一般热乎的,效果基本达到理想的状态,及时更新记录防老年痴呆!!!(但是算法的迭代时没有上限的,优化空间仍然巨大!!!)

1 数据特点

自然图像中的找眼(找瞳孔)在目前计算机视觉方法库中已经是小学生的加减乘除(当然你得是大学生),但是特定领域下的非自然图像由于光学系统、被摄对象的特殊性,仍旧无法直接照搬现有的方法解决问题,需要具体问题具体分析。

Fig 1. 非自然图像对比自然图像下的瞳孔特征

我们的光学系统有以下几个特点:

1 视场角(FOV)极小 -- 导致ccd捕捉到的信息量骤减,Harr等现有算法失效

2 对象远离景深范围 -- 信息同质化(说人话就是 :糊)

3 光照不均匀 -- 干扰图像真实的灰度分布

2 现有方法及问题

图像内容(Fig1)是模糊可见的瞳孔和部分眼白,接受绿光照射,投影有效像素范围大概限制在内切圆(500*500)内,其中由于光源打在人眼上的漫反射加上远离了景深范围,导致图像各部分的梯度并不明显,同时瞳孔可能在画面的任何地方(因为光学系统所在的电机平台可运动)。

2.1 大致算法流程(屏蔽细节)

我们目前设计的瞳孔检测算法能用,但是依旧存在一些问题,导致效果不够理想。

目前算法流程:

1 reise && clip

2 cv::GaussianBlur

3 灰度二值化

cpp 复制代码
 // 分割方法1 -- bernsen局部阈值分割 -- 抖动太厉害
bernsenThreshold(gray, binary, 15, 15); //51

// 分割方法2 -- 大津Otsu -- 太保守,检测范围不够大
cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);

// 分割方法3 -- shanbhagThreshold -- 比Otsu范围增大一点
shanbhagThreshold(gray, binary, 3);  // radius默认为5

4 形态学处理(谨慎使用非对称算子)

cpp 复制代码
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(7, 3)); //7
cv::dilate(binary, binary, element);

element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 7)); //7
cv::erode(binary, binary, element); //腐蚀

5 霍夫圆检测

cpp 复制代码
// ========== 5. 霍夫圆检测 ==========
std::vector<cv::Vec3f> circles2;
cv::HoughCircles(  );

关键步骤在二值化和形态学处理上,前者找到瞳孔所在大区域,后者对瞳孔作修边和填充。

二者共同为后面的霍夫圆检测提供尽可能准确干净的数据用于回归。二值化方法的使用上,Otsu和bernsen方法在调整参数后依然无法做到鲁棒,遂使用shanbhag方法。

2.2 shanbhag自适应阈值分割

Shanbhag 方法,通常指 Shanbhag 自适应阈值分割算法(Shanbhag Thresholding, 1994) ,是一种基于熵(Entropy)的自动阈值选择方法,常用于灰度图像二值化。

它和 Otsu、Kapur 属于同一类: **直方图驱动的全局阈值方法,**但思想不一样。

Shanbhag 的核心思想是:找到一个阈值,使"前景 + 背景"的信息不确定性(熵)达到一种平衡状态。既不过度强调前景,也不过度强调背景。(下面表格整理自GPT供参考)

方法 阈值来源 是否依赖直方图 是否假设双峰 主要失败原因
Otsu 类间方差 ✅ 强 灰度拖尾、单峰
Bernsen 局部极值 噪声、纹理
Shanbhag 熵平衡 极端光照不均
[Table 1. 关键机制对比]
方法 主要参数 调参难度 工程稳定性
Otsu ⭐ 极低 ⭐⭐⭐
Bernsen 窗口大小、对比度阈值 ❌ 高
Shanbhag ⭐⭐ 低 ⭐⭐⭐⭐
[Table 2. 参数 & 调参成本]

3. 尝试过的改进思路

3.1 滑动窗口+步长 + 灰度分析(均值方差、梯度)

起初是想借鉴分治思想,将全图(经过预处理)划分成网格,计算每个sub_image的灰度信息给出大致的瞳孔范围再进一步处理。网格大小的设置需要考虑到瞳孔(或其他特征)在画面中的比例以及电机前后运动时的缩放情况,容易漏检。进一步想到滑动窗口机制(数据结构课本中的数组遍历、YOLO检测模型中的端到端检测思想均是滑动窗口机制的体现),使用固定步长的滑动窗口遍历全图,找到最有区分度的分度特征作为瞳孔区域的是非判断。

Fig 2. 滑动窗口+梯度计算

如上图Fig2,在实际滑动过程中使用每个子窗的导数信息统计边界的分布情况,期望找到瞳孔和眼白的过渡区作为初步的mask。但是反复调整参数也无效,因为边界过度区域太多,无法通过限制条件做筛选,当瞳孔远离图像中心时情况加剧。

python 复制代码
# 先进行高斯平滑
# gray_smooth = cv2.GaussianBlur(gray, (15, 15), 1.0)
    # gray_smooth = gray
    #
    # # 使用Laplacian算子求导数
    # laplacian = cv2.Laplacian(gray_smooth, cv2.CV_64F)
    # # laplacian = cv2.Laplacian(laplacian, cv2.CV_64F)
    # # 取绝对值以便更好地可视化
    # Z_laplacian = np.abs(laplacian)

3.2 深度学习方法

有考虑是否能快速训练一个UNet实现精度和鲁棒性的提升,但是模型的训练加上嵌入式调试,需要的时间成本巨大,所以后期再考虑。

3.3 shanbhag+嵌套霍夫检测

因后续可能考虑专利申请,此部分暂时无法详细描述算法流程。

大致思路是对shanbhag算子使用大胆一点的参数设置,后续叠加复杂的形态学处理后使用嵌套霍夫圆检测作错值剔除,最终实现了较为满意的效果。但是不考虑工程之外的成本和结构设计复杂度,最好的方法就是加外设。有时候一个硬件能解决的简单问题,使用算法替代硬件会复杂几十倍不止,而且还达不到最好的效果。呜呜呜

最后军火展示

Fig 3. 结果

相关推荐
hcnaisd23 小时前
深入理解C++内存模型
开发语言·c++·算法
李老师讲编程3 小时前
C++信息学奥赛练习题-杨辉三角
数据结构·c++·算法·青少年编程·信息学奥赛
qq_296544653 小时前
短视频下载教程,抖音B站视频下载
c++
2201_756989093 小时前
C++中的事件驱动编程
开发语言·c++·算法
2301_822377654 小时前
模板元编程调试方法
开发语言·c++·算法
啟明起鸣4 小时前
【C++ 性能提升技巧】C++ 的引用、值类型、构造函数、移动语义与 noexcept 特性,可扩容的容器
开发语言·c++
故以往之不谏4 小时前
函数--值传递
开发语言·数据结构·c++·算法·学习方法
2301_811232984 小时前
低延迟系统C++优化
开发语言·c++·算法
txinyu的博客5 小时前
解析muduo源码之 ThreadLocal.h
c++