ISP之AWB(auto white balance)

为了方便显示效果本文把自动白平衡(AWB)和Demosaic(bayer域转为RGB)同时实现。

一、AWB

为什么需要做WB

人的视觉系统具有颜色恒常性(Color Constancy),能从变化的光照环境和成像条件下获取物体表面颜色的不变特性。

但成像设备不具有这样的调节功能,如彩色相机内部有三个CCD电子耦合元件,分别用来感受红绿蓝三种颜色的光线,默认情况下,三个颜色的感光电路信号的放大比例是1:1:1的。在理想的拍摄环境下,纯白色的RGB分量按照1:1:1的比例放大之后,得到的是没有偏色的白色,

但大多数拍摄环境为非理想环境,那白色成像出来的效果就不是纯白,会偏向环境光的颜色。也就是说,不同的光照环境会导致采集的图像颜色与真实颜色存在一定程度的偏差,需要选择合适的颜色平衡(校正)算法,消除光照环境对颜色显现的影响。让受环境光影响的白色还原成纯白色,保证在各种光线条件下,成像色彩跟物体真实的色彩保持一致。

二、白平衡的传统实现方法

1、基于灰度世界的白平衡矫正 

(1)理论基础

灰度世界法的白平衡基于这样一个假设:当图像中色彩复杂丰富,图片红/绿/蓝通道的均值相等。

算法实现分为以下几个部分

1)分别计算R/G/B三通道的均值Rmean,Bmean,Gmean

2)计算三通道均值的均值Kmean=(Rmean + Bmean + Gmean)/3

3)计算三通道的gain值:Rgain = Kmean/Rmean,Bgain = Kmean/Bmean,Ggain = Kmean/Gmean

(2)代码实现

    img1 = np.fromfile(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.dng',dtype = 'uint16')
    x = cv2.imread(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.jpg')
    a = len(img1)
    b = 4032 * 3024
    #dng与raw的差别是,dng有头部信息,需要剪掉头部信息才是真实像素值,原图为10bit图像
    img_tmp = img1[a-b:a].reshape(3024,4032)
    img_wb = np.zeros((3024,4032))
    #LSC_gain为之前LSC求得的gain
    img = img_tmp * LSC_gain
    #拆分通道
    B0 = img[::2, ::2]
    GB0 = img[::2, 1::2]
    GR0 = img[1::2, ::2]
    R0 = img[1::2, 1::2]


    ##########基于灰度世界-start
    #计算各通道的平均值

    G0 = (GB0 + GR0)/2

    Bmean = np.mean(B0)
    Gmean = np.mean(G0)
    Rmean = np.mean(R0)

    #计算系数
    K = (Bmean + Gmean + Rmean)/3

    #计算各通道的gain值
    Kb = K/Bmean
    Kg = K/Gmean
    Kr = K/Rmean

    #根据各通道的gain值更新各通道的值
    B1 = Kb * B0
    G1 = Kg * G0
    R1 = Kr * R0

    B = np.zeros((3024,4032))
    G = np.zeros((3024,4032))
    R = np.zeros((3024,4032))
    
    #Demosaic--start

    scale = B0.shape[0]/B.shape[0]

    img_new = np.zeros((3024, 4032, 3))
    img_new.astype(int)

    for j in range(B.shape[1]):
        for i in range(B.shape[0]):
            src_x = i * scale
            src_y = j * scale
            x = int(math.floor(src_x))
            y = int(math.floor(src_y))

            u = src_x - x
            v = src_y - y

            if x == 1511:
                x = 1510
            if y == 2015:
                y = 2014

            R[i, j] = (1 - u) * (1 - v) * R1[x, y] + (1 - u) * v * R1[x, y + 1] + u * (1 - v) * \
                      R1[x + 1, y] + u * v * R1[x + 1, y + 1]
            G[i, j] = (1 - u) * (1 - v) * G1[x, y] + (1 - u) * v * G1[x, y + 1] + u * (1 - v) * \
                     G1[x + 1, y] + u * v * G1[x + 1, y + 1]
            B[i, j] = (1 - u) * (1 - v) * B1[x, y] + (1 - u) * v * B1[x, y + 1] + u * (1 - v) * \
                         B1[x + 1, y] + u * v * B1[x + 1, y + 1]


#10bit转为8bit
    img_new[:, :, 0] = B/4
    img_new[:, :, 1] = G/4
    img_new[:, :, 2] = R/4

    plt.subplot(2, 3, 1)
    plt.imshow(img, 'gray')
    plt.subplot(2, 3, 2)
    plt.imshow(img_new)
    plt.subplot(2, 3, 3)
    plt.imshow(R)
    plt.subplot(2, 3, 4)
    plt.imshow(G)
    plt.subplot(2, 3, 5)
    plt.imshow(B)

    plt.show()

(3)结果展示

2、基于完美反射的白平衡矫正

(1)理论基础

基于完美反射的白平衡矫正是基于这样一个假设:白点所展示的颜色就是环境光的属性。所以假设图像中最亮的点为白点,并以这个白点为基础计算出gain值做矫正。

算法实现分为以下几个部分

1)分别计算R/G/B三通道的最大值Rmax,Bmax,Gmax

2)计算三通道均值的均值Rgain = max(Rmax,Bmax,Gmax)/Rmax;

Ggain = max(Rmax,Bmax,Gmax)/Gmax;Bgain = max(Rmax,Bmax,Gmax)/Bmax

(2)代码实现

    img1 = np.fromfile(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.dng',dtype = 'uint16')
    x = cv2.imread(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.jpg')
    a = len(img1)
    b = 4032 * 3024
    #dng与raw的差别是,dng有头部信息,需要剪掉头部信息才是真实像素值,原图为10bit图像
    img_tmp = img1[a-b:a].reshape(3024,4032)
    img_wb = np.zeros((3024,4032))
    #LSC_gain为之前LSC求得的gain
    img = img_tmp * LSC_gain
    #拆分通道
    B0 = img[::2, ::2]
    GB0 = img[::2, 1::2]
    GR0 = img[1::2, ::2]
    R0 = img[1::2, 1::2]
###########基于完美反射-start
    G1 = (GB1 + GR1)/2
    RGB_sum = B1 + G1 + R1

    #RGB_max = np.max(RGB_sum)
    R1max = np.max(R1)
    G1max = np.max(G1)
    B1max = np.max(B1)

    r = 1
    b = 1
    g = 1

    Rmean = 0
    Bmean = 0
    Gmean = 0


    for j in range(R1.shape[1]):
        for i in range(R1.shape[0]):

            R1_ratio = R1max * 0.95
            G1_ratio = G1max * 0.95
            B1_ratio = B1max * 0.95


            if(R1[i, j] > R1_ratio):
                r = r + 1
                Rmean = Rmean + R1[i,j]
            if (G1[i, j] > G1_ratio):
                g = g + 1
                Gmean = Gmean + G1[i, j]
            if (B1[i, j] > B1_ratio):
                b = b + 1
                Bmean = Bmean + B1[i, j]

            Rmax = Rmean / r
            Gmax = Gmean / g
            Bmax = Bmean / b

    #Rmax = Rmean/r
    #Gmax = Gmean/g
    #Bmax = Bmean/b

    RGB_max = np.max([Rmax,Gmax,Bmax])
    Rgain = RGB_max/Rmax
    Bgain = RGB_max/Bmax
    Ggain = RGB_max/Gmax
    print(RGB_max)
    print(Rgain)
    print(Bgain)
    print(Ggain)


    B = np.zeros((3024,4032))
    G = np.zeros((3024,4032))
    R = np.zeros((3024,4032))

    scale = B1.shape[0] / B.shape[0]
    for j in range(B.shape[1]):
        for i in range(B.shape[0]):
            src_x = i * scale
            src_y = j * scale
            x = int(math.floor(src_x))
            y = int(math.floor(src_y))

            u = src_x - x
            v = src_y - y

            if x == 1511:
                x = 1510
            if y == 2015:
                y = 2014

            R[i, j] = int((1 - u) * (1 - v) * R1[x, y] + (1 - u) * v * R1[x, y + 1] + u * (1 - v) * \
                      R1[x + 1, y] + u * v * R1[x + 1, y + 1])
            G[i, j] = int((1 - u) * (1 - v) * G1[x, y] + (1 - u) * v * G1[x, y + 1] + u * (1 - v) * \
                           G1[x + 1, y] + u * v * G1[x + 1, y + 1])
            B[i, j] = int((1 - u) * (1 - v) * B1[x, y] + (1 - u) * v * B1[x, y + 1] + u * (1 - v) * \
                         B1[x + 1, y] + u * v * B1[x + 1, y + 1])

    img_new = np.zeros((3024, 4032, 3))

    img_new[:, :, 0] = (B*Bgain)/4
    img_new[:, :, 1] = (G*Ggain)/4
    img_new[:, :, 2] = (R*Rgain)/4

    plt.subplot(2, 3, 1)
    plt.imshow(img, 'gray')
    plt.subplot(2, 3, 2)
    plt.imshow(img_new)
    plt.subplot(2, 3, 3)
    plt.imshow(R)
    plt.subplot(2, 3, 4)
    plt.imshow(G)
    plt.subplot(2, 3, 5)
    plt.imshow(B)

(3)结果展示

3、结合灰度世界和完美反射的白平衡矫正

(1) 理论基础

基于完美反射和灰度世界的白平衡矫正各有优缺点,所以就有人提出了结合完美反射和灰度世界的的白平衡矫正理论:对灰度世界和完美反射做线性运算

算法实现包括下面几步:

1)分别计算R/G/B三通道的均值和平均值Rmax,Bmax,Gmax,Rmean,Bmean,Gmean

2)Rcoe1 = Ur*Rmean^2 +Vr*Rmean,Rcoe2 = Ur*Rmax^2 +Vr*Rmax

3)Kmean=(Rmean + Bmean + Gmean)/3,Kmax = max(Rmax,Bmax,Gmax) 

4)Rcoe1 = Kmean,Rcoe2 = Kmax,可求得Ur和Vr

5)Rnew = Ur*R + Vr*R,其他通道同理

(2)代码实现

    img1 = np.fromfile(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.dng',dtype = 'uint16')
    x = cv2.imread(r'/Users/Desktop/ISP/AWB/IMG_20230808_155222.jpg')
    a = len(img1)
    b = 4032 * 3024
    #dng与raw的差别是,dng有头部信息,需要剪掉头部信息才是真实像素值,原图为10bit图像
    img_tmp = img1[a-b:a].reshape(3024,4032)
    img_wb = np.zeros((3024,4032))
    #LSC_gain为之前LSC求得的gain
    img = img_tmp * LSC_gain
    #拆分通道
    B0 = img[::2, ::2]
    GB0 = img[::2, 1::2]
    GR0 = img[1::2, ::2]
    R0 = img[1::2, 1::2]
###结合灰度世界和完美反射的白平衡start
    G0 = (GR0 + GB0)/2
    Bmean = np.mean(B0)
    Gmean = np.mean(G0)
    Rmean = np.mean(R0)
    Kmean = (Bmean + Gmean + Rmean)/3

    R0max = np.max(R0)
    G0max = np.max(G0)
    B0max = np.max(B0)

    r = 1
    b = 1
    g = 1

    R_max_mean = 0
    G_max_mean = 0
    B_max_mean = 0


    for j in range(R0.shape[1]):
        for i in range(R0.shape[0]):

            R0_ratio = R0max * 0.95
            G0_ratio = G0max * 0.95
            B0_ratio = B0max * 0.95


            if(R0[i, j] > R0_ratio):
                r = r + 1
                R_max_mean = R_max_mean + R0[i,j]
            if (G0[i, j] > G0_ratio):
                g = g + 1
                G_max_mean = G_max_mean + G0[i, j]
            if (B0[i, j] > B0_ratio):
                b = b + 1
                B_max_mean = B_max_mean + B0[i, j]


            Rmax = R_max_mean / r
            Gmax = G_max_mean / g
            Bmax = B_max_mean / b

            Kmax = (Rmax + Gmax + Bmax)/3


    Rcoe = np.array([[Rmean * Rmean, Rmean],[Rmax * Rmax, Rmax]])
    Bcoe = np.array([[Bmean * Bmean, Bmean], [Bmax * Bmax, Bmax]])
    Gcoe = np.array([[Gmean * Gmean, Gmean], [Gmax * Gmax, Gmax]])



    K = np.array([Kmean, Kmax])
    #Rcoe取逆
    Pr = np.dot(np.linalg.inv(Rcoe), K)
    Pb = np.dot(np.linalg.inv(Bcoe), K)
    Pg = np.dot(np.linalg.inv(Gcoe), K)

    R1 = Pr[0] * R0 * R0 + Pr[1] * R0
    B1 = Pb[0] * B0 * B0 + Pb[1] * B0
    G1 = Pg[0] * G0 * G0 + Pg[1] * G0

    B = np.zeros((3024,4032))
    G = np.zeros((3024,4032))
    R = np.zeros((3024,4032))

    scale = B1.shape[0] / B.shape[0]
    for j in range(B.shape[1]):
        for i in range(B.shape[0]):
            src_x = i * scale
            src_y = j * scale
            x = int(math.floor(src_x))
            y = int(math.floor(src_y))

            u = src_x - x
            v = src_y - y

            if x == 1511:
                x = 1510
            if y == 2015:
                y = 2014

            R[i, j] = int((1 - u) * (1 - v) * R1[x, y] + (1 - u) * v * R1[x, y + 1] + u * (1 - v) * R1[x + 1, y] + u * v * R1[x + 1, y + 1])
            G[i, j] = int((1 - u) * (1 - v) * G1[x, y] + (1 - u) * v * G1[x, y + 1] + u * (1 - v) * G1[x + 1, y] + u * v * G1[x + 1, y + 1])
            B[i, j] = int((1 - u) * (1 - v) * B1[x, y] + (1 - u) * v * B1[x, y + 1] + u * (1 - v) * B1[x + 1, y] + u * v * B1[x + 1, y + 1])



    img_new = np.zeros((3024, 4032, 3))
    img_new[:, :, 0] = B/4
    img_new[:, :, 1] = G/4
    img_new[:, :, 2] = R/4

    plt.subplot(2, 3, 1)
    plt.imshow(img, 'gray')
    plt.subplot(2, 3, 2)
    plt.imshow(img_new)
    plt.subplot(2, 3, 3)
    plt.imshow(R)
    plt.subplot(2, 3, 4)
    plt.imshow(G)
    plt.subplot(2, 3, 5)
    plt.imshow(B)

    plt.show()
    cv2.imwrite('/Users/Downloads/0522/AWB_correct.jpg', img_new)

(3)结果展示
​​​​​​​

  • 25
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值