python - 如何将视频流从一个 Python 传递到另一个?
问题描述
在我之前的帖子中,我们找到了一种将图像文件从一个 Python 传递到另一个 Python 的方法: 将视频数据从一个 Python 脚本传递到另一个
我现在正在尝试传递视频(连续图像):
写.py
import sys
import numpy as np
import cv2
from PIL import Image
import io
import time
while True:
img = cv2.imread('cat.jpg')
bimg = cv2.imencode('.jpg',img)[1]
sys.stdout.buffer.write(bimg)
sys.stdout.flush()
time.sleep(1)
读取.py:
import sys
from PIL import Image
import io
import cv2
import numpy as np
from io import BytesIO
while True:
data = sys.stdin.buffer.read()
img_np = cv2.imdecode(np.frombuffer(BytesIO(data).read(), np.uint8), cv2.IMREAD_UNCHANGED)
cv2.imshow('image', img_np)
cv2.waitKey(0)
如果我将 write.py 数据输出到终端,它会打印出来。如果我手动将数据交给 read.py 读取。但是将它们放在一起(python3 write.py | python3 read.py
),它就会挂起。write.py 只写一次,而 read.py 似乎永远不会得到它。
我的猜测是,读取代码正在等待写入代码“结束”,然后才打包数据包并将其称为图像。虽然如果是这样的话,我会认为做一个冲洗会解决它。
解决方案
我想我想通了。In read.py
,sys.stdin.buffer.read()
读取并等待stdin
管道关闭,但由于循环而write.py
从未真正关闭它。这个概念证明简化示例有效:stdout
while True
write.py
import sys
import time
sys.stdout.buffer.write(b"Hello world")
sys.stdout.buffer.flush()
# Note if we comment out the code bellow it works again
while True:
# Keep this alive but don't have `while True:pass`
# because my computer might crash :D
time.sleep(10)
和read.py
import sys
with open("output.txt", "w") as file:
file.write(sys.stdin.read())
这也将挂起,并且永远不会真正向"output.txt"
. 如果我们while True
从代码中删除循环write.py
将不再挂起"Hello World"
并将被写入,"output.py"
因为当write.py
完成写入时它将关闭其进程并关闭管道。要解决此问题,我建议更改read.py
为以下内容:
import sys
while True:
with open("output.txt", "a") as file:
file.write(sys.stdin.read(1))
解决方案:
write.py
import sys
import time
MAX_FILE_SIZE = 16 # bytes
msg = b"Hello world"
# Tell `reader.py` that it needs to read x number of bytes.
length = len(msg)
# We also need to tell `read.py` how many bytes it needs to read.
# This means that we have reached the same problem as before.
# To fix that issue we are always going to send the number of bytes but
# We are going to pad it with `0`s at the start.
# https://stackoverflow.com/a/339013/11106801
length = str(length).zfill(MAX_FILE_SIZE)
sys.stdout.buffer.write(length.encode())
sys.stdout.buffer.write(msg)
sys.stdout.buffer.flush()
# We also need to tell `read.py` that it was the last file that we send
# Sending `1` means that the file has ended
sys.stdout.buffer.write(b"1")
sys.stdout.buffer.flush()
# Note if we comment out the code bellow it works again
while True:
# Keep this alive but don't have `while True:pass`
# because my computer might crash :D
time.sleep(10)
和read.py
import sys
import time
MAX_FILE_SIZE = 16 # bytes
while True:
time.sleep(1) # Make sure `write.py` has sent the data
# Read `MAX_FILE_SIZE` number of bytes and convert it to an int
# So that we know the size of the file comming in
length = int(sys.stdin.buffer.read(MAX_FILE_SIZE))
time.sleep(1) # Make sure `write.py` has sent the data
# Here you can switch to a different file every time `writer.py`
# Sends a new file
with open("output.txt", "wb") as file:
file.write(sys.stdin.buffer.read(length))
file_ended = sys.stdin.buffer.read(1)
if file_ended == b"1":
# File has ended
break
else:
# We are going to start reading again for the next file:
pass
编辑: 解决方案的工作原理如下:
- 发送文件大小
- 发送实际的文件数据
- 发送一个字节,告诉
read.py
它是否应该期待另一个文件
对于第 1 部分,我们只是将文件的长度编码为一个在前面用 0 填充的字符串。注意:确保MAX_FILE_SIZE
大于最大文件的大小(大数字会稍微降低性能)。对于第 3 部分,如果我们发送一个"1"
,则意味着没有更多文件要发送。否则reader.py
将等待并接受下一个文件。所以write.py
会变成:
from math import log
import time
import sys
import cv2
MAX_FILE_SIZE = 62914560 # bytes
MAX_FILE_SIZE = int(log(MAX_FILE_SIZE, 2)+1)
def write_file(buffer, data, last_file=False):
# Tell `reader.py` that it needs to read x number of bytes.
length = len(data)
# We also need to tell `read.py` how many bytes it needs to read.
# This means that we have reached the same problem as before.
# To fix that issue we are always going to send the number of bytes but
# We are going to pad it with `0`s at the start.
# https://stackoverflow.com/a/339013/11106801
length = str(length).zfill(MAX_FILE_SIZE)
with open("output.txt", "w") as file:
file.write(length)
buffer.write(length.encode())
# Write the actual data
buffer.write(data)
# We also need to tell `read.py` that it was the last file that we send
# Sending `1` means that the file has ended
buffer.write(str(int(last_file)).encode())
buffer.flush()
while True:
img = cv2.imread("img.jpg")
bimg = cv2.imencode(".jpg", img)[1]
# Call write_data
write_file(sys.stdout.buffer, bimg, last_file=False)
# time.sleep(1) # Don't need this
并将read.py
成为:
from io import BytesIO
from math import log
import numpy as np
import time
import cv2
import sys
MAX_FILE_SIZE = 62914560 # bytes
MAX_FILE_SIZE = int(log(MAX_FILE_SIZE, 2)+1)
def read(buffer, number_of_bytes):
output = b""
while len(output) < number_of_bytes:
output += buffer.read(number_of_bytes - len(output))
assert len(output) == number_of_bytes, "An error occured."
return output
def read_file(buffer):
# Read `MAX_FILE_SIZE` number of bytes and convert it to an int
# So that we know the size of the file comming in
length = int(read(buffer, MAX_FILE_SIZE))
# Here you can switch to a different file every time `writer.py`
# Sends a new file
data = read(buffer, length)
# Read a byte so that we know if it is the last file
file_ended = read(buffer, 1)
return data, (file_ended == b"1")
while True:
print("Reading file")
data, last_file = read_file(sys.stdin.buffer)
img_np = cv2.imdecode(np.frombuffer(BytesIO(data).read(), np.uint8),
cv2.IMREAD_UNCHANGED)
cv2.imshow("image", img_np)
cv2.waitKey(0)
if last_file:
break;
推荐阅读
- android - 安装 CalyxOS 时出错:发送稀疏 'system_a' 2/4 - (读取稀疏文件时出错)
- react-native - 如何在 react native expo 中实现可以在前台和后台运行的秒表?
- php - 如何在 Laravel 中获取大于 x 的记录数(使用 MySQL 查询)
- java - 不同包中的类的密封类
- javascript - 如何使用 React Native 将数据数组加载到数据集格式?
- android - 是否可以同时存在同一个库的两个不同版本?
- delphi - 如何解决我的 Livebinding 和组合框问题
- apache-spark - 无法使用 spark-submit 调用特定包
- django - 如果 server_name 上没有请求,nginx 将停止工作
- android - 导航组件替换/更改后台堆栈