OpenCV 网络视频传输 C++ 和 python实现

这篇博客展示了如何使用C++和Python实现UDP视频流的发送和接收。C++接收端通过绑定套接字并监听指定端口接收图像数据,Python发送端则捕获摄像头帧,将图像压缩后分段发送。接收端接收到完整数据后解码显示图像。代码实现了数据重组和丢包处理,确保了视频流的稳定传输。
摘要由CSDN通过智能技术生成

C++

接收端

#include <iostream>
#include <opencv.hpp>
#include <WinSock2.h>
#pragma comment(lib,"WS2_32.lib")

using namespace std;
using namespace cv;

int main()
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		printf("init windows socket error: errno %d\n",WSAGetLastError());
		return -1;
	}//创建初始化句柄

	SOCKET sockServe;
	if ((sockServe = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)    //创建socket句柄,采用UDP协议
	{
		printf("create socket error: errno %d\n", WSAGetLastError());
		return -1;
	}
	sockaddr_in servaddr;
	servaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	servaddr.sin_family = AF_INET;           //设置通信方式
	servaddr.sin_port = htons(8554);         //设置端口号
	if (::bind(sockServe, (const sockaddr*)&servaddr, sizeof(servaddr)) == SOCKET_ERROR)
	{
		printf("bind socket  with IP error: errno %d\n", WSAGetLastError());
		return -1;
	}//绑定套接字
	listen(sockServe, 3);
	Mat image;
	int MAX_DGRAM = 65536;
	char buf[65536];
	while (true)
	{
		std::vector<uchar> decode;
		int count;
		do
		{
			int nBytes = recv(sockServe, buf, sizeof(buf), 0);//接受缓存
			count = buf[0];
			for (int i = 1; i < nBytes; i++)
			{
				decode.push_back(buf[i]);
			}
			cout << count << endl;
		} while (count > 1);
		image = imdecode(decode, cv::ImreadModes::IMREAD_COLOR);//图像解码
		cout<<image.size()<<endl;
		//在UDP传输的过程中,可能回发生丢包,因此需要对接受到的影像进行判断
		if (image.empty())
		{
			continue;
		}
		imshow("serve image", image);
		cv::waitKey(1);
	}
	closesocket(sockServe);
	WSACleanup();
	return 0;
}


发送端

#include <iostream>
#include <opencv.hpp>
#include <WinSock2.h>
#pragma comment(lib,"WS2_32.lib")
using namespace std;
using namespace cv;
#define MAX_DGRAM 65535


int main()
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		//errno 是error.h中定义的宏,表示的是程序运行是发生错误的错误代码
		//strerror定义在string.h中,用于打印错误代码对应的错误描述
		printf("init windows socket error:errno %d\n",WSAGetLastError());
		return -1;
	}//创建初始化句柄
	SOCKET clientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (clientSock == INVALID_SOCKET)
	{
		printf("create client socket error:errno %d\n",WSAGetLastError());
		return -1;
	}
	//定义服务端的IP地址,向服务端发送请求
	sockaddr_in serveaddr;
	memset(&serveaddr, 0, sizeof(serveaddr));
	serveaddr.sin_family = AF_INET;
	serveaddr.sin_port = htons(8554);
	serveaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	VideoCapture vCapture(0);
	while (true)
	{
		if (!vCapture.isOpened())
		{
			break;
		}
		Mat image;
		bool flag = vCapture.read(image);
		if (!flag)
		{
			break;
		}
		vector<uchar> imageBuf;
		int bufSize;
		imencode(".jpg", image, imageBuf);
		bufSize = imageBuf.size();
		char imageBlockBuf[65536];
		int count = ceil(1.0*bufSize / MAX_DGRAM);
		int start_index = 0, end_index;
		while (count)
		{
			end_index = start_index + MAX_DGRAM;
			if (end_index > bufSize)
			{
				end_index = bufSize;
			}
			imageBlockBuf[0] = count;
			int pos = 1;
			for (int i = start_index; i < end_index; i++)
			{
				imageBlockBuf[pos] = imageBuf[i];
				pos++;
			}
			sendto(clientSock, imageBlockBuf, end_index-start_index+1, 0, (const sockaddr *)&serveaddr, sizeof(serveaddr));
			count--;
			start_index = end_index;
		}
		
		cv::imshow("client", image);
		cv::waitKey(1);
	}
	closesocket(clientSock);
	WSACleanup();
	return 0;
}

python

接收端

#!/usr/bin/env python

from __future__ import division
import cv2
import numpy as np
import socket
import struct

MAX_DGRAM = 2**16

def main():
    """ Getting image udp frame &
    concate before decode and output image """

    # Set up socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind(('127.0.0.1', 8554))
    dat = b''

    while True:
        seg, addr = s.recvfrom(MAX_DGRAM)
        if struct.unpack("B", seg[0:1])[0] > 1:
            dat += seg[1:]
        else:
            dat += seg[1:]
            print(np.fromstring(dat, dtype=np.uint8))
            img = cv2.imdecode(np.fromstring(dat, dtype=np.uint8), 1)
            print(img)
            cv2.imshow('frame', img)
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            dat = b''

    cv2.destroyAllWindows()
    s.close()


if __name__ == "__main__":
    main()

发送端

#!/usr/bin/env python

from __future__ import division
import cv2
import numpy as np
import socket
import struct
import math


class FrameSegment(object):
    """
    Object to break down image frame segment
    if the size of image exceed maximum datagram size
    """
    MAX_DGRAM = 2**16
    MAX_IMAGE_DGRAM = MAX_DGRAM - 64 # extract 64 bytes in case UDP frame overflown
    def __init__(self, sock:socket.socket, port, addr="127.0.0.1"):
        self.s = sock
        self.port = port
        self.addr = addr

    def udp_frame(self, img):
        """
        Compress image and Break down
        into data segments
        """
        compress_img = cv2.imencode('.jpg', img)[1]
        dat = compress_img.tostring()
        size = len(dat)
        print(size)
        count = math.ceil(size/(self.MAX_IMAGE_DGRAM))
        array_pos_start = 0
        while count:
            array_pos_end = min(size, array_pos_start + self.MAX_IMAGE_DGRAM)
            self.s.sendto(struct.pack("B", count) +
                dat[array_pos_start:array_pos_end],
                (self.addr, self.port)
                )
            array_pos_start = array_pos_end
            count -= 1


def main():
    """ Top level main function """
    # Set up UDP socket
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    port = 8554

    fs = FrameSegment(s, port)

    cap = cv2.VideoCapture(0)
    while (cap.isOpened()):
        _, frame = cap.read()
        fs.udp_frame(frame)
        cv2.imshow("client",frame)
        cv2.waitKey(1)
    cap.release()
    cv2.destroyAllWindows()
    s.close()

if __name__ == "__main__":
    main()

运行

两种不同语言的接收端和服务端可以混合使用
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值