分析RGB和YUV二进制格式图像的三通道概率分布及其熵
一. 全彩图像及其RGB三通道
全彩色图像也被称为RGB图像,大部分是24bit的RGB图像。其由三个通道,分别是R-红色通道,G-绿色通道,B-蓝色通道组成。而R(Red),G(Green),B(Green)也被称为光的三基色,三基色是指无法通过其他颜色混合得到的“基本色”,由于人的肉眼由感知RGB三种不同颜色的锥体细胞,因此色彩空间通常可以由RGB三种基本色来表达。
二. 使用Python分析RGB二进制格式的图像
1. 读取RGB二进制格式的图像
我们使用二进制的方式打开并读取.rgb文件,并将列表中的数据均转换成int型以方便后续概率与熵的计算,当然我们要及时的关闭打开的文件以释放其占用的内存,加快处理速度。
#以2进制文件读取"down.rgb"文件
f = open("down.rgb", "rb")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
2. 分离RGB三通道
因为本次实验采用的文件分辨率为256*256,且该二进制图像文件的存储格式为:rgb文件按照每个像素BGR分量一次储存,即“BGR BGR BGR …”
#RGB通道的分离
bGroup = []
gGroup = []
rGroup = []
for i in range(256*256):#本次实验采用的文件分辨率为256*256
bGroup.append(data[i*3])
gGroup.append(data[i*3+1])
rGroup.append(data[i*3+2])
3. 重现RGB三通道的图像
为了直观的感受图像三个通道与原图的关系,我们将三个通道的图像进行了重现。代码中是将单独的RGB三通道补全成完整的三彩图像(当然其中有两个通道均为零),然后使用data_b = np.array(data_b).reshape((256, 256, 3)).astype(np.uint8)
将RGB转换为openCV可以显示的格式,从而重现图像。
data_b = []
for i in range(256*256):
data_b.append(bGroup[i])
data_b.append(0)
data_b.append(0)
data_g = []
for i in range(256*256):
data_g.append(0)
data_g.append(gGroup[i])
data_g.append(0)
data_r = []
for i in range(256*256):
data_r.append(0)
data_r.append(0)
data_r.append(rGroup[i])
#转换格式使用OpenCV显示
#b通道
data_b = np.array(data_b).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down_b.rgb", data_b)
#g通道
data_g = np.array(data_g).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("data_g.rgb", data_g)
#r通道
data_r = np.array(data_r).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("data_r.rgb", data_r)
#原彩图片
data = np.array(data).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down.rgb", data)
cv.waitKey()
重新图像如下:
4. 计算RGB三通道的概率分布并作图
因为图像时8bit的色彩,所以每一个通道都是0-255的256个值。所以我们可以通过代码来计算每一个通道中0-255各值的数量除以整张图像像素总数量计算其概率分布。然后通过matplotlib.pyplot以0-255为横坐标,各值下的概率为纵坐标绘制图像,可以更加显示化的展示三通道的概率分布。
#计算三通道的概率分布
#b通道
b_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
b_gailv = [int(x) for x in b_gailv]
for i in range(256*256):
for j in range(256):
if bGroup[i]==j:
b_gailv[j] = b_gailv[j] + 1
for j in range(256):
b_gailv[j] = b_gailv[j]/(256*256)
#b通道
g_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
g_gailv = [int(x) for x in g_gailv]
for i in range(256*256):
for j in range(256):
if gGroup[i]==j:
g_gailv[j] = g_gailv[j] + 1
for j in range(256):
g_gailv[j] = g_gailv[j]/(256*256)
#b通道
r_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
r_gailv = [int(x) for x in r_gailv]
for i in range(256*256):
for j in range(256):
if rGroup[i]==j:
r_gailv[j] = r_gailv[j] + 1
for j in range(256):
r_gailv[j] = r_gailv[j]/(256*256)
#作图
plt.plot(b_gailv, 'b')
plt.plot(g_gailv, 'g')
plt.plot(r_gailv, 'r')
plt.legend(["b aisle", "g aisle", "r aisle"])
plt.show()
绘制的概率分布图如下:
5. 计算RGB三通道的熵
我们在第3步中已经得到了三个通道的概率,然后根据熵的计算公式即可计算出对应通道的熵:
#计算各自的熵
#b通道
H_b = 0
for j in range(256):
if b_gailv[j] != 0:
H_b = H_b + b_gailv[j]*ma.log2(b_gailv[j])
H_b = -H_b
#g通道
H_g = 0
for j in range(256):
if g_gailv[j] != 0:
H_g = H_g + g_gailv[j]*ma.log2(g_gailv[j])
H_g = -H_g
#r通道
H_r = 0
for j in range(256):
if r_gailv[j] != 0:
H_r = H_r + r_gailv[j]*ma.log2(r_gailv[j])
H_r = -H_r
print("H(b)=", H_b)
print("H(g)=", H_g)
print("H(r)=", H_r)
各通道熵值结果如下:
H ( b ) | H ( g ) | H ( r ) |
---|---|---|
6.856861210882992 | 7.178462484835098 | 7.229552890551846 |
6. 完整代码
#导入程序所需要的包
import cv2 as cv
import numpy as np
import math as ma
import matplotlib.pyplot as plt
#以2进制文件读取"down.rgb"文件
f = open("down.rgb", "rb")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
#RGB通道的分离
bGroup = []
gGroup = []
rGroup = []
for i in range(256*256):
bGroup.append(data[i*3])
gGroup.append(data[i*3+1])
rGroup.append(data[i*3+2])
data_b = []
for i in range(256*256):
data_b.append(bGroup[i])
data_b.append(0)
data_b.append(0)
data_g = []
for i in range(256*256):
data_g.append(0)
data_g.append(gGroup[i])
data_g.append(0)
data_r = []
for i in range(256*256):
data_r.append(0)
data_r.append(0)
data_r.append(rGroup[i])
#计算三通道的概率分布
#b通道
b_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
b_gailv = [int(x) for x in b_gailv]
for i in range(256*256):
for j in range(256):
if bGroup[i]==j:
b_gailv[j] = b_gailv[j] + 1
for j in range(256):
b_gailv[j] = b_gailv[j]/(256*256)
#b通道
g_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
g_gailv = [int(x) for x in g_gailv]
for i in range(256*256):
for j in range(256):
if gGroup[i]==j:
g_gailv[j] = g_gailv[j] + 1
for j in range(256):
g_gailv[j] = g_gailv[j]/(256*256)
#b通道
r_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
r_gailv = [int(x) for x in r_gailv]
for i in range(256*256):
for j in range(256):
if rGroup[i]==j:
r_gailv[j] = r_gailv[j] + 1
for j in range(256):
r_gailv[j] = r_gailv[j]/(256*256)
#作图
plt.plot(b_gailv, 'b')
plt.plot(g_gailv, 'g')
plt.plot(r_gailv, 'r')
plt.legend(["b aisle", "g aisle", "r aisle"])
plt.show()
#计算各自的熵
#b通道
H_b = 0
for j in range(256):
if b_gailv[j] != 0:
H_b = H_b + b_gailv[j]*ma.log2(b_gailv[j])
H_b = -H_b
#g通道
H_g = 0
for j in range(256):
if g_gailv[j] != 0:
H_g = H_g + g_gailv[j]*ma.log2(g_gailv[j])
H_g = -H_g
#r通道
H_r = 0
for j in range(256):
if r_gailv[j] != 0:
H_r = H_r + r_gailv[j]*ma.log2(r_gailv[j])
H_r = -H_r
print("H(b)=", H_b)
print("H(g)=", H_g)
print("H(r)=", H_r)
#转换格式使用OpenCV显示
#b通道
data_b = np.array(data_b).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down_b.rgb", data_b)
#g通道
data_g = np.array(data_g).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("data_g.rgb", data_g)
#r通道
data_r = np.array(data_r).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("data_r.rgb", data_r)
#原彩图片
data = np.array(data).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down.rgb", data)
cv.waitKey()
三. 全彩图像及其YUV三通道
同样彩色图像也可以使用yuv来表示,其中y表示明亮度,反应了灰阶值;u和v则表示图像的色度和饱和度。
四. 使用Python分析YUV二进制格式的图像
1. 读取YUV二进制格式的图像
yuv文件的读取与rgb文件的读取相似
#以2进制文件读取"down.yuv"文件
f = open("down.yuv", "rb")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
2. 分离YUV三通道
因为本次实验采用的文件分辨率为256*256,且该二进制图像文件的存储格式为:yuv格式按照全部像素的y数据块、u数据块、v数据块以此存放,即“YYYY… UUUU… VVVV…”
#YUV通道的分离
yGroup = []
uGroup = []
vGroup = []
for i in range(256*256):
yGroup.append(data[i])
for i in range(256*256, int(256*256*1.25)):
uGroup.append(data[i])
for i in range(int(256*256*1.25), int(256*256*1.5)):
vGroup.append(data[i])
3. 计算YUV三通道的概率分布并作图
yuv文件的三通道的概率分布并作图与rgb文件的三通道的概率分布并作图相似
#计算三通道的概率分布
#y通道
y_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
y_gailv = [int(x) for x in y_gailv]
for i in range(256*256):
for j in range(256):
if yGroup[i]==j:
y_gailv[j] = y_gailv[j] + 1
for j in range(256):
y_gailv[j] = y_gailv[j]/(256*256)
#u通道
u_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
u_gailv = [int(x) for x in u_gailv]
for i in range(int(256*256/4)):
for j in range(256):
if uGroup[i]==j:
u_gailv[j] = u_gailv[j] + 1
for j in range(256):
u_gailv[j] = u_gailv[j]/(int(256*256/4))
#v通道
v_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
v_gailv = [int(x) for x in v_gailv]
for i in range(int(256*256/4)):
for j in range(256):
if vGroup[i]==j:
v_gailv[j] = v_gailv[j] + 1
for j in range(256):
v_gailv[j] = v_gailv[j]/(int(256*256/4))
#作图
plt.plot(y_gailv, 'b')
plt.plot(u_gailv, 'g')
plt.plot(v_gailv, 'r')
plt.legend(["y aisle", "u aisle", "v aisle"])
plt.show()
绘制的概率分布图如下:
4. 计算YUV三通道的熵
yuv文件的三通道的熵与rgb文件的三通道的熵相似
#计算各自的熵
#y通道
H_y = 0
for j in range(256):
if y_gailv[j] != 0:
H_y = H_y + y_gailv[j]*ma.log2(y_gailv[j])
H_y = -H_y
#u通道
H_u = 0
for j in range(256):
if u_gailv[j] != 0:
H_u = H_u + u_gailv[j]*ma.log2(u_gailv[j])
H_u = -H_u
#b通道
H_v = 0
for j in range(256):
if v_gailv[j] != 0:
H_v = H_v + v_gailv[j]*ma.log2(v_gailv[j])
H_v = -H_v
print("H(y)=", H_y)
print("H(u)=", H_u)
print("H(v)=", H_v)
各通道熵值结果如下:
H ( y ) | H ( u ) | H ( v ) |
---|---|---|
6.3318185418675075 | 5.12640191439972 | 4.113143002049819 |
5. 完整代码
#导入程序所需要的包
import math as ma
import matplotlib.pyplot as plt
#以2进制文件读取"down.rgb"文件
f = open("down.yuv", "rb")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
#YUV通道的分离
yGroup = []
uGroup = []
vGroup = []
for i in range(256*256):
yGroup.append(data[i])
for i in range(256*256, int(256*256*1.25)):
uGroup.append(data[i])
for i in range(int(256*256*1.25), int(256*256*1.5)):
vGroup.append(data[i])
#计算三通道的概率分布
#y通道
y_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
y_gailv = [int(x) for x in y_gailv]
for i in range(256*256):
for j in range(256):
if yGroup[i]==j:
y_gailv[j] = y_gailv[j] + 1
for j in range(256):
y_gailv[j] = y_gailv[j]/(256*256)
#u通道
u_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
u_gailv = [int(x) for x in u_gailv]
for i in range(int(256*256/4)):
for j in range(256):
if uGroup[i]==j:
u_gailv[j] = u_gailv[j] + 1
for j in range(256):
u_gailv[j] = u_gailv[j]/(int(256*256/4))
#v通道
v_gailv = [0 for x in range(0, 256)]#创建一个长度为256的默认值为0的列表
v_gailv = [int(x) for x in v_gailv]
for i in range(int(256*256/4)):
for j in range(256):
if vGroup[i]==j:
v_gailv[j] = v_gailv[j] + 1
for j in range(256):
v_gailv[j] = v_gailv[j]/(int(256*256/4))
#作图
plt.plot(y_gailv, 'b')
plt.plot(u_gailv, 'g')
plt.plot(v_gailv, 'r')
plt.legend(["y aisle", "u aisle", "v aisle"])
plt.show()
#计算各自的熵
#y通道
H_y = 0
for j in range(256):
if y_gailv[j] != 0:
H_y = H_y + y_gailv[j]*ma.log2(y_gailv[j])
H_y = -H_y
#u通道
H_u = 0
for j in range(256):
if u_gailv[j] != 0:
H_u = H_u + u_gailv[j]*ma.log2(u_gailv[j])
H_u = -H_u
#b通道
H_v = 0
for j in range(256):
if v_gailv[j] != 0:
H_v = H_v + v_gailv[j]*ma.log2(v_gailv[j])
H_v = -H_v
print("H(y)=", H_y)
print("H(u)=", H_u)
print("H(v)=", H_v)
五. RGB文件与YUV文件压缩比较
通过os库可以轻松的获取到“down.rgb”和“down.yuv”两个文件的大小,我们可以轻松地看到这两个相同的图像,yuv文件明显比rgb文件要小,从概率分布的图像中可以看到yuv文件的概率分布要比rgb文件的概率分布更加均衡,yuv图像三通道的熵值也比rgb图像三通道的熵值要小,这些数据迹象均表明yuv文件要比rgb文件的压缩比大,也就是文件占用存储会更小。
import os
size_rgb = os.path.getsize('down.rgb')
size_yuv = os.path.getsize('down.yuv')
print(size_rgb)
print(size_yuv)
文件大小如下:
rgb文件 | yuv文件 |
---|---|
196608 | 98304 |
六. 归纳总结
使用Python可以比较方便快捷地读取文件,其代码结构和平时学习的c和c++差不多,通过对全彩图像的分析,验证了图像文件的存储格式与采样空间。并且通过对三通道的概率分布,以及三通道的熵值的分析,验证了yuv文件的存储占用要比rgb文件占用小的事实。为后续进一步学习数据压缩打下了基础。