大家好,又见面了,我是你们的朋友全栈君。
图像gamma校正
1.为什么要进行Gamma校正
(Gamma Correction,伽玛校正):所谓伽玛校正就是对图像的伽玛曲线进行编辑,以对图像进行非线性色调编辑的方法,检出图像信号中的深色部分和浅色部分,并使两者比例增大,从而提高图像对比度效果。计算机绘图领域惯以此屏幕输出电压与对应亮度的转换关系曲线,称为伽玛曲线(Gamma Curve)。
我们使用显示器去呈现,检查和调节照片或视频。它们是一个特殊的非线性系统可以显示数字图像数据。由于非线性,输入端的数字亮度值和显示端不是相应的线性关系。更进一步的考虑,这些系统产生连续的曲线,也就是其特征曲线。这个梯度曲线的线性部分被定义为伽马值。
范例
假设一张图片的信号和显示器是线性关系(左上方的图片),这会是RAW图像。显示器描绘出输入的数据,在这个例子中输入的信号是被显示器的特征曲线所控。考虑的更进一步,对角线的曲线转换为一条曲线(右上方图像)。输入的亮度值也就和监视器上的输出成非线性关系。导致的结果就是在监视器上图像看起来很黑暗。
要确保输出端的线性这样才能确保一个真实的还原。在非线性显示的操作情况下,对系统的失真进行补偿是必要的。可以通过输入线性信号的伽马校正做到。这种调整就导致了输入信号的非线性行为(左下图片),这和监视器上是相反的(中下图片)。这两个一起计算,监视器输出端就仍能保持线性(右下图片)。
许多相机都可以产生RAW格式的图片。产生的数据可以用不同的图像编辑程序的伽马校正去开发并进行相应的调整。相机还使用非线性的伽马校正内部产生的JPG格式的图片。除了在非线性显示器上显示的真实亮度,输入信号的校正提供了一个额外的好处。虽然原始数据的位深为12到14位,JPG只有8位。8位的是使用256亮度等级代替了4096(12位)的亮度等级水平。
在不使用伽马校正去产生JPG图像意味着亮度等级水平在进行8位的转换时会有一定的损失,就会产生“台阶”效果。通过调校亮度的分布就是进行伽马校正意味着那些效果会得到补偿修复。gamma校正的意义就在于将人眼感受到的灰度值, 转换到自然界真正的灰度值(因为人不是测量器, 人只能感受和比较)
2.色彩校正中的 gamma 值是什么
韦伯定律:即感觉的差别阈限随原来刺激量的变化而变化,而且表现为一定的规律性,用公式来表示,就是△Φ/Φ=C,其中Φ为原刺激量,△Φ为此时的差别阈限,C为常数,又称为韦伯率。
这个0-1区间的曲线,就是所谓的Gamma曲线。我们若定义黑是0,白是1,那么在0-1区间,我们是可以用一个幂函数来描述客观自然数值和主观心理感知的对应关系的(等比数列通项公式就是幂函数):
- Gamma=2.2怎么来的?
将人眼感受到的灰阶转换成自然界真实的灰阶
比如 (0.5)^2.2=0.2
3.gamma校正原理
假设图像中有一个像素,值是 200 ,那么对这个像素进行校正必须执行如下步骤:
1. 归一化 :将像素值转换为 0 ~ 1 之间的实数。 算法如下 : ( i + 0. 5)/256 这里包含 1 个除法和 1 个加法操作。对于像素 A 而言 , 其对应的归一化值为 0. 783203 。
2. 预补偿 :根据公式 , 求出像素归一化后的 数据以 1 /gamma 为指数的对应值。这一步包含一个 求指数运算。若 gamma 值为 2. 2 , 则 1 /gamma 为 0. 454545 , 对归一化后的 A 值进行预补偿的结果就 是 0. 783203 ^0. 454545 = 0. 894872 。
3. 反归一化 :将经过预补偿的实数值反变换为 0 ~ 255 之间的整数值。具体算法为 : f*256 – 0. 5 此步骤包含一个乘法和一个减法运算。续前 例 , 将 A 的预补偿结果 0. 894872 代入上式 , 得到 A 预补偿后对应的像素值为 228 , 这个 228 就是最后送 入显示器的数据。
如上所述如果直接按公式编程的话,假设图像的分辨率为 800*600 ,对它进行 gamma 校正,需要执行 48 万个浮点数乘法、除法和指数运算。效率太低,根本达不到实时的效果。
针对上述情况,提出了一种快速算法,如果能够确知图像的像素取值范围 , 例如 , 0 ~ 255 之间的整数 , 则图像中任何一个像素值只能 是 0 到 255 这 256 个整数中的某一个 ; 在 gamma 值 已知的情况下 ,0 ~ 255 之间的任一整数 , 经过“归一 化、预补偿、反归一化”操作后 , 所对应的结果是唯一的 , 并且也落在 0 ~ 255 这个范围内。
如前例 , 已知 gamma 值为 2. 2 , 像素 A 的原始值是 200 , 就可求得 经 gamma 校正后 A 对应的预补偿值为 228 。基于上述原理 , 我们只需为 0 ~ 255 之间的每个整数执行一次预补偿操作 , 将其对应的预补偿值存入一个预先建立的 gamma 校正查找表 (LUT:Look Up Table) , 就可以使用该表对任何像素值在 0 ~ 255 之 间的图像进行 gamma 校正。
4.Gamma校正实现
–c++代码
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include<cmath>
using namespace cv;
Mat gammaTransform(Mat &srcImage, float kFactor)
{
unsigned char LUT[256];
for (int i = 0; i < 256; i++)
{
float f = (i + 0.5f) / 255;
f = (float)(pow(f, kFactor));
LUT[i] = saturate_cast<uchar>(f*255.0f - 0.5f);
}
Mat resultImage = srcImage.clone();
if (srcImage.channels() == 1)
{
MatIterator_<uchar> iterator = resultImage.begin<uchar>();
MatIterator_<uchar> iteratorEnd = resultImage.end<uchar>();
for (; iterator != iteratorEnd; iterator++)
{
*iterator = LUT[(*iterator)];
}
}
else
{
MatIterator_<Vec3b> iterator = resultImage.begin<Vec3b>();
MatIterator_<Vec3b> iteratorEnd = resultImage.end<Vec3b>();
for (; iterator != iteratorEnd; iterator++)
{
(*iterator)[0] = LUT[((*iterator)[0])];//b
(*iterator)[1] = LUT[((*iterator)[1])];//g
(*iterator)[2] = LUT[((*iterator)[2])];//r
}
}
return resultImage;
}
int main()
{
Mat srcImage = imread("lakeWater.jpg");
if (!srcImage.data)
{
printf("could not load image...\n");
return -1;
}
//取两种不同的gamma值
float gamma1 = 3.33f;
float gamma2 = 0.33f;
float kFactor1 = 1 / gamma1;
float kFactor2 = 1 / gamma2;
Mat result1 = gammaTransform(srcImage, kFactor1);
Mat result2 = gammaTransform(srcImage, kFactor2);
imshow("srcImage", srcImage);
imshow("res1", result1);
imshow("res2", result2);
waitKey(0);
return 0;
}
–python代码
#-*-coding: UTF-8-*-
############gamma增强
import cv2
import numpy as np
img = cv2.imread('lenna.bmp')
img1 = np.power(img/float(np.max(img)), 1/2.2)
img2 = np.power(img/float(np.max(img)), 2.2)
cv2.imshow('src',img)
cv2.imshow('gamma',img1)
cv2.imshow('gamma_process',img2)
#由于后面需要进行类型转换,转换成uint8,所以需要对其乘以255处理
img2 = img2 * 255
cv2.imwrite('gamma_process.jpg',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
5.实验结果
从左到右分别为原图,gamma=2.2,gamma=1/2.2的图片。
简单来说,也就是调整一个参数,对图片中的每个像素根据这个gamma参数做出调整,而这个调整
的原理就是原像素的灰度值的gamma次方,所以就可以解释图片中的三个结果了,当gamma小于1的时
候,很明显a^gamma > a(a<1, a是通过运算得到a = gray / 255.0),这样就会让整体的亮度提升,并且
也可以预见到,当像素的灰度值越低(暗),运算的结果就越明显,而比较亮的部分(灰度值比较高)
的整体的提升就比较少。同样的对于gamma大于1的时候就就会有相反的效果。
【转载】:https://blog.csdn.net/linqianbi/article/details/78617615
https://blog.csdn.net/qq_35044509/article/details/78713289
https://www.cnblogs.com/qiqibaby/p/5325193.html
https://www.jianshu.com/p/52fc2192ae7b
https://www.zhihu.com/question/27467127
http://www.52rd.com/s_txt/2017_5/txt95785.htm
http://www.cnblogs.com/denny402/p/5122328.html(图像类型转换)
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148951.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...