摄像的流程:从被拍摄景物上传来的入射光依次穿过相机的光学器件,颜色滤镜后到达传感器,经过模拟前端转换为数字信号,最后通过相机内的图像处理系统转换为最终的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
以及相关的色彩信息。

执行了这一步会生成一个勉强还能看得见的图,但是指示里面也说不要用这个图,咱就照着呗。
再次执行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)
本文介绍了使用Python处理相机RAW图像的过程,包括CR2转TIFF、图像初始化、线性化、判断拜耳滤波阵列模式、白平衡、去马赛克、降噪以及亮度和gamma编码的调整。通过这些步骤,将RAW图像转化为可观察的RGB图像。
1779

被折叠的 条评论
为什么被折叠?



