• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

参考Unprocessing Images for Learned Raw Denoising实现RGB图转RAW图

武飞扬头像
三火-sy
帮助1

开头

对Unprocessing Images for Learned Raw Denoising中sRGB转raw图过程的实践分析,对应源码和论文重新用numpy实现,分析过程就是sRGB转RAW的过程,与ISP的正好相反,这种方法可以转成RAW图,但是会有一些损失,比如色调映射提出的曲线不大可能符合所有ISP输出图片和在mosaic的时候会损失掉一部分像素值,另外,论文里的方法输出的是R,Gr,Gb,B四通道的图像,为了方便保存和观察,我在最后会按照R,Gr,Gb,B的排列输出为单通道的图像(depack)。

色调映射

因为高动态范围的图像要显示出来需要经过色调映射,甚至普通的低动态范围的图像也会经过色调映射,所以如果想要还有raw图,需要对这一过程进行逆向,但是不同的图片使用的色调映射方法不同,很难对所有的图像做与其对应的方法的逆向工程,原文对此的处理办法是,假设所有图片都是用一个简单的平滑曲线做色调映射,然后对该过程逆向处理,并且输出的值需要在0~1之间。
论文中假设的曲线及逆向如图:
学新通
numpy版本代码如下,过于简单不再多嘴:

def inverse_smoothstep(image):
    img = np.clip(image, 0.0, 1.0)
    return 0.5 - np.sin(np.arcsin(1.0 - 2.0 * img) / 3)

原图:
学新通
逆向后的图:
学新通

伽马压缩

因为人对图片中暗区域的梯度变化更加敏感,所以会使用伽马压缩将为图片的暗区域分配更多的动态范围位(bits of dynamic range),该过程论文作者使用了和和Benchmarking denoising algorithms with real photographs. CVPR, 2017相同的标准伽马曲线:
学新通
对图片做该过程的逆向操作:
学新通
在做这一操作之前将图片像素值范围限制在10^-8以上。
numpy版本代码如下,

def gamma_expansion(image):
    return np.maximum(image, 1e-8) ** 2.2

该过程逆向后的图:
学新通

颜色校正

颜色校正是为了将senor的颜色空间转变为SRGB的颜色空间,需要一个3*3的颜色校正矩阵(CCM)来完成,论文作者使用的数据集有四个摄像头拍摄,为了恢复的raw图可以兼具四个摄像头的特性,作者对这四个摄像头各自的CCM的凸组合进行随 机采样。
从四个摄像头的CCM中采样的过程:

def random_ccm():

    xyz2cams = [[[1.0234, -0.2969, -0.2266],
                 [-0.5625, 1.6328, -0.0469],
                 [-0.0703, 0.2188, 0.6406]],
                [[0.4913, -0.0541, -0.0202],
                 [-0.613, 1.3513, 0.2906],
                 [-0.1564, 0.2151, 0.7183]],
                [[0.838, -0.263, -0.0639],
                 [-0.2887, 1.0725, 0.2496],
                 [-0.0627, 0.1427, 0.5438]],
                [[0.6596, -0.2079, -0.0562],
                 [-0.4782, 1.3016, 0.1933],
                 [-0.097, 0.1581, 0.5181]]]
    num_ccms = len(xyz2cams)
    xyz2cams = np.array(xyz2cams)
    weights = np.random.uniform(1e-8, 1e8, (num_ccms, 1, 1))
    weights_sum = np.sum(weights)
    xyz2cam = np.sum(xyz2cams * weights, axis=0) / weights_sum
    rgb2xyz = np.array([[0.4124564, 0.3575761, 0.1804375],
                        [0.2126729, 0.7151522, 0.0721750],
                        [0.0193339, 0.1191920, 0.9503041]])

    rgb2cam = np.matmul(xyz2cam, rgb2xyz)

    rgb2cam = rgb2cam / np.sum(rgb2cam)
    return rgb2cam

学新通

对图片进行颜色校正:

def apply_ccm(image, ccm):
    shape = np.shape(image)
    image = np.reshape(image, [-1, 3])
    image = np.tensordot(image, ccm, axes=[[-1], [-1]])
    return np.reshape(image, shape)

该过程逆向后的图:
学新通

白平衡和数值增益

相机会对拍出的图片的数值乘上一个数字增益,同样这个增益受到自动曝光等影响是一个未知的值,如果对某一个图片单独做该过程的逆过程是很困难的,但是作者考虑到,如果是两组数据,这两组数据假设都可以在一个指数分布中采样,就可以用这两组数据的像素值均值之比来表示这两组图片的数字增益,作者的得到的值是1.25,取逆增益为0.8,为了更加真实,作者从均值0.8,标准差0.1的均匀分布中采样得到这个逆增益
相机拍摄的图片的颜色是环境中光的颜色和拍摄物本身的颜色的乘积,白平衡的作用就是尽可能的去除掉光的颜色的影响,逆转该过程也是比较困难的,因为拍摄图片的曝光和白平衡方法是未知的,但是论文作者使用的数据集里面有与白平衡的元数据,通过分析这些数据得到,红增益从[1.9, 2.4]中采样,蓝增益从[1.5, 1.9]采样
如果直接将得到的白平衡和数字增益的值求逆然后处理图像的话,会造成结果整体偏暗,降低饱和会影响到后面的去噪,所以作者提出来了一个保持高亮部分变换的函数(highlight-preserving transformation):
学新通
该函数可以在像素值比较低的情况下,乘以一个逆增益也就是增益小于1的值,会让其变小,而在高亮情况下,像素值较大,则可以保留原值,如下:
学新通
numpy版本代码如下,
获取增益:

def random_gains():
    rgb_gain = 1.0 / np.random.normal(loc=0.8, scale=0.1)

    red_gain = np.random.uniform(1.9, 2.4)
    blue_gain = np.random.uniform(1.5, 1.9)
    return rgb_gain, red_gain, blue_gain

逆向处理图像:

def safe_invert_gains(image, rgb_gain, red_gain, blue_gain):
    gains = np.stack([1.0 / red_gain, 1.0, 1.0 / blue_gain]) / rgb_gain
    gains = gains[np.newaxis, np.newaxis, :]

    gray = np.mean(image, axis=-1)
    inflection = 0.9
    mask = (np.maximum(gray - inflection, 0.0) / (1.0 - inflection)) ** 2.0
    mask = mask[:, :, np.newaxis]
    safe_gains = np.maximum(mask   (1.0 - mask) * gains, gains)  # (1,480,3)
    return image * safe_gains

逆向该过程处理效果:
学新通

去马赛克

普通RGB图片是三通道图片,图片的颜色由R,G,B确定,RAW图会有R,Gr,Gb,B四种颜色信息,RAW转RGB的过程会通过下采样将R,Gr,Gb,B输出为R,G,B,逆向这个过程很简单,采样RGB图中的各个通道颜色,然后输出R,Gr,Gb,B即可,论文中输出的为长宽均为原图1/2,四通道的图像。
numpy版本实现如下:

def mosaic(image):
    shape = np.shape(image)
    red = image[0::2, 0::2, 0]
    green_red = image[0::2, 1::2, 1]
    green_blue = image[1::2, 0::2, 1]
    blue = image[1::2, 1::2, 2]
    image = np.stack((red, green_red, green_blue, blue), axis=-1)
    image = np.reshape(image, (shape[0] // 2, shape[1] // 2, 4))
    return image

该过程处理后的图:
学新通

结尾

论文是对图片归一化之后将其处理成一个四通道的raw图,以便可以输入网络训练去噪模型,为了方便保存和观察,需要将其它变成单通道,即将四个通道以R,Gr,Gb,B方式排列为单通道图像,处理后图像大小会和未处理为raw图的原图一样。
处理代码为:

def depack_rggb_raw(raw, byter):
    out = np.zeros((raw.shape[0] * 2, raw.shape[1] * 2))

    # 按R,Gr,Gb,B的顺序赋值给out数组,最后out就是raw数据的单通道数组
    out[0::2, 0::2] = raw[:, :, 0]
    out[1::2, 0::2] = raw[:, :, 1]
    out[0::2, 1::2] = raw[:, :, 2]
    out[1::2, 1::2] = raw[:, :, 3]

    output = out * (2 ** byter)  # 恢复到byter位
    output = output.astype(np.uint16)

    return output

处理结果为:
学新通
附带原始图片读取代码:

def read_image(image_path):
    image = cv2.imread(image_path)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    return image.astype(np.float32) / 255

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcfgfj
系列文章
更多 icon
同类精品
更多 icon
继续加载