拓冰建站拓冰建站
首页 / 资讯中心 / 正文

OpenCV目标定位识别实战:从HSV阈值分割到轮廓检测

简介围绕OpenCV目标定位识别搭建的机器视觉项目面向具备基础Python与图像处理知识的开发者适合用于课程设计、毕业设计或工业检测场景的快速落地。资源覆盖目标检测、特征提取、图像分割与对象跟踪四大关键模块既提供Haar级联分类器、SIFT/ORB等传统方法示例也包含SSD/YOLO等深度学习模型调用可帮助理解从图像采集、预处理、特征提取到定位识别的完整工程流程。压缩包共47个文件以15个py源码为主辅以18个pyc编译文件、6个txt配置/数据文件、6个jpg测试图像和2个md说明文档整体4.68MB目录结构包含源码、配置、测试图像与说明文档便于按模块查阅。目前已有79人学习下载项目附带README与多场景扫描区配置适合需要快速上手OpenCV目标识别项目并开展实验验证的中级学习者。1. 目标定位识别第一步不是深度学习产线上装好了相机拍回来的图也清楚真正的麻烦往往在“目标到底在哪”这一个问题上。背景一变、光源一闪、角度一歪同一个工件的坐标就可能偏出十几像素后续机械臂抓取、PLC纠偏全部跟着错。基于OpenCV目标定位识别的机器视觉项目压缩包内工程名为 MVision要处理的正是这个问题用纯OpenCV的经典方案把目标从复杂背景里稳定地找出来并输出坐标。工程里除了一套规范结构src、config和可直接运行的 test.py / hsv.py 之外还配了四个“扫描区”配置文件和多张现场测试图用来做不同光线、不同位置下的定位验证。这套工程不只适合刚学opencv的入门者对写过一段时间图像处理、但没把“扫描区HSV阈值”这套组合用熟练的从业者也能提供一套可复用的现场调优思路。2. 工程结构与运行环境先让代码不报错地跑起来在聊HSV阈值和坐标计算之前先把工程拆开。MVision 的目录组织是典型的“可交付式”视觉项目源码、配置、测试图、说明文档分开管理拿到手第一眼就能知道每个文件负责什么这也是工程项目和课程作业最明显的区别。2.1 目录里每个文件是干什么的MVision-main/ ├── src/ # 核心源码封装图像读取、预处理、目标定位函数 ├── config/ # 运行级配置比如相机参数、输出路径 ├── hsv.py # HSV 阈值调参脚本拖滑条实时看掩膜效果 ├── test.py # 单张图片定位入口输出目标坐标和画框结果 ├── requirements.txt # Python 依赖清单 ├── README.md # 项目说明文档 ├── read.md # 简版运行说明 ├── 1.jpg / 2.jpg / 3.jpg # 常规场景测试图 ├── Y1.JPG / Y3.JPG # 不同光照或角度下的现场图 ├── cat.jpg # 负样本测试图用来验证误检率 └── 扫描区A.txt ~ 扫描区D.txt # 四个 ROI 区域的坐标与阈值配置这个结构透露出设计意图code 和 config 分离。扫描区 txt 文件不属于 src这一点后面第 5 章细讲这里先记住一个结论——“ROI 和阈值都不该硬编码进代码里”。另外补充一点我在本地测试时用相对路径读图工业现场建议改成绝对路径或者把路径写进 config 统一管理不然换一台机器文件路径立刻失效。文件/目录角色说明src/核心模块包含目标定位的主要函数config/环境级配置相机参数、路径等hsv.pyHSV 阈值可视化调参工具test.py单图定位入口输出坐标和结果图扫描区A.txt~D.txtROI 与 HSV 阈值、过滤参数的文本配置Y1.JPG / Y3.JPG现场不同光照、角度测试图cat.jpg负样本图用于误检回归2.2 依赖安装与 OpenCV 版本差异requirements.txt 里的核心依赖是 opencv-python 和 numpy有些分支工程会带 PyQt5 做界面MVision 不做界面依赖保持轻量。安装建议用虚拟环境避免污染系统 Pythonpython -m venv venv source venv/bin/activate # Windows 上执行 venv\Scripts\activate pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple如果只想单独装 OpenCV直接指定版本更稳pip install opencv-python4.8.0.74 numpy装完验证一下能不能正常导入python -c import cv2; print(cv2.__version__)能打印出 4.x 版本号说明环境没问题。这里要特别提醒一个高频坑OpenCV 4.x 的cv2.findContours返回两个值contours, hierarchyOpenCV 3.x 返回三个值image, contours, hierarchy网上大量老教程还在用 3.x 写法直接抄到 4.x 环境下运行必然报ValueError: not enough values to unpack。MVision 的 test.py 是按 4.x 写法解包的不要降级 OpenCV也不要把两种写法混在一起。2.3 先认清 OpenCV 的图像坐标系做目标定位离不开坐标系。OpenCV 的图像坐标原点在左上角x 轴水平向右y 轴垂直向下单位是像素。cv2.boundingRect返回的(x, y, w, h)中x、y 是矩形左上角坐标w、h 是宽高。后面第 4 章输出目标中心坐标时要在这个基础上加宽高的一半别直接用矩形的左上角当目标位置那会偏半个身位。3. HSV 颜色空间与阈值分割目标定位的核心原理hsv.py 这个脚本名已经暗示了项目的颜色处理核心HSV。目标定位识别在工业场景里最常见的手段就是先把目标物的颜色从背景中分离出来再对分离后的二值图像做几何分析。3.1 为什么不用 RGB 直接做分割RGB 三个通道是线性叠加的亮度一变三个值跟着一起变。金属工件、彩色标贴、塑料外壳这些常见被检物在不同光源下同一像素的 RGB 分布差异很大直接拿cv2.inRange在 RGB 空间设阈值几乎找不到一个稳定的区间。HSV 把色相H、饱和度S、明度V拆开逻辑上更接近人眼感知先锁定目标固有的色相区间S 和 V 放宽这样能规避大部分亮度波动的影响。OpenCV 里转 HSV 有一个关键细节结果的 H 范围是 0~179不是 0~360S 和 V 是 0~255。用惯了 PIL 或者 Matplotlib 的人特别容易在这里栽跟头明明在别的库调好的颜色范围换到 OpenCV 全偏了。常用目标颜色在 OpenCV 8-bit HSV 下的参考范围如下目标颜色H 范围S 范围V 范围红色系0~10 / 156~17970~25550~255绿色系35~7760~25550~255蓝色系100~12470~25550~255黄色系20~3480~25560~255橙色系11~1980~25560~255提示红色在 HSV 空间里跨了 H0 和 H180 两端工程上通常做两次inRange再用cv2.bitwise_or合并掩膜只写一组阈值会漏掉一半目标。3.2 hsv.py 的调参思路先把阈值试出来再写进配置直接拍脑袋定 HSV 阈值不现实常见做法是写一个滑条脚本实时观察掩膜变化。hsv.py 的逻辑大致是读一张现场图转 HSV用六个 createTrackbar 分别控制 H/S/V 的上下限循环里实时生成掩膜并显示。import cv2 import numpy as np img cv2.imread(Y1.JPG) hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) def nothing(x): pass cv2.namedWindow(mask) cv2.createTrackbar(H_min, mask, 0, 179, nothing) cv2.createTrackbar(H_max, mask, 179, 179, nothing) cv2.createTrackbar(S_min, mask, 60, 255, nothing) cv2.createTrackbar(S_max, mask, 255, 255, nothing) cv2.createTrackbar(V_min, mask, 60, 255, nothing) cv2.createTrackbar(V_max, mask, 255, 255, nothing) while True: h_min cv2.getTrackbarPos(H_min, mask) h_max cv2.getTrackbarPos(H_max, mask) s_min cv2.getTrackbarPos(S_min, mask) s_max cv2.getTrackbarPos(S_max, mask) v_min cv2.getTrackbarPos(V_min, mask) v_max cv2.getTrackbarPos(V_max, mask) lower np.array([h_min, s_min, v_min]) upper np.array([h_max, s_max, v_max]) mask cv2.inRange(hsv, lower, upper) cv2.imshow(mask, mask) if cv2.waitKey(30) 0xFF ord(q): break cv2.destroyAllWindows()这里每个cv2.createTrackbar都绑定到名为mask的窗口getTrackbarPos实时读取滑块位置构建lower和upper后调用cv2.inRange。返回的 mask 是单通道 0/255 图像255 的区域表示命中颜色区间。我调试时的顺序一般是先把 H 放开到全范围只收 S 和 V 的下限把背景大体滤掉然后慢慢缩窄 H 范围圈定目标色相最后微调 S 和 V让目标区域的白块最完整、背景噪声最少。调好的数值抄进扫描区配置文件hsv.py 的任务就算完成了。这里顺带说一个容易误判的地方inRange是严格闭区间匹配金属表面如果有反光高光区域的 S 值会骤降导致目标内部出现黑色空洞轮廓被拆成两截。解决办法不是无限放宽 V 上限而是交给第 4 章形态学闭运算去处理先看掩膜形态再决定怎么改阈值。4. 从读图到输出坐标test.py 的完整定位流程test.py 是所有检测逻辑的落地点也是把“目标定位识别”这六个字变成实际输出的地方。它做的事情可以拆成一条清晰的流水线。4.1 主流程拆解一次完整的定位链路是读入测试图按扫描区配置文件裁剪 ROIROI 转 HSV做 inRange 生成掩膜形态学闭运算填充目标内部孔洞cv2.findContours提取轮廓按面积、宽高比过滤噪声用boundingRect得到矩形框计算目标中心在原图画框并打印坐标。流程本身不难难在每一步的取舍。拿裁剪这一步举例扫描区的作用是“只让算法看该看的地方”。四个扫描区文本对应四个不同的区域定义避免整幅图像里的灯架、线缆、地面标记干扰检测结果。这比一上来就做全图检测要省事得多也是工业视觉里最常用的先验约束毕竟大多数产线场景下目标的运动范围是有限的。4.2 核心实现与参数说明一个与 MVision 风格一致的 test.py 核心逻辑如下读取 Y1.JPG 和扫描区A.txt 完成定位import cv2 import numpy as np def load_zone(path): 读取扫描区配置返回 ROI 和 HSV 阈值 cfg {} with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if not line or line.startswith(#): continue key, _, val line.partition() cfg[key.strip()] int(val.strip()) return cfg def detect_zone(img, cfg): 在 ROI 内做 HSV 分割 轮廓过滤返回目标坐标 x, y, w, h cfg[x], cfg[y], cfg[width], cfg[height] roi img[y:yh, x:xw] hsv cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) lower np.array([cfg[h_min], cfg[s_min], cfg[v_min]]) upper np.array([cfg[h_max], cfg[s_max], cfg[v_max]]) mask cv2.inRange(hsv, lower, upper) kernel cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations2) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) results [] for cnt in contours: area cv2.contourArea(cnt) if area cfg.get(min_area, 200): continue bx, by, bw, bh cv2.boundingRect(cnt) ratio bw / max(bh, 1) if not (cfg.get(min_ratio, 0.3) ratio cfg.get(max_ratio, 3.0)): continue cx x bx bw // 2 cy y by bh // 2 results.append((cx, cy, bw, bh)) cv2.rectangle(img, (x bx, y by), (x bx bw, y by bh), (0, 0, 255), 2) return img, results if __name__ __main__: cfg load_zone(扫描区A.txt) img cv2.imread(Y1.JPG) out, objs detect_zone(img, cfg) for obj in objs: print(目标中心: (%d, %d), 尺寸: %dx%d % (obj[0], obj[1], obj[2], obj[3])) cv2.imwrite(result_Y1.jpg, out)代码的逻辑分成两层load_zone负责把 txt 配置解析成字典跳过#注释行detect_zone负责核心的检测流程。注意findContours是在 roi 裁剪图上做的得到的bx, by是 ROI 内的局部坐标所以画框和计算中心时都必须加回x, y偏移量。很多新手把框画到原图上位置对不上基本都是漏了这一步。再看几个关键参数对结果的影响参数含义调整建议x, y, width, heightROI 左上角坐标和宽高现场标定给出范围略大于目标活动区h_min / h_max色相下限/上限从 hsv.py 滑条结果拷贝s_min / s_max饱和度区间背景偏灰暗就降低下限v_min / v_max明度区间反光严重时把 v_max 降到 220 左右min_area最小轮廓面积过滤灰尘和噪点一般 100~500min_ratio / max_ratio外接矩形宽高比范围按目标真实长宽比留 20% 余量形态学闭运算MORPH_CLOSE的作用是先膨胀再腐蚀能把目标内部因为反光或纹理产生的小孔洞填上让轮廓更完整。核大小从 5x5 起调如果目标在一个 ROI 内被拆成多个碎块优先加大核和迭代次数而不是改阈值。4.3 负样本回归cat.jpg 的用途工程里专门放了一张 cat.jpg它的意义是验证误检率。拿一张不包含目标的图跑同一套配置如果 results 里还有结果说明当前阈值或面积过滤放得太宽。我一般会把这种图固定作为回归样本每次调完参数先拿目标图确认能检测到再拿 cat.jpg 确认不会误检。两个条件同时满足参数才算合格这也是 cat.jpg 真正的作用。5. 扫描区配置与现场快速调优四个扫描区文本是这套工程里最有工程价值的部分它把 ROI 坐标、HSV 阈值、面积和宽高比过滤参数全部外置。换一个工位、换一种光照只改 txt不重新编译、不编辑代码。5.1 配置文件格式与切换机制扫描区配置一般写成键值对支持注释行# 扫描区A: 基准定位区 x360 y240 width420 height320 h_min35 h_max77 s_min90 s_max255 v_min60 v_max255 min_area200 min_ratio0.4 max_ratio2.6load_zone函数解析后与 test.py 形成映射。到了现场我习惯一个扫描区对应一个产品型号切换时用命令行参数指定配置文件python test.py --image Y1.JPG --zone 扫描区B.txt5.2 三个现场调优技巧第一先微调 V 再动 H。光照变化首先体现在 V 通道阴影影响下把 v_min 从 60 升到 80往往就能滤掉背景暗部不需要重调色相。第二掩膜孔洞多时优先加大闭运算核。前文示例用的是 5x5 的 MORPH_RECT 迭代两次金属反光严重的面可以换成 7x7 迭代三次。第三多目标计数要注意RETR_EXTERNAL只取外轮廓两个目标一旦粘连就会合并成一个结果。场景里目标互不接触时外轮廓更干净如果会粘连改成RETR_LIST再按面积二次拆分。调参完成后还有一个验证习惯值得坚持用 hsv.py 重新打开现场图把扫描区载入后只显示 ROI 内的掩膜检查目标区域的白块是否完整、是否有不该白的区域被放进来。这一步比连续跑十几张测试图更能快速定位问题是出在阈值还是出在形态学处理。python hsv.py --image Y1.JPG --zone 扫描区A.txt本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门