【OpenCV 进阶笔记】—— 图像增强算法详细解析以及算法实现 1 —— 伽马变换算法(Based on C++)

一、伽马变换算法详解

我们先来看一下伽马变换的公式: s = C r γ s = Cr^γ ,其中, s s 是做了伽马变换之后的图像的像素值、 r r 是原图像的对应位置的像素值。 C C γ γ 是正的常数。我们下面重点关心一下 γ γ 的取值对图像产生的影响:

这个图到底是什么意思呢?我这么解释吧:大家是不是都有用过 P图 软件,在一般的软件界面中,都会有一个 “ 曲线 ”的选项,我们可以通过改变曲线的形状来调整图像局部或者全局的亮度。
在这里插入图片描述
这样的比喻可能从数字图像处理的角度不完全准确,但是它给我们的启示是:曲线是凸状的,则图像就会变亮;曲线是凹状的,则图像就会变暗。

这反应在外面上面的伽马变换曲线图也是类似的:曲线是凸状的,则图像就会变亮;曲线是凹状的,则图像就会变暗。 所以外面可以归纳出:

  1. γ < 1 γ < 1 :则经过伽马变换之后图像会变亮。
  2. γ > 1 γ > 1 :则经过伽马变换之后图像会变暗。

那么,我们为什么需要伽马校正,又或者说,伽马校正为何有效?

首先、伽马校正能更有效的保存图像亮度信息。下图展示的是未经过伽马矫正和经过伽马矫正的图像信息:

我们发现,未经伽马校正的情况下,在低灰度值时,有较大范围的灰度值被保存成同一个值,造成信息丢失;同时高灰度值时,很多比较接近的灰度值却被保存成不同的值,造成空间浪费。经过伽马校正后,改善了存储的有效性和效率。

另外,人眼对外界光源的感光值与输入光强不是呈线性关系的,而是呈指数型关系的。在光强较弱的时候,我们人眼能够感觉到较为细微的光强变化;但是在亮度较高时,要想察觉到光强的改变就没那么容易了。

二、在实现算法之前的预备知识

  1. 两种图像存储类型 CV_8U 和 CV_32F 的区别:CV_8U是 unsign 的8位/像素-即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围CV_32F是 float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示
  2. OpenCV 的归一化函数 normalize。函数原型如下:
void cv::normalize(InputArry src,InputOutputArray dst,double alpha=1,double beta=0,int norm_type=NORM_L2,int dtype=-1,InputArray mark=noArry())

其中,对于归一化方式,CV_MINMAX 方式的公式如下: P = A k m a x ( A i ) m i n ( A i ) P = \frac{A_k}{max(A_i) - min(A_i)}
其中,如果 A k A_k 是最小值的时候, P = 0 P = 0 ;如果 A k A_k 是最大值的时候, P = 1 P = 1

  1. OpenCV 里面 convertScaleAbs 函数功能是将CV_32F 等类型的输出图像转变成CV_8U型的图像

四、代码实现(C++版)

Mat gammaTrans(Mat& m_img, double gamma, int n_c)
{
	/*
	CV_8U是 unsign 的8位/像素-即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围。
	CV_32F是 float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示。
	*/
	Mat m_imgGamma(m_img.size(), CV_32FC3);
	for (int i = 0; i < m_img.rows; i++)
		for (int j = 0; j < m_img.cols; j++)
		{
			m_imgGamma.at<Vec3f>(i, j)[0] = n_c * pow(m_img.at<Vec3b>(i, j)[0], gamma);  //使用动态寻址方式
			m_imgGamma.at<Vec3f>(i, j)[1] = n_c * pow(m_img.at<Vec3b>(i, j)[1], gamma);
			m_imgGamma.at<Vec3f>(i, j)[2] = n_c * pow(m_img.at<Vec3b>(i, j)[2], gamma);
		}
	normalize(m_imgGamma, m_imgGamma, 0, 255, CV_MINMAX);
	convertScaleAbs(m_imgGamma, m_img);  //将CV_32F的图像转成CV_8U的
	return m_img;
}

下面,我们对于一张水下模糊图片进行伽马矫正(这张水下图像整体由于亮度偏高而导致模糊),我们看看效果

#include <opencv2/highgui/highgui.hpp>    
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
Mat gammaTrans(Mat& m_img, double gamma, int n_c);
int main()
{
	Mat scr, dst;
	scr = imread("test1.jpg", 1);
	imshow("原图", scr);
	dst = gammaTrans(scr, 4, 1);
	imshow("Gamma变换效果图", dst);
	waitKey();
	return 0;
}

在这里插入图片描述

这暂时就是本次 B l o g Blog 的全部内容啦!

猜你喜欢

转载自blog.csdn.net/weixin_44586473/article/details/105757712