Procesamiento de imágenes de desempañado de imágenes basado en la teoría anterior del canal oscuro

Deshacimiento de imágenes basado en la teoría anterior del canal oscuro


Por lo general, las imágenes en color incluyen tres canales, a saber, tres canales RGB, que también se pueden entender como una imagen y tres capas de RGB (colores primarios ópticos: rojo, verde y azul) del mismo tamaño apilados juntos, y la imagen se compone esencialmente de Píxeles, correspondientes a los tres colores RGB, cada color es una combinación de estos tres colores primarios, por ejemplo, el rojo es (255, 0, 0), el verde es (0, 255, 0) y el rosa es (255 , 192, 203), lo que significa que cada píxel de una imagen en color se representa de esta forma.

Entonces solemos referirnos al canal oscuro, es decir, en una imagen clara y sin niebla, excepto por el área del cielo (porque el área del cielo o el área blanca y la niebla están más cerca), al menos un valor de canal de cualquier píxel de área local es muy bajo, casi tiende a Casi cero. Entonces, cómo encontrar el canal oscuro, hemos hecho una descripción detallada en el código anterior, es decir, primero busque el valor mínimo de cada píxel en los tres canales y luego obtenga una imagen en escala de grises con el mismo tamaño que la imagen original. Luego use el filtro de valor mínimo para suavizar, es decir, en la imagen en escala de grises obtenida, tome una ventana rectangular de cierto tamaño con cada píxel como centro (este artículo es 15x15), y tome el valor de gris mínimo en la ventana rectangular en lugar del píxel central El valor de para obtener la imagen del canal oscuro correspondiente a la imagen original. (El valor mínimo debe calcularse dos veces en el proceso de búsqueda del canal oscuro)

Para mostrar la imagen del canal oscuro de manera más intuitiva, primero echemos un vistazo al resultado, es decir, el mapa de niebla y su mapa de canal oscuro correspondiente:

Inserte la descripción de la imagen aquí

¿Por qué calcular el canal oscuro? Los círculos académicos definen la imagen brumosa así:
Inserte la descripción de la imagen aquí

Imagen con niebla = imagen sin niebla * transmitancia + componente de luz atmosférica global (1-transmitancia)
Ahora necesitamos conocer la imagen con niebla, para encontrar la imagen sin niebla
escribimos J (x) en el lado izquierdo de la ecuación, entonces esta ecuación tiene Hay innumerables soluciones. Por lo tanto, necesitamos utilizar algunos conocimientos previos para restringir la ecuación. La fórmula original se puede escribir:
Inserte la descripción de la imagen aquí
Como se mencionó anteriormente, el superíndice C representa el significado de los tres canales de R / G / B. Primero, suponga que la transmitancia t (x) en cada ventana es una constante, defínela como ~ t (x), y se ha dado el valor de A, y luego calcule el valor mínimo dos veces en ambos lados de la ecuación (4) para obtener la siguiente ecuación:
Inserte la descripción de la imagen aquí
En la fórmula anterior, J es la imagen libre de turbidez a obtener De acuerdo con la teoría anterior del color primario oscuro antes mencionada:
Inserte la descripción de la imagen aquí
Por lo tanto, se puede deducir que
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
este es el valor estimado de transmitancia ~ t (x).

En la vida real, incluso si hace sol y hay nubes blancas, hay algunas partículas en el aire. Por lo tanto, aún puedes sentir la influencia de la niebla cuando miras objetos a lo lejos. Además, la existencia de niebla hace que los humanos sientan la existencia de profundidad de campo. Por lo tanto, es necesario Al desempañar se retiene un cierto grado de niebla, que puede corregirse introduciendo un factor entre [0,1] en la fórmula:

Inserte la descripción de la imagen aquí
En este artículo, doble w = 0,75;

Todas las inferencias anteriores suponen que se conoce el valor de la luz atmosférica global A. En la práctica, podemos utilizar el mapa de canales oscuros para obtener este valor a partir de la imagen de niebla. Los pasos específicos son los siguientes:

1) Tome los píxeles superiores al 0,1% de la imagen del canal oscuro según el brillo.

2) Entre estas posiciones, encuentre el valor correspondiente del punto con el brillo más alto en la imagen original de niebla I como el valor A.

En este punto, podemos restaurar la imagen sin niebla.

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
#include<vector>
#include <algorithm>

using namespace cv;
using namespace std;


Mat darkChannel(Mat src)//求暗通道
{
    
    
	Mat rgbmin = Mat::zeros(src.rows, src.cols, CV_8UC1);
	Mat dark = Mat::zeros(src.rows, src.cols, CV_8UC1);
	Vec3b intensity;


	//找出每个点三通道中最小的通道,建立rgbmin图
	for (int m = 0; m < src.rows; m++)
	{
    
    
		for (int n = 0; n < src.cols; n++)
		{
    
    
			intensity = src.at<Vec3b>(m, n);
			rgbmin.at<uchar>(m, n) = min(min(intensity.val[0], intensity.val[1]), intensity.val[2]);
		}
	}

	//模板尺寸
	int scale = 7;
	//cout << "Please enter the mask scale: " << endl;
	//cin >> scale;

	//边界扩充
	int radius = (scale - 1) / 2;
	//border是填充后的图像
	Mat border;
	//由于要求最小值,所以扩充的边界可以用复制边界填充,填充范围上下左右为radius
	copyMakeBorder(rgbmin, border, radius, radius, radius, radius, BORDER_REPLICATE);

	//最小值滤波
	for (int i = 0; i < src.cols; i++)
	{
    
    
		for (int j = 0; j < src.rows; j++)
		{
    
    
			//选取兴趣区域
			Mat roi;
			roi = border(Rect(i, j, scale, scale));

			//求兴趣区域的最小值
			double minVal = 0;
			double maxVal = 0;
			Point minLoc(0, 0);
			Point maxLoc(0, 0);
			minMaxLoc(roi, &minVal, &maxVal, &minLoc, &maxLoc, noArray());

			dark.at<uchar>(Point(i, j)) = (uchar)minVal;           //对填充后的图像以scale作为范围进行最小值滤波,得到的结果存dark
		}
	}
	return dark;
}


uchar light(vector<uchar> inputIamgeMax)//找出全局最亮的点作为估计大气光
{
    
    
	uchar maxA = 0;
	for (int i = 0; i < inputIamgeMax.size() - 1; i++)
	{
    
    

		if (maxA < inputIamgeMax[i + 1])
		{
    
    
			maxA = inputIamgeMax[i + 1];
		}
	}
	return maxA;
}


vector<uchar> calLight(Mat temp)
{
    
    
	Mat image;
	Mat dark = darkChannel(image);
	dark.copyTo(temp);
	vector<Point> darkMaxPoint;
	vector<uchar> inputMax;
	for (long i = 0; i < ((dark.rows * dark.cols) / 1000); i++)
	{
    
    
		double minVal = 0;
		double maxVal = 0;
		Point minLoc(0, 0);
		Point maxLoc(0, 0);
		minMaxLoc(temp, &minVal, &maxVal, &minLoc, &maxLoc, noArray());

		darkMaxPoint.push_back(maxLoc);//找到最亮点位置
		inputMax.push_back(image.at<uchar>(maxLoc));
		circle(temp, maxLoc, 5, Scalar(0), 1, 8, 0);
		temp.at<uchar>(maxLoc) = temp.at<uchar>(minLoc);
	}
	return inputMax;
}


int main(int argc, char* argv[])
{
    
    

	Mat image = imread("E:\\picture\\wutian.jpg");
	//imshow("image", image);
	Mat darkChannel1 = darkChannel(image);
	//imshow("darkChannel", darkChannel1);

	namedWindow("dehazed");

	//估计大气光
	Mat temp;
	darkChannel1.copyTo(temp);
	vector<Point> darkMaxPoint;
	vector<uchar> inputMax;
	for (long i = 0; i < ((darkChannel1.rows * darkChannel1.cols) / 1000); i++)
	{
    
    
		double minVal = 0;
		double maxVal = 0;
		Point minLoc(0, 0);
		Point maxLoc(0, 0);
		minMaxLoc(temp, &minVal, &maxVal, &minLoc, &maxLoc, noArray());

		darkMaxPoint.push_back(maxLoc);//找到最亮点位置
		inputMax.push_back(image.at<uchar>(maxLoc));
		circle(temp, maxLoc, 5, Scalar(0), 1, 8, 0);
		temp.at<uchar>(maxLoc) = temp.at<uchar>(minLoc);
	}

	uchar A = light(inputMax);


	double w = 0.75;

	//createTrackbar("w1", "dehazed", &w1, 100, NULL);

	//求折射率
	Mat T = Mat::zeros(image.rows, image.cols, CV_8UC3);
	Scalar intensity;

	for (int m = 0; m < image.rows; m++)
	{
    
    
		for (int n = 0; n < image.cols; n++)
		{
    
    
			intensity = darkChannel1.at<uchar>(m, n);
			T.at<Vec3b>(m, n)[0] = (1 - w * intensity.val[0] / A) * 255;
			T.at<Vec3b>(m, n)[1] = (1 - w * intensity.val[0] / A) * 255;
			T.at<Vec3b>(m, n)[2] = (1 - w * intensity.val[0] / A) * 255;
		}
	}


	//去雾
	Mat J(image.rows, image.cols, CV_8UC3, Scalar(180, 120, 50));
	Mat temp1(image.rows, image.cols, CV_8UC3, Scalar(180, 120, 50));

	//subtract(image, Scalar(A, A, A), temp1);
	temp1 = abs(image - Scalar(A, A, A));
	double t0 = 0.1;

	Scalar T1;
	Vec3b intsrc;
	
	for (int i = 0; i < image.cols; i++)
	{
    
    
		for (int j = 0; j < image.rows; j++)
		{
    
    
			T1 = T.at<uchar>(Point(i, j));
			intsrc = image.at<Vec3b>(Point(i, j));
			double tmax = (T1.val[0] / 255) < t0 ? t0 : (T1.val[0] / 255);

			for (int k = 0; k < 3; k++)
			{
    
    
				J.at<Vec3b>(Point(i, j))[k] = abs((intsrc.val[k] - A) / tmax + A) > 255 ? 255 : abs((intsrc.val[k] - A) / tmax + A);
			}
		}
	}

	imshow("dehazed", J);
	while (char(waitKey(1)) != 'q') {
    
    }




	return 0;
}

Los resultados son los siguientes:

Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_36587495/article/details/108580940
Recomendado
Clasificación