使用WebSocket-Python从H.264视频流中捕获第一幅图像 [英] Capture first image from h.264 video streaming using websocket - Python

查看:0
本文介绍了使用WebSocket-Python从H.264视频流中捕获第一幅图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的覆盆子PI中从H.264视频流中捕获一张图像。流正在使用raspivid和WebSocket。但是,无法在imshow()中显示正确的图像。我也尝试设置.reshape(),但收到ValueError: cannot reshape array of size 3607 into shape (480,640,3)

在客户端,我成功地连接到视频流,并获得了传入的字节。服务器正在使用raspivid-broadcaster进行视频流。我猜第一个字节可以解码成图像吗?因此,我执行以下代码。

async def get_image_from_h264_streaming():

    uri = "ws://127.0.0.1:8080"
    async with websockets.connect(uri) as websocket:
        frame = json.loads(await websocket.recv())

        print(frame)
        width, height = frame["width"], frame["height"]

        response = await websocket.recv()
        print(response)

        # transform the byte read into a numpy array
        in_frame = (
            numpy
            .frombuffer(response, numpy.uint8)
            # .reshape([height, width, 3])
        )

        # #Display the frame
        cv2.imshow('in_frame', in_frame)

        cv2.waitKey(0)

asyncio.get_event_loop().run_until_complete(get_image_from_h264_streaming())

打印(框架)显示

{'action': 'init', 'width': 640, 'height': 480}

打印(响应)显示

b"x00x00x00x01'Bx80(x95xa0(x0fhx0..............xfcx9fxffxf9?xffxf2x7fxffxe4x80"

有什么建议吗?

感谢this suggestion。以下是我的更新代码。

def decode(raw_bytes: bytes):
    code_ctx = av.CodecContext.create("h264", "r")
    packets = code_ctx.parse(raw_bytes)
    for i, packet in enumerate(packets):
        frames = code_ctx.decode(packet)
        if frames:
            return frames[0].to_ndarray() 

async def save_img():
    async with websockets.connect("ws://127.0.0.1:8080") as websocket:
        image_init = await websocket.recv()

        count = 0
        combined = b''

        while count < 3:
            response = await websocket.recv()
            combined += response
            count += 1

        frame = decode(combined)
        print(frame)

        cv2.imwrite('test.jpg', frame)

asyncio.get_event_loop().run_until_complete(save_img())

print(frame)显示

[[109 109 109 ... 115  97 236]
 [109 109 109 ... 115  97 236]
 [108 108 108 ... 115  97 236]
 ...
 [111 111 111 ... 101 103 107]
 [110 110 110 ... 101 103 107]
 [112 112 112 ... 104 106 110]]
下面是我得到的保存的图像。它的尺寸不是740(高)x640(宽)。正确的是480(高)x 640(宽)。并且,不确定为什么图像是灰度图像而不是彩色图像。

下面是raspivid中发送数据的主要方法。

raspivid-index.js

const {port, ...raspividOptions} = {...options, profile: 'baseline', timeout: 0};
videoStream = raspivid(raspividOptions)
    .pipe(new Splitter(NALSeparator))
    .pipe(new stream.Transform({
        transform: function (chunk, _encoding, callback){
            ...
            callback();
        }
    }));

videoStream.on('data', (data) => {
    wsServer.clients.forEach((socket) => {
        socket.send(data, {binary: true});
    });
});
Stream-plit-index.js(一行代码显示最大。大小为1MB)

class Splitter extends Transform {

  constructor(separator, options) {
    ...
    this.bufferSize  = options.bufferSize  || 1024 * 1024 * 1  ; //1Mb
    ...
  }

  _transform(chunk, encoding, next) {

    if (this.offset + chunk.length > this.bufferSize - this.bufferFlush) {
        var minimalLength = this.bufferSize - this.bodyOffset + chunk.length;
        if(this.bufferSize < minimalLength) {
          //console.warn("Increasing buffer size to ", minimalLength);
          this.bufferSize = minimalLength;
        }
          
        var tmp = new Buffer(this.bufferSize);
        this.buffer.copy(tmp, 0, this.bodyOffset);
        this.buffer = tmp;
        this.offset = this.offset - this.bodyOffset;
        this.bodyOffset = 0;
    }
    ...
  }
};

-已完成的答案(感谢Ann和Christoph的指导)-

请参阅答案部分。

推荐答案

一个问题,帧/流是如何通过WebSocket传输的?字节序列看起来像一个NAL单元,它可以是PPS或SPS等,你怎么知道它是一个IFRAME例如,我不知道cv2.imshow是否支持原始的H.64。查看pyav在那里您可以打开h264个原始字节,然后您可以尝试从中提取一帧:)如果您需要有关pyav的帮助,请让我知道,请查看此post 下面是一个如何做到这一点的例子。

更新

根据您的注释,您需要一种方法来解析和解码原始的H.64流, 下面是一个函数,给出了你的想法,你需要把你收到的字节从WebSocket传递给这个函数,注意需要足够的数据来提取一帧。

pip install av

PyAV docs

import av

# Feed in your raw bytes from socket
def decode(raw_bytes: bytes):
    code_ctx = av.CodecContext.create("h264", "r")
    packets = code_ctx.parse(raw_bytes)
    for i, packet in enumerate(packets):
        frames = code_ctx.decode(packet)
        if frames:
            return frame[0].to_ndarray() 

您还可以尝试使用pyav直接使用av.open(tcp://127.0.0.1:";)

读取数据流。

更新2 你能测试一下这个吗,你在编辑上遇到的问题很奇怪,你不需要一个可以直接从raspivid读取的WebSocket Layer I东西

raspivid -a 12 -t 0 -w 1280 -h 720 -vf -ih -fps 30 -l -o tcp://0.0.0.0:5000

def get_first_frame(path):
    stream = av.open(path, 'r')
    for packet in stream.demux():
        frames = packet.decode()
        if frames:
            return frames[0].to_ndarray(format='bgr24')

ff = get_first_frame("tcp://0.0.0.0:5000")
cv2.imshow("Video", ff)
cv2.waitKey(0)

这篇关于使用WebSocket-Python从H.264视频流中捕获第一幅图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆