Combate de proyecto OpenCV basado en C++: inspección óptica automática de piezas

Combate de proyecto OpenCV basado en C++: inspección óptica automática de piezas

1. Antecedentes

El fondo de la primera tarea es AOI (Inspección óptica automática)

El propósito más importante es: segmentar y clasificar el primer plano y los objetos;

Diagrama de escenario:

inserte la descripción de la imagen aquí

Cabe señalar que en la cinta transportadora de nueces, debe haber una luz frontal y una luz trasera, y solo cuando el objeto está iluminado se puede tomar una imagen clara;


2. Conocimientos básicos

En primer lugar, se divide en los siguientes pasos:

1. Supresión de ruido (preprocesamiento)

2. Eliminación de fondo (segmentación)

3. Binarización

4. Algoritmo de búsqueda de contorno y dominio conectado


  • Algoritmo de reducción de ruido:

Primero use el filtro mediano para filtrar el ruido de sal y pimienta, y luego use el filtro gaussiano para desenfocar el borde del objeto;

  • eliminación de fondo

    En primer lugar, existen dos esquemas para lograr la eliminación de fondo, a saber, la resta y la división;

inserte la descripción de la imagen aquí

  • Recuento de detección de gráfico conectado

    Primero, los tipos de dominios conectados se dividen en conectividad de 4 vías y conectividad de 8 vías:

inserte la descripción de la imagen aquí

Usando el algoritmo de detección de gráficos conectados, cada objeto que no está conectado se puede dividir en diferentes colores;


3. Implementación del código

1. Realice la visualización de múltiples ventanas

Si desea mostrar varias imágenes en una ventana, es decir, para realizar la operación de empalme de imágenes, puede ser más conveniente usar el código Python para implementarlo. El código C++ necesita definir una clase, y la escritura real es más engorrosa. ;

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

inserte la descripción de la imagen aquí


2. Procesamiento de reducción de ruido

Método de reducción de ruido utilizando filtro mediano + filtro gaussiano:

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. Eliminación de fondo

Dividido en dos formas, una para la resta, otra para la división;

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

inserte la descripción de la imagen aquí

Del gráfico de resultados, el método de división puede retener mejor la información de la parte blanca, por lo que se selecciona el método de división;


4. Realización de gráfico conexo

La imagen binarizada se divide en dominios conectados y se dibuja en el mapa Máscara con colores aleatorios;

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

inserte la descripción de la imagen aquí


5. Calcula el área del dominio conectado

OpenCV también viene con el cálculo del área de área:

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

inserte la descripción de la imagen aquí


6. Detección de contorno

Realizar la detección del contorno del objeto;

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

inserte la descripción de la imagen aquí

Como se puede ver en la figura de resultados anterior, los contornos detectados no incluyen contornos internos.Si desea detectar todos los contornos, debe cambiar el parámetro de tipo en la función findContours a RETR_LIST;

Cuatro Resumen

Los puntos técnicos involucrados en este proyecto son los siguientes:

  • Visualización de múltiples ventanas
  • eliminación de fondo
  • Realización de Gráfico Conectado
  • Detección de bordes de contorno

Y en el código C++ real, también implica conocimientos de alto nivel, como punteros inteligentes;

Como un proyecto de aterrizaje relativamente maduro en el campo visual, el proyecto de inspección de calidad industrial se implementa principalmente en base al aprendizaje profundo, pero si puede dominar algunos métodos de OpenCV, también puede desempeñar un papel en la optimización del proyecto;

Supongo que te gusta

Origin blog.csdn.net/weixin_40620310/article/details/126848699
Recomendado
Clasificación