OpenCV结合socket进行实时视频传输(TCP协议)

来源:互联网 发布:约翰马尔科维奇 知乎 编辑:程序博客网 时间:2024/06/06 03:07

一、概述

内容:由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端:
这里写图片描述

原创粉丝点击