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;
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;
}
}
}
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); //28804
int 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端: