基於Socket和OpenCV的實時視頻傳輸(On Windows)

目前由於項目的需要,實現了基於Socket和OpenCV的實時視頻傳輸。

由一臺PC(Client客戶端)採集攝像頭圖像後經Socket傳輸到另一臺PC(Server服務器)再顯示出來。


這一篇介紹在Windows上的實現,在下一篇講解在Linux上的實現。


環境:

Server: Windows 10 + OpenCV2.4.10 

Client:: Windows 10 + OpenCV2.4.10 


我採用的是TCP協議的通信。

TCP協議通信的一般步驟是:

客戶端:

1、創建一個socket,用函數socket(); 
  2、設置socket屬性,用函數setsockopt();* 可選 
  3、綁定IP地址、端口等信息到socket上,用函數bind();* 可選 
  4、設置要連接的對方的IP地址和端口等屬性; 
  5、連接服務器,用函數connect(); 
  6、收發數據,用函數send()和recv(),或者read()和write(); 
  7、關閉網絡連接;

服務器端:

1、創建一個socket,用函數socket(); 
  2、設置socket屬性,用函數setsockopt(); * 可選 
  3、綁定IP地址、端口等信息到socket上,用函數bind(); 
  4、開啓監聽,用函數listen(); 
  5、接收客戶端上來的連接,用函數accept(); 
  6、收發數據,用函數send()和recv(),或者read()和write(); 
  7、關閉網絡連接; 
  8、關閉監聽; 


我把圖像的發送和接收分別封裝在了兩個類中:

採集與發送:

WinsockMatTransmissionClient.h

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(發送)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/

#ifndef __WINSOCKMATTRANSMISSIONCLIENT_H__
#define __WINSOCKMATTRANSMISSIONCLIENT_H__
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib,"WS2_32.lib")


//待傳輸圖像默認大小爲 640*480,可修改
#define IMG_WIDTH 640	// 需傳輸圖像的寬
#define IMG_HEIGHT 480	// 需傳輸圖像的高
//默認格式爲CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/32

struct sentbuf
{
	char buf[BUFFER_SIZE];
	int flag;
};

class WinsockMatTransmissionClient
{
public:
	WinsockMatTransmissionClient(void);
	~WinsockMatTransmissionClient(void);

private:
	SOCKET sockClient;
	struct sentbuf data;

public:

	// 打開socket連接
	// params :	IP		服務器的ip地址
	//			PORT	傳輸端口
	// return : -1		連接失敗
	//			1		連接成功
	int socketConnect(const char* IP, int PORT);


	// 傳輸圖像
	// params : image 待傳輸圖像
	// return : -1		傳輸失敗
	//			1		傳輸成功
	int transmit(cv::Mat image);


	// 斷開socket連接
	void socketDisconnect(void);
};

#endif


WinsockMatTransmissionClient.cpp

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(發送)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/


#include "WinsockMatTransmissionClient.h"


WinsockMatTransmissionClient::WinsockMatTransmissionClient(void)
{
}


WinsockMatTransmissionClient::~WinsockMatTransmissionClient(void)
{
}


int WinsockMatTransmissionClient::socketConnect(const char* IP, int PORT)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return -1;
	}

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return -1;
	}

	err = (sockClient = socket(AF_INET,SOCK_STREAM,0));
	if (err < 0) {
		printf("create socket error: %s(errno: %d)\n\n", strerror(errno), errno);
		return -1;
	}
	else
	{
		printf("create socket successful!\nnow connect ...\n\n");
	}

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr=inet_addr(IP);
	addrSrv.sin_family=AF_INET;
	addrSrv.sin_port=htons(PORT);

	err = connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	if (err < 0) 
	{
		printf("connect error: %s(errno: %d)\n\n", strerror(errno), errno);
		return -1;
	}
	else 
	{
		printf("connect successful!\n\n");
		return 1;
	}
}


void WinsockMatTransmissionClient::socketDisconnect(void)
{
	closesocket(sockClient);
	WSACleanup();
}

int WinsockMatTransmissionClient::transmit(cv::Mat image)
{
	if (image.empty())
	{
		printf("empty image\n\n");
		return -1;
	}

	if(image.cols != IMG_WIDTH || image.rows != IMG_HEIGHT || image.type() != CV_8UC3)
	{
		printf("the image must satisfy : cols == IMG_WIDTH(%d)  rows == IMG_HEIGHT(%d) type == CV_8UC3\n\n", IMG_WIDTH, IMG_HEIGHT);
		return -1;
	}

	for(int k = 0; k < 32; k++) 
	{
		int num1 = IMG_HEIGHT / 32 * k;
		for (int i = 0; i < IMG_HEIGHT / 32; i++)
		{
			int num2 = i * IMG_WIDTH * 3;
			uchar* ucdata = image.ptr<uchar>(i + num1);
			for (int j = 0; j < IMG_WIDTH * 3; j++)
			{
				data.buf[num2 + j] = ucdata[j];
			}
		}

		if(k == 31)
			data.flag = 2;
		else
			data.flag = 1;

		if (send(sockClient, (char *)(&data), sizeof(data), 0) < 0)
		{
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			return -1;
		}
	}
}

圖像的接收與顯示:

WinsockMatTransmissionServer.h

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(接收)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/

#ifndef __WINSOCKMATTRANSMISSIONSEVER_H__
#define __WINSOCKMATTRANSMISSIONSEVER_H__

#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include <stdio.h>
#include <Winsock2.h>

#pragma comment(lib,"WS2_32.lib")

//待傳輸圖像默認大小爲 640*480,可修改
#define IMG_WIDTH 640	// 需傳輸圖像的寬
#define IMG_HEIGHT 480	// 需傳輸圖像的高
//默認格式爲CV_8UC3
#define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/32

struct recvbuf
{
	char buf[BUFFER_SIZE];
	int flag;
};

class WinsockMatTransmissionServer
{
public:
	WinsockMatTransmissionServer(void);
	~WinsockMatTransmissionServer(void);

private:
	SOCKET sockConn;
	struct recvbuf data;

public:

	// 打開socket連接
	// params :	PORT	傳輸端口
	// return : -1		連接失敗
	//			1		連接成功
	int socketConnect(int PORT);


	// 傳輸圖像
	// params : image	待接收圖像
	// return : -1		接收失敗
	//			1		接收成功
	int receive(cv::Mat& image);


	// 斷開socket連接
	void socketDisconnect(void);
};

#endif


WinsockMatTransmissionServer.cpp

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(接收)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/


#include "WinsockMatTransmissionServer.h"


WinsockMatTransmissionServer::WinsockMatTransmissionServer(void)
{
}


WinsockMatTransmissionServer::~WinsockMatTransmissionServer(void)
{
}


int WinsockMatTransmissionServer::socketConnect(int PORT)
{
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD(1, 1);

	err = WSAStartup(wVersionRequested, &wsaData);

	if (err != 0)
	{
		return -1;
	}

	if (LOBYTE(wsaData.wVersion) != 1 ||
		HIBYTE(wsaData.wVersion) != 1)
	{
		WSACleanup();
		return -1;
	}

	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(PORT);
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
	listen(sockSrv, 5);

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);

	int nRecvBuf = 1024 * 1024 * 10;
	setsockopt(sockConn, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));
}


void WinsockMatTransmissionServer::socketDisconnect(void)
{
	closesocket(sockConn);
}

int WinsockMatTransmissionServer::receive(cv::Mat& image)
{
	cv::Mat img(IMG_HEIGHT,IMG_WIDTH,CV_8UC3,cv::Scalar(0));

	int needRecv = sizeof(recvbuf);
	int count = 0;

	while (1)
	{
		for (int i = 0; i < 32; i++)
		{
			int pos = 0;
			int len0 = 0;

			while (pos < needRecv)
			{
				len0 = recv(sockConn, (char*)(&data) + pos, needRecv - pos, 0);
				if (len0 < 0)
				{
					printf("Server Recieve Data Failed!\n");
					return -1;
				}
				pos += len0;
			}

			count = count + data.flag;

			int num1 = IMG_HEIGHT / 32 * i;
			for (int j = 0; j < IMG_HEIGHT / 32; j++)
			{
				int num2 = j * IMG_WIDTH * 3;
				uchar* ucdata = img.ptr<uchar>(j + num1);
				for (int k = 0; k < IMG_WIDTH * 3; k++)
				{
					ucdata[k] = data.buf[num2 + k];
				}
			}

			if (data.flag == 2)
			{
				if (count == 33)
				{
					image = img;
					return 1;
					count = 0;
				}
				else
				{
					count = 0;
					i = 0;
				}
			}
		}
	}
}


示例代碼:

採集與發送:

SocketClientMat.cpp

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(發送)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/



#include "WinsockMatTransmissionClient.h"

int main()
{
	WinsockMatTransmissionClient socketMat;
	if (socketMat.socketConnect("192.168.1.101", 6666) < 0)
	{
		return 0;
	}
	
	cv::VideoCapture capture(0);
	cv::Mat image;

	while (1)
	{
		if (!capture.isOpened())
			return 0;

		capture >> image;

		if (image.empty())
			return 0;

		socketMat.transmit(image);
	}

	socketMat.socketDisconnect();
	return 0;
}

接收與顯示:

WinsockServerMat.cpp

/*M///////////////////////////////////////////////////////////////////////////////////////
//
//  基於OpenCV和Winsock的圖像傳輸(接收)
//	
//	By 彭曾 , at CUST, 2016.08.06 
//
//	website: www.pengz0807.com  email: [email protected] 
//	
//M*/




#include "WinsockMatTransmissionServer.h"

int main()
{
	WinsockMatTransmissionServer socketMat;
	if (socketMat.socketConnect(6666) < 0)
	{
		return 0;
	}

	cv::Mat image;
	while (1)
	{
		if(socketMat.receive(image) > 0)
		{
			cv::imshow("",image);
			cv::waitKey(30);
		}
	}

	socketMat.socketDisconnect();
	return 0;
}

將在下一篇文章中講解Linux中基於Socket和OpenCV的實時視頻傳輸的實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章