OpenCV结合socket进行实时视频传输(TCP协议)
一、概述
內容:由Client客戶端采集攝像頭圖像后經Socket傳輸到Server服務器端再顯示出來。本實驗在同一臺電腦上實驗,即運行服務器程序,又跑客戶端程序,也就是說通過socket編程來實現數據的自發自收,這一步通過了接下來跑服務器和客戶端分開的實驗就簡單了。 
 實驗平臺: VS2013 + opencv2.4.11(Windows 7) 
 說明:近期項目需要進行圖像的采集傳輸任務,遂在網上尋找,并進行了些修改,記錄在此。
二、實現
1、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、關閉監聽; 
2、Client 的實現(數據發送)
// Client.cpp 主函數 // 基于OpenCV和Winsock的圖像傳輸(發送)#include "WinsockMatTransmissionClient.h" int main() {WinsockMatTransmissionClient socketMat;if (socketMat.socketConnect("192.168.191.1", 6666) < 0) //地址自行設置{return 0;}cv::VideoCapture capture(0);cv::Mat frame;while (1){if (!capture.isOpened())return 0;capture >> frame;imshow("client", frame);cv::waitKey(30);if (frame.empty())return 0;socketMat.transmit(frame);}socketMat.socketDisconnect();return 0; }數據的發送——WinsockMatTransmissionClient.h
// WinsockMatTransmissionClient.h // 基于OpenCV和Winsock的圖像傳輸(發送)#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
// WinsockMatTransmissionClient.cpp // 基于OpenCV和Winsock的圖像傳輸(發送)#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;elsedata.flag = 1;if (send(sockClient, (char *)(&data), sizeof(data), 0) < 0){printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);return -1;}} }3、Server 的實現(數據接收)
// Server.cpp 主函數 // 基于OpenCV和Winsock的圖像傳輸(接收)#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("server", image);cv::waitKey(30);}}socketMat.socketDisconnect();return 0; }數據的接收——WinsockMatTransmissionServer.h
// WinsockMatTransmissionServer.h // 基于OpenCV和Winsock的圖像傳輸(接收)#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
// WinsockMatTransmissionServer.cpp // 基于OpenCV和Winsock的圖像傳輸(接收)// 基于OpenCV和Winsock的圖像傳輸(接收)#include <iostream> #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); //28804int count = 0;extern int errno;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;}}}} }三、結果
Client端: 
  
 Server端: 
 
總結
以上是生活随笔為你收集整理的OpenCV结合socket进行实时视频传输(TCP协议)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 数据结构——字符串(未完)
- 下一篇: 机器学习——KNN实现
