欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 18:47:11
...

一、概述

内容:由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端:
OpenCV结合socket进行实时视频传输(TCP协议)
Server端:
OpenCV结合socket进行实时视频传输(TCP协议)