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操作

-测试效果

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

相关推荐
大江东去浪淘尽千古风流人物1 小时前
【VGGT-Ω】前馈式3D重建的规模化之路:Register Attention、自监督训练与10B参数Scaling Law深度解析
深度学习·计算机视觉·transformer·slam·vio·3d重建
断眉的派大星2 小时前
YOLO26 完整学习笔记:从 Anchor-Free、TAL、STAL 到端到端无 NMS 部署
人工智能·笔记·学习·yolo·目标检测·计算机视觉·目标跟踪
人月神话-Lee3 小时前
【图像处理】颜色空间——RGB之外的世界
图像处理·人工智能·ios·ai编程·swift·rgb·颜色空间
却道天凉_好个秋3 小时前
HEVC(六):CTC
人工智能·计算机视觉·hevc·ctc
zh路西法4 小时前
【三维重建NeRF本地部署】基于 Nerfstudio 的单目视频三维重建全流程
图像处理
惊鸿一博4 小时前
图像修复_MPMF-Net中的“多维特征交互块”(Multi-dimension Feature Interaction Block, MFIB)
图像处理·深度学习
FOORIR 客流统计5 小时前
客流统计系统的工程实现:从线穿越计数到多目标跟踪
人工智能·计算机视觉·目标跟踪
YOLO数据集集合5 小时前
无人机电力巡检图像数据集 | 输电线路故障智能识别 深度学习目标检测数据集实战
人工智能·深度学习·目标检测·计算机视觉·无人机
9527华安5 小时前
FPGA实现GTP高速收发器2路视频传输,基于aurora 8b10b编解码架构,提供4套工程源码和技术支持
图像处理·fpga开发·aurora·高速收发器·8b10b·derdes