什么是Gamma 校正
Gamma 是指灰度系数,是显示设备都有的一种指标,每种显示设备都不一样,会影响输出设备的亮度:
显示设备输出亮度 = 电压的Gamma次幂
使用 gamma 校正有下面两个原因:
-
显示设备的亮度的非线性特性
输出的亮度和输入的电压并非线性关系,而是近似 2.2 次幂的关系,导致进入人眼的亮度要比计算机上存储的亮度要低
例如:计算机上存储的亮度为 0.5,经过显示器调整后变为 0.5 的 2.2 次幂,即 0.218。为了让进入人眼的亮度与计算机中存储的值相同,需要在显示器调整前将亮度变为自身的 1/2.2 次幂,即 0.73,这样在经过显示器的调整,进入人眼就是 0.5 了,2.2 这个值是一个近似值,或者可以说是一个标准,实际上不同的显示器可能会有不同 -
人眼对不同亮度的敏感性不同
人眼对暗部的敏感要高于亮部, 也就是非线性的,但是我们存储计算机颜色的空间是有限的,比如常见的有ARGB8888 格式,如果直接到显示设备上显示,会损失较多的暗部细节,所以需要我们对输入的内容做gamma 变换,可以展示更多的暗部细节,有更好的显示效果。
Gamma 校正算法
Gamma 校正公式:
f(I) = I^gamma
自变量 #I 是颜色像素值归一化后的值,区间分为是从0 到 1, 归一化通过下面的公式就可以实现:
归一化后的像素值 = 当前像素值 / 像素的最大值
不同gamma 值的函数曲线如下:
gamma_fx.png
- 当 gamma = 1 的时候,输出值等于输入值,只会有原图的显示效果
- 当 gamma 大于1 的时候,如图中的黄色线所示,低灰度区域的动态值变小,高灰度区域的动态值变大,降低了低灰度区域图像的对比度,提高了高灰度值区域图像的对比度, 图像整体变暗。
- 当 gamma 小于1 的时候,低灰度区域的动态值变大,进而图像对比度增强,高灰度值区域,动态值变小,图像整体变亮。
显示效果如下:
gamma_lessthan1.png gamma_greatherthan1.png
Gamma 校正的LUT实现
gamma 校正的公式涉及到指数运算,在实际应用中效率太低,需要优化;
可以使用查找表(LUT)法,输入的像素值的范围是有限的 一般为[0,255], 在 gamma 值 已知的情况下 ,0 ~ 255 之间的任一整数 , 经过归一 化、预补偿、反归一化操作后 , 所对应的结果是唯一的 , 并且也落在 0 ~ 255 这个范围内;
如果可以预先建立一张表,使得每个输入的像素值都有唯一的gamma 校正后输出像素值对应,就可以大大降低运算量;
使用LUT实现Gamma 校正的C++ 代码
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
// 存储 gamma LUT 的数组
uint8_t gammaLUT[256] = { 0 };
void _gammaCorrection(Mat& src, Mat& dst, float K)
{
src.copyTo(dst);
for (int i = 0; i < 256; i++) {
float f = i / 255.0f; // 注意不可以写成 i / 255
f = pow(f, K);
gammaLUT[i] = static_cast<uint8_t>(f*255.0);
}
if (dst.channels() == 1) {
MatIterator_<uint8_t> it = dst.begin<uint8_t>();
MatIterator_<uint8_t> it_end = dst.end<uint8_t>();
for (; it != it_end; it++)
{
*it = gammaLUT[*it];
}
} else {
cout << "dst image channel" << dst.channels() << endl;
MatIterator_<Vec3b> it = dst.begin<Vec3b>();
MatIterator_<Vec3b> it_end = dst.end<Vec3b>();
for (; it != it_end; it++)
{
(*it)[0] = gammaLUT[(*it)[0]];
(*it)[1] = gammaLUT[(*it)[1]];
(*it)[2] = gammaLUT[(*it)[2]];
}
}
}
bool testGammaCorrection()
{
cout << "gamma correection demo" << endl;
const char *ImageName = "C:/Users/86185/Documents/Visual Studio 2015/Projects/opencvdemo/x64/Debug/demoImage.png";
Mat img = imread(ImageName, 1);
if (img.empty()) {
cout << "read image empty" << endl;
return false;
}
Mat dstimg;
_gammaCorrection(img, dstimg, 1/2.2);
imshow("src_image", img);
imshow("dst_image", dstimg);
return true;
}
Review History
日期 | 说明 |
---|---|
2022/10/1 | 修正部分格式 |
NA | NA |
网友评论