【数据压缩】Python读图片(“down.yuv“,“down.rgb“),计算RGB、YUV各分量的熵

作业:提供图像文件"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.2295528905518467.1784624848350986.856861210882992

YUV文件的读取和计算

与rgb文件的唯一的不同就是文件的读取了。对于yuv文件,因为之前读过yuv444,我觉得这种文件的结构更可能是第一种形式,但是输出文件长度,发现是 256 ∗ 256 ∗ 1.5 256*256*1.5 2562561.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.33181854186750755.126401914399724.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)
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值