一、伽马变换算法详解
我们先来看一下伽马变换的公式: ,其中, 是做了伽马变换之后的图像的像素值、 是原图像的对应位置的像素值。 和 是正的常数。我们下面重点关心一下 的取值对图像产生的影响:
这个图到底是什么意思呢?我这么解释吧:大家是不是都有用过 P图 软件,在一般的软件界面中,都会有一个 “ 曲线 ”的选项,我们可以通过改变曲线的形状来调整图像局部或者全局的亮度。
这样的比喻可能从数字图像处理的角度不完全准确,但是它给我们的启示是:曲线是凸状的,则图像就会变亮;曲线是凹状的,则图像就会变暗。
这反应在外面上面的伽马变换曲线图也是类似的:曲线是凸状的,则图像就会变亮;曲线是凹状的,则图像就会变暗。 所以外面可以归纳出:
- :则经过伽马变换之后图像会变亮。
- :则经过伽马变换之后图像会变暗。
那么,我们为什么需要伽马校正,又或者说,伽马校正为何有效?
首先、伽马校正能更有效的保存图像亮度信息。下图展示的是未经过伽马矫正和经过伽马矫正的图像信息:
我们发现,未经伽马校正的情况下,在低灰度值时,有较大范围的灰度值被保存成同一个值,造成信息丢失;同时高灰度值时,很多比较接近的灰度值却被保存成不同的值,造成空间浪费。经过伽马校正后,改善了存储的有效性和效率。
另外,人眼对外界光源的感光值与输入光强不是呈线性关系的,而是呈指数型关系的。在光强较弱的时候,我们人眼能够感觉到较为细微的光强变化;但是在亮度较高时,要想察觉到光强的改变就没那么容易了。
二、在实现算法之前的预备知识
- 两种图像存储类型 CV_8U 和 CV_32F 的区别:CV_8U是 unsign 的8位/像素-即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围。CV_32F是 float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示
- 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 方式的公式如下:
其中,如果
是最小值的时候,
;如果
是最大值的时候,
- 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;
}
这暂时就是本次 的全部内容啦!