返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++ OpenCV实战之零部件的自动光学检测
  • 484
分享到

C++ OpenCV实战之零部件的自动光学检测

2024-04-02 19:04:59 484人浏览 泡泡鱼
摘要

目录一、背景二、基础知识三、代码实现1、实现多窗口展示2、降噪处理3、背景去除4、连通图实现5、计算连通域面积6、轮廓检测四、总结一、背景 首先任务背景是AOI(自动光学检测) 最重

一、背景

首先任务背景是AOI(自动光学检测)

最重要的目的在于:将前景和物体进行分割与分类;

场景示意图:

需要注意,在螺母的传送带上,需要有前光和背光,给物体打光才能够拍摄清晰的图像;

二、基础知识

首先分为以下几步:

1、噪声抑制(预处理)

2、背景移除(分割)

3、二值化

4、连通域、轮廓查找算法

降噪算法

先使用中值滤波对椒盐噪声进行过滤,再使用高斯滤波对物体边缘进行模糊;

背景移除

首先有两种方案可以实现背景移除,也就是减法和除法;

连通图检测计数

首先连通域类型分为4路连通和8路连通:

使用连通图检测算法,可以将不连通的每个物体都用不同颜色划分出来;

三、代码实现

1、实现多窗口展示

如果想要多张图像展示在一个窗口中,也就是实现拼接图片的操作,使用python代码实现起来可能比较便捷,c++代码需要定义一个类,并且实际编写也比较繁琐;

class Display {
private:
	int cols, rows, width, height;
	String title;
	vector<String> win_names;
	vector<Mat> images;
	Mat canvas;
public:
	Display(String t, int c, int r, int flags) :title(t), cols(c), rows(r) {
		height = 1080;
		width = 1920;
		namedWindow(title, flags);
		canvas = Mat(height, width, CV_8UC3);
		imshow(title, canvas);
	}

	int add_window(String win_name, Mat image, bool flag = true) {
		win_names.push_back(win_name);
		images.push_back(image);
		if (flag) {
			draw();
		}
		return win_names.size();
	}
	
    // 实现删除窗口
	int delete_window(String win_name) {
		int index = 0;
		for (const auto& it : win_names) {
			if (it == win_name) break;
			index++;
		}
		win_names.erase(win_names.begin() + index);
		images.erase(images.begin() + index);

		return win_names.size();
	}

	void draw() {
		canvas.setTo(Scalar(20, 20, 20));
		int single_width = width / cols;
		int single_height = height / rows;
		int max_win = win_names.size() > cols * rows ? cols * rows : win_names.size();
		
		int i = 0;
		auto iw = win_names.begin();
		for (auto it = images.begin(); it != images.end()&&i<max_win; it++,i++,iw++) {
			String win_name = *iw;
			Mat img = *it;

			int x = (single_width) * (i % cols);
			int y = (single_height)*floor(i * 1.0 / cols);
			Rect mask(x, y, single_width, single_height);

			rectangle(canvas, mask, Scalar(255, 255, 255), 9);

			Mat resized_img;
			resize(img, resized_img, Size(single_width, single_height));

			Mat sub_canvas(canvas, mask);
			if (resized_img.channels() == 1) {
				cvtColor(resized_img, resized_img, COLOR_GRAY2BGR);
			}
			resized_img.copyTo(sub_canvas);
            putText(sub_canvas, win_name, Point(50, 50), FONT_HERSHEY_COMPLEX, 2, Scalar(0, 0, 255), 3, LINE_AA);
		}
		imshow(title, canvas);
	}
};

// 使用智能指针
shared_ptr<Display> multi_window;

int main(int arGC, char** argv)
{

	// 实现多窗口
	String total_path = "imgpath";
	String background_path = "imgpath";

	Mat abc = imread(total_path, 0);

	multi_window = make_shared<Display>("Review for all", 3, 2, WINDOW_NORMAL);
	multi_window->add_window("ABC", abc);
	multi_window->add_window("ABCC", abc);
    multi_window->delete_window("ABC");		// 也支持删除窗口
	multi_window->draw();

	waiTKEy(0);
	return 0;
}

2、降噪处理

采用中值滤波+高斯滤波结合的降噪方法:

Mat get_background(const Mat& bg){
    Mat img;
    medianBlur(bg,img,3);
    GaussianBlur(bg,img,Size(3,3),0);
    return img;
}

Mat smoothen_img(const Mat& noise_img){
    Mat img;
    medianBlur(noise_img,img,5);
    GaussianBlur(img,img,Size(3,3),0);
    return img;
}

3、背景去除

分为两种方式,一种为减法,一种为除法;

Mat remove_background_divide(Mat image, Mat background) {
	Mat tmp;
	Mat fg, bg;
	image.convertTo(fg, CV_32F);
	background.convertTo(bg, CV_32F);
	tmp = 1 - (fg / bg);
	tmp.convertTo(tmp, CV_8U, 255);
	return tmp;
}

Mat remove_background_minus(Mat image, Mat background) {
	return background - image;
}

从结果图上看,使用除法的方式能更好的保留白色部分的信息,因此选用除法的方式;

4、连通图实现

对二值化后的图像进行连通域划分,并且用随机颜色绘制到Mask图上;

void connection_check(Mat image) {
	Mat labels;
	int num = connectedComponents(image, labels);
	if (num <= 1) {
		cout << "No stuff detect!!" << endl;
		return;
	}
	else
	{
		cout << num << " objects detected!!" << endl;
	}

	Mat display = Mat::zeros(image.rows, image.cols, CV_8UC3);
	for (int i = 1; i < num; i++) {
		Mat mask = (labels == i);
		display.setTo(random_color_generator(seed), mask);
	}
	multi_window->add_window("Segment", display);
}

5、计算连通域面积

OpenCV中也自带了对面积区域的计算:

void connection_heavy_check(Mat image) {
    Mat labels, stats, centroids;
    int num =connectedComponentsWithStats(image, labels, stats, centroids);
    if (num <= 1) {
        cout << "No stuff detect!!" << endl;
        return;
    }
    else
    {
        cout << num << " objects detected!!" << endl;
    }
    Mat display = Mat::zeros(image.rows, image.cols, CV_8UC3);
    for (int i = 1; i < num; i++) {
        // 得到连通域的质心点
        Point2i pt(centroids.at<double>(i, 0), centroids.at<double>(i, 1));
        // 打印标签和连通域坐标和面积
        cout << "Stuff #" << i << ", Position: " << pt << " ,Area: " << stats.at<int>(i, CC_STAT_AREA) << endl;
        Mat mask = (labels == i);
        display.setTo(random_color_generator(seed), mask);
        stringstream ss;
        ss << stats.at<int>(i, CC_STAT_AREA);
        putText(display, ss.str(), pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 2);
    }
    multi_window->add_window("Segment more", display);
}

6、轮廓检测

实现对物体轮廓的检测;

void get_contour(Mat image) {
    vector<vector<Point>> contours;
    findContours(image, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    Mat display = Mat::zeros(image.rows, image.cols, CV_8UC3);
    if (contours.size() == 0) {
        cout << "No contour detect!!" << endl;
        return;
    }
    else
    {
        cout << contours.size() << " contour detected!!" << endl;
    }
    for (int i = 0; i < contours.size(); i++) {
        drawContours(display, contours, i, random_color_generator(seed), 2);
    }
    multi_window->add_window("CONTOURS", display);
}

从上结果图可知,检测到的轮廓并不包含内部轮廓,如果想检测所有轮廓应该将findContours函数中的类型参数改为RETR_LIST即可;

四、总结

本次项目中涉及的技术点如下:

  • 多窗口展示
  • 背景去除
  • 连通图的实现
  • 轮廓边缘检测

并且在实际的C++代码中,还涉及了智能指针等高阶知识;

工业质检项目作为视觉领域较为成熟的落地项目,其大部分都是基于深度学习的方式实现了,但如果能掌握一些OpenCV的方法,也可以在项目中起到优化效果的作用;

到此这篇关于C++ OpenCV实战之零部件的自动光学检测的文章就介绍到这了,更多相关C++ OpenCV光学检测内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++ OpenCV实战之零部件的自动光学检测

本文链接: https://lsjlt.com/news/167513.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++ OpenCV实战之零部件的自动光学检测
    目录一、背景二、基础知识三、代码实现1、实现多窗口展示2、降噪处理3、背景去除4、连通图实现5、计算连通域面积6、轮廓检测四、总结一、背景 首先任务背景是AOI(自动光学检测) 最重...
    99+
    2024-04-02
  • C++ OpenCV实战之车道检测
    目录前言一、获取车道ROI区域二、车道检测1.灰度、阈值2.获取非零像素点3.绘制车道线总结前言 本文将使用OpenCV C++ 进行车道检测。 一、获取车道ROI区域 原图如图所...
    99+
    2024-04-02
  • C++OpenCV实战之网孔检测的实现
    目录前言一、HSV通道转换二、图像修复2.1 OpenCV函数实现2.2 MyFunction三、轮廓提取四、效果显示五、源码总结前言 前段时间,有位粉丝私信我,给我发了一张图片,如...
    99+
    2024-04-02
  • C++OpenCV实战之标记点检测的实现
    在实际应用中,能够直接利用霍夫圆检测这些理想方法的应用场景是非常少的,更多的是利用拟合的办法去寻找圆形。 大致思路如下,首先先选择要处理的ROI部分,记录下该图的左上点在原图的坐标,...
    99+
    2024-04-02
  • Python实战之自动发送邮件的实现
    目录1.开启SMTP服务2.准备3.编写脚本4.小例子自动发送邮件能应用于许多场景,比如我想要知道股票策略中的股票池是否有实时的更新,这时候如果再拉一遍数据,跑一遍脚本,实在是太浪费...
    99+
    2024-04-02
  • Shell脚本实现自动检测修改最快的Ubuntu软件源
    每次装好Ubuntu,对于大多数用户来说,首先要做的事就是手动修改/etc/apt/sources.list文件,将里面的官方软件源地址更换为自己学校或者公司的软件源。当我们更换一个工作环境后,可能伴随着又...
    99+
    2022-06-04
    脚本 自动检测 最快
  • C#递归应用之实现JS文件的自动引用
    目录背景需求分析1、返回结果类型2、算法选择代码实现背景 两张表,分别是 :sys_tbl,和 sys_field,其中:sys_tbl 是系统所有表的信息,包含两个字段 :code...
    99+
    2023-03-11
    C#递归实现JS文件引用 C#递归 C# JS文件引用 C# 文件引用
  • 如何编写Shell脚本实现自动检测修改最快的Ubuntu软件源
    这篇文章主要讲解了“如何编写Shell脚本实现自动检测修改最快的Ubuntu软件源”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何编写Shell脚本实现自动检测修改最快的Ubuntu软件源...
    99+
    2023-06-09
  • C#递归应用之如何实现JS文件的自动引用
    这篇文章主要介绍了C#递归应用之如何实现JS文件的自动引用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#递归应用之如何实现JS文件的自动引用文章都会有所收获,下面我们一起来看看吧。背景两张表,分别是 :sy...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作