python实现相机成像原理

本文介绍了使用Python处理相机RAW图像的过程,包括CR2转TIFF、图像初始化、线性化、判断拜耳滤波阵列模式、白平衡、去马赛克、降噪以及亮度和gamma编码的调整。通过这些步骤,将RAW图像转化为可观察的RGB图像。

摄像的流程:从被拍摄景物上传来的入射光依次穿过相机的光学器件,颜色滤镜后到达传感器,经过模拟前端转换为数字信号,最后通过相机内的图像处理系统转换为最终的RGB格式图像并进行存储。

本次实验要实现的就是:相机内的图像处理系统。

即下图红色区域。

什么是Raw图像:经过颜色滤波阵列(CFA)到达传感器的光线,经过模拟前端放大和模数转换器的转换的图像称为RAW图像。RAW图像没有颜色,显示出像马赛克一样的方块。就像下图:

 

 

 

1、将一张.CR2图片变成.tiff格式的图片

首先第一步将raw image 变成tiff文件,因为cr2格式的图像不能被skimage处理。

用dcraw这个工具,在cmd(windows操作系统下)里面敲:dcraw -4 -d -v -T banana_slug.CR2得到banana_slug.CR2.tiff

以及相关的色彩信息。

Scaling with darkness <black>, saturation <white>, and
multipliers <r_scale> <g_scale> <b_scale> <g_scale>
 

执行了这一步会生成一个勉强还能看得见的图,但是指示里面也说不要用这个图,咱就照着呗。

再次执行dcraw -4 -D -T bata\banana_slug.cr2

原来生成的那个图,被一个黑黑的图覆盖掉了,指示说咱就用这个图,继续后面的操作。(咋感觉是在坑我)

 

2、初始化图片,查看图片的长宽,位深度,把图片变成线性,双精度图片。


img = io.imread(".\data\\banana_slug.tiff") #4290*285
plt.imshow(img)
plt.show()

用io.imread()读那个Windows里面显示为黑黑的图,竟然能看得见。


def initials(img):
    print("图片类型type(image)=", type(img))
    print("像素位数image.dtype=", img.dtype)
    print("形状:",img.shape)
    width = img.shape[0]
    height = img.shape[1]
    print("图片的width=%d,height=%d" % (img.shape[0], img.shape[1]))
    print("***********************************")
    img_array = img.astype(np.double) #感觉这样应该算转化为双精度了吧
    print("数组位数:", img_array.dtype)
    print("数组的尺寸:", img_array.shape)
    print("数组最大值:",np.max(img_array))
    print("数组平均值:",np.mean(img_array))

 

3、线性化

利用了cv2.normalize()函数和np.clip()函数


def linearization(imarray):
    my_matrix_normorlize=imarray
    np.clip(imarray,2047,13584)
    cv2.normalize(imarray,my_matrix_normorlize,norm_type=cv2.NORM_MINMAX)
    np.clip(my_matrix_normorlize,0,1)


    return my_matrix_normorlize

 

感觉图片没啥变化,黑得发紫。

 

4、判断拜耳颜色滤波阵列是那种?

 

指示说看左上角的2*2的图片文件就知道是哪种了,我咋就看不出来呢,全被一片黑紫色掩盖了。

然后左上角2*2的矩阵(0,0)(0,1)(1,0)(1,1)的数据是:

0.01616419818058793
0.029245921359296302
0.03044883843320052
0.015562739643635824

bayer模式人眼对绿色比较敏感,所以一般绿色格式的像素是红色和蓝色格式像素的和。所以可以判断出(0,1)和(1,0)是绿色滤镜,在模式“rggb”和“bggr”中选择。

不知道怎么选了,假设是模式2吧!“rggb”

 

 

5、白平衡

什么是白平衡呢?我们都知道人眼有色彩恒常性,白平衡就是相机的色彩恒常性,即在不同环境下,根据得到的物体颜色,想办法恢复物体的固有色

疑惑:网上所有方法都是针对RGB三个通道的,即它是先进行插值,再来做白平衡,但是我们的图像经线性变换后的图依旧是一个通道的(混合了红绿蓝三色像素),我要怎么获得它的各个通道的值。

前面既然让你判断拜耳模式,就是让你从一个通道里面抠出r,g,b。主要有两种方法:灰色世界的假设和白色世界的假设。

5.1灰色世界的假设(假设场景的平均颜色是灰色)

核心思想:假设场景的平均颜色是灰色。

具体步骤就是:

1、求每个通道的均值avgR,avgG,avgB。

2、均值求平均得到我们的灰色值Gray=(avgR+avgG+avgB)/3,然后求每个通道的增益系数kr=Gray/avgR , kg=Gray/avgG , kb=Gray/avgB

3、利用增益系数求出每个通道新的值(NR=R * kr,NG=G * kg,NB = B * kb)

基于灰度世界假设的白平衡:

def greyworld(img):
    width = img.shape[0]
    height = img.shape[1]
    r_total,b_total,g_total=0,0,0
    r_count,b_count,g_count=0,0,0
    #假设是"rggb模式"
    for i in range(width):
        for j in range(height):
            if i%2!=0 and j%2!=0:
                b_total+=img[i][j]
                b_count+=1
            if i%2==0 and j%2==0:
                r_total+=img[i][j]
                r_count+=1
            if (i%2!=0 and j%2==0) or (i%2==0 and j%2!=0):
                g_total+=img[i][j]
                g_count+=1
    print("b,r,g count:",b_count,r_count,g_count)

    r_avg=b_total/b_count
    g_avg=g_total/g_count
    b_avg=b_total/b_count
    print("b,r,g的mean",b_avg,r_avg,g_avg)
    K=(r_avg+g_avg+b_avg)/3
    kr=K/r_avg
    kg=K/g_avg
    kb=K/b_avg
    for i in range(width):
        for j in range(height):
            if i%2!=0 and j%2!=0:
                img[i][j]*=kb
            if i%2==0 and j%2==0:
                img[i][j] *= kr
            if (i%2!=0 and j%2==0) or (i%2==0 and j%2!=0):
                img[i][j] *= kg
    return img

 

5.2白色世界的假设(假设场景中最亮的颜色是白色)

核心思想:假设场景中最亮的点即为白色。

具体步骤:

1、分别计算每个通道的最大值maxR,maxG,maxB

2、对最大值取平均,得到一个值,然后求各通道的增益系数。

3、利用增益系数求出每个通道新的值。

def whiteworld(img):
    width = img.shape[0]
    height = img.shape[1]
    # 假设是"rggb模式"
    b_max,r_max,g_max=0,0,0
    for i in range(width):
        for j in range(height):
            if i % 2 != 0 and j % 2 != 0:
                b_max = max(img[i][j],b_max)
            if i % 2 == 0 and j % 2 == 0:
                r_max = max(img[i][j], r_max)
            if (i % 2 != 0 and j % 2 == 0) or (i % 2 == 0 and j % 2 != 0):
                g_max = max(img[i][j],g_max)
    M=(r_max+g_max+b_max)/3
    M_r=M/r_max
    M_g=M/g_max
    M_b=M/b_max
    for i in range(width):
        for j in range(height):
            if i%2!=0 and j%2!=0:
                img[i][j]*=M_b
            if i%2==0 and j%2==0:
                img[i][j] *= M_r
            if (i%2!=0 and j%2==0) or (i%2==0 and j%2!=0):
                img[i][j] *= M_g

    return img

实验结果:左边是灰度世界白平衡,右边是白色世界白平衡。

 

6.去马赛克

我们经过白平衡的图片,依旧是单通道的图像,只是一个通道里汇聚了红绿蓝三种颜色的信息,现在我们需要对每个颜色通道进行插值处理,从而能得到完整的红色通道,绿色通道和蓝色通道。我们主要采用双线性插值的方法。

def demosacing(img):
    # print(img)
    # interpolate.interp2d()
    width = img.shape[0]
    height = img.shape[1]
    # 假设是"rggb模式"
    b_channel, r_channel, g_channel = img.copy(),img.copy(),img.copy()
    for i in range(width):
        for j in range(height):
            if i % 2 != 0 and j % 2 != 0:#b的 位置
                if 0<i<2855 and 0<j<4289:
                    g_channel[i][j]=(img[i][j-1]+img[i-1][j]+img[i+1][j]+img[i][j+1])/4
                    r_channel[i][j]=(img[i-1][j-1]+img[i-1][j-1]+img[i+1][j+1]+img[i+1][j+1])/4
            if i % 2 == 0 and j % 2 == 0:#r的位置
                if 0<i<2855 and 0<j<4289:
                    g_channel[i][j]=(img[i][j-1]+img[i-1][j]+img[i+1][j]+img[i][j+1])/4
                    b_channel[i][j]=(img[i-1][j-1]+img[i-1][j-1]+img[i+1][j+1]+img[i+1][j+1])/4
            # if (i % 2 != 0 and j % 2 == 0) or (i % 2 == 0 and j % 2 != 0):
            #     g_max = max(img[i][j], g_max)
    res = np.dstack((r_channel, g_channel, b_channel))
    return res

7.去噪

由于大部分传感器都会自带噪声,所以我们会在处理过程中加上降噪这一步。

降噪的方式有很多,比较常用的就是均值降噪,中值降噪和高斯模糊。

均值降噪:

目标像素的值取它上下左右,对角线和反对角线方向相邻的八个像素块的平均值。

中值降噪:

目标像素的值取它上下左右,对角线和反对角线方向相邻的八个像素块的中间值。

高斯降噪:

图像矩阵乘高斯核矩阵,高斯核矩阵类似于这样,从中间向四周逐渐减小。

去噪之后的图像没有什么变换,可能是因为太黑了,看不出来。

 

 

8.亮度调整和gamma 编码

我们经过去马赛克,去噪后的图片还是很暗,主要是由于我们任务开始之间对他进行了缩放,并且这幅图还没有经过gamma编码。所以我们需要先把它的像素值变成一个较大的值,使整个图片看起来较亮。

就像下图展示的,人眼对于光的感知并不是线性的,人眼对暗处的亮度变化会更敏感一些。而相机的对光的感知是线性的,我们需要gamma编码,把他变成非线性的。

代码:

def brighten(a,b,img):
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            for c in range(3):
                img[i][j][c]=a*img[i][j][c]+b
                if img[i][j][c]>1:img[i][j][c]=1

    return img
def gamma_code(img):
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            for c in range(3):
                if img[i][j][c]<=0.0031308:
                    img[i][j][c]*=12.92
                else:
                    img[i][j][c]=(1+0.055)*(img[i][j][c]**(1/2.4))-0.055

    return img

结果:

 

# 参考文献

[bayer模式的介绍](https://www.cnblogs.com/whw19818/p/6223143.html

[白平衡算法简单原理以及灰色世界、完美反射实现](https://blog.youkuaiyun.com/Primavera37/article/details/105465331)

论文:基于 Raw 格式图像的自动白平衡方法

[ISP中去马赛克-demosiac入门](https://www.cnblogs.com/sunny-li/p/8641767.html)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值