跟着视频学了用thonny,利用udp协议将开发版拍摄的视频通过WiFi传到pc端
需要在pc端输入ip和端口才能进行连接
但是在设计的pyqt代码中,视频流需要通过LoadWebcam(self.source, img_size=imgsz, stride=stride)进行操作,LoadWebcam()又来自官方代码中datasets.py文件,我不敢乱动,只能输入source = 0进行本地视频流输入
请问怎么通过udp协议进行视频流输入
对于视频流通过UDP协议传输,可以通过OpenCV库中的cv2.VideoCapture()函数实现,在调用该函数时将传输地址填入即可。
以接收端(PC端)为例,先使用socket库创建一个UDP连接。代码如下所示:
import cv2
import socket
import numpy as np
# 创建UDP连接
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 9876)) # 填入接收端口号
# 循环接收视频流
while True:
# 接收数据和发送端IP
data, addr = sock.recvfrom(64000)
# 二进制数据解码
img = np.frombuffer(data, dtype=np.uint8)
# 解码成图片
img = cv2.imdecode(img, flags=cv2.IMREAD_COLOR)
cv2.imshow('Video Stream', img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
运行代码后即可开启PC端接受数据的服务,此时需要修改yolo代码文件(dataset.py)的视频源部分。替换0为udp协议的传输地址,代码示例如下:
def init_dataset(name):
global img_size
if ("coco" in name):
dataset = COCODataset(name)
elif ("VOC" in name) or ("pascal" in name):
dataset = PascalVOCDataset(name)
elif ("ytvos" in name):
dataset = YTVOSDataset(name)
elif re.match("^image-[0-9]+$", name) is not None:
dataset = ImageFolderDataset(name)
elif re.match("^video-[0-9]+$", name) is not None:
# cap = cv2.VideoCapture(int(name[6:]))
cap = cv2.VideoCapture('udp://<UDP传输地址>:<端口号>')
dataset = VideoDataset(name, cap)
else:
...
将代码中的UDP传输地址和端口号修改为实际使用的IP和端口号即可在本地运行yolo模型对接收到的视频流进行目标检测。
请采纳
要在 Thonny 中使用 UDP 协议将开发版拍摄的视频通过 WiFi 传输到 PC 端,您需要执行以下步骤:
在 Thonny 中安装 Python 的 udp 模块,以便您可以在 Python 中使用 UDP 协议。您可以使用以下命令安装 udp 模块:
pip install udp
在 Thonny 中创建一个新的 Python 文件,例如 send_video.py。
在 send_video.py 中,导入必要的模块:
import udp
import cv2
定义一个函数,该函数将使用 UDP 协议将视频流发送到指定的 IP 地址和端口:
def send_video(ip, port, video_source):
# 创建 UDP socket
sock = udp.socket(ip, port)
# 打开视频源并获取帧
cap = cv2.VideoCapture(video_source)
while True:
ret, frame = cap.read()
if not ret:
break
# 编码视频帧以发送给服务器
encoded_frame = cv2.VideoEncodeFormat(cv2.VideoWriter_fourcc(*'mp4v'), int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)), int(cap.get(cv2.CAP_PROP_FPS)), frame.tobytes())
sock.sendto(encoded_frame, (ip, port))
在 send_video.py 的主函数中,调用 send_video 函数,并将必要的参数传递给它:
if __name__ == '__main__':
send_video('192.168.0.100', 5000, 0) # 将视频源设为本地摄像头(source=0)并发送到 IP 地址为 192.168.0.100、端口号为 5000 的服务器。
在 PC 端运行一个 Python 程序,该程序将接收来自 Thonny 的视频流,并将其显示在窗口中。例如,您可以使用以下代码创建一个简单的窗口,并在其中显示接收到的视频流:
import cv2
import socket
import threading
# 创建 UDP socket 并绑定到本地端口号
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', 5000))
# 创建窗口以显示视频流
cv2.namedWindow('Video')
cv2.resizeWindow('Video', 800, 600)
cv2.setWindowProperty('Video', cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
while True:
# 从 UDP socket 中接收数据并解码帧以显示在窗口中
data, addr = sock.recvfrom(1024)
frame = cv2.imdecode(bytearray(data), cv2.IMREAD_UNCHANGED)
cv2.imshow('Video', frame)
if cv2.waitKey(1) & 0xFF == ord('q'): # 按 'q' 键退出循环并关闭窗口
break # 关闭窗口并退出循环
将esp-32相机流式传输到YouTube等RTMP服务器
可以借鉴下
https://www.it1352.com/1681205.html
跟着视频学了用thonny,利用udp协议将开发版拍摄的视频通过WiFi传到pc端
需要在pc端输入ip和端口才能进行连接
但是在设计的pyqt代码中,视频流需要通过LoadWebcam(self.source, img_size=imgsz, stride=stride)进行操作,LoadWebcam()又来自官方代码中datasets.py文件,我不敢乱动,只能输入source = 0进行本地视频流输入
请问怎么通过udp协议进行视频流输入
```c
import cv2
import socket
cap = cv2.VideoCapture(0) # 打开本地摄像头
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建UDP客户端
ip = '192.168.1.100' # 开发版的IP
port = 10001 # 开发版的端口
while True:
ret, frame = cap.read()
# 对视频数据进行编码
img_encode = cv2.imencode('.jpg', frame)[1]
data = img_encode.tostring()
# 发送UDP数据包
sock.sendto(data, (ip, port))
# 接收方
import cv2
import socket
from PyQt5.QtGui import QPixmap
def receive_udp_video(ip, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 创建UDP服务端
sock.bind((ip, port)) # 绑定IP和端口
while True:
# 接收UDP数据包
data, addr = sock.recvfrom(65536)
# 对视频数据进行解码
img_decode = cv2.imdecode(np.fromstring(data, dtype=np.uint8), cv2.IMREAD_COLOR)
# 向UI界面显示视频帧
pixmap = QPixmap.fromImage(QImage(img_decode, img_decode.shape[1], img_decode.shape[0], QImage.Format_RGB888))
self.label.setPixmap(pixmap)
```
要通过UDP协议进行视频流输入,需要进行以下步骤:
在拍摄视频的设备上,使用UDP协议将视频数据发送给PC端。
在PC端使用Python的socket模块创建一个UDP socket,指定业务端口和IP地址(由用户在界面上输入)。
在PC端,通过socket.recv()函数接收来自拍摄视频设备发送的视频数据。
将接收到的视频数据解码并显示在PC端的界面上。
下面是Python示例代码:
import cv2
import numpy as np
import socket
# 定义UDP地址和端口
UDP_IP = "192.168.0.1"
UDP_PORT = 12345
# 创建UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))
# 创建窗口并加载视频流
window_name = "Video Stream"
cv2.namedWindow(window_name)
# 不断循环读取和显示视频流
while True:
# 接收UDP数据包
data, addr = sock.recvfrom(65536)
# 解码数据包
nparr = np.frombuffer(data, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# 显示视频流
cv2.imshow(window_name, img)
# 检测键盘按键
key = cv2.waitKey(1)
if key == ord('q'):
break
# 释放资源
cv2.destroyAllWindows()
sock.close()
可以借鉴下
/*
网络调试助手
https://soft.3dmgame.com/down/213757.html
*/
#include <Arduino.h>
#include <WiFi.h>
#include "esp_camera.h"
#include <vector>
const char *ssid = "dsx_zj";
const char *password = "dsxbs725";
const IPAddress serverIP(192,168,0,2); //欲访问的地址
uint16_t serverPort = 8080; //服务器端口号
#define maxcache 1430
WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
//CAMERA_MODEL_AI_THINKER类型摄像头的引脚定义
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
.frame_size = FRAMESIZE_VGA,
.jpeg_quality = 12,
.fb_count = 1,
};
void wifi_init()
{
WiFi.mode(WIFI_STA);
WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("WiFi Connected!");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
}
esp_err_t camera_init() {
//initialize the camera
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.println("Camera Init Failed");
return err;
}
sensor_t * s = esp_camera_sensor_get();
//initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV2640_PID) {
// s->set_vflip(s, 1);//flip it back
// s->set_brightness(s, 1);//up the blightness just a bit
// s->set_contrast(s, 1);
}
Serial.println("Camera Init OK!");
return ESP_OK;
}
void setup()
{
Serial.begin(115200);
wifi_init();
camera_init();
}
void loop()
{
Serial.println("Try To Connect TCP Server!");
if (client.connect(serverIP, serverPort)) //尝试访问目标地址
{
Serial.println("Connect Tcp Server Success!");
//client.println("Frame Begin"); //46 72 61 6D 65 20 42 65 67 69 6E // 0D 0A 代表换行 //向服务器发送数据
while (1){
camera_fb_t * fb = esp_camera_fb_get();
uint8_t * temp = fb->buf; //这个是为了保存一个地址,在摄像头数据发送完毕后需要返回,否则会出现板子发送一段时间后自动重启,不断重复
if (!fb)
{
Serial.println( "Camera Capture Failed");
}
else
{
//先发送Frame Begin 表示开始发送图片 然后将图片数据分包发送 每次发送1430 余数最后发送
//完毕后发送结束标志 Frame Over 表示一张图片发送完毕
client.print("Frame Begin"); //一张图片的起始标志
// 将图片数据分段发送
int leng = fb->len;
int timess = leng/maxcache;
int extra = leng%maxcache;
for(int j = 0;j< timess;j++)
{
client.write(fb->buf, maxcache);
for(int i =0;i< maxcache;i++)
{
fb->buf++;
}
}
client.write(fb->buf, extra);
client.print("Frame Over"); // 一张图片的结束标志
Serial.print("This Frame Length:");
Serial.print(fb->len);
Serial.println(".Succes To Send Image For TCP!");
//return the frame buffer back to the driver for reuse
fb->buf = temp; //将当时保存的指针重新返还
esp_camera_fb_return(fb); //这一步在发送完毕后要执行,具体作用还未可知。
}
delay(20);//短暂延时 增加数据传输可靠性
}
/*
while (client.connected() || client.available()) //如果已连接或有收到的未读取的数据
{
if (client.available()) //如果有数据可读取
{
String line = client.readStringUntil('\n'); //读取数据到换行符
Serial.print("ReceiveData:");
Serial.println(line);
client.print("--From ESP32--:Hello Server!");
}
}
Serial.println("close connect!");
client.stop(); //关闭客户端
*/
}
else
{
Serial.println("Connect To Tcp Server Failed!After 10 Seconds Try Again!");
client.stop(); //关闭客户端
}
delay(10000);
}
答案由GPT-4Plus大模型(语音)与博主@晓码自在合作编写(完整代码在最后):
根据您的描述,现在遇到的问题是:
需要通过UDP协议获取开发板发送的视频流,但是LoadWebcam()函数只接受来自本地摄像头的输入,无法直接使用UDP视频流。
解决思路如下:
UDP视频流的基本原理是:
所以关键是在PC端接收UDP socket,得到图片数据。
可以编写一个函数,如receive_img_from_udp(),其基本逻辑为:
该函数的代码大致如下:
python
import socket
import cv2
def receive_img_from_udp(port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('localhost', port))
while True:
data, addr = sock.recvfrom(4096) # buffer size is 4096 bytes
img = cv2.imdecode(np.fromstring(data, dtype=np.uint8), cv2.IMREAD_COLOR)
return img
在pyqt代码中,调用该函数获得图片,然后传入LoadWebcam()的source参数,即可显示UDP视频流:
python
img = receive_img_from_udp(port) # 获得一帧图片
self.source = img # 传入LoadWebcam
LoadWebcam(self.source, ......) # 显示视频流
完整代码加完美注释参考如下:
python
import socket
import cv2
from PyQt5.QtCore import QThread, pyqtSignal
# 定义信号,用于更新视频帧
frame_signal = pyqtSignal(np.ndarray)
# 视频流接收线程
class VideoReceive(QThread):
def __init__(self, port):
super().__init__()
self.port = port
def run(self):
# 建立socket,监听port端口
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('127.0.0.1', self.port))
while True:
# 不断接收数据
data, addr = sock.recvfrom(4096)
# 将数据解码为图片
img = cv2.imdecode(np.fromstring(data, dtype=np.uint8), cv2.IMREAD_COLOR)
# 发出信号更新视频帧
frame_signal.emit(img)
# PyQt界面
class MyApp(QWidget):
def __init__(self):
super().__init__()
...
def start_receive(self, port):
# 创建并启动视频接收线程
self.receive_thread = VideoReceive(port)
self.receive_thread.start()
# 连接信号槽,更新视频帧
frame_signal.connect(self.update_frame)
def update_frame(self, frame):
# 将新的视频帧显示
self.source = frame
self.LoadWebcam(self.source, ...)
# 程序入口
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyApp()
window.show()
# 输入IP和端口,启动接收线程
ip = input('Input IP: ')
port = int(input('Input port: '))
window.start_receive(port)
sys.exit(app.exec_())
注释说明:
这段代码实现了通过UDP协议获取视频流并显示在pyqt界面上的效果。
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
首先,你需要在PC端编写一个UDP server端,用于接收从ESP32-CAM发送的视频流数据,然后将这些数据传递给LoadWebcam()函数。下面是一个简单的UDP server端代码示例:
import socket
import cv2
import numpy as np
UDP_IP = "0.0.0.0" # 设置为0.0.0.0表明可以接收任意IP地址发送过来的数据
UDP_PORT = 5005 # 设置UDP端口号
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
img_size = (640, 480) # 设置图像大小,需要和ESP32-CAM发送的图像大小一致
stride = 32 # 设置stride,默认为32
window = 'camera' # 设置窗口名称
while True:
data, addr = sock.recvfrom(1024) # 接收数据,接收缓冲区大小为1024字节
if not data:
break
nparr = np.frombuffer(data, np.uint8) # 将数据转化为numpy数组
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) # 解码图像数据
img = cv2.resize(img, img_size) # 将图像大小调整为设定大小
cv2.imshow(window, img) # 将图像显示到窗口
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
上面的代码会接收从UDP端口5005发送过来的数据,将其解码为图像数据,并显示在名为'camera'的窗口中。你可以根据需要修改图像大小。此外,如果你想要使用YoloV5进行目标检测,你需要将图像转换为适合YoloV5模型输入的格式。
在ESP32-CAM开发板中,你可以使用以下代码将摄像头拍摄的视频流通过UDP发送到PC端:
import sensor
import time
import network
import socket
import ujson
import gc
# 初始化摄像头配置,详细配置可以参考开发板官方文档
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224)) # 将图像大小调整为224x224,需要和UDP server端的大小一致
sensor.set_vflip(True)
sensor.set_hmirror(True)
sensor.run(1)
# 连接WiFi网络
ssid = "Your_SSID"
key = "Your_Password"
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect(ssid, key)
while not sta_if.isconnected():
pass
print("WiFi connected")
# 创建UDP客户端
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
dst_addr = ("Your_PC_IP", 5005) # 将PC的IP地址和UDP server的端口号作为目标地址
udp_socket.connect(dst_addr)
# 利用UDP协议传输视频流
while True:
try:
img = sensor.snapshot() # 拍摄一帧图像
img_bytes = ujson.dumps(img).encode('utf-8') # 将图像数据转化为json字符串,并将其编码为utf-8格式
udp_socket.send(img_bytes) # 发送数据
gc.collect() # 手动垃圾回收,释放内存
except OSError as ex:
print(ex)
udp_socket.close() # 关闭UDP socket
上面的代码会将拍摄的图像转化为json字符串,并通过UDP协议发送到指定的IP地址和端口号。你可以根据需要修改图像大小。
最后,在你的PyQt代码中,你需要修改LoadWebcam()函数,将其接口修改为:
def LoadWebcam(self, source, img_size=(640,480), stride=0):
self.stride = stride
self.cap = cv2.VideoCapture(source)
self.img_size = img_size
source参数是用来指定视频流来源,可以是本地摄像头、本地视频文件或者从网络接收的视频流。在这里,你可以传递一个空的source参数,在UDP server端中将视频流数据传递给LoadWebcam()函数即可。
如果你使用的是YoloV5预训练模型,你可以使用以下代码将图像转换为适合YoloV5输入的格式:
from PIL import Image
import numpy as np
def yolov5_input(img):
img = Image.fromarray(img) # 将OpenCV图像转换为PIL图像
img = img.convert('RGB') # 将图像转化为RGB模式
img = img.resize((640, 640), Image.BICUBIC) # 将图像大小调整为640x640
img = np.array(img).astype(np.float32) / 255.0 # 将图像数据转化为numpy数组,并将像素值缩放到[0, 1]的范围内
img = np.transpose(img, (2, 0, 1)) # 转置图像维度,符合YoloV5模型输入的格式
img = np.expand_dims(img, 0) # 添加一个维度,符合YoloV5模型输入的格式
return img
将上面的代码添加到你的PyQt代码中,然后在处理视频流时,将每一帧图像传递给yolov5_input()函数,然后将返回的结果传递给YoloV5模型即可。
希望这些代码能够对你有所帮助!
如果我的回答解决了您的问题,请采纳!