对某工业产品进行缺陷检测,图像示例如下所示,其缺陷为图中的大面积黑色区域
上图为灰度图,由网格状排列黑点和大面积的黑点区域(即缺陷)组成,具体缺陷类型为粘连,其形态学特征为 连通域面积较大。
查找图中缺陷(大面积的黑点)算法的核心思想为:遍历所有的轮廓,根据面积判断缺陷,当连通域面积大于一定的值(面积比普通的黑点大),即判断为缺陷,并在原图上标出缺陷轮廓。
1、读取图像、修改尺寸并进行灰度化处理 【图像路径要用双斜杠】
2、对图片进行全局2值化处理 【二值化阈值要适度调整;因为目标连通域是黑色,故进行颜色翻转】
3、对图形进行腐蚀 【使图形变细,连通域断开,避免误检】
4、遍历连通域,按面积筛选缺陷 【findContours:查找所有连通域;aero:计算特定连通域的面积 ;drawContours:绘制特定连通域】
读取图片后将图片修改成合适的大小,这样方便看到展示的效果。读取图片时要注意读取为彩色后,因为最后的缺陷检测出来后要用红色线条标记在原图上。
Mat img = imread("C:\\Users\\aaa\\Desktop\\缺陷检测.png");//注意文件名之间用双//
Mat img2;
resize(img, img2, { 600,400 });//将原图修改为600*400的图片
运用cvtColor函数将BGR颜色空间的图片(img2)转换为GRAY(灰度图),结果保存到gray_mat。
Mat gray_mat;
cvtColor(img2, gray_mat, cv::COLOR_BGR2GRAY);
imshow("01. 灰度图", gray_mat);
代码运行效果如下所示:
运用threshold函数将图片gray_mat进行全局二值化,结果存入bin_mat图片中。二值化阈值要根据不同的图片适度调整,也可以使用大津法进行全局二值化(THRESH_OTSU),亦可使用局部二值化方法。
Mat bin_mat;
threshold(gray_mat, bin_mat, 128, 255, THRESH_BINARY);
//threshold(gray_mat, bin_mat, 0, 255, THRESH_OTSU);//大津法
//adaptiveThreshold(img, ez_mat, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 35, 10);//局部二值化方法,自适应阈值
imshow("02.2值图", bin_mat);
因为二值化后目标连通域是黑色,故进行颜色翻转,方便下一步的操作。代码如下图所示:
Mat inv_mat;
inv_mat = 255 - bin_mat;
imshow("03.颜色翻转", inv_mat);
颜色翻转之后,二值化后黑色的大面积连通区域(缺陷)就变为了白色。
使图形变细,连通域断开,避免误检(因为可能潜在边缘区域粘连);腐蚀操作的结构元素(element )的大小要按照实际效果进行调整。
//定义一个3*3的矩形元素
Mat erode_mat;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
erode(inv_mat, erode_mat, element);//用element进行腐蚀
imshow("04.腐蚀", erode_mat);
遍历所有连通域,按面积筛选缺陷 。运用findContours函数查找指定变量(erode_mat)的所有连通域,结果存在contours里面;运用aero函数计算特定连通域(contours[i])的面积 ;当连通域面积大于指定值(1000)时运用drawContours函数绘制特定连通域(contours,i)到img2(修改尺寸后的彩色图)上。
//查找erode_mat的轮廓,结果存在contours里面
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(erode_mat, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//遍历所有轮廓,绘制缺陷轮廓
for (int i = 0; i < contours.size(); i++)
{
int aero=contourArea(contours[i]);//计算轮廓面积
if (aero > 1000) {
//绘制轮廓
drawContours(img2, contours, i, Scalar(0,0,255), -1, 8, hierarchy);
cout << i << " aero:" << aero<<endl;
}
}
imshow("05.Contours Image", img2); //轮廓
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
/*进行基本缺陷检测,具体缺陷类型为粘连,具体形态学特征为 连通域面积较大
核心思想: 遍历所有的轮廓,根据面积判断缺陷
基本步骤:
1、读取图像、修改尺寸并进行灰度化处理 【图像路径要用双斜杠】
2、对图片进行全局2值化处理 【二值化阈值要适度调整;因为目标连通域是黑色,故进行颜色翻转】
3、对图形进行腐蚀 【使图形变细,连通域断开,避免误检】
4、遍历连通域,按面积筛选缺陷 【findContours:查找所有连通域;
aero:计算特定连同域的面积 ;drawContours:绘制特定连通域】
*/
Mat img = imread("C:\\Users\\aaa\\Desktop\\缺陷检测.png");
Mat img2;
resize(img, img2, { 600,400 });
imshow("0修改尺寸", img2);
Mat gray_mat;
cvtColor(img2, gray_mat, cv::COLOR_BGR2GRAY);
imshow("01. 灰度图", gray_mat);
Mat bin_mat;
threshold(gray_mat, bin_mat, 128, 255, THRESH_BINARY);
imshow("02.2值图", bin_mat);
Mat inv_mat;
inv_mat = 255 - bin_mat;
imshow("03.颜色翻转", inv_mat);
//定义一个3*3的矩形元素
Mat erode_mat;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
erode(inv_mat, erode_mat, element);//用element进行腐蚀
imshow("04.腐蚀", erode_mat);
//查找erode_mat的轮廓,结果存在contours里面
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(erode_mat, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());
//遍历所有轮廓,绘制缺陷轮廓
for (int i = 0; i < contours.size(); i++)
{
int aero=contourArea(contours[i]);//计算轮廓面积
if (aero > 1000) {
//绘制轮廓
drawContours(img2, contours, i, Scalar(0,0,255), -1, 8, hierarchy);
cout << i << " aero:" << aero<<endl;
}
}
imshow("05.Contours Image", img2); //轮廓
waitKey(0);
return 0;
}