用 YOLO 数筷子——从 OpenCV 到深度学习的目标检测实战

不锈钢筷子头挨着头、反光闪瞎眼,OpenCV 霍夫圆直接投降。这个小项目走了一遍从传统 CV 到深度学习的完整路径。 那些反光的筷子头,把 OpenCV 逼疯了 事情是这样的:要数一把不锈钢筷子有多少根。听起来很简单对吧?拿个相机拍一下,数圆圈就行了。 用 OpenCV 的霍夫圆变换(HoughCircles)试了一下——惨不忍睹。不锈钢顶部的金属反光和阴影被边缘检测器当成独立实体,满屏幕乱飞的假阳性圆圈。筷子头挨得又近,阴影轮廓互相粘连,算法根本分不清这是 16 根筷子还是 2 个异形物体。 更要命的是调参。换个光照条件,param1、param2 又得重来。换个桌面背景,再来一遍。这不是写代码,这是在做手工。 这就是传统 CV 的死穴:它只认几何形状,不认"东西"。霍夫圆变换的逻辑是"找到符合圆方程的像素点集合",它不理解"这是一根筷子的截面"这个概念。光照一变、角度一变、排列一变,像素梯度就变了,算法就废了。 为什么 YOLO 能搞定 YOLO 的思路完全不同。它不是在像素层面找几何形状,而是从数百万张图片里学会了"什么是筷子头"。它理解的是语义特征,不是像素梯度。 具体到这个场景: 问题 OpenCV (霍夫圆) YOLO 不锈钢高光 把光斑误判为实体 能通过上下文推理出这是光斑 筷子头紧密排列 把多个物体融合成一个 擅长分辨紧密排列的多个实例 换光照/换背景 每次都要重调参 一次训练,到处通用 简单说:OpenCV 是"按规则找",YOLO 是"凭经验认"。规则怕变化,经验怕没见过——但筷子头这种常见物体,YOLO 的训练集里见得多了。 从标注到训练:三步走 第一步:安装标注工具 先检查 Python 版本: python --version Python ≥ 3.9 → 用 LabelMe,支持圆形/多边形标注,本项目用的就是它 Python ≤ 3.8 → 建议用 LabelImg,只支持矩形框,但兼容性更好 💡 LabelMe 依赖的 pyqt5 在 Python 3.8 以下版本经常装不上,折腾半小时不如直接换工具。 ...

June 28, 2026 · FXIO

机器视觉入门——光、镜头、相机,以及为什么选错一个全白搭

工业产线上,机器怎么"看"东西?答案不在算法里,在硬件选型的每一个细节里。 开场:一个真实的翻车现场 去年帮一个朋友调一条五金件检测线。缺陷检测算法在实验室跑得好好的,一上产线就各种误报。排查了半天,最后发现原因特别朴素——光源选小了。光源有效发光面积没覆盖住整个视野,画面中间亮、两边暗,边缘的工件特征被算法当成了缺陷。 一个两千块的光源问题,浪费了三天调算法。 这就是机器视觉的第一课:它是一个链式系统,光→镜头→相机→算法,环环相扣。任何一个环节的短板都会被后续环节放大。 算法再牛,喂给它的图像质量不行,结果就是垃圾进、垃圾出。 下面记录这条链路的学习要点。 硬件三件套:光源、镜头、相机 光源:最容易被低估的环节 很多工程师(包括以前的我)觉得光源就是"把东西照亮"。错。光源的核心目标是让目标特征和背景之间的对比度最大化。 打个比方:你要在白纸上检测白色划痕,用白光正面打,什么都看不到。但换成低角度的条形光源,光线擦过表面,划痕边缘产生反射,背景是暗的,划痕是亮的——缺陷就"跳"出来了。 课程里讲了五六种常见光源,我按使用频率排个序: 环形光源:最通用,360° 均匀照明,适合大多数表面检测。如果不知道用什么,先试环形光。 条形光源:方向性强,擅长消除镜面反光。检测金属平面字符时,条形光比环形光好用得多——环形光容易在光滑表面打出大面积光晕。 背光源:从底部打光,物体轮廓变成剪影。做尺寸测量、孔径检测时的标配。 同轴光源:内置分光镜,光线垂直照射。专门对付高反光的镜面、抛光金属,能消除凹凸产生的阴影。 穹顶光源:半球形漫射,360° 无死角。检测曲面工件(比如球形外壳)时的首选,普通光源打上去全是反光斑点。 还有一个容易忽略的点:光源颜色选择遵循互补色原理。红光照绿色物体,黑白相机下物体是黑色的;红光照红色物体,物体是白色的。想让红色字符在白色背景上更突出?用蓝色光源——红色字符吸收蓝光变黑,白色背景反射蓝光变亮,对比度直接拉满。 镜头:焦距只是起点 镜头选型的第一步是算焦距,公式不复杂: 焦距 = 工作距离 × 芯片尺寸 / 视场 但焦距选对了只是及格线。真正容易踩坑的是两个参数: 景深——简单理解就是"前后多大范围内的东西是清楚的"。景深越小,对焦平面以外的物体就越模糊。影响景深的三个因素:光圈越大景深越小,焦距越长景深越小,工作距离越近景深越小。做尺寸测量时,如果工件有厚度(比如一个盒子),景深不够就会出现"上面清楚、下面模糊"的问题。 畸变——普通定焦镜头天然有畸变,短焦是桶形畸变(像被捏扁了),长焦是枕形畸变(像被拉长了)。人眼看不出小于 2% 的畸变,但做精密测量时,边缘 1mm 的畸变就是实打实的误差。这时候就得上远心镜头——它的特点是"无论物体离镜头远近,在图像里的大小不变"。类比一下:普通镜头像人眼看东西,近大远小;远心镜头像用尺子量,不管尺子放多远,读数不变。 另外注意镜头和相机的接口匹配:C 口后截距 17.5mm,CS 口 12.5mm,差 5mm。CS 口相机接 C 口镜头要加 5mm 接圈,反过来不行。接口不匹配,图像就是糊的。 相机:一个公式定成败 相机选型的核心公式: 像素精度 = 视场 / 分辨率 意思是:图像上一个像素代表实际空间中多大的尺寸。 举个例子:用 500 万像素相机(2592×1944)拍 80mm 宽的视野,像素精度 = 80 / 2592 ≈ 0.031 mm/pixel。也就是说图像里一个像素对应实际 0.031mm。 经验值:要检测的最小特征,至少要在图像中占 2~3 个像素。 如果你要检测 0.1mm 的缺陷,像素精度至少要到 0.03~0.05mm/pixel,否则缺陷在图像里就一两个模糊的点,算法根本抓不住。 ...

June 27, 2026 · FXIO

Android OpenCV CMake 配置

Android-opencv-cmake Android opencv image process samples ,build with cmake 环境要求 Android studio 2.3.3 Cmake ndk14 opencv2.4.13 or opencv3.3 配置 在local.properties 文件中配置opencv sdk 路径 opencv.dir = ${opencv_sdk_dir} ndk.dir=/Users/king/Library/Android/ndk14 sdk.dir=/Users/king/Library/Android/sdk opencv.dir=/Users/king/Libs/OpenCV2413 在build.gradle中配置cmake参数 externalNativeBuild { cmake { cppFlags "-std=c++11 -frtti -fexceptions" arguments "-DOpenCV_DIR=" + getOpenCVDir().toString()+"/sdk/native/jni", "-DANDROID_ARM_NEON=TRUE" } } ndk { abiFilters "armeabi-v7a" //, "arm64-v8a","armeabi", } ... def getOpenCVDir() { Properties properties = new Properties() properties.load(new File(rootDir.absolutePath + "/local.properties").newDataInputStream()) def externalModuleDir = properties.getProperty('opencv.dir', null) if (externalModuleDir == null) { throw new GradleException( "OpenCV location not found. Define location with opencv.dir in the local.properties file!") } return externalModuleDir } 项目地址 源码

March 15, 2017 · FXIO