资讯详情

OpenCV C++案例实战二十三《网孔检测》

OpenCV C 案例实战23《网络检测》

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


前言

前段时间有粉丝私信我,给我发了一张图片,如下图所示: 请添加图片描述 把他的原话贴在这里。 根据他给出的图片分析,图片被屏蔽,因此不能简单地进行二值化,然后提取图像轮廓以找到结果。因此,我想知道如何去除这些屏蔽(即图像修复)。从图像中可以看出,屏蔽是一条黄线,所以我想知道它是否可以使用hsv从色彩空间中提取黄色,然后获得二值掩模图像,最后修复原图。接下来,让我们来看看如何一步一步地实现它。

一、HSV通道转换

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

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

结果如图所示:

二、图像修复

关于图像修复的相关知识可以参考我之前的博文。这里就不细说了。 OpenCV C 案例实战十四《图像修复》 OpenCV C 案例实战十七《图像去水印》

我们得到了上面的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、通过轮廓提取定位网孔位置。 以上就是我对该案例的构思以及实现方法,如果大家有更好的算法可以实现,欢迎交流学习。。。

标签: mo高压共轨传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

 锐单商城 - 一站式电子元器件采购平台  

 深圳锐单电子有限公司