詳解如何實(shí)現(xiàn)C#和Python間實(shí)時(shí)視頻數(shù)據(jù)交互
我們?cè)谧鯮TSP|RTMP播放的時(shí)候,遇到好多開(kāi)發(fā)者,他們的視覺(jué)算法大多運(yùn)行在python下,需要高效率的實(shí)現(xiàn)C#和Python的視頻數(shù)據(jù)交互,常用的方法如下:
方法一:通過(guò)HTTP請(qǐng)求傳輸視頻數(shù)據(jù)
服務(wù)器端(Python)
使用Flask或Django等Web框架創(chuàng)建一個(gè)HTTP服務(wù)器,通過(guò)API接口提供視頻數(shù)據(jù)。
from flask import Flask, send_file, request, jsonify import cv2 import io import base64 app = Flask(__name__) def get_video_frame(): # 讀取視頻幀 cap = cv2.VideoCapture('your_video.mp4') ret, frame = cap.read() cap.release() if not ret: return None, "No more frames" # 轉(zhuǎn)換為RGB格式 frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 轉(zhuǎn)換為字節(jié)流 ret, buffer = cv2.imencode('.jpg', frame_rgb) if not ret: return None, "Could not encode frame" # 將字節(jié)流轉(zhuǎn)換為base64字符串 img_str = base64.b64encode(buffer).decode('utf-8') return img_str, 200 @app.route('/video_frame', methods=['GET']) def video_frame(): img_str, status_code = get_video_frame() if img_str is None: return jsonify({"error": status_code}), status_code return jsonify({"image": img_str}) if __name__ == '__main__': app.run(debug=True)
客戶端(C#)
使用HttpClient從Python服務(wù)器獲取視頻幀,并將其顯示在PictureBox中。
using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Net.Http; using System.Windows.Forms; public class VideoForm : Form { private PictureBox pictureBox; private HttpClient httpClient; public VideoForm() { pictureBox = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.StretchImage }; this.Controls.Add(pictureBox); httpClient = new HttpClient(); Timer timer = new Timer(); timer.Interval = 100; // Adjust the interval as needed timer.Tick += Timer_Tick; timer.Start(); } private async void Timer_Tick(object sender, EventArgs e) { try { HttpResponseMessage response = await httpClient.GetAsync("http://localhost:5000/video_frame"); response.EnsureSuccessStatusCode(); string jsonResponse = await response.Content.ReadAsStringAsync(); dynamic jsonData = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse); string base64String = jsonData.image; byte[] imageBytes = Convert.FromBase64String(base64String); using (MemoryStream ms = new MemoryStream(imageBytes)) { Image image = Image.FromStream(ms); pictureBox.Image = image; } } catch (Exception ex) { MessageBox.Show($"Error: {ex.Message}"); } } [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.Run(new VideoForm()); } }
方法二:通過(guò)ZeroMQ傳輸視頻數(shù)據(jù)
ZeroMQ是一種高性能的異步消息庫(kù),適用于需要低延遲和高吞吐量的應(yīng)用。
服務(wù)器端(Python)
使用pyzmq
庫(kù)發(fā)送視頻幀。
import zmq import cv2 import numpy as np context = zmq.Context() socket = context.socket(zmq.PUB) socket.bind("tcp://*:5555") cap = cv2.VideoCapture('your_video.mp4') while True: ret, frame = cap.read() if not ret: break frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame_bytes = frame_rgb.tobytes() socket.send_string("FRAME", zmq.SNDMORE) socket.send(frame_bytes, flags=0, copy=False)
客戶端(C#)
使用NetMQ
庫(kù)接收視頻幀。
using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; using NetMQ; using NetMQ.Sockets; public class VideoForm : Form { private PictureBox pictureBox; private SubscriberSocket subscriber; public VideoForm() { pictureBox = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.StretchImage }; this.Controls.Add(pictureBox); var address = "tcp://localhost:5555"; using (var context = NetMQContext.Create()) { subscriber = context.CreateSubscriberSocket(); subscriber.Connect(address); subscriber.Subscribe("FRAME"); Task.Run(() => ReceiveFramesAsync(subscriber)); } } private async Task ReceiveFramesAsync(SubscriberSocket subscriber) { while (true) { var topic = await subscriber.ReceiveFrameStringAsync(); var frameBytes = await subscriber.ReceiveFrameBytesAsync(); if (topic == "FRAME") { int width = 640; // Adjust based on your video resolution int height = 480; // Adjust based on your video resolution int stride = width * 3; Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb); BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat); Marshal.Copy(frameBytes, 0, bitmapData.Scan0, frameBytes.Length); bitmap.UnlockBits(bitmapData); pictureBox.Image = bitmap; } } } [STAThread] public static void Main() { Application.EnableVisualStyles(); Application.Run(new VideoForm()); } }
方法三:通過(guò)共享內(nèi)存或文件
如果C#和Python運(yùn)行在同一臺(tái)機(jī)器上,可以通過(guò)共享內(nèi)存或文件系統(tǒng)進(jìn)行數(shù)據(jù)交換。這種方法相對(duì)簡(jiǎn)單,但性能可能不如前兩種方法。
以上就是詳解如何實(shí)現(xiàn)C#和Python間實(shí)時(shí)視頻數(shù)據(jù)交互的詳細(xì)內(nèi)容,更多關(guān)于C#和Python視頻數(shù)據(jù)交互的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C# 獲取硬件參數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了C# 獲取硬件參數(shù)的實(shí)現(xiàn)方法的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-10-10C#難點(diǎn)逐個(gè)擊破(8):可空類(lèi)型System.Nullable
null值用來(lái)表示數(shù)據(jù)類(lèi)型未被賦予任何值,它是一種引用類(lèi)型;void表示沒(méi)有類(lèi)型,或者說(shuō)是沒(méi)有任何值。null與void的區(qū)別可以認(rèn)為void是根本沒(méi)有,而null是一個(gè)空箱子,里面什么都沒(méi)有。2010-02-02C#中Foreach循環(huán)遍歷的本質(zhì)與枚舉器詳解
這篇文章主要給大家介紹了關(guān)于C#中Foreach循環(huán)遍歷本質(zhì)與枚舉器的相關(guān)資料,foreach循環(huán)用于列舉出集合中所有的元素,foreach語(yǔ)句中的表達(dá)式由關(guān)鍵字in隔開(kāi)的兩個(gè)項(xiàng)組成,本文通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-08-08C#使?XmlReader和XmlWriter操作XML?件
這篇文章介紹了C#使?XmlReader和XmlWriter操作XML?件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06C# [ImportDll()] 知識(shí)小結(jié)
今天小編就為大家分享一篇關(guān)于C# [ImportDll()] 知識(shí)小結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01C#使用log4net結(jié)合sqlite數(shù)據(jù)庫(kù)實(shí)現(xiàn)記錄日志
因?yàn)榻Y(jié)構(gòu)化的數(shù)據(jù)庫(kù)存儲(chǔ)的日志信息,可以寫(xiě)專(zhuān)門(mén)的軟件讀取歷史日志信息,通過(guò)各種條件篩選,可操作性極大增強(qiáng),有這方面需求的開(kāi)發(fā)人員可以考慮,本文給大家介紹了C#使用log4net結(jié)合sqlite數(shù)據(jù)庫(kù)記錄日志,需要的朋友可以參考下2024-10-10C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法
這篇文章主要介紹了C#解碼base64編碼二進(jìn)制數(shù)據(jù)的方法,涉及C#中Convert類(lèi)的靜態(tài)方法Convert.FromBase64String使用技巧,需要的朋友可以參考下2015-04-04