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

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值