IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【编程实践/嵌入式比赛】嵌入式比赛学习记录(三):基于TCP的视频流传输 -> 正文阅读

[网络协议]【编程实践/嵌入式比赛】嵌入式比赛学习记录(三):基于TCP的视频流传输

0.前言

经过前面的实验,队长要求我使用opencv的camera作为摄像头来实现真正的视频流传输,因此本次实验我将讲述如何实现一个视频流的传输

附之前文章的链接:

【编程实践/嵌入式比赛】嵌入式比赛学习记录(一):TCP服务器和web界面的建立
【编程实践/嵌入式比赛】嵌入式比赛学习记录(二):基于TCP的图片流传输

1.视频流传输思想

我们提到视频,一般会想到帧的概念。从不严谨的角度来看,帧率就是指视频每秒刷新多少次。如果视频帧率是30,那么就是指1秒视频刷新30次。

因此我们可以将摄像头的视频截获下来,这样就是一张图片。通过刷新这样一张图片便可以视频流的形式展现。事实上很多网络IP摄像头就是通过传视频截获的帧来展示在前端的。

因此对于客户端(摄像头)而言,关键在于如何把视频帧进行编码传出去。而对于服务器端而言,则在于如何进行视频帧的解码。
大致的传输流程如下:
在这里插入图片描述
其中下位机部分暂且用一个单独进程来实现,摄像头使用opencv的camera,TCP client使用python socket实现。

2.摄像头部分

摄像头部分就是如之前所说,使用opencv来调用电脑的摄像头,模拟下位机的摄像头

另:貌似opencv的摄像头窗口大小只能选择预设的几个值,如果必须要自定义大小只能自己手动修改大小。

import cv2
#自定义模块
from MyClient import *
from MyImage import *

#TCP client,自己实现的类
client=TCPClient("192.168.71.1",5001)

def StartCamera():
    cap=cv2.VideoCapture(0,cv2.CAP_DSHOW)
    if not cap.isOpened():
        print("Camera error!")
        return
    fps=15
    #设置为640*480,帧率为15
    cap.set(3,height)
    cap.set(4,width)
    cap.set(cv2.CAP_PROP_FPS,fps)
    while 1:
        #截取帧
        ret,frame=cap.read()
        if not ret:
            print("Get frame error")
            break
        #print(frame.shape)
        #type:numpy.ndarray
        #shape(480,640,3)
        #翻转
        frame=cv2.flip(frame,180)

        '''调用TCP client的接口传输'''
        '''image_encode是图片编码'''
        client.send_data(image_encode(frame))

        time.sleep(0.05)

if __name__=="__main__":
    StartCamera()

3.图片编码格式

本实验中,由于打算传输彩色图片,因此对于传输图片流的格式进行了修改,如下:
在这里插入图片描述
相较于上一次实验,多了个通道值,是因为opencv截取的图片帧是RGB通道的值,因此需要指明第三个维度,即通道数量,一般为3。

前两个分别是图片的宽度和高度,因为实际上我们图片的尺寸可能大于255*255,且为了方便处理,我们将图片的尺寸值放大到2字节,也就是最大可以允许图片的长宽为65535,这绝对够用

处理部分的代码如下

height=480
width=640
channel=3
def image_decode(hexstream:bytes):
    #hex -> np.array
    #bytes[0:1]:width bytes[2:3]:height bytes[4]:channel
    #width,height=int(hexstream[0]),int(hexstream[1])


    # width=int.from_bytes(hexstream[0:2],byteorder="big")
    # height=int.from_bytes(hexstream[2:4],byteorder="big")
    # channel=int(hexstream[4])


    img=list(hexstream[5:width*height*channel+5])
    img=np.array(img).reshape((height,width,channel))
    
    #np.array->jpg stream
    img=cv2.imencode(".jpg",img)[1]
    img=img.tobytes()
    #jpg stream => base64
    # b64stream=base64.b64encode(img)
    return img

def image_encode(img:np.ndarray):
    height,width,channel=img.shape
    stream=bytes(0)
    stream+=width.to_bytes(2,byteorder="big")
    stream+=height.to_bytes(2,byteorder="big")
    stream+=channel.to_bytes(1,byteorder="big")

    array=img.flatten().tolist()
    stream+=bytes(array)
    return stream

4.TCP client部分

为了模拟下位机发送数据,client不可少,这里同样用socket实现。
稍作封装,便于其他模块调用

class TCPClient:
    def __init__(self,host:str,port:int):
        self.client=socket.socket()
        self.client.connect((host,port))

    def send_data(self,data:bytes):
        self.client.sendall(data)

    def close(self):
        self.client.close()

5.TCP server部分

实现与之前差不多,这里不再修改

import threading
import socketserver
from MyImage import *

class DataBuffer:
    def __init__(self,max_size):
        self.tempdata=bytes(0)
        self.pic=bytes(0)
        #控制读写互斥
        self.mutex=threading.Semaphore(1)
        #通知可以取
        self.pic_mutex=threading.Semaphore(1)
        #最大量
        self.max_size=max_size

    def insert_data(self,data:bytes):
        self.mutex.acquire()
        self.tempdata+=data
        if(len(self.tempdata)>=self.max_size):
            next_length=len(self.tempdata)-self.max_size
            self.pic_mutex.acquire()
            self.pic=image_decode(self.tempdata)
            self.pic_mutex.release()
            if(next_length>0):
                self.tempdata=data[-next_length:]
            else:
                self.tempdata=bytes(0)
        self.mutex.release()

    def get_data(self):
        self.pic_mutex.acquire()
        pic=self.pic
        self.pic_mutex.release()
        return pic

    def clearbuffer(self):
        self.tempdata=bytes(0)

#width=640 height=480 channel=3
Buffer=DataBuffer(max_size=width*height*channel+5)

class MyHandler(socketserver.BaseRequestHandler):
    def handle(self):
        global Buffer
        while True:
            data=self.request.recv(8192)
            if not data:
                break                
            else:
                Buffer.insert_data(data)

6.web server部分

也是与之前差不多

from flask import *
from MyServer import Buffer

app1=Flask(__name__)

@app1.route("/",methods=["GET","POST"])
def index_page():
    return render_template("index0.html")

def gen():
    while True:
        pic=Buffer.get_data()
        yield (b'--frame\r\n'
        b'Content-Type: image/jpeg\r\n\r\n' + pic + b'\r\n')

@app1.route("/video_feed")
def video_feed():
    return Response(gen(),mimetype="multipart/x-mixed-replace; boundary=frame")

前端部分:

<!DOCTYPE html>
<html>
    <head>

    </head>
    <body>
        <img id="camera" src="{{url_for('video_feed')}}">
    </body>
</html>

7.验收

启动摄像头部分和server部分,效果如下:
在这里插入图片描述
这里其实是开启了图片翻转,如果各位不喜欢这样也可以去掉摄像头中的cv2.flip一行

8.思考

  1. 帧率超过30的时候,如果不加sleep就会出现明显的卡顿,如何解决?
    目前我感觉是传输的图片帧太大,一帧就有将近90KB,感觉可以把截取到的图片缩小一下再传,前端用img标签放大一下
  2. 如果前端打算用vue等框架,有没有办法将视频流传过去?
    因为目前是用了Flask框架下的Response来实现视频流的传输,不知道如果使用了特定的前端框架是否需要做出修改,看后续的需求吧

最后,感谢你的观看!

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 12:12:36  更:2022-04-26 12:13:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 3:35:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码