作业:提供图像文件"down.yuv",“down.rgb”,分辨率已知为256*256,计算各分量的熵
RGB文件的读取和熵的计算
《数字式音频处理》实验课中用C++读YUV图像,但这次作业还不太知道图片文件的格式和分布,python能够很方便的调库来显示图片,来看读取是否正确,所以用Python来读图。
Python二进制读入
无论是rgb还是yuv,首先二进制读入 open(filename,"rb")
,再将数据转化为int
类型:
width=256
height=256
with open('down.rgb','rb') as f:
content=f.read()
content=[int(i) for i in content] #类型转化
print(len(content))
print(width*height*3)
遇到一个问题
当时老师还没发作业要求,才有下面的问题(;′⌒`),我还是不删掉了。
已知文件分辨率为256*256,但并不知道它的三通道是如何排列的,当时感觉有两种可能:
- 类似于[rrrrrrrrr,ggggggg,bbbbbbb]的形式
- 类似于[rgb,rgb,rgb,rbg…]的形式
如果是第一种的形式,那么三通道的分量为:
channel=[]
for i in range(3):
channel.append(content[width * height * i:width * height * (i + 1)])
#假设是红绿蓝三通道
而如果是三种分量交替出现,那么三通道应该是这样:
channel=[]
channel.append(content[0::3])
channel.append(content[1::3])
channel.append(content[2::3])
用两种形式去处理列表,转化为numpy.array
后,再用 opencv 的 cv2.imshow()
输出看看
for i in range(height): #所有行
row=[]
for j in range(width): #每一行
row.append([channel[0][i*height+j],channel[1][i*height+j],channel[2][i*height+j]])
#一个像素的三分量
image_list.append(row)
import numpy as np
import cv2
image=np.array(image_list,dtype='uint8') #opencv要求uint8
cv2.imshow('Image',image)
cv2.waitKey(0)
结果:
所以文件是第二种结构,又由于Python-Opencv imshow()
时图像的数据是[B,G,R]的格式,所以可知之前代码中的channel[0]
对应的是B分量,channel[1]
是G分量,channel[2]
是R分量,
统计概率分布:
每个分量都是8bit的,有0~255级变化,所以有256中可能性,下面的函数返回一个通道的概率分布。
def cul_distribution(li):
'''输入为通道列表'''
res=[0 for i in range(256)]
count=0
for i in li:
res[int(i)]+=1
count+=1
res=[i/count for i in res]
return res
计算各分量的熵:
得到概率分布后,根据信源熵的公式 H ( x ) = − ∑ i P ( x i ) log 2 ( x i ) H(x)=-\sum_i P(x_i)\log_2(x_i) H(x)=−∑iP(xi)log2(xi)来计算,如下面的函数所示:
import math
def cul_entropy(li):
'''输入为概率分布列表'''
Hx=0
for p in li:
if p !=0:
Hx-=p*math.log2(p)
return Hx
结果:
利用上述函数统计概率分布,可以用matplotlib
得到RGB概率分布曲线,最后输出三分量的熵:
#概率分布
Db=cul_distribution(channel[0],height,width)
Dg=cul_distribution(channel[1],height,width)
Dr=cul_distribution(channel[2],height,width)
#概率分布曲线
from matplotlib import pyplot as plt
plt.plot(Db,'b')
plt.plot(Dg,'g')
plt.plot(Dr,'r')
plt.legend(["B","G","R"])
plt.show()
#计算熵
Hb=cul_entropy(Db)
Hr=cul_entropy(Dr)
Hg=cul_entropy(Dg)
print(Hb,Hr,Hg)
概率分布:
熵:
H (R) | H(G) | H(B) |
---|---|---|
7.229552890551846 | 7.178462484835098 | 6.856861210882992 |
YUV文件的读取和计算
与rgb文件的唯一的不同就是文件的读取了。对于yuv文件,因为之前读过yuv444,我觉得这种文件的结构更可能是第一种形式,但是输出文件长度,发现是
256
∗
256
∗
1.5
256*256*1.5
256∗256∗1.5,这样的文件长度更可能是YUV 4:2:0
因此这样读文件:
y=content[0:width * height]
u=content[width * height:int(width * height*1.25)]
v=content[int(width * height*1.25):int(width * height*1.5)]
验证
但是现在也不能确定是不是对的,总得看看图片才放心。于是把他转化成opencv认识的YUV4:4:4格式,再转化为BGR图像并imshow()
def get_map(x,height,width):
map=[]
for i in range(height):
row=[]
for j in range(width):
# row.append([channel[0][i*height+j],channel[1][(i*height+j)//4],channel[2][(i*height+j)//4]])
row.append(x[i * height + j])
map.append(row)
return np.array(map,dtype='uint8')
y_img=get_map(y,height,width)
u_img=get_map(u,height//2,width//2)
v_img=get_map(v,height//2,width//2)
yuv_444=[]
for i in range(height):
row = []
for j in range(width):
row.append([y_img[i][j],u_img[i//2][j//2],v_img[i//2][j//2]])
yuv_444.append(row)
yuv_444=np.array(yuv_444,dtype='uint8')
image=cv2.cvtColor(yuv_444,cv2.COLOR_YUV2BGR)
cv2.imshow('y',y_img)
cv2.imshow('u',u_img)
cv2.imshow('v',v_img)
cv2.imshow('Image',image)
cv2.waitKey(0)
结果
这么看来,应该确实是这个结构没错了,接下来就可以统计上面三种分量的概率分布和熵了。
概率分布和熵
计算的函数和流程和RGB文件完全一样,因此就只贴结果:
概率分布:
YUV三分量的熵:
H (Y) | H(U) | H(V) |
---|---|---|
6.3318185418675075 | 5.12640191439972 | 4.113143002049819 |
总结和想法:
概率分布和熵
通过概率分布的图我们可以观察到,UV分量的值更像是一种中间高两边低的分布(更不像是均匀分布),所以他的熵更小是可以看出来的。而RGB的三个分量虽然也有尖的高峰,但是他们在不是高峰的地方也无法下降到0,因此相对来说更均匀,所以熵会更大。
熵与数据压缩
上课时我们学习到,信源的熵越大,平均码长的下界就会越大。也就是说熵越小,无损编码时可以压缩的程度就会越大。由实验知道,YUV文件的熵是明显 小于 RGB文件的。
我就用压缩软件压缩了两个文件试了试(并不知道对不对)
看上去RGB文件压缩前后大小的比为
146
/
192
=
76
%
146/192=76\%
146/192=76%,YUV文件为
53
/
96
=
55
%
53/96=55\%
53/96=55%,感觉确实熵更小的可以压缩的更多。(并不知道由有没有关系)
记一下:
同学告诉我rgb图像可以用numpy array的reshape函数变成这个格式,确实是比我几层循环方便多了。
data = np.array(data).reshape((256, 256, 3)).astype(np.uint8)