如何高效使用Micropython看你喜欢看的视频?【下】(生成图片帧和WIFI传输播放)

如何高效使用Micropython看你喜欢看的视频?【下】(生成图片帧和WIFI传输播放)

上一篇教程,我们谈到本项目还有两个重要的问题没有解决:一个是没有TF卡的话,应该如何播放视频,另一个就是如何高效生成视频帧,这篇教程我们就要好好聊聊。

0x02 如何把视频帧进行WIFI传输

我额外多加这一步,其实是为了方便没有TF卡的小伙伴进行实验。因为我发现:当我使用ESP32去读取我自己制作的PBM文件时,我遇到了读取速度慢的问题(当然这是一个伏笔,我们后面会提到),我怀疑这是因为ESP32的性能不足导致的,因此萌生了一个想法:

先在电脑端编写好python脚本,然后读取准备好的视频帧,再发送给ESP32!

那怎么样才能发送数据给ESP32呢?当然是通过WIFI了。

很久之前,我就研究过,只要电脑和ESP32接入了同一个WIFI环境,就可以让电脑创建一个Socket服务端,ESP32作为客户端进行数据交换,我用这个方法制作了很多项目,所以这次我也打算用Socket。

不过这里,我遇到了第一个疑难问题,也一直没有解决。

当我使用【ESP32作为服务端电脑作为客户端】的时候,只要电脑发送数据给ESP32,32就会卡死,报错内容如下:don’t call tcp_recved for listen-pcbs

在这里插入图片描述

我在网上查阅资料时,看到DF-ROBOT的一篇关于掌控板的博客,里面竟然使用ESP32作为服务端成功播放了视频,而我使用他们的代码还是出现了上述问题,处理了很久都没有解决,所以我还是换回来,使用【电脑作为服务端,ESP32作为客户端】进行接收,并且成功看到了视频!

这里是电脑端的脚本:

import socket,time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostbyname(socket.gethostname())
print(host)
port = 8080

s.bind((host,port))
s.listen(5)

print('等待客户端连接中…')

client,client_address = s.accept()
print('新连接')
print('IP:'+str(client_address[0]))
client_IP = str(client_address[0])
print('Port:' + str(client_address[1]))
client_port = str(client_address[1])


for i in range(0,355,5):#这里大家可以根据自己生成的图片编号、数量、帧率进行调节,第一个数字是第一帧的文件名,第二个是最后一个的文件名,第三个是每一个文件名之间相隔多少,如果使用我提供的脚本生成图片,第三个数就是帧率。
    dirt = 'after/' + str(i) + '.pbm'#各位自行根据自己帧保存的位置来修改
    with open(dirt,'rb') as f:
        f.readline()
        f.readline()
        data= f.read()
        client.send(data)
        print(i)
        time.sleep(0.19)#5帧极限了,这个帧率不需要修改了

这里是ESP32的脚本

import socket,framebuf,time,machine
from machine import I2C,Pin
from ssd1306 import SSD1306_I2C

i2c = I2C(scl = Pin(23),sda = Pin(22))
display = SSD1306_I2C(128,64,i2c)
host = '192.168.137.1'
port = 8080

s = socket.socket()
addr = (host,port)
s.connect(addr)

i = 0

while True:
    data = s.recv(1024)
    if data != 0:#其实这个要不要无所谓,没影响
        fbuf = framebuf.FrameBuffer(bytearray(data),88,64,framebuf.MONO_HLSB)#这个参数不能换
        display.fill(0)
        display.blit(fbuf,19,0)
        display.show()
        del data

至此,我们已经成功可以在OLED上用Micropython看Bad Apple了,可以装逼了!

0x03 如何高效生成帧文件?

刚刚提到,我读取自己生成的PBM文件时,遇到了读取慢的问题,就算是用电脑读取,速度也很慢,在OLED屏幕上显示也是花屏,所以我怀疑是我生成的PBM文件有问题。

当我查看我生成的文件属性时,发现了问题所在:文件太大了!

在这里插入图片描述
在这里插入图片描述

可以发现,我制作的PBM文件比正常的文件整整大了几十倍!也怪不得读取这么慢了,那究竟是我制作时哪一步出了问题?

我制作PBM文件时,我是使用PR先把视频素材进行预剪辑,剪辑出自己需要的一部分,然后导出为几千个BPM图片帧,这一步没有问题。

接着,我用PS对所有图片进行批处理:二值化、保存为PBM。我猜测就是这一步出了问题。

于是邪门的事情发生了:明明我的图片和BAD APPLE里的图片所包含的像素是一样多的,为什么它就是700B,我是16K呢?难道说不能使用PS来操作?

索性的,我决定自己临时查看OPENCV - python的文档,学习如何进行视频帧处理、二值化、反相、裁剪、导出帧文件,争取在一个脚本中集成所有功能。最后还真的让我捣鼓出来了!上代码:

import cv2 as cv
import numpy as np

vc = cv.VideoCapture('1.avi')#打开视频
c = 0#累计帧数
timeF = 5#隔5帧截一次图,数字越小,播放越细腻

def binary_image(image):#将图像处理为二值化的程序
    gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY)  #把输入图像灰度化
    h, w =gray.shape[:2]
    m = np.reshape(gray, [1,w*h])
    mean = m.sum()/(w*h)
    print("mean:",mean)
    ret, binary =  cv.threshold(gray, mean, 255, cv.THRESH_BINARY)
    return binary

def save_binary(dirt):#保存二值化图像的程序,用字符串传入文件位置
    image = cv.imread(dirt)
    image = binary_image(image)#二值化
    image = cv.bitwise_not(image)#反相,根据视频内容来定需不需要反相
    image = cv.resize(image,(88,64))#调整尺寸
    cv.imwrite('1.pbm',image)#保存

def access_pixels(image):#相反操作
    height, width, channels = image.shape
    print("width:%s,height:%s,channels:%s" % (width, height, channels))
 
    for row in range(height):
        for list in range(width):
            for c in range(channels):
                pv = image[row, list, c]
                image[row, list, c] = 255 - pv
    return image

###########################################################
#此部分为循环导出视频帧

if vc.isOpened():  # 判断是否正常打开
    rval, frame = vc.read()#返回一个元组,frame是帧对象
else:
    rval = False
print(rval)
while rval:  # 循环读取视频帧
    rval, frame = vc.read()
    if rval is True:
        if (c % timeF == 0):  # 每隔timeF帧进行存储操作
            frame = binary_image(frame)#二值化
            #frame = cv.bitwise_not(frame)#反相,根据视频内容来定需不需要反相
            frame = cv.resize(frame,(88,64))#调整尺寸
            cv.imwrite('after/' + str(c) + '.pbm',frame)#保存

        c = c + 1
        cv.waitKey(0)
##########################################################################

使用这个脚本有几个好处:

①如果你只是想显示图片,那么可以不执行下面的村换,直接调用我提供的save_binary(),即可保存二值化之后的图片;

②如果你想显示视频,视频也无需压缩和处理,直接执行循环即可导出可以使用的帧文件;

至此,本工程的进度也全部交代完毕,各位玩的开心~

0x04 后续更新方向

本项目其实还有几个地方可以改进,我也会慢慢跟进:

第一个是换用SPI接口的屏幕,或许可以加快屏幕的刷新速率,观看更流畅的视频!

第二个是直接把我处理帧文件的脚本和电脑发送帧的脚本直接结合在一起,处理完直接发送给ESP32,这样就能做成一个小电视了!

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值