首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用websocket - Python从h.264视频流中捕获第一张图像

使用websocket - Python从h.264视频流中捕获第一张图像
EN

Stack Overflow用户
提问于 2022-03-07 18:57:19
回答 2查看 1.5K关注 0票数 2

我正试图在我的Raspberry Pi中捕捉H.264视频流中的单个图像。流媒体使用的是拉斯皮维和websocket。但是,不能在imshow()中显示正确的图像。我也试图设置.reshape(),但得到了ValueError: cannot reshape array of size 3607 into shape (480,640,3)

在客户端,我成功地连接到视频流并获取传入的字节。服务器正在使用电视播音员进行视频流传输。我想第一个字节可以解码成图像?因此,我执行以下代码。

代码语言:javascript
运行
复制
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())

打印(帧)显示

代码语言:javascript
运行
复制
{'action': 'init', 'width': 640, 'height': 480}

打印(响应)显示

代码语言:javascript
运行
复制
b"\x00\x00\x00\x01'B\x80(\x95\xa0(\x0fh\x0..............xfc\x9f\xff\xf9?\xff\xf2\x7f\xff\xe4\x80"

有什么建议吗?

----------------------------------编辑----------------------------------

谢谢这一建议。这是我更新的代码。

代码语言:javascript
运行
复制
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)显示

代码语言:javascript
运行
复制
[[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(宽度)。也不知道为什么图像是灰度的,而不是彩色的。

----------------------------------编辑2 ----------------------------------

下面是在拉斯皮维中发送数据的主要方法。

raspivid - index.js

代码语言:javascript
运行
复制
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});
    });
});

流拆分- index.js (一行代码显示最大值)。尺寸是1兆)

代码语言:javascript
运行
复制
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;
    }
    ...
  }
};

请参阅答题部分。

EN

回答 2

Stack Overflow用户

发布于 2022-03-15 17:36:09

需要一包PyAV和枕头。不再需要使用cv2了。所以,添加包

代码语言:javascript
运行
复制
pip3 install av
pip3 install Pillow

代码

代码语言:javascript
运行
复制
import asyncio
import websockets
import av
import PIL

def decode_image(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_image()

async def save_img_from_streaming():

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

        count = 0
        combined = b''

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

        img = decode_image(combined)
        img.save("img1.png","PNG")

asyncio.get_event_loop().run_until_complete(save_img_from_streaming())

根据Christoph的回答,建议使用to_ndarray,但我发现它以某种方式产生了灰度图像,这是由于返回不正确的numpy数组形式(如[[...], [...], [...], ...] )而造成的。彩色图像应该是一个类似于[[[...], [...], [...], ...], ...]的数组。然后,我查看PyAV文档,有另一个名为to_image的方法,它可以返回帧的RGB PIL.Image。所以,只要使用这个函数就能得到我所需要的。

注意到来自await websocket.recv()的响应可能有所不同。这取决于服务器发送的方式。

票数 2
EN

Stack Overflow用户

发布于 2022-03-10 05:48:26

这是我试图通过套接字发送numpy图像(转换为字节)时遇到的一个问题。问题是字节字符串太长了。

所以,我没有一次发送整个图像,而是对图像进行切片,这样我就必须发送,比如说,10片图像。一旦另一端接收到这10片,只需将它们叠加在一起即可。

请记住,取决于图像的大小,您可能需要或多或少地分割它们,以获得最佳结果(效率,没有错误)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71385967

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档