Retinex算法的C++/opencv实现

最近在做图像增强方面的算法,在参考了一些博客,论文和源代码后 ,自己整理了Retinex相关算法的opencv实现,在这里贴出来供大家参考,有不当的地方欢迎大家指出!
一.Retinex算法原理
基础理论:物体的颜色是由物体对长波(红色),中波(绿色),短波(蓝色)光线的反射能力来决定的,而不是由反射光强度的绝对值来决定的;物体的颜色不受光照非均匀性的影响,具有一致性,即Retinex算法是基于物体的颜色恒常性来实现的。
Retinex算法可以在图像的动态颜色范围压缩,边缘增强和颜色恒常三个方面达到平衡,在图像除雾方面有着较好的效果(对正常图像的增强效果不明显)。
二.SSR(Single Scale Retinex)
根据Retinex算法的基础理论,我们可以得到以下数学表达式:
S(x,y)=R(x,y)*L(x,y)  (2-1)
图示如下:
在这里插入图片描述
其中R(x,y)表示物体的反射性质,即图像的内在属性,应该最大程度的保留;L(x,y)表示入射光图像,决定了图像像素能够达到的动态范围,应该尽量去除;S(x,y)为人眼观察到或者相机接收到的图像。
对公式2-1的等式两边取对数,得到:
r(x,y)=Log[R(x,y)] = Log[S(x,y)]-Log[L(x,y)] (2-2)

算法的关键在于如何得到图像的入射光图像L(x,y),其中比较经典且效果较好的方法是通过对相机接收的图像S(x,y)做高斯模糊来估计L(x,y)。
算法的具体步骤如下:
(1)输入高斯模糊的高斯环绕尺度C(即二维高斯函数的标准差);
(2)根据C对原始图像数据S(x,y)做高斯模糊得到L(x,y);
(3)根据公式2-2对S(x,y)和L(x,y)分别取对数并作差得到r(x,y);
(4)将r(x,y)的像素值量化到0到255的范围内得到R(x,y),R(x,y)即我们想得到的增强图像。量化的公式如下:
R(x,y) = ( Value - Min ) / (Max - Min) * (255-0) (2-3)
算法的源代码如下:

void ssr(Mat src, Mat& dst, double sigma) {
	Mat src_log,gauss,gauss_log,dst_log;
	src_log= Mat(src.size(), CV_32FC3);
	gauss_log= Mat(src.size(), CV_32FC3);
	dst_log = Mat(src.size(), CV_32FC3);
	dst = Mat(src.size(), CV_32FC3);
	int height = dst_log.rows;
	int width = dst_log.cols;
	int ksize = (int)(sigma * 3/2);
	ksize = ksize * 2 + 1;
	//求Log(S(x,y)
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = src.at<Vec3b>(i, j)[k];
				if (value <= 0.01) value = 0.01;
				src_log.at<Vec3f>(i, j)[k] = log10(value);
			}
		}
	}
	GaussianBlur(src, gauss, Size(ksize,ksize),sigma,sigma,4);
	//求Log(L(x,y))
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = gauss.at<Vec3b>(i, j)[k];
				if (value <= 0.01) value = 0.01;
				gauss_log.at<Vec3f>(i, j)[k] = log10(value);
			}
		}
	}
	//求Log(S(x,y))-Log(L(x,y))
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value1 = src_log.at<Vec3f>(i, j)[k];
				float value2 = gauss_log.at<Vec3f>(i, j)[k];
				dst_log.at<Vec3f>(i, j)[k] = value1-value2;
			}
		}
	}
	float min[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	float max[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	//求R/G/B三通道的min,max
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				if (value > max[k]) max[k] = value;
				if (value < min[k]) min[k] = value;
			}
		}
	}
	//量化处理
	cout << min[0] << " " << min[1] << " " << min[2] << endl;
	cout << max[0] << " " << max[1] << " " << max[2] << endl;
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				dst.at<Vec3f>(i, j)[k] = (saturate_cast<float>(255 * (value - min[k]) / (max[k] - min[k])));
			}
		}
	}
	dst.convertTo(dst, CV_8UC3);
	return;
}

效果图如下:
原图:
原图
SSR(C=300):
在这里插入图片描述
原图:
在这里插入图片描述
SSR(C=300):
在这里插入图片描述
三.MSR(Multi Scale Retinex)
MSR是在SSR算法的基础上提出的,用不同的尺度C来估计L(x,y)。数学表达如下:
r(x,y)=∑k Wk*{logS(x,y)−log[Fk(x,y)⋅S(x,y)]} (3-1)
为了兼有SSR高,中,低三个尺度的优点的考虑,K通常取3,且有W1=W2=W3=1/3.
算法步骤:
(1)输入权值矩阵Wk和K个高斯环绕尺度;
(2)根据公式3-1得到r(x,y);
(3)对r(x,y)做量化处理得到增强图像R(x,y).
算法的源代码如下:

void msr(Mat src, Mat& dst, vector<float>weight, vector<float>sigmas) {
	Mat src_log, gauss, gauss_log, dst_log;
	src_log = Mat(src.size(), CV_32FC3);
	gauss_log = Mat(src.size(), CV_32FC3);
	dst_log = Mat::zeros(src.size(), CV_32FC3);
	dst = Mat::zeros(src.size(), CV_32FC3);
	int height = dst_log.rows;
	int width = dst_log.cols;
    
	//求Log(S(x,y)
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = src.at<Vec3b>(i, j)[k];
				if (value < 0.01) value = 0.01;
				src_log.at<Vec3f>(i, j)[k] = log10(value);
			}
		}
	}
	int scale = weight.size();
	for (int t = 0;t < scale;t++) {
		int ksize = (int)(sigmas[t] * 3 / 2);
		ksize = ksize * 2 + 1;
		GaussianBlur(src, gauss, Size(ksize, ksize), sigmas[t], sigmas[t], 4);
		//求Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value = gauss.at<Vec3b>(i, j)[k];
					if (value < 0.01) value = 0.01;
					gauss_log.at<Vec3f>(i, j)[k] = log10(value);
				}
			}
		}
		//求Log(S(x,y))-Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value1 = src_log.at<Vec3f>(i, j)[k];
					float value2 = gauss_log.at<Vec3f>(i, j)[k];
					dst_log.at<Vec3f>(i, j)[k] +=weight[t] * (value1 - value2);
				}
			}
		}
	}
	float min[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	float max[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	//求R/G/B三通道的min,max
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				if (value > max[k]) max[k] = value;
				if (value < min[k]) min[k] = value;
			}
		}
	}
	//量化处理
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				dst.at<Vec3f>(i, j)[k] =saturate_cast<float> (255 * (value - min[k]) / (max[k] - min[k]));
			}
		}
	}
	dst.convertTo(dst, CV_8UC3);
}

算法的效果图如下:
原图:
在这里插入图片描述
MSR(C1=40,C2=100,C3=300):
在这里插入图片描述
原图:
在这里插入图片描述
MSR(C1=40,C2=100,C3=300):
在这里插入图片描述

四.MSRCR(带颜色恢复的MSR)
由SSR和MSR算法的效果图我们可以看到一般的Retinex算法在图像去雾时可能会导致图像失真。一般的Retinex算法处理图像时,都会假设初始图像灰度值是缓慢变化的,即图像是平滑的,在实际情况下,Retinex算法在亮度差异大的区域会产生光晕。
为了解决上述的图像颜色失真的情况,有人提出了一种带有颜色恢复的MSR算法。MSRCR算法在MSR算法的基础上,加入了色彩恢复因子来调节由于图像局部区域对比度增强导致的颜色失真。
算法核心如下:
在这里插入图片描述
算法源代码如下:

void scr(Mat &src, float low_clip, float high_clip) {
	int height = src.rows;
	int width = src.cols;
	int total = height*width;
	int u[3][256];//统计每个通道每个像素值出现的次数
	memset(u, 0, sizeof(u));
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				int v = src.at<Vec3b>(i, j)[k];
				u[k][v]++;
			}
		}
	}
	
	for (int i = 0; i < 3; i++) {
		for (int j = 1;j < 256;j++) {
			u[i][j] = u[i][j - 1] + u[i][j];
		}
	}
	int low_val[3], high_val[3];
	float low_rate = 0, high_rate = 0;
	for (int i = 0; i < 3; i++) {
		for (int j = 0;j < 256;j++) {
			float rate = u[i][j] / total;
			if (rate<  low_clip&&rate>low_rate) {
				low_val[i] = j;
				low_rate = rate;
			}
			if (rate < high_clip&&rate>high_rate) { 
				high_val[i] = j;
				high_rate = rate;
			}
		}
	}
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				int value = src.at<Vec3b>(i, j)[k];
				src.at<Vec3b>(i, j)[k] = max(min(value, high_val[k]), low_val[k]);
			}
		}
	}
}
void colorRestoration(Mat src, Mat& dst, float alpha, float beta) {
	dst = Mat(src.size(), CV_32FC3);
	for (int  i = 0; i < src.rows; i++)
	{
		for (int j = 0;j < src.cols;j++)
		{   
			float sum = 0;
			for (int t = 0;t < src.channels();t++) sum += src.at<Vec3b>(i, j)[t];
			for (int k=0;k<3;k++)
			{
				dst.at<Vec3f>(i, j)[k] = beta*(log10(alpha*src.at<Vec3b>(i, j)[k]) - log10(sum));
			}
		}
	}
}
void msrcr(Mat src, Mat& dst, vector<float>weight, vector<float>sigmas, float alpha, float beta, float low_clip, float high_clip) {
	Mat src_log, gauss, gauss_log, dst_log,dst_ci;
	src_log = Mat(src.size(), CV_32FC3);
	gauss_log = Mat(src.size(), CV_32FC3);
	dst_log = Mat::zeros(src.size(), CV_32FC3);
	dst_ci = Mat(src.size(), CV_32FC3);
	dst = Mat::zeros(src.size(), CV_32FC3);
	int height = dst_log.rows;
	int width = dst_log.cols;

	//求Log(S(x,y)
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = src.at<Vec3b>(i, j)[k];
				if (value < 0.01) value = 0.01;
				src_log.at<Vec3f>(i, j)[k] = log10(value);
			}
		}
	}
	int scale = weight.size();
	for (int t = 0;t < scale;t++) {
		int ksize = (int)(sigmas[t] * 3 / 2);
		ksize = ksize * 2 + 1;
		GaussianBlur(src, gauss, Size(ksize, ksize), sigmas[t], sigmas[t], 4);
		//求Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value = gauss.at<Vec3b>(i, j)[k];
					if (value < 0.01) value = 0.01;
					gauss_log.at<Vec3f>(i, j)[k] = log10(value);
				}
			}
		}
		//求Log(S(x,y))-Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value1 = src_log.at<Vec3f>(i, j)[k];
					float value2 = gauss_log.at<Vec3f>(i, j)[k];
					dst_log.at<Vec3f>(i, j)[k] += weight[t] * (value1 - value2);
				}
			}
		}
	}
	//求色彩恢复因子Ci
	colorRestoration(src, dst_ci, alpha, beta);
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float v1 = dst_log.at<Vec3f>(i, j)[k];
				float v2 = dst_ci.at<Vec3f>(i, j)[k];
				dst_log.at<Vec3f>(i, j)[k] = v1*v2 ;
			}
		}
	}

	float min[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	float max[3] = { dst_log.at<Vec3f>(0, 0)[0], dst_log.at<Vec3f>(0, 0)[1],dst_log.at<Vec3f>(0, 0)[2] };
	//求R/G/B三通道的min,max
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				if (value > max[k]) max[k] = value;
				if (value < min[k]) min[k] = value;
			}
		}
	}
	//量化处理
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				dst.at<Vec3f>(i, j)[k] = saturate_cast<float> (255 * (value - min[k]) / (max[k] - min[k]));
			}
		}
	}
	dst.convertTo(dst, CV_8UC3);
	scr(dst, low_clip, high_clip);
}

上面的算法有着很多的经验参数,不利于手动实现,有人提出了一种基于GIMP的MSR算法,用参数Dynamic来控制图像的动态范围实现去除图像的色偏问题(一般取值2或3效果比较好)。这种算法的效果很好,能够一定程度解决颜色失真的问题。
算法的简要描述如下:
1.计算出 log[R(x,y)]中R/G/B各通道数据的均值Mean和均方差Var(注意是均方差)。
2.类似下述公式计算各通道的Min和Max值。
Min = Mean - Dynamic * Var;
Max = Mean + Dynamic * Var;
3.对Log[R(x,y)]的每一个值Value,进行线性映射:
R(x,y) = ( Value - Min ) / (Max - Min) * (255 - 0)
同时要注意增加一个溢出判断,即:
if (R(x, y) > 255) R(x,y) = 255;
else if (R(x,y) < 0) R(x,y) = 0;

算法的源码如下:

void msrcr_GIMP(Mat src, Mat& dst, vector<float>weight, vector<float>sigmas, int Dynamic) {
	Mat src_log, gauss, gauss_log, dst_log;
	src_log = Mat(src.size(), CV_32FC3);
	gauss_log = Mat(src.size(), CV_32FC3);
	dst_log = Mat(src.size(), CV_32FC3);
	dst = Mat(src.size(), CV_32FC3);
	float min[3] = { 0 };
	float max[3] = { 0 };
	float mean[3] = { 0 };
	float var[3] = { 0 };
	int height = dst_log.rows;
	int width = dst_log.cols;
	//求Log(S(x,y)
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = src.at<Vec3b>(i, j)[k];
				if (value < 0.01) value = 0.01;
				src_log.at<Vec3f>(i, j)[k] = log10(value);
			}
		}
	}
	int scale = weight.size();
	for (int t = 0;t < scale;t++) {
		int ksize = (int)(sigmas[t] * 3 / 2);
		ksize = ksize * 2 + 1;
		GaussianBlur(src, gauss, Size(ksize, ksize), sigmas[t], sigmas[t], 4);
		//求Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value = gauss.at<Vec3b>(i, j)[k];
					if (value < 0.01) value = 0.01;
					gauss_log.at<Vec3f>(i, j)[k] = log10(value);
				}
			}
		}
		//求Log(S(x,y))-Log(L(x,y))
		for (int i = 0;i < height;i++) {
			for (int j = 0;j < width;j++) {
				for (int k = 0;k < 3;k++) {
					float value1 = src_log.at<Vec3f>(i, j)[k];
					float value2 = gauss_log.at<Vec3f>(i, j)[k];
					dst_log.at<Vec3f>(i, j)[k] += weight[t] * (value1 - value2);
				}
			}
		}
	}
	//求R/G/B各通道的均值mean和均方差var
	float sum[3] = {0};
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				sum[k] += value;
			}
		}
	}
	for (int i = 0; i < 3; i++)
	{
		mean[i] = sum[i] / (height*width);

	}
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				var[k] += (value - mean[k])*(value - mean[k]);
			}
		}
	}
	for (int i = 0; i < 3; i++)
	{
		var[i] = sqrt(var[i] / (height*width));

	}
	//计算R/G/B各通道的Min,Max
	for (int  i = 0; i < 3; i++)
	{
		min[i] = mean[i] - Dynamic*var[i];
		max[i] = mean[i] + Dynamic*var[i];
	}
	//量化处理
	for (int i = 0;i < height;i++) {
		for (int j = 0;j < width;j++) {
			for (int k = 0;k < 3;k++) {
				float value = dst_log.at<Vec3f>(i, j)[k];
				dst.at<Vec3f>(i, j)[k] = saturate_cast<float>(255 * (value - min[k]) / (max[k] - min[k]));
			}
		}
	}
	dst.convertTo(dst, CV_8UC3);
}

算法的效果图如下:
MSR(C1=40,C2=100,C3=300):
在这里插入图片描述
MSRCR_GIMP(C1=40,C2=100,C3=300,Dynamic=2):
在这里插入图片描述
原图:
在这里插入图片描述
MSRCR_GIMP(C1=40,C2=100,C3=300,Dynamic=2):
在这里插入图片描述

以上就是我的Retinex算法的总结过程,算法本人已经实现过,效果图也贴出来供大家参考,有不当的地方欢迎大家指出来,楼主会及时修正,最后希望我的总结能够帮到大家!

  • 24
    点赞
  • 128
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
automatedMSRCR (Automatic Multi-Scale Retinex with Color Restoration) 算法是一种图像增强算法,它可以增强图像的对比度,保留细节和颜色。下面是使用 OpenCV C++ 实现 automatedMSRCR 算法的示例代码: ```c++ #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> using namespace cv; Mat automatedMSRCR(Mat input, float sigma_list[], int sigma_list_size, float G, float b, int alpha, int beta, int low_clip, int high_clip) { Mat L; input.convertTo(L, CV_32FC1); L = log(L + 1.0f); Mat R; for (int i = 0; i < sigma_list_size; i++) { Mat tmp; GaussianBlur(input, tmp, Size(0, 0), sigma_list[i], sigma_list[i], BORDER_DEFAULT); tmp.convertTo(tmp, CV_32FC1); tmp = log(tmp + 1.0f) - L; tmp = exp(tmp - G); R += tmp; } R /= sigma_list_size; Mat output; output = input * (1.0f - b) + R * b; output.convertTo(output, CV_8UC3); std::vector<Mat> channels; split(output, channels); for (int i = 0; i < 3; i++) { channels[i] = (channels[i] - mean(channels[i])) * alpha + mean(channels[i]); threshold(channels[i], channels[i], 0, 255, THRESH_TOZERO); normalize(channels[i], channels[i], low_clip, high_clip, NORM_MINMAX); } merge(channels, output); return output; } int main() { Mat input = imread("input.jpg"); float sigma_list[] = { 15.0f, 80.0f, 250.0f }; int sigma_list_size = sizeof(sigma_list) / sizeof(float); float G = 5.0f; float b = 25.0f; int alpha = 125; int beta = 46; int low_clip = 0; int high_clip = 255; Mat output = automatedMSRCR(input, sigma_list, sigma_list_size, G, b, alpha, beta, low_clip, high_clip); imshow("input", input); imshow("output", output); waitKey(); return 0; } ``` 在这个示例中,我们使用了 OpenCV 的核心模块(core)、图像 I/O 模块(highgui)和图像处理模块(imgproc)。具体来说,我们使用了以下函数: - `imread()`:加载输入图像。 - `GaussianBlur()`:对输入图像进行高斯模糊。 - `split()`:将输出图像的三个通道分离。 - `mean()`:计算每个通道的均值。 - `threshold()`:将每个通道的像素值截断为 0 到 255 之间。 - `normalize()`:将每个通道的像素值归一化为 0 到 255 之间。 - `merge()`:将三个通道合并为一幅图像。 - `imshow()`:显示图像。 - `waitKey()`:等待用户按键。 在实现 automatedMSRCR 算法时,我们需要将输入图像转换为浮点数类型,然后进行对数变换。接着,我们使用高斯模糊对输入图像进行多尺度处理,计算出 R 通道。最后,我们将 R 通道与输入图像的原始亮度值进行加权平均,并对每个通道进行颜色修正和像素值调整,最终得到增强后的图像

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值