上一篇介绍了在Windows上实现基于Socket和openCV的实时视频传输,这一篇将继续讲解在Linux上的实现。
环境:
Server: Ubuntu 14.04 LTS + OpenCV2.4.10
Client:: Ubuntu 14.04 LTS + OpenCV2.4.10
我采用的仍是TCP协议的通信,Linux上的实现和Windows大同小异。
Linux中OpenCV的编译安装可以参考 http://blog.csdn.net/pengz0807/article/details/49915573
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、关闭监听;
我把图像的发送和接收分别封装在了两个类中:
采集与发送:
SocketMatTransmissionClient.h
/*M/// // // 基于OpenCV和Socket的图像传输(发送) // // By 彭曾 , at CUST, 2016.08.07 // // website: www.pengz0807.com email: pengz0807@163.com // //M*/ #ifndef __SOCKETMATTRANSMISSIONCLIENT_H__ #define __SOCKETMATTRANSMISSIONCLIENT_H__ #include "opencv2/opencv.hpp" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> using namespace cv; //待传输图像默认大小为 640*480,可修改 #define IMG_WIDTH 640 // 需传输图像的宽 #define IMG_HEIGHT 480 // 需传输图像的高 #define PACKAGE_NUM 2 //默认格式为CV_8UC3 #define BUFFER_SIZE IMG_WIDTH*IMG_HEIGHT*3/PACKAGE_NUM struct sentbuf { char buf[BUFFER_SIZE]; int flag; }; class SocketMatTransmissionClient { public: SocketMatTransmissionClient(void); ~SocketMatTransmissionClient(void); private: int 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 SocketMatTransmissionClient.cpp /*M/// // // 基于OpenCV和Socket的图像传输(发送) // // By 彭曾 , at CUST, 2016.08.07 // // website: www.pengz0807.com email: pengz0807@163.com // //M*/ #include "SocketMatTransmissionClient.h" SocketMatTransmissionClient::SocketMatTransmissionClient(void) { } SocketMatTransmissionClient::~SocketMatTransmissionClient(void) { } int SocketMatTransmissionClient::socketConnect(const char* IP, int PORT) { struct sockaddr_in servaddr; if ((sockClient = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket error: %s(errno: %d)\n", strerror(errno), errno); return -1; } memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); if (inet_pton(AF_INET, IP, &servaddr.sin_addr) <= 0) { printf("inet_pton error for %s\n", IP); return -1; } if (connect(sockClient, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { printf("connect error: %s(errno: %d)\n", strerror(errno), errno); return -1; } else { printf("connect successful!\n"); } } void SocketMatTransmissionClient::socketDisconnect(void) { close(sockClient); } int SocketMatTransmissionClient::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 < PACKAGE_NUM; k++) { int num1 = IMG_HEIGHT / PACKAGE_NUM * k; for (int i = 0; i < IMG_HEIGHT / PACKAGE_NUM; 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 == PACKAGE_NUM - 1) data.flag = 2; else data.flag = 1; if (send(sockClient, (char *)(&data), sizeof(data), 0) < 0) { printf("send image error: %s(errno: %d)\n", strerror(errno), errno); return -1; } } }
接收与显示:
SocketMatTransmissionServer.h
/*M/// // // 基于OpenCV和Socket的图像传输(接收) // // By 彭曾 , at CUST, 2016.08.07 // // website: www.pengz0807.com email: pengz0807@163.com // //M*/ #ifndef __SOCKETMATTRANSMISSIONSEVER_H__ #define __SOCKETMATTRANSMISSIONSEVER_H__ #include "opencv2/opencv.hpp" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> using namespace cv; #define PACKAGE_NUM 2 #define IMG_WIDTH 640 #define IMG_HEIGHT 480 #define BLOCKSIZE IMG_WIDTH*IMG_HEIGHT*3/PACKAGE_NUM struct recvBuf { char buf[BLOCKSIZE]; int flag; }; class SocketMatTransmissionServer { public: SocketMatTransmissionServer(void); ~SocketMatTransmissionServer(void); int sockConn; private: struct recvBuf data; int needRecv; int count; public: // 打开socket连接 // params : PORT 传输端口 // return : -1 连接失败 // 1 连接成功 int socketConnect(int PORT); // 传输图像 // params : image 待接收图像 // image 待接收图像 // return : -1 接收失败 // 1 接收成功 int receive(cv::Mat& image); // 断开socket连接 void socketDisconnect(void); }; #endif
SocketMatTransmissionServer.cpp
/*M/// // // 基于OpenCV和Socket的图像传输(接收) // // By 彭曾 , at CUST, 2016.08.07 // // website: www.pengz0807.com email: pengz0807@163.com // //M*/ #include "SocketMatTransmissionServer.h" SocketMatTransmissionServer::SocketMatTransmissionServer(void) { } SocketMatTransmissionServer::~SocketMatTransmissionServer(void) { } int SocketMatTransmissionServer::socketConnect(int PORT) { int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); struct sockaddr_in server_sockaddr; server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1) { perror("bind"); return -1; } if(listen(server_sockfd,5) == -1) { perror("listen"); return -1; } struct sockaddr_in client_addr; socklen_t length = sizeof(client_addr); sockConn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length); if(sockConn<0) { perror("connect"); return -1; } else { printf("connect successful!\n"); return 1; } close(server_sockfd); } void SocketMatTransmissionServer::socketDisconnect(void) { close(sockConn); } int SocketMatTransmissionServer::receive(cv::Mat& image) { int returnflag = 0; cv::Mat img(IMG_HEIGHT, IMG_WIDTH, CV_8UC3, cv::Scalar(0)); needRecv = sizeof(recvBuf); count = 0; memset(&data,0,sizeof(data)); for (int i = 0; i < PACKAGE_NUM; 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"); break; } pos += len0; } count = count + data.flag; int num1 = IMG_HEIGHT / PACKAGE_NUM * i; for (int j = 0; j < IMG_HEIGHT / PACKAGE_NUM; 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 == PACKAGE_NUM + 1) { image = img; returnflag = 1; count = 0; } else { count = 0; i = 0; } } } if(returnflag == 1) return 1; else return -1; } 示例代码:
图像的采集与发送:
SocketClientMat.cpp
#include "SocketMatTransmissionClient.h" int main() { SocketMatTransmissionClient socketMat; if (socketMat.socketConnect("127.0.0.1", 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; }
接收与显示:
SocketServerMat.cpp
#include "SocketMatTransmissionServer.h" int main() { SocketMatTransmissionServer 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; }