Práctica de caso treinta y tres de OpenCV C ++ "Detección de defectos"


Prefacio

Este caso utilizará OpenCV C++ para detectar defectos de impresión de PCB. En la actualidad, los algoritmos de detección de defectos se pueden dividir en dos categorías principales:
una: detección de defectos basada en la coincidencia de plantillas;
dos: detección de defectos basada en el aprendizaje profundo, que utiliza principalmente la detección de objetivos para identificar piezas defectuosas.
El algoritmo de este artículo se basa principalmente en un algoritmo de coincidencia de plantillas para la detección de defectos. Consulte el artículo "Algoritmo de detección de defectos de etiquetas de impresión basado en un modelo de diferencia" para reproducir el algoritmo. Los amigos interesados ​​pueden leer el artículo original.
Insertar descripción de la imagen aquí

1. Demostración de resultados

Insertar descripción de la imagen aquí

2. Algoritmo de detección de defectos

2.1 Múltiples imágenes de plantilla

Las imágenes de etiquetas calificadas se recopilan a través de cámaras industriales como un conjunto de datos de entrenamiento para el modelo de diferencia. Una de las imágenes de etiqueta calificadas se selecciona para realizar operaciones de suavizado gaussiano, erosión en escala de grises y expansión en escala de grises para obtener una imagen de plantilla multivariada para entrenar el modelo de diferencia.
Convolucione la imagen calificada f (x, y) con el filtro del núcleo gaussiano para obtener la imagen suave gaussiana f1 (x, y). Construya un elemento estructural rectangular con un tamaño de 11 × 11, realice una operación de corrosión en escala de grises en la imagen de etiqueta calificada y obtenga una imagen de corrosión en escala de grises f2 (x, y). Luego construya un elemento estructural rectangular de 13 × 13, realice una operación de expansión en escala de grises en la imagen de etiqueta calificada [3] y obtenga la imagen de expansión en escala de grises f3 (x, y).

2.2 Modelo de diferencia de entrenamiento

Las imágenes de plantilla multivariante f1 (x, y), f 2 (x, y) y f 3 (x, y) se utilizan como conjuntos de datos de entrenamiento para entrenar el modelo de diferencias. Calcule el promedio y la desviación estándar de los píxeles en las mismas coordenadas de todas las imágenes [4] para obtener la imagen promedio F (x, y):
Insertar descripción de la imagen aquí

Imagen de desviación estándar V(x,y):
Insertar descripción de la imagen aquí

En este artículo, F (x, y) y V (x, y) son las imágenes estándar y las imágenes de diferencia en el proceso de entrenamiento del modelo de diferencia.

Para adaptar el modelo de diferencia ideal al rango de error del proceso normal, se agrega el parámetro umbral relativo VarThreshold=[bu, bl]. Entre ellos, bu es el umbral relativo del límite superior y bl es el umbral relativo del límite inferior. como se muestra en la imagen 2. Luego, las dos imágenes de umbral T u, l (x, y) se calculan de la siguiente manera:
imagen de umbral brillante: Tu (x, y) = F (x, y) + bu* V (x, y)
imagen de umbral oscura: Tl (x,y)=F(x,y)-bl* V(x,y)

Compare el valor de gris entre píxeles entre la imagen registrada c(x, y) y la imagen umbral Tu, l(x, y) del modelo de diferencia. Cuando se cumplen las siguientes condiciones, se detecta. al área defectuosa.
c(x,y)>Tu(x,y)∨c(x,y)<T l(x,y)

3. Registro de imágenes

Como se muestra en la imagen es la imagen de la plantilla.

Como se muestra en la figura, es la imagen que se va a detectar, debemos realizar el registro de la imagen entre la imagen que se va a detectar y la imagen de plantilla. Aquí utilizo la transformación afín de imagen para corregir las dos imágenes. No entraré en detalles sobre la corrección de imágenes. Puede consultar la publicación de mi blog Estudio de caso 4 de OpenCV C++ "Corrección de perspectiva de imagen" . Vaya directamente al código aquí
Insertar descripción de la imagen aquí

3.1 Código fuente de la función

//图像定位矫正
bool ImageLocal(cv::Mat srcImg, cv::Mat& warpImg, Point2f SrcAffinePts[])
{
    
    
	Mat grayImg;
	if (srcImg.channels() != 1)
	{
    
    
		cvtColor(srcImg, grayImg, COLOR_BGR2GRAY);
	}
	else
	{
    
    
		grayImg = srcImg.clone();
	}

	Mat blurImg;
	medianBlur(grayImg, blurImg, 5);

	Mat binImg;
	threshold(blurImg, binImg, 10, 255, THRESH_BINARY);
	//namedWindow("binImg", WINDOW_NORMAL);
	//imshow("binImg", binImg);

	vector<vector<Point>>contours;
	findContours(binImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
	RotatedRect bRect;
	for (int cnt = 0; cnt < contours.size(); cnt++)
	{
    
    
		double area = contourArea(contours[cnt]);
		if (area > 1000)
		{
    
    
			bRect = minAreaRect(contours[cnt]);
		}
	}

	if (bRect.size.empty())return false;//如果没有找到最小外接矩形,返回false

	//找到最小外接矩形四个顶点
	Point2f srcPoints[4];
	bRect.points(srcPoints);
	//for (int i = 0; i < 4; i++)
	//{
    
    
	//	line(srcImg, srcPoints[i], srcPoints[(i + 1) % 4], Scalar(0, 255, 0), 3);
	//}

	//将四个点按照左上、右上、右下、左下进行区分
	int TL, TR, BR, BL;
	double addmax = 0.0, addmin = 999.9, submax = 0.0, submin = 999.9;
	for (int i = 0; i < 4; i++)
	{
    
    
		double addval = srcPoints[i].x + srcPoints[i].y;
		double subval = srcPoints[i].x - srcPoints[i].y;
		if (addval > addmax)
		{
    
    
			addmax = addval;
			BR = i;
		}
		if (addval < addmin)
		{
    
    
			addmin = addval;
			TL = i;
		}
		if (subval > submax)
		{
    
    
			submax = subval;
			TR = i;
		}
		if (subval < submin)
		{
    
    
			submin = subval;
			BL = i;
		}
	}

	double LeftHeight = EuDis(srcPoints[TL], srcPoints[BL]);
	double RightHeight = EuDis(srcPoints[TR], srcPoints[BR]);
	double MaxHeight = max(LeftHeight, RightHeight);

	double UpWidth = EuDis(srcPoints[TL], srcPoints[TR]);
	double DownWidth = EuDis(srcPoints[BL], srcPoints[BR]);
	double MaxWidth = max(UpWidth, DownWidth);

	//这里使用的顺序是左上、右上、右下、左下顺时针顺序。SrcAffinePts、DstAffinePts要一一对应
	SrcAffinePts[0] = Point2f(srcPoints[TL]);
	SrcAffinePts[1] = Point2f(srcPoints[TR]);
	SrcAffinePts[2] = Point2f(srcPoints[BR]);
	SrcAffinePts[3] = Point2f(srcPoints[BL]);
	Point2f DstAffinePts[4] = {
    
     Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };

	Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);

	warpPerspective(srcImg, warpImg, M, Size(MaxWidth, MaxHeight), 1, 0, Scalar::all(0));

	return true;
}

3.1 Efectos funcionales

Insertar descripción de la imagen aquí

4. Múltiples imágenes de plantilla

En el código fuente a continuación se reproduce cómo calcular la imagen media, la imagen de diferencia y las imágenes de umbral brillante y oscuro. Lea el código fuente para obtener más detalles.

4.1 Código fuente de la función

//计算均值图像
void meanImage(cv::Mat gaussianImg, cv::Mat erodeImg, cv::Mat dilateImg, cv::Mat& meanImg)
{
    
    
	meanImg = Mat::zeros(gaussianImg.size(), CV_8U);
	for (int i = 0; i < gaussianImg.rows; i++)
	{
    
    
		uchar* gData = gaussianImg.ptr<uchar>(i);
		uchar* eData = erodeImg.ptr<uchar>(i);
		uchar* dData = dilateImg.ptr<uchar>(i);
		uchar* mData = meanImg.ptr<uchar>(i);

		for (int j = 0; j < gaussianImg.cols; j++)
		{
    
    
			mData[j] = (gData[j] + eData[j] + dData[j]) / 3;
		}
	}
}


//计算差异图像
void diffImage(cv::Mat gaussianImg, cv::Mat erodeImg, cv::Mat dilateImg, cv::Mat meanImg, cv::Mat& diffImg)
{
    
    
	diffImg = Mat::zeros(gaussianImg.size(), CV_8U);
	for (int i = 0; i < gaussianImg.rows; i++)
	{
    
    
		uchar* gData = gaussianImg.ptr<uchar>(i);
		uchar* eData = erodeImg.ptr<uchar>(i);
		uchar* dData = dilateImg.ptr<uchar>(i);
		uchar* mData = meanImg.ptr<uchar>(i);
		uchar* Data = diffImg.ptr<uchar>(i);

		for (int j = 0; j < gaussianImg.cols; j++)
		{
    
    
			Data[j] = sqrt(powf((gData[j] - mData[j]), 2) + powf((eData[j] - mData[j]), 2) + powf((dData[j] - mData[j]), 2) / 3.0);
		}
	}
}


//计算亮、暗阈值图像
void threshImg(cv::Mat meanImg, cv::Mat diffImg,cv::Mat &LightImg,cv::Mat& DarkImg)
{
    
    
	double bu = 1.2;
	double bl = 0.8;

	Mat mul_bu, mul_bl;
	multiply(diffImg, bu, mul_bu);
	multiply(diffImg, bl, mul_bl);

	LightImg = Mat::zeros(meanImg.size(), CV_8U);
	DarkImg = Mat::zeros(meanImg.size(), CV_8U);

	for (int i = 0; i < meanImg.rows; i++)
	{
    
    
		uchar* mData = meanImg.ptr<uchar>(i);
		uchar* dData = diffImg.ptr<uchar>(i);
		uchar* lData = LightImg.ptr<uchar>(i);
		uchar* DData = DarkImg.ptr<uchar>(i);
		uchar* buData = mul_bu.ptr<uchar>(i);
		uchar* blData = mul_bl.ptr<uchar>(i);

		for (int j = 0; j < meanImg.cols; j++)
		{
    
    
			lData[j] = saturate_cast<uchar>(mData[j] + buData[j]);
			DData[j] = saturate_cast<uchar>(mData[j] - blData[j]);
		}
	}
}

La siguiente imagen es una imagen de umbral brillante.
Insertar descripción de la imagen aquí

La siguiente imagen es una imagen de umbral oscuro.
Insertar descripción de la imagen aquí

5. Detección de defectos

Arriba, calculamos las imágenes de umbral brillante y oscura de la plantilla, principalmente comparándolas con los valores de gris de las dos imágenes para determinar la pieza defectuosa.
Insertar descripción de la imagen aquí
Como se muestra en la figura: compare el valor de gris entre píxeles entre la imagen registrada c (x, y) y la imagen umbral Tu, l (x, y) del modelo de diferencia. Cuando se cumplen las siguientes condiciones, cuál es el valor detectado zona defectuosa.
c(x,y)>Tu(x,y)∨c(x,y)<T l(x,y)

Dado que los defectos extraídos en este momento se basan en una corrección afín, si es necesario mostrar los resultados en la imagen original, los resultados de la detección deben transformarse inversamente. Lea el código fuente para obtener más detalles.

5.1 Código fuente de la función

//缺陷检测
void DetectImg(cv::Mat warpImg,cv::Mat LightImg, cv::Mat DarkImg, Point2f SrcAffinePts[],cv::Mat decImg, cv::Mat& showImg)
{
    
    
	int th = 10;//容差阈值

	Mat resImg = Mat::zeros(warpImg.size(), CV_8U);
	for (int i = 0; i < warpImg.rows; i++)
	{
    
    
		uchar* sData = warpImg.ptr<uchar>(i);
		uchar* lData = LightImg.ptr<uchar>(i);
		uchar* dData = DarkImg.ptr<uchar>(i);
		uchar* rData = resImg.ptr<uchar>(i);

		for (int j = 0; j < warpImg.cols; j++)
		{
    
    
			//识别缺陷
			if ((sData[j]-th) > lData[j]||(sData[j]+th) < dData[j])
			{
    
    
				rData[j] = 255;
			}
		}
	}

	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	morphologyEx(resImg, resImg, MORPH_OPEN, kernel);

	kernel = getStructuringElement(MORPH_RECT, Size(7, 7));
	dilate(resImg, resImg, kernel);

	//namedWindow("resImg", WINDOW_NORMAL);
	//imshow("resImg", resImg);

	//绘制缺陷结果
	vector<vector<Point>>contours;
	findContours(resImg, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
	for (int t = 0; t < contours.size(); t++)
	{
    
    
		if (contourArea(contours[t]) > 50)
		{
    
    
			Rect rect = boundingRect(contours[t]);
			rectangle(showImg, rect, Scalar(0, 0, 255), 2);
		}
	}

	//将结果反变换回原图像
	Point2f DstAffinePts[4] = {
    
     Point2f(0,0),Point2f(decImg.cols,0),Point2f(decImg.cols,decImg.rows),Point2f(0,decImg.rows) };

	Mat M = getPerspectiveTransform( DstAffinePts, SrcAffinePts);

	warpPerspective(showImg, showImg, M, decImg.size(), 1, 0, Scalar::all(0));
}

6. Demostración de efectos

1

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
Como se muestra en el efecto anterior, en comparación con la imagen de plantilla, básicamente se detectan todos los defectos en la imagen que se va a probar y hay muy pocas detecciones falsas. Cuando se aplica a la detección de diferentes objetos, debe realizar ajustes de parámetros ligeramente más pequeños en función de sus propios datos de imagen. Aquí solo les proporciono una idea de algoritmo, ¡y todos son bienvenidos a comunicarse y aprender! ! !


Resumir

Este artículo utiliza OpenCV C ++ para detectar defectos de impresión de PCB. Las operaciones principales son las siguientes.
1. Realice una transformación afín en la imagen y regístrela con la imagen de plantilla.
2. Calcule la imagen de diferencia para obtener las imágenes de umbral de luz y oscuridad basadas en la plantilla.
3. Compare la imagen que se detectará con las imágenes de umbral de luz y oscuridad. píxel por píxel y establece el umbral. Cualquier cosa que supere el umbral es un defecto.

Supongo que te gusta

Origin blog.csdn.net/Zero___Chen/article/details/132634606
Recomendado
Clasificación