C++ OpenCV网孔检测
Zero___Chen 人气:0前言
前段时间,有位粉丝私信我,给我发了一张图片,如下图所示:
在这里贴出他的原话。
从他给的图片分析,该图存在遮挡,所以不能简单的二值化,然后提取图像轮廓去寻找结果。所以,我就想如何去掉这些遮挡物(即图像修复)。从图像可知,该遮挡物是黄色的线,所以,我就想可否使用hsv色彩空间提取出黄色,然后得到二值掩模图像,最后对原图进行修复。接下来,就一起看看是如何一步步实现的吧。
一、HSV通道转换
通过hsv通道转换,可以提取出图像中的黄色分量。
//hsv颜色通道转换,提取图像中黄色线部分,生成掩膜图像 Mat hsv; cvtColor(src, hsv, COLOR_BGR2HSV); Mat mask; inRange(hsv, Scalar(10, 50, 255), Scalar(40, 255, 255), mask);
结果如图所示:
二、图像修复
关于图像修复的相关知识可以参考我之前的博文。这里就不细说了。
我们拿到上面的mask掩模图像,需要对其进行膨胀处理,使修复区域范围扩大。
//将生成的掩膜mask膨胀一下,使掩膜区域放大 Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9)); dilate(mask, mask, kernel);
接下来,需要对图像进行修复。这里我提供两种修复方法,一种是OpenCV提供的inpaint函数,一种是我自己写的。
2.1 OpenCV函数实现
//使用OpenCV自带的inpaint函数进行图像修复,得到目标图像 Mat inpaintImg; inpaint(src, mask, inpaintImg, 1, INPAINT_NS);
效果如图所示。
2.2 MyFunction
通过修改图像像素达到图像修复的效果。具体请看源码注释。
//自己写的算法,修改图像像素,完成图像修复 Mat canvas = Mat::zeros(src.size(), src.type()); int r = 1;//像素查找范围--表示在该像素点上下几行像素进行查找 for (int i = r; i < src.rows- r; i++) { for (int j = 0; j < src.cols; j++) { if (mask.at<uchar>(i, j) != 255) { //对于非掩膜区域,直接将原像素进行像素赋值 for (int c = 0; c < 3; c++) { canvas.at<Vec3b>(i, j)[c] = src.at<Vec3b>(i, j)[c]; } } else { //找到距离该掩膜像素点最近的非掩膜区域像素进行赋值 Point res = find_Nearest_Point(mask, i, j, r); for (int c = 0; c < 3; c++) { canvas.at<Vec3b>(i, j)[c] = src.at<Vec3b>(res.x, res.y)[c]; } } } }
效果如何所示
三、轮廓提取
接下来我们只需要对修复之后的图像进行轮廓提取就可以了。
//将修复之后的目标图像进行图像预处理,提取轮廓 Mat gray; cvtColor(canvas, gray, COLOR_BGR2GRAY); Mat gaussian; GaussianBlur(gray, gaussian, Size(3, 3), 0); Mat thresh; threshold(gaussian, thresh, 30, 255, THRESH_BINARY_INV); Mat kernel1 = getStructuringElement(MORPH_RECT, Size(3, 3)); morphologyEx(thresh, thresh, MORPH_OPEN, kernel); //namedWindow("thresh", WINDOW_NORMAL); //imshow("thresh", thresh); //轮廓提取 vector<vector<Point>>contours; findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); //经过面积,外接矩形特征筛选出目标区域 vector<vector<Point>>EffectiveConts; for (int i = 0; i < contours.size(); i++) { double area = contourArea(contours[i]); if (area>100) { Rect rect = boundingRect(contours[i]); if (double(rect.height) > 30 && double(rect.width) > 30) { EffectiveConts.push_back(contours[i]); } } }
四、效果显示
for (int i = 0; i < EffectiveConts.size(); i++) { //计算轮廓矩 Moments Mo = moments(EffectiveConts[i]); //计算质心--即插孔坐标 Point center = Point(Mo.m10 / Mo.m00, Mo.m01 / Mo.m00); //效果绘制 Rect rect = boundingRect(EffectiveConts[i]); rectangle(src, rect, Scalar(0, 255, 0), 5); circle(src, center, 3, Scalar(0, 0, 255), -1); }
如图为该案例最终效果。
五、源码
#include<opencv2/opencv.hpp> #include <iostream> #include<opencv2/photo.hpp> using namespace std; using namespace cv; double EuDis(Point pt1, Point pt2) { return sqrt(pow(pt1.x - pt2.x, 2) + pow(pt1.y - pt2.y, 2)); } Point find_Nearest_Point(Mat mask , int currentrow, int currentcol, int r) { double mindis = 100000.0; Point res(0,0); //查找该像素点上下r行像素,找到最接近该像素的非掩膜区域像素 for (int i = currentrow - r; i < currentrow + r; i++) { for (int j = 0; j < mask.cols; j++) { if (mask.at<uchar>(i, j) != 255) { //Point(currentrow, currentcol) 表示当前需要赋值的掩膜像素点 double dis = EuDis(Point(currentrow, currentcol), Point(i, j)); if (dis < mindis) { mindis = dis; res = Point(i, j); //目标像素点 } } } } return res; } int main() { Mat src = imread("test.jpg"); if (src.empty()) { cout << "No Image!" << endl; system("pause"); return -1; } //hsv颜色通道转换,提取图像中黄色线部分,生成掩膜图像 Mat hsv; cvtColor(src, hsv, COLOR_BGR2HSV); Mat mask; inRange(hsv, Scalar(10, 50, 255), Scalar(40, 255, 255), mask); //将生成的掩膜mask膨胀一下,使掩膜区域放大 Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9)); dilate(mask, mask, kernel); //使用OpenCV自带的inpaint函数进行图像修复,得到目标图像 //Mat inpaintImg; //inpaint(src, mask, inpaintImg, 1, INPAINT_NS); //namedWindow("inpaintImg", WINDOW_NORMAL); //imshow("inpaintImg", inpaintImg); //自己写的算法,修改图像像素,完成图像修复 Mat canvas = Mat::zeros(src.size(), src.type()); int r = 1;//像素查找范围--表示在该像素点上下几行像素进行查找 for (int i = r; i < src.rows- r; i++) { for (int j = 0; j < src.cols; j++) { if (mask.at<uchar>(i, j) != 255) { //对于非掩膜区域,直接将原像素进行像素赋值 for (int c = 0; c < 3; c++) { canvas.at<Vec3b>(i, j)[c] = src.at<Vec3b>(i, j)[c]; } } else { //找到距离该掩膜像素点最近的非掩膜区域像素进行赋值 Point res = find_Nearest_Point(mask, i, j, r); for (int c = 0; c < 3; c++) { canvas.at<Vec3b>(i, j)[c] = src.at<Vec3b>(res.x, res.y)[c]; } } } } //namedWindow("canvas", WINDOW_NORMAL); //imshow("canvas", canvas); //将修复之后的目标图像进行图像预处理,提取轮廓 Mat gray; cvtColor(canvas, gray, COLOR_BGR2GRAY); Mat gaussian; GaussianBlur(gray, gaussian, Size(3, 3), 0); Mat thresh; threshold(gaussian, thresh, 30, 255, THRESH_BINARY_INV); Mat kernel1 = getStructuringElement(MORPH_RECT, Size(3, 3)); morphologyEx(thresh, thresh, MORPH_OPEN, kernel); //namedWindow("thresh", WINDOW_NORMAL); //imshow("thresh", thresh); //轮廓提取 vector<vector<Point>>contours; findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); //经过面积,外接矩形特征筛选出目标区域 vector<vector<Point>>EffectiveConts; for (int i = 0; i < contours.size(); i++) { double area = contourArea(contours[i]); if (area>100) { Rect rect = boundingRect(contours[i]); if (double(rect.height) > 30 && double(rect.width) > 30) { EffectiveConts.push_back(contours[i]); } } } for (int i = 0; i < EffectiveConts.size(); i++) { //计算轮廓矩 Moments Mo = moments(EffectiveConts[i]); //计算质心--即插孔坐标 Point center = Point(Mo.m10 / Mo.m00, Mo.m01 / Mo.m00); //效果绘制 Rect rect = boundingRect(EffectiveConts[i]); rectangle(src, rect, Scalar(0, 255, 0), 5); circle(src, center, 3, Scalar(0, 0, 255), -1); } namedWindow("src", WINDOW_NORMAL); imshow("src", src); waitKey(0); system("pause"); return 0; }
总结
本文使用OpenCV C++实现网孔检测,主要操作有以下几点。
1、hsv通道转换,提取出黄色分量,得到掩模图像。
2、利用掩模图像对原图进行图像修复。
3、通过轮廓提取定位网孔位置。
加载全部内容