), [使用LCD12864屏幕播放电影变形金刚-预告](), 当然也可以用于格式转换,视频处理等功能。
本文提供的视频转换功能支持`8bit位图`,`2bit位图`,和`1bit位图`三种格式。三种格式的区别和文件格式见下文。
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库, 可以运行在Linux, Windows, Android和Mac-OS操作系统上. 它轻量级而且高效---由一系列 C 函数和少量 C++ 类构成, 同时提供了Python, Ruby, MATLAB等语言的接口, 实现了图像处理和计算机视觉方面的很多通用算法.本文主要使用了OpenCV的视频采集, 图像色域转换, 视频格式转换, 等功能.
## 准备工作
### 安装 **Python-OpenCV** 库
```bash
pip install opencv-python -i https://mirrors.ustc.edu.cn/pypi/web/simple
```
### 安装 **Numpy** 科学计算库
```bash
pip install numpy -i https://mirrors.ustc.edu.cn/pypi/web/simple
```
## 视频信息提取
### 打开视频文件
```python
# cap = cv2.VideoCapture(0) # 从摄像头捕获视频
cap = cv2.VideoCapture(VIDEO_NAME) # 打开视频文件
```
### 获取视频帧率
```python
VIDEO_FPS = cap.get(cv2.CAP_PROP_FPS)
VIDEO_FPS = int(round(VIDEO_FPS)) # 四舍五入
print("Video FPS:", VIDEO_FPS)
```
### 获取视频帧数
```python
VIDEO_FFRAME = cap.get(cv2.CAP_PROP_FRAME_COUNT)
VIDEO_FFRAME = int(round(VIDEO_FFRAME)) # 四舍五入
print("Video Frame Count:", VIDEO_FFRAME)
```
### 获取视频帧大小
```python
VIDEO_WIDTH = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
VIDEO_HEIGHT = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
VIDEO_WIDTH = int(round(VIDEO_WIDTH)) # 四舍五入
VIDEO_HEIGHT = int(round(VIDEO_HEIGHT)) # 四舍五入
print("Video Size: (", VIDEO_WIDTH, VIDEO_HEIGHT, ")")
print("LCD Size: (", LCD_WIDTH, LCD_HEIGHT, ")")
```
### 计算图像缩放比例
> 把原始视频缩放为LCD屏幕能容纳的最大分辨率
```python
VIDEO_WSCAL = LCD_WIDTH / VIDEO_WIDTH # 横向缩放比例
VIDEO_HSCAL = LCD_HEIGHT / VIDEO_HEIGHT # 纵向缩放比例
VIDEO_SCAL = min(VIDEO_WSCAL, VIDEO_HSCAL) # 等比缩放(以横向和纵向缩放中的最小值作为左后的缩放比例,以此保证缩放过后的分辨率不会超过LCD屏幕的大小)
LCD_WIDTH = int(VIDEO_WIDTH * VIDEO_SCAL) # 取整,忽略小数
LCD_HEIGHT = int(VIDEO_HEIGHT * VIDEO_SCAL)# 取整,忽略小数
print("Video Scaling Ratio: (", VIDEO_WSCAL, VIDEO_HSCAL, ")")
print("Resize Video Size To: (", LCD_WIDTH, LCD_HEIGHT, ")")
```
### 转换后的视频文件头信息
```c
typedef struct lcd_video_img_hdr_s
{
int32_t flag; //file flag, "LVIF",文件标志
int32_t video_width; //video width, 1920,原始视频的横向分辨率
int32_t video_height; //video height, 1080,原始视频的纵向分辨率
int32_t lcd_width; //lcd width, 128,新视频的横向分辨率
int32_t lcd_height; //lcd height, 64,新始视频的纵向分辨率
int32_t video_fps; //video fps, 25,视频的帧率
int32_t video_frame; //video frame count, 875,视频的帧数
int32_t pixel_bit; //每个像素点所占的bit,视频的色彩空间
} lcd_video_img_hdr_t;
```
![转换后的视频文件头信息](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113150257.png)
### 像素格式
#### 8bit位图
![8bit位图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113153730.png)
#### 2bit位图
![2bit位图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113153351.png)
#### 1bit位图
![1bit位图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113153254.png)
### 视频旋转,灰度,缩放
```python
# frame = cv2.flip(frame, 0) # 旋转
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 灰度化
# frame = cv2.resize(frame, None, fx=VIDEO_SCAL, fy=VIDEO_SCAL, interpolation=cv2.INTER_CUBIC) # 改变大小,等比缩放
frame = cv2.resize(frame, (LCD_WIDTH, LCD_HEIGHT),
interpolation=cv2.INTER_CUBIC) # 改变大小,按尺寸缩放
```
### 循环处理视频帧
```python
while(cap.isOpened()):
ret, frame = cap.read()
if ret == True:
# frame = cv2.flip(frame, 0) # 旋转
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 灰度化
# frame = cv2.resize(frame, None, fx=VIDEO_SCAL, fy=VIDEO_SCAL, interpolation=cv2.INTER_CUBIC) # 改变大小,等比缩放
frame = cv2.resize(frame, (LCD_WIDTH, LCD_HEIGHT),
interpolation=cv2.INTER_CUBIC) # 改变大小,按尺寸缩放
# print("Img Size: ", frame.shape)
# print(frame.dtype)
# print(frame.shape[0], frame.shape[1])
# 显示视频帧
cv2.imshow(VIDEO_NAME, frame)
# 写入镜像文件
if LCD_IMAGE_PIXEL_BIT == 8: # 8位色彩空间,256色位图
LCD_IMAGE_FILE.write(bytearray(frame))
elif LCD_IMAGE_PIXEL_BIT == 1: # 1位色彩空间,黑白双色位图
frame = cv2.GaussianBlur(
frame, ksize=_DEFAULT_GAUSSIAN_BLUR_KERNEL, sigmaX=0) # 高斯模糊
_, frame = cv2.threshold(
frame, 0, 255, _DEFAULT_THRESHOLD_TYPE) # 二值化
# Time Consuming: 57 seconds
byten = 0
bytei = 0
frame.dtype = np.bool
arrya_x = math.ceil(LCD_WIDTH / 8)
array_y = LCD_HEIGHT # array_y = math.ceil(LCD_HEIGHT / 8)
byte_arry = np.zeros((arrya_x*array_y), dtype=np.uint8)
for y in range(LCD_HEIGHT): # range(frame.shape[0]):
for x in range(LCD_WIDTH): # range(frame.shape[1]):
# print(byte_arry[byten])
if ((x % 8) == 0) and (x != 0):
byte_arry[bytei] = byten
bytei += 1
byten = 0
byten = byten << 1
if frame[y][x] != False:
byten = byten | 1
else:
pass
# print(bytei, byten)
byte_arry[bytei] = byten
bytei += 1
byten = 0
# print(byte_arry.size, byten)
LCD_IMAGE_FILE.write(bytearray(byte_arry))
elif LCD_IMAGE_PIXEL_BIT == 2:# 2位色彩控件,黑白灰四色位图
# Time Consuming: 57 seconds
byten = 0
bytei = 0
frame.dtype = np.uint8
array_page = math.ceil(LCD_HEIGHT / LCD_IMAGE_COLOUR_DEPTH)
array_x = LCD_WIDTH
array_y = array_page
byte_arry = np.zeros((array_x*array_y), dtype=np.uint8)
for page in range(array_page):
for x in range(LCD_WIDTH):
byten = 0
for i in range(LCD_IMAGE_COLOUR_DEPTH):
y = (page * LCD_IMAGE_COLOUR_DEPTH) + i
if y >= LCD_HEIGHT:
col = 0
else:
col = lcdCvtGrayColor(
frame[y][x], LCD_IMAGE_COLOUR_DEPTH)
# print("x, y, page, col0 = ", x, y, page, col)
'''
BIT_MAP: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
Y_DOT : | 0 | 1 | 2 | 3 |
'''
bitmv = LCD_IMAGE_COLOUR_DEPTH - i - 1
# col = (col << (2 * bitmv))
col = col * math_pow(2, bitmv * 2)
# print("x, y, page, col1 = ", x, y, page, col)
byten = (byten + col)
byte_arry[bytei] = byten
# print("byte_arry.size, bytei, byten = ", byte_arry.size, bytei, byten)
bytei += 1
byten = 0
LCD_IMAGE_FILE.write(bytearray(byte_arry))
else:
pass
frmcnt += 1
# print("Frame Count:", frmcnt)
# 显示进度
print("#", end="", flush=True)
process_bar += 1
if process_bar >= 60: # 大于60换行
process_bar = 0
print("")
else:
break
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# cv2.waitKey(1)
```
## 运行方法
使用LedSimulator工具打开转换后的bin格式视频文件,观看播放效果,同时也可以把bin格式视频文件导入到LCD192x96液晶屏上播放。
本文提供的LedSimulator工具仅支持192x96 2bit格式文件的播放,源代码请关注后续demo。
## 程序运行截图
![程序运行截图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113155151.png)
![程序运行截图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113155215.png)
![转换结果对比](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113154032.png)
![转换结果对比](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113154132.png)
![转换结果对比](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113154421.png)
## 项目内文件截图
![项目内文件截图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113155325.png)
![项目内文件截图](https://raw.githubusercontent.com/WHJWNAVY/myImage/master/PicGo20200113155407.png)