TCP實時傳圖像

目的:

QT+openCV,在Ubuntu16.04版本下,通過TCP實現圖片的傳輸。

步驟:

客戶端建一個相機線程,一個TCP線程,相機線程捕獲畫面並將Mat傳到TCP線程,再通過TCP線程傳到服務器,服務器通過死循環監聽客戶端,獲取客戶端發什麼內容,並顯示出來。

效果:

 代碼:

server:(接受客戶端發送的Mat,並顯示)

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <socketmattransmissionserver.h>
#include<QDebug>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    QImage MatImageToQt(const cv::Mat &src);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
        cv::Mat image;
SocketMatTransmissionServer socketMat;
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}
QImage MainWindow::MatImageToQt(const cv::Mat &src)//Mat轉成QImage
{
    //CV_8UC1 8位無符號的單通道---灰度圖片
    if(src.type() == CV_8UC1)
    {
        //使用給定的大小和格式構造圖像
        //QImage(int width, int height, Format format)
        QImage qImage(src.cols,src.rows,QImage::Format_Indexed8);
        //擴展顏色表的顏色數目
        qImage.setColorCount(256);

        //在給定的索引設置顏色
        for(int i = 0; i < 256; i ++)
        {
            //得到一個黑白圖
            qImage.setColor(i,qRgb(i,i,i));
        }
        //複製輸入圖像,data數據段的首地址
        uchar *pSrc = src.data;
        //
        for(int row = 0; row < src.rows; row ++)
        {
            //遍歷像素指針
            uchar *pDest = qImage.scanLine(row);
            //從源src所指的內存地址的起始位置開始拷貝n個
            //字節到目標dest所指的內存地址的起始位置中
            memcmp(pDest,pSrc,src.cols);
            //圖像層像素地址
            pSrc += src.step;
        }
        return qImage;
    }
    //爲3通道的彩色圖片
    else if(src.type() == CV_8UC3)
    {
        //得到圖像的的首地址
        const uchar *pSrc = (const uchar*)src.data;
        //以src構造圖片
        QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
        //在不改變實際圖像數據的條件下,交換紅藍通道
        return qImage.rgbSwapped();
    }
    //四通道圖片,帶Alpha通道的RGB彩色圖像
    else if(src.type() == CV_8UC4)
    {
        const uchar *pSrc = (const uchar*)src.data;
        QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32);
        //返回圖像的子區域作爲一個新圖像
        return qImage.copy();
    }
    else
    {
        return QImage();
    }
}

void MainWindow::on_pushButton_clicked()
{
    if (socketMat.socketConnect(8000) < 0)
    {
    }

    while (1)
    {
        if(socketMat.receive(image) > 0)
        {
            QImage imag = MatImageToQt(image);
            ui->label_img->setPixmap(QPixmap::fromImage(imag));
            cv::waitKey(30);
        }
    }
    socketMat.socketDisconnect();
}

socketmattransmissionserver.h

#ifndef SOCKETMATTRANSMISSIONSERVER_H
#define SOCKETMATTRANSMISSIONSERVER_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:

    int socketConnect(int PORT);

    int receive(cv::Mat& image);

    void socketDisconnect(void);
};

#endif // SOCKETMATTRANSMISSIONSERVER_H

socketmattransmissionserver.cpp

#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;
}

client:(通過cv打開相機,並把Mat傳到服務器)

camthread.h

#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;
}

camthread.cpp

#include "camthread.h"

camThread::camThread(QObject *parent) :
    QThread(parent)
{
    isStop = false;
}
void camThread::closeThread()
{
    isStop = true;
}
void camThread::waitThread()
{
    isStop = false;
}

void camThread::run()
{
    cap_ct.open(1);
    while (1)
    {
        if(isStop)
            return;
        cap_ct.read(src_image_ct);
        emit freshImg(src_image_ct);
        sleep(0.1);
    }

}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include<QDebug>
#include <socketmattransmissionclient.h>
#include<camthread.h>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    QImage MatImageToQt(const cv::Mat &src);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();
    void send_img(const cv::Mat &);
    void show_img(const cv::Mat &);

private:
    Ui::MainWindow *ui;
    camThread *thread1;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
SocketMatTransmissionClient socketMat;

cv::Mat image;
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    thread1 =new camThread();
    connect(thread1, SIGNAL(freshImg(const cv::Mat &)), this, SLOT(send_img(const cv::Mat &)),Qt::DirectConnection);
    connect(thread1, SIGNAL(freshImg(const cv::Mat &)), this, SLOT(show_img(const cv::Mat &)),Qt::DirectConnection);
}

MainWindow::~MainWindow()//析構
{
    socketMat.socketDisconnect();
    delete ui;
}
QImage MainWindow::MatImageToQt(const cv::Mat &src)//Mat轉成QImage
{
    //CV_8UC1 8位無符號的單通道---灰度圖片
    if(src.type() == CV_8UC1)
    {
        //使用給定的大小和格式構造圖像
        //QImage(int width, int height, Format format)
        QImage qImage(src.cols,src.rows,QImage::Format_Indexed8);
        //擴展顏色表的顏色數目
        qImage.setColorCount(256);

        //在給定的索引設置顏色
        for(int i = 0; i < 256; i ++)
        {
            //得到一個黑白圖
            qImage.setColor(i,qRgb(i,i,i));
        }
        //複製輸入圖像,data數據段的首地址
        uchar *pSrc = src.data;
        //
        for(int row = 0; row < src.rows; row ++)
        {
            //遍歷像素指針
            uchar *pDest = qImage.scanLine(row);
            //從源src所指的內存地址的起始位置開始拷貝n個
            //字節到目標dest所指的內存地址的起始位置中
            memcmp(pDest,pSrc,src.cols);
            //圖像層像素地址
            pSrc += src.step;
        }
        return qImage;
    }
    //爲3通道的彩色圖片
    else if(src.type() == CV_8UC3)
    {
        //得到圖像的的首地址
        const uchar *pSrc = (const uchar*)src.data;
        //以src構造圖片
        QImage qImage(pSrc,src.cols,src.rows,src.step,QImage::Format_RGB888);
        //在不改變實際圖像數據的條件下,交換紅藍通道
        return qImage.rgbSwapped();
    }
    //四通道圖片,帶Alpha通道的RGB彩色圖像
    else if(src.type() == CV_8UC4)
    {
        const uchar *pSrc = (const uchar*)src.data;
        QImage qImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32);
        //返回圖像的子區域作爲一個新圖像
        return qImage.copy();
    }
    else
    {
        return QImage();
    }
}

void MainWindow::on_pushButton_clicked()
{
    if (socketMat.socketConnect("127.0.0.1", 8000) < 0)
    {
        qDebug()<<"11111";
    }
    thread1->start();

}
void MainWindow::show_img(const cv::Mat & img)//線程顯示圖片
{
    if(img.data == NULL)
    {
        std::cout<<"Capture image failed!"<<std::endl;
        return;
    }
    QImage imag = MatImageToQt(img);
    ui->label_img->setPixmap(QPixmap::fromImage(imag));
}
void MainWindow::send_img(const cv::Mat & img)//線程發送圖片
{
    socketMat.transmit(img);
}

socketmattransmissionclient.h

#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();
    ~SocketMatTransmissionClient();

private:
    int sockClient;
    struct sentbuf data;

public:

    int socketConnect(const char* IP, int PORT);

    int transmit(cv::Mat image);

    void socketDisconnect(void);
};


#endif // SOCKETMATTRANSMISSIONCLIENT_H

socketmattransmissionclient.cpp

#include "socketmattransmissionclient.h"


SocketMatTransmissionClient::SocketMatTransmissionClient()
{
}


SocketMatTransmissionClient::~SocketMatTransmissionClient()
{
}


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()
{
    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;
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章