目录
  • 前言
  • 一、HSV通道转换
  • 二、图像修复
    • 2.1 OpenCV函数实现
    • 2.2 MyFunction
  • 三、轮廓提取
    • 四、效果显示
      • 五、源码
        • 总结

          前言

          前段时间,有位粉丝私信我,给我发了一张图片,如下图所示:

          C++ OpenCV实战之网孔检测的实现

          在这里贴出他的原话。

          C++ OpenCV实战之网孔检测的实现

          从他给的图片分析,该图存在遮挡,所以不能简单的二值化,然后提取图像轮廓去寻找结果。所以,我就想如何去掉这些遮挡物(即图像修复)。从图像可知,该遮挡物是黄色的线,所以,我就想可否使用hsv色彩空间提取出黄色,然后得到二值掩模图像,最后对原图进行修复。接下来,就一起看看是如何一步步实现的吧。

          一、HSV通道转换

          通过hsv通道转换,可以提取出图像中的黄色分量。

              //hsv颜色通道转换,提取图像中黄色线部分,生成掩膜图像
              Mat hsv;
              cvtColor(src, hsv, COLOR_BGR2HSV);
          
              Mat mask;
              inRange(hsv, Scalar(10, 50, 255), Scalar(40, 255, 255), mask);
          

          结果如图所示:

          C++ OpenCV实战之网孔检测的实现

          二、图像修复

          关于图像修复的相关知识可以参考我之前的博文。这里就不细说了。

          OpenCV C++案例实战十四《图像修复》

          OpenCV C++案例实战十七《图像去水印》

          我们拿到上面的mask掩模图像,需要对其进行膨胀处理,使修复区域范围扩大。

              //将生成的掩膜mask膨胀一下,使掩膜区域放大
              Mat kernel = getStructuringElement(MORPH_RECT, Size(9, 9));
              dilate(mask, mask, kernel);
          

          C++ OpenCV实战之网孔检测的实现

          接下来,需要对图像进行修复。这里我提供两种修复方法,一种是OpenCV提供的inpaint函数,一种是我自己写的。

          2.1 OpenCV函数实现

              //使用OpenCV自带的inpaint函数进行图像修复,得到目标图像
              Mat inpaintImg;
              inpaint(src, mask, inpaintImg, 1, INPAINT_NS);
          

          C++ OpenCV实战之网孔检测的实现

          效果如图所示。

          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];
                          }
                      }
                  }
              }
          

          C++ OpenCV实战之网孔检测的实现

          效果如何所示

          三、轮廓提取

          接下来我们只需要对修复之后的图像进行轮廓提取就可以了。

              //将修复之后的目标图像进行图像预处理,提取轮廓
              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);
              }
          

          C++ OpenCV实战之网孔检测的实现

          如图为该案例最终效果。

          五、源码

          #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、通过轮廓提取定位网孔位置。

          以上就是C++ OpenCV实战之网孔检测的实现的详细内容,更多关于C++ OpenCV网孔检测的资料请关注其它相关文章!

          声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。