歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux下基於OpenCV的攝像頭數據采集與傳輸

Linux下基於OpenCV的攝像頭數據采集與傳輸

日期:2017/3/1 9:47:01   编辑:Linux編程

最近一段時間在搞攝像頭的采集和傳輸。采集通過OpenCV自帶的函數庫,不用自己編寫V4L2,省去了很多事情。主要工作就是在視頻的傳輸了。主要思路是:將采集的一幀視頻圖像壓縮成jpg格式的圖片,這樣進行過壓縮的數據量大大減少。然後通過socket的UDP傳輸協議將圖片通過網絡傳送到客戶端。我之前用的TCP傳的,總是有部分數據丟失重傳,導致現實界面偶爾出現閃動,出現的錯誤提示:Corrupt JPEG data: premature end of data segment 本以為網絡足夠好,可有用下TCP,看來還是沒設計好,就轉用的UDP協議傳輸,問題就解決了。

在linux下QT環境中進行的程序編寫,服務器端用的是linux C socket編寫的,客戶端是QT封裝的QUdpSocket類編寫的,成功實現了傳輸。但是將客戶端放在windows下就沒有數據傳輸,客戶端一直不能readyread(),一直沒有找到原因,看來還要繼續尋找了。

服務器端代碼:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <opencv2/opencv.hpp>
#include <QImage>
#include <QTimer>

#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORTNUMBER 4444
#define MAX_SIZE 1024;

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

int init_socket();

private:
Ui::Widget *ui;

cv::VideoCapture capture; //攝像頭
cv::Mat frame; //幀圖像
QTimer *timer; //定時器
QImage img; //QT 圖像

int sockfd;
struct sockaddr_in server_addr;


private slots:
void slot_timer();

};

#endif

widget.c

#include "widget.h"
#include "ui_widget.h"
#include <QFile>

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);

timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(slot_timer()));

timer->start(33); //啟動定時器,設置幀率

capture = cv::VideoCapture(1); //打開攝像頭,這裡使用了固定的攝像頭,隨機的話為-1

sockfd = init_socket(); //初始化套接字
}

Widget::~Widget()
{
delete ui;
delete timer;
capture.release();
}

int Widget::init_socket()
{
int sockfd;
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 )
{
perror("Socket error");
exit(1);
}

bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORTNUMBER);
//server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_addr.s_addr = inet_addr("192.168.1.115"); //主機IP

return sockfd;
}

void Widget::slot_timer()
{
capture>>frame; //將攝像頭的每一幀放入frame
if(frame.empty())
{
printf("frame is empty\n");
return;
}
/*將opencv采集的BGR格式的圖像轉化成QT下的RGB格式的圖像*/
cv::cvtColor(frame, frame, CV_BGR2RGB);
img = QImage((unsigned const char*)frame.data, frame.cols, frame.rows, QImage::Format_RGB888 );
img.save("test.jpg", "JPEG");

QFile file("test.jpg");
if(!file.open(QIODevice::ReadOnly))
{
perror("File open error");
return;
}

QByteArray buffer = file.readAll();
file.flush(); //好像這裡沒起作用

if(sendto(sockfd, buffer.data(),buffer.size(), 0, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr_in)) < 0)
{
printf("send fail %d\n", buffer.size());
perror("sendto error");
}

file.close();
}

客戶端程序:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
#include <QImage>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

private:
Ui::Widget *ui;
QImage img;
QUdpSocket* receiver;


private slots:
void processPendingDatagram();

};

#endif // WIDGET_H

widget.c

#include "widget.h"
#include "ui_widget.h"
#include <QFile>
#include <QtNetwork>


#define PORTNUMBER 4444


Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);

receiver = new QUdpSocket(this);
bool result = receiver->bind(PORTNUMBER, QUdpSocket::ShareAddress);
if(result)
{
printf("bind right\n");
}
else
{
printf("Bind error");
}
connect(receiver, SIGNAL(readyRead()), this, SLOT(processPendingDatagram()));
}

void Widget::processPendingDatagram()
{
qint64 num = receiver->pendingDatagramSize();
//printf("receive size = %ld\n", num);

QByteArray buffer;
buffer.resize(num);
receiver->readDatagram((char*)buffer.data(), num); //將接收到的數據放入buffer中

QFile file("test.jpg");
if(!file.open(QIODevice::WriteOnly))
{
printf("file open error\n");
return;
}

file.write(buffer); //將數據寫到硬盤,保存成JPG格式圖片
file.flush();
file.close();

img = QImage("test.jpg");
ui->label->setPixmap(QPixmap::fromImage(img)); // 在label中顯示圖片
ui->label->resize(ui->label->pixmap()->size());
}

Widget::~Widget()
{
delete ui;
}

如果有客戶端在Windows下的QT實現的還請大神指教。

Copyright © Linux教程網 All Rights Reserved