BMP序列转YUV视频文件
一. BMP图像序列的部分细节
1. BMP文件大体上分为四个部分:
- 位图头文件
- 位图信息头
- 调色板
- 实际的位图数据
我们使用FlexHEX打开已有的bmp文件可以分析出,当前bmp文件的实际的位图数据是从第54字节开始的,且图片尺寸为256*256。
二. 将BMP图像序列转YUV视频文件
1. 读取BMP图像序列
我们使用二进制的方式打开并读取.bmp文件,并将列表中的数据均转换成int型以方便后续概率与熵的计算,当然我们要及时的关闭打开的文件以释放其占用的内存,加快处理速度。
#以2进制文件读取"down.yuv"文件
path = "picture/bmp_24bit_" + str(zhenshu) + ".bmp"
f = open(path, "rb")
f_save = open("picture/bmp_video3.yuv", "wb+")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
data = data[54:]
2. 分离RGB三通道
因为本次实验采用的文件分辨率为256*256,且该bmp图像文件的实际的位图数据存储格式为:rgb文件按照每个像素BGR分量一次储存,即“BGR BGR BGR …”
#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 = []
3. 重现RGB三通道的图像
为了直观的感受单独从bmp文件中抽离出来的rgb文件与原图的关系,我们将抽离出的rgb图像进行了重现。代码中是将单独的RGB三通道组合成完整的三彩图像,并且我们知道在bmp文件的存储中是低位在前,高位在后,因此我们还需要将rgb的数据进行反转(其中包含整体反转和镜像反转),然后使用data_b = np.array(data_b).reshape((256, 256, 3)).astype(np.uint8)
将RGB转换为openCV可以显示的格式,从而重现图像。
#整合rgb图像
data_rgb = []
#整体反转
for i in range(256*256):
data_rgb.append(bGroup[256*256-i-1])
data_rgb.append(gGroup[256*256-i-1])
data_rgb.append(rGroup[256*256-i-1])
#镜像反转
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3]
data_rgb[(i+256*j)*3] = data_rgb[((256-i)+256*j-1)*3]
data_rgb[((256 - i)+256*j-1)*3] = temp
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3+1]
data_rgb[(i+256*j)*3+1] = data_rgb[((256-i)+256*j-1)*3+1]
data_rgb[((256 - i)+256*j-1)*3+1] = temp
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3+2]
data_rgb[(i+256*j)*3+2] = data_rgb[((256-i)+256*j-1)*3+2]
data_rgb[((256 - i)+256*j-1)*3+2] = temp
#转换格式使用OpenCV显示rgb的图像
data_rgb = np.array(data_rgb).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down_yuv2rgb.rgb", data_rgb)
cv.waitKey()
重新图像如下:
4. 将RGB的数值转为YUV的数值
在这一步中我们就使用到了转换公式,同时因为RGB三通道的取值范围是[0, 255],而Y是[16, 235],U和V均是[16, 239]所以我们要对超出这一范围的非法值进行限制。最后在按照yuv文件的存储格式,即“YYY … UUU… VVV… ”的格式进行三通道的合并。
#RGB转换成YUV
yGroup = []
uGroup = []
vGroup = []
for i in range(256*256):
yGroup.append(0.257*rGroup[i] + 0.504*gGroup[i] + 0.098*bGroup[i]+16)
uGroup.append(0.493*rGroup[i] - 0.368*gGroup[i] - 0.071*bGroup[i]+128)
vGroup.append(-0.148*rGroup[i] - 0.029*gGroup[i] + 0.439*bGroup[i]+128)
for i in range(256*256):
yGroup[i] = 16 if rGroup[i] < 16 else rGroup[i]
uGroup[i] = 16 if gGroup[i] < 16 else gGroup[i]
vGroup[i] = 16 if bGroup[i] < 16 else bGroup[i]
yGroup[i] = 235 if rGroup[i] > 235 else rGroup[i]
uGroup[i] = 239 if gGroup[i] > 239 else gGroup[i]
vGroup[i] = 239 if bGroup[i] > 239 else bGroup[i]
5. 反转YUV图像
同样的yuv图像也要进行类似与rgb图像的反转才能正常显示:
#整合YUV图像
data_yuv = []
for i in range(10):
for i in range(256*256):
data_yuv.append(yGroup[256*256-i-1])
for i in range(256 * 256):
data_yuv.append(uGroup[256*256-i-1])
for i in range(256 * 256):
data_yuv.append(vGroup[256*256-i-1])
for n in range(3):
for j in range(256):
for i in range(128):
temp = data_yuv[(i+256*j)+(256*256-1)*n]
data_yuv[(i+256*j)+(256*256-1)*n] = data_yuv[((256-i)+256*j-1)+(256*256-1)*n]
data_yuv[((256 - i)+256*j-1)+(256*256-1)*n] = temp
for j in range(40):
for i in range(256*256*3):
data_video.append(data_yuv[i])
6. 完整代码
因为,我们要处理多张bmp图像,将其合成为一段yuv视频,所以用一个大的for循环老构成整个程序的大框架。
#导入程序所需要的包
from shlex import join
import cv2 as cv
import numpy as np
import math as ma
import matplotlib.pyplot as plt
data_video = []
for zhenshu in range(6):
#以2进制文件读取"down.yuv"文件
path = "picture/bmp_24bit_" + str(zhenshu) + ".bmp"
f = open(path, "rb")
f_save = open("picture/bmp_video3.yuv", "wb+")
data = f.read()
#关闭打开的文件
f.close()
#将数据转换成int
data = [int(x) for x in data]
data = data[54:]
#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 = []
#整合rgb图像
data_rgb = []
#整体反转
for i in range(256*256):
data_rgb.append(bGroup[256*256-i-1])
data_rgb.append(gGroup[256*256-i-1])
data_rgb.append(rGroup[256*256-i-1])
#镜像反转
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3]
data_rgb[(i+256*j)*3] = data_rgb[((256-i)+256*j-1)*3]
data_rgb[((256 - i)+256*j-1)*3] = temp
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3+1]
data_rgb[(i+256*j)*3+1] = data_rgb[((256-i)+256*j-1)*3+1]
data_rgb[((256 - i)+256*j-1)*3+1] = temp
for j in range(256):
for i in range(128):
temp = data_rgb[(i+256*j)*3+2]
data_rgb[(i+256*j)*3+2] = data_rgb[((256-i)+256*j-1)*3+2]
data_rgb[((256 - i)+256*j-1)*3+2] = temp
#转换格式使用OpenCV显示rgb的图像
data_rgb = np.array(data_rgb).reshape((256, 256, 3)).astype(np.uint8)
cv.imshow("down_yuv2rgb.rgb", data_rgb)
cv.waitKey()
#RGB转换成YUV
yGroup = []
uGroup = []
vGroup = []
for i in range(256*256):
yGroup.append(0.257*rGroup[i] + 0.504*gGroup[i] + 0.098*bGroup[i]+16)
uGroup.append(0.493*rGroup[i] - 0.368*gGroup[i] - 0.071*bGroup[i]+128)
vGroup.append(-0.148*rGroup[i] - 0.029*gGroup[i] + 0.439*bGroup[i]+128)
for i in range(256*256):
yGroup[i] = 16 if rGroup[i] < 16 else rGroup[i]
uGroup[i] = 16 if gGroup[i] < 16 else gGroup[i]
vGroup[i] = 16 if bGroup[i] < 16 else bGroup[i]
yGroup[i] = 235 if rGroup[i] > 235 else rGroup[i]
uGroup[i] = 239 if gGroup[i] > 239 else gGroup[i]
vGroup[i] = 239 if bGroup[i] > 239 else bGroup[i]
#整合YUV图像
data_yuv = []
for i in range(10):
for i in range(256*256):
data_yuv.append(yGroup[256*256-i-1])
for i in range(256 * 256):
data_yuv.append(uGroup[256*256-i-1])
for i in range(256 * 256):
data_yuv.append(vGroup[256*256-i-1])
for n in range(3):
for j in range(256):
for i in range(128):
temp = data_yuv[(i+256*j)+(256*256-1)*n]
data_yuv[(i+256*j)+(256*256-1)*n] = data_yuv[((256-i)+256*j-1)+(256*256-1)*n]
data_yuv[((256 - i)+256*j-1)+(256*256-1)*n] = temp
for j in range(40):
for i in range(256*256*3):
data_video.append(data_yuv[i])
f_save.write(bytes(data_video))
print("输出完成!")
三. 使用YUVviewerPlus验证YUV视频
使用YUVviewerPlus打开刚刚输出的yuv视频,可以验证代码的正确性。
四. 归纳总结
通过本次实验,更加深入了解了bmp格式文件的技术细节,对代码操作文件更加地熟练了。同时学会了同时使用多种工具来实现需求,因为不同种类的工具能够更加方便快捷地完成其所负责的方面的任务。