基于二维伽马函数的光照不均匀图像自适应校正算法_SongpingWang的博客-程序员信息网_基于二维伽马函数的光照不均匀图像自适应校正算法

技术标签: 计算机视觉  opencv  

关于数字图像处理:本文介绍一种用于解决光照不均匀的图像自适应校正算法。光照不均匀其实是非常常见的一种状况,为了解决图像曝光不足,或者不均衡而提出来的解决方案之一,关于【Retiex解决方案请查看】
论文原文: 点击查看

算法原理
论文使用了Retinex的多尺度高斯滤波求取「光照分量」,然后使用了「二维Gamma函数」针对原图的「HSV空间的V(亮度)分量」进行亮度改变,得到结果。

具体实现流程如下:

在这里插入图片描述
其中:
在这里插入图片描述
上面图像中 γ \gamma γ 应该是:
γ = ( 1 2 ) m − I ( x , y ) m \gamma = \left ( \frac{1}{2} \right )^{\frac{m - I(x,y)}{m}} γ=(21)mmI(x,y)

Mat RGB2HSV(Mat src) {
    
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_32FC3);
	for (int i = 0; i < row; i++) {
    
		for (int j = 0; j < col; j++) {
    
			float b = src.at<Vec3b>(i, j)[0] / 255.0;
			float g = src.at<Vec3b>(i, j)[1] / 255.0;
			float r = src.at<Vec3b>(i, j)[2] / 255.0;
			float minn = min(r, min(g, b));
			float maxx = max(r, max(g, b));
			dst.at<Vec3f>(i, j)[2] = maxx; //V
			float delta = maxx - minn;
			float h, s;
			if (maxx != 0) {
    
				s = delta / maxx;
			}
			else {
    
				s = 0;
			}
			if (r == maxx) {
    
				h = (g - b) / delta;
			}
			else if (g == maxx) {
    
				h = 2 + (b - r) / delta;
			}
			else {
    
				h = 4 + (r - g) / delta;
			}
			h *= 60;
			if (h < 0)
				h += 360;
			dst.at<Vec3f>(i, j)[0] = h;
			dst.at<Vec3f>(i, j)[1] = s;
		}
	}
	return dst;
}

Mat HSV2RGB(Mat src) {
    
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	float r, g, b, h, s, v;
	for (int i = 0; i < row; i++) {
    
		for (int j = 0; j < col; j++) {
    
			h = src.at<Vec3f>(i, j)[0];
			s = src.at<Vec3f>(i, j)[1];
			v = src.at<Vec3f>(i, j)[2];
			if (s == 0) {
    
				r = g = b = v;
			}
			else {
    
				h /= 60;
				int offset = floor(h);
				float f = h - offset;
				float p = v * (1 - s);
				float q = v * (1 - s * f);
				float t = v * (1 - s * (1 - f));
				switch (offset)
				{
    
				case 0: r = v; g = t; b = p; break;
				case 1: r = q; g = v; b = p; break;
				case 2: r = p; g = v; b = t; break;
				case 3: r = p; g = q; b = v; break;
				case 4: r = t; g = p; b = v; break;
				case 5: r = v; g = p; b = q; break;
				default:
					break;
				}
			}
			dst.at<Vec3b>(i, j)[0] = int(b * 255);
			dst.at<Vec3b>(i, j)[1] = int(g * 255);
			dst.at<Vec3b>(i, j)[2] = int(r * 255);
		}
	}
	return dst;
}

Mat work(Mat src) {
    
	int row = src.rows;
	int col = src.cols;
	Mat now = RGB2HSV(src);
	Mat H(row, col, CV_32FC1);
	Mat S(row, col, CV_32FC1);
	Mat V(row, col, CV_32FC1);
	for (int i = 0; i < row; i++) {
    
		for (int j = 0; j < col; j++) {
    
			H.at<float>(i, j) = now.at<Vec3f>(i, j)[0];
			S.at<float>(i, j) = now.at<Vec3f>(i, j)[1];
			V.at<float>(i, j) = now.at<Vec3f>(i, j)[2];
		}
	}
	int kernel_size = min(row, col);
	if (kernel_size % 2 == 0) {
    
		kernel_size -= 1;
	}
	float SIGMA1 = 15;
	float SIGMA2 = 80;
	float SIGMA3 = 250;
	float q = sqrt(2.0);
	Mat F(row, col, CV_32FC1);
	Mat F1, F2, F3;
	GaussianBlur(V, F1, Size(kernel_size, kernel_size), SIGMA1 / q);
	GaussianBlur(V, F2, Size(kernel_size, kernel_size), SIGMA2 / q);
	GaussianBlur(V, F3, Size(kernel_size, kernel_size), SIGMA3 / q);
	for (int i = 0; i < row; i++) {
    
		for (int j = 0; j < col; j++) {
    
			F.at <float>(i, j) = (F1.at<float>(i, j) + F2.at<float>(i, j) + F3.at<float>(i, j)) / 3.0;
		}
	}
	float average = mean(F)[0];
	Mat out(row, col, CV_32FC1);
	for (int i = 0; i < row; i++) {
    
		for (int j = 0; j < col; j++) {
    
			float gamma = powf(0.5, (average - F.at<float>(i, j)) / average);
			out.at<float>(i, j) = powf(V.at<float>(i, j), gamma);
		}
	}
	vector <Mat> v;
	v.push_back(H);
	v.push_back(S);
	v.push_back(out);
	Mat merge_;
	merge(v, merge_);
	Mat dst = HSV2RGB(merge_);
	return dst;
}

python实现

import cv2
import numpy as np


def Adaptive_light_correction(img):
    height = img.shape[0]
    width = img.shape[1]

    HSV_img = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    V = HSV_img[:,:,2]

    kernel_size = min(height, width)
    if kernel_size % 2 == 0:
        kernel_size -= 1

    SIGMA1 = 15
    SIGMA2 = 80
    SIGMA3 = 250
    q = np.sqrt(2.0)
    F = np.zeros((height,width,3),dtype=np.float64)
    F[:,:,0] = cv2.GaussianBlur(V,(kernel_size, kernel_size),SIGMA1 / q)
    F[:,:,1] = cv2.GaussianBlur(V,(kernel_size, kernel_size),SIGMA2 / q)
    F[:,:,2] = cv2.GaussianBlur(V,(kernel_size, kernel_size),SIGMA3 / q)
    F_mean = np.mean(F,axis=2)
    average = np.mean(F_mean)
    gamma = np.power(0.5,np.divide(np.subtract(average,F_mean),average))
    out = np.power(V/255.0,gamma)*255.0
    HSV_img[:,:,2] = out
    img = cv2.cvtColor(HSV_img,cv2.COLOR_HSV2BGR)
    return img


if __name__ == '__main__':
    img = cv2.imread("./112.jpg")
    result_img = Adaptive_light_correction(img)
    cv2.imshow("result_img",result_img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

测试原图与效果如下:
在这里插入图片描述
在这里插入图片描述
参考:https://cloud.tencent.com/developer/article/1588007
特别鸣谢:https://blog.csdn.net/codeswarrior

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wsp_1138886114/article/details/108640611

智能推荐

linux 当前目录动态库加载找不到文件,linux动态库路径查找不到_西门镜湖的博客-程序员信息网

GCC的-wl,-rpath=参数使用GCC编译动态链接库的项目时,在其他目录下执行很可能出现找不到动态链接库的问题。这种情况多发生在动态链接库是自己开发的情况下,原因就是程序运行时找不到去何处加载动态链接库。可能会说在编译时指定了链接的目录啊!编译时指定的 -L的目录,只是在程序链接成可执行文件时使用的。程序执行时动态链接库加载不到动态链接库。解决办法有两种,第一程序链接时指定链接库的位置,就是...

【TensorFlow】解决AttributeError: module ‘scipy.misc‘ has no attribute ‘toimage‘问题_绝不做九漏鱼的博客-程序员信息网

〇、前情提要在b站跟着up主 白手起家的百万富翁 学mnist手写体识别实践,但到了scipy.misc.toimage(image_array, cmin=0.0, cmax=1.0).save(image_file)报错AttributeError: module 'scipy.misc' has no attribute 'toimage'。参考:我的笔记-AttributeError: module ‘scipy.misc’ has no attribute ‘toimage’ht

VMware新建虚拟机安装黑屏_国际撩妹大佬的博客-程序员信息网

在liunx中用netsh winsock reset命令重启后可解决win10中在首选项中打开虚拟打印机可解决亲测有效

刘帅嵌入式系统-协处理器Load/Store指令的寻址方式_shuai532209720的博客-程序员信息网

​一条协处理器Load/Store指令可以再ARM处理器和协处理器之间传输批量数据。其语法格式如下:&lt;opcode&gt;{&lt;cond&gt;} {L} &lt;coproc&gt;,&lt;CRd&gt;,&lt;addressing_mode&gt;其中&lt;addressing_mode&gt;表示地址的变化方式,有如下4种: [&lt;Rn&gt;,#+/...

Linux load函数,kexec_load()函数 Unix/Linux_帅元昊的博客-程序员信息网

kexec_load -加载新的内核映像到内存内容简介#include #include long kexec_load(unsigned longentry, unsigned longnr_segments, struct kexec_segment *flags);描述kexec_load加载从当前地址空间中的新内核。这个系统调用只能用于由root。条目是一个指向新加载的可执行映像的入口点。...

Java 创建 List 和 Map 同时赋初值的 5 种方式_福州-司马懿的博客-程序员信息网_java创建map并赋值

一、最常见的初始化方式先创建,再添加元素List从源码可以看出:List 的默认长度为10递增后的长度为先前长度的 1.5 倍最大长度是 Integer.MAX_VALUE,即 2,147,483,647 或 0x7f ff ff ffList&lt;String&gt; list = new ArrayList&lt;&gt;();list.add("str1");list...

随便推点

php502 bad gateway_weixin_43599047的博客-程序员信息网

在project structure 中增加项目module

一加9pro好不好 综合体验再创高分_普通网友的博客-程序员信息网

从消费者体验中,记者得知,全新的一加9pro系列手机,为消费者带来良好的体验的同时,也不断升级了手机的内核与外在性能的诸多方面,致力于以国产领先手机的地位,给消费者以更进一步的手机体验。在手机性能和参数上则显然有许多飞跃和提升。据悉,一加标配是骁龙888处理器,搭配UFS3.1,LPDDR5,性能三件套齐发,全新的散热系统。屏幕采用的1080P+120Hz单孔直屏设计,孔径只有3mm。Pro版本采用的2K+120Hz曲面屏,在这个基础上还新增了10Bit、自动色温感应、8192级亮度调节和自适应性刷新率

STVP烧录教程_Angela㐅cc的博客-程序员信息网

可以运行独立的烧录软件ST Visual Programmer (STVP)进行STM8芯片烧录。运行“开始”-&gt;ST Toolset-&gt;Development Tools -&gt; ST Visual Programmer,运行界面如图2-65所示。图2-65 STVP界面图烧录软件配置:运行 Configure -&gt; Configure ST Vis...

WEB-INF下jsp之间的跳转_爱敲代码的zhaahz的博客-程序员信息网

WEB-INF下jsp之间的跳转&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;nbsp;在做项目的过程中,发现自己在WEB-INF目录下

svm算法 java实现_SVM算法实现(一)_程序员必修课的博客-程序员信息网

关键字(keywords):SVM支持向量机 SMO算法 实现机器学习假设对SVM原理不是非常懂的,能够先看一下入门的视频,对帮助理解非常实用的,然后再深入一点能够看看这几篇入门文章,作者写得挺具体,看完以后SVM的基础就了解得差点儿相同了,再然后买本《支持向量机导论》作者是Nello Cristianini 和 John Shawe-Taylor,电子工业出版社的。然后把书本后面的那个SMO...

linux下nginx的安装及配置_地铁程序员的博客-程序员信息网

一、安装nginx前,我们首先要确保系统安装了g++、gcc、openssl-devel、pcre-devel和zlib-devel软件,可通过如图所示命令进行检测,如果以安装我们可以通过图二所示卸载:yum install gcc-c++yum -y install zlib zlib-devel openssl openssl--devel pcre pcre-devel## 如果...

推荐文章

热门文章

相关标签