openISP学习8-GC — Gamma Correction(Gamma 校正)

文章目录

1.Gamma校正的作用

在图像信号处理器(ISP)中,Gamma校正(Gamma Correction)是图像处理流水线中至关重要的非线性操作。它的核心作用是解决图像传感器、人眼视觉感知以及显示设备之间的非线性差异,从而让最终呈现的图像更加自然、通透。

Gamma校正的核心作用

适配人眼视觉特性:相机传感器(CMOS/CCD)捕获的光信号与入射光强呈线性关系,但人眼对亮度的感知是非线性的(对暗部细节更敏感,对亮部相对迟钝)。Gamma校正通过非线性映射,将更多的数据位分配给人眼敏感的暗部和中间调,使图像观感更符合人类视觉习惯。

2.Gamma校正算法的原理

图像传感器输出与光强成线性关系(线性域),但人眼对亮度的感知是对数/幂次关系,显示器也有自己的伽马特性。Gamma 校正通过幂函数将线性图像映射到感知均匀的空间:

复制代码
output = (input / maxval)^γ × maxval

γ = 0.5(即平方根)是 sRGB 的简化近似,实际 sRGB 使用分段函数。

下面我用desmos画出了常用γ的函数图像,内心感受一下,从函数的形状能看到,当γ值越小. 因为函数是凸函数,所以映射后的像素值会更大,反映在图像上是"图像更亮" .相反,当γ越大,因为图像是一个凹函数,映射后的像素值会更小,反映在图像上是"图像更暗"

如下图

  • 下图中的图像γ为1,就是一条直线,像素值不会被拉伸.
  • 下图左γ=0.5,可以从函数图像就能看出,像素值被拉伸到更大的值
  • 下图右γ=2,中间的像素被压低了,图像偏暗.

查找表(LUT)实现

根据上面的公式

复制代码
output = (input / maxval)^γ × maxval

这里假设图像是10bit,即色彩范围是0-1023. 预先计算全部输入值对应的输出值,存入字典,运行时直接查表:(关于这个构建更详细的解释,再测试代码中也有提到)

python 复制代码
ind = range(0, 1024)
val = [round((i/1024)^0.5 * 1024) for i in ind]
lut = dict(zip(ind, val))

这里我们画出(i/1024)^0.5 * 1024图像, 输入值最大为1024. 因为i∈(0,1024),结合上面的图像,理解一下.

3.算法代码实现

  • 支持 'rgb' 模式(R/G/B 共用同一 LUT)和 'yuv' 模式(Y/UV 分开)
  • RGB 模式输出除以 4,将 10-bit (0~1023) 映射到 8-bit (0~255)
  • 逐像素查表,计算量 O(H×W)
python 复制代码
class GC:
    'Gamma Correction'

    def __init__(self, img, lut, mode):
        self.img = img
        self.lut = lut
        self.mode = mode

    def execute(self):
        img_h = self.img.shape[0]
        img_w = self.img.shape[1]
        img_c = self.img.shape[2]
        gc_img = np.empty((img_h, img_w, img_c), np.uint16)
        for y in range(self.img.shape[0]):
            for x in range(self.img.shape[1]):
                if self.mode == 'rgb':
                    gc_img[y, x, 0] = self.lut[self.img[y, x, 0]]
                    gc_img[y, x, 1] = self.lut[self.img[y, x, 1]]
                    gc_img[y, x, 2] = self.lut[self.img[y, x, 2]]
                    gc_img[y, x, :] = gc_img[y, x, :] / 4 # 映射到0-255
                elif self.mode == 'yuv':
                    gc_img[y, x, 0] = self.lut[0][self.img[y, x, 0]]
                    gc_img[y, x, 1] = self.lut[1][self.img[y, x, 1]]
                    gc_img[y, x, 2] = self.lut[1][self.img[y, x, 2]]
        self.img = gc_img
        return self.img

4.测试代码

测试代码1(限制像素值在(0-255)

构建GC 查找表LUT, 然后在GC算法中,根据像素值索引查找映射后的值.

python 复制代码
    def _build_lut(self, bw=10, gamma=0.5):
        """构建 gamma LUT(与主程序一致)。"""
        maxval = pow(2, bw)
        ind = range(0, int(maxval))
        val = [round(pow(float(i) / maxval, gamma) * maxval) for i in ind]
        return dict(zip(ind, val))

上面最关键的是这句号 val = [round(pow(float(i) / maxval, gamma) * maxval) for i in ind]

  • float(i) / maxval:将当前的整数像素值归一化到 0,1 的浮点数范围内。
  • pow(..., gamma):对归一化后的值进行 Gamma 幂运算。
  • maxval:将运算结果重新映射回 0,1023 的整数范围。
  • round(...):四舍五入取整,确保输出值是合法的像素整数。
python 复制代码
  • 测试效果图
    这里γ等于0.5,按理说图像应该更亮,这是因为GC算法内部,对RGB各分量都做了一个➗4的操作.这是想让各通道都在(0,255)之间,所以看到右边图像没有左边亮.

测试代码2(解除限制验证正确性)

下面我把(r=20, g=80, b=20)各分量都改小一点,去掉GC算法的除4操作,

python 复制代码
    def test_output_shape(self):
        """输出形状应与输入相同。"""
        img = make_rgb(88, 88, r=20, g=80, b=20)
        lut = self._build_lut()
        gc = GC(img.copy(), lut, 'rgb')
        out = gc.execute()
        show_RGB_RGB_images(img, out, "left", "right-shape")

去掉除4操作

-测试效果

如预期一样,图像变的更亮了.

相关推荐
fai厅的秃头姐!13 小时前
OpenCV-基础篇
人工智能·opencv·计算机视觉
丨白色风车丨13 小时前
OpenCV 基于 HSV 颜色空间实时颜色分割(摄像头蓝色识别)
人工智能·opencv·计算机视觉·hsv颜色识别·python计算机视觉
深度研习笔记16 小时前
OpenCV工业视觉实战10|违规语音播报+弹窗告警+重复告警过滤,打造智能声光预警系统(项目加分必备)
人工智能·opencv·计算机视觉
AI人工智能+16 小时前
银行回单识别技术通过AI驱动的智能文档理解方案,实现财务处理的革命性升级
深度学习·计算机视觉·自然语言处理·ocr·银行回单识别
hhzz18 小时前
CNN图像分类入门:基于TensorFlow+Keras的CIFAR-10数据集全流程实战
图像处理·计算机视觉·ai·cnn·大模型
科技林总1 天前
图像处理领域的技术发展
人工智能·深度学习·计算机视觉
神奇霸王龙2 天前
可审计 Agent 对比:5 旗舰步骤回放
网络·图像处理·人工智能·python·ai·ai编程
2zcode2 天前
项目文档:基于MATLAB图像处理与FCM聚类的骨癌X射线影像辅助检测系统设计与实现
图像处理·matlab·聚类
在世修行3 天前
二值化技术详解
图像处理·opencv·计算机视觉·二值化
雪的季节3 天前
Qt +C++ OpenCV+YOLO ONNX+PyTorch 整套技术栈分层掌握标准
计算机视觉