Linux下基于qt的视频监控系统

您所在的位置:网站首页 linux监控摄像头系统 Linux下基于qt的视频监控系统

Linux下基于qt的视频监控系统

2023-12-13 11:17| 来源: 网络整理| 查看: 265

目录

一、原始需求

二、环境安装

2.1 qt安装

2.2 opencv安装

三、系统设计

3.1、 整体流程设计

3.2 、数据传输交互流程

3.3 、数据库设计

四、关键代码

4.1、如何实现通信(TCP)

4.1.1 服务端

4.1.2 客户端

4.2、如何实现视频读取(V4L2)

4.3、如何实现图像处理(opencv)

4.4 登录验证

五、实现效果

5.1、服务端GUI

5.2、客户端GUI

六、参考文献

一、原始需求

Linux下基于qt的视频监控系统

服务端: 用v4l2实现视频采集、视频传输,用tcp实现服务器端与客户端通信客户端:用qt实现

功能:

1、注册登录

2、暂停

3、截图

4、保存视频

5、视频上显示时间

6、可以对视频进行边缘检测、锐化、阈值分割

7、同时监控两个及以上个摄像头

二、环境安装

本人开发环境ubuntu 16.04 qt5.6.3 opencv3.4

2.1 qt安装

Index of /new_archive/qt/5.6/5.6.3

下载linux版本

修改可执行权限,Terminal中运行

一路傻瓜式安装即可

2.2 opencv安装

安装cmake

sudo apt-get install cmake

安装依赖环境

sudo apt-get install build-essential libgtk2.0-dev libavcodec-dev libavformat-dev libjpeg-dev libswscale-dev libtiff5-dev sudo apt-get install libgtk2.0-dev sudo apt-get install pkg-config  

下载opencv3.4版本

下载地址:Releases - OpenCV,点击Sources进行下载自己需要的版

下载完后解压文件,进入,当前位置打开Terminal

mkdir build cd build sudo cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local .. sudo make -j4 sudo make install

到此所有依赖环境安装完毕

三、系统设计 3.1、 整体流程设计

3.2 、数据传输交互流程

3.3 、数据库设计

一张用户表记录注册信息即可

四、关键代码 4.1、如何实现通信(TCP) 4.1.1 服务端

在头文件里面定义一个server指针变量,一个socket指针变量

QTcpServer *tcpserver; QTcpSocket *tcpsocket;

实例化server,监听的对象设成Any,端口号设置一个固定值,客户端与服务端端口号一致才能通信成功

tcpserver = new QTcpServer(this); tcpserver->listen(QHostAddress::Any, 48797);

监听到有新连接接入时,连接到信号槽来处理连接的客户端

connect(tcpserver, SIGNAL(newConnection()), this, SLOT(slotTcpNewConnection())); void MainWindow::slotTcpNewConnection() { qDebug("NEW CONNECTION"); tcpsocket = tcpserver->nextPendingConnection(); //获取监听到的socket /*获取对方IP和端口*/ QString ip = tcpsocket->peerAddress().toString(); quint16 port = tcpsocket->peerPort(); QString str = QString("[%1:%2]成功连接").arg(ip).arg(port); ui->textBrowser->append(str); /*显示编辑区*/ //连接成功后我们再连接一个信号槽到准备接收信号槽函数中去 connect(tcpsocket, SIGNAL(readyRead()), this, SLOT(slotTcpReadyRead())); //已连接套接字的断开信号与自身的稍后删除信号相连接 connect(tcpsocket, SIGNAL(disconnected()), this, SLOT(slotDisconnect())); }  

当收客户端发来的信息后,开启读取摄像头数据线程

void MainWindow::slotTcpReadyRead() { QByteArray recvbuf = tcpsocket->readAll(); qDebug("recv:"); qDebug(recvbuf); vapi.start(); }

V4l2Api为线程,启动线程后,会发送sendImage信号,关联slotSendImage

V4l2Api vapi; connect(&vapi, &V4l2Api::sendImage, this, &MainWindow::slotSendImage); void MainWindow::slotSendImage(QImage image) { qDebug("send image:"); QImage tempimage = image; tempimage = tempimage.scaled(800, 480, Qt::KeepAspectRatio, Qt::FastTransformation) .scaled(400, 240, Qt::KeepAspectRatio, Qt::SmoothTransformation); QPixmap pixmap = QPixmap::fromImage(tempimage); //把img转成位图,我们要转成jpg格式 QByteArray ba; QBuffer buf(&ba); //把ba绑定到buf上,操作buf就等于操作ba pixmap.save(&buf, "jpg", 50); //把pixmap保存成jpg,压缩质量50 数据保存到buf //先写大小过去,告诉主机我们要传输的数据有多大 tcpsocket->write(QString("size=%1").arg(ba.size()).toLocal8Bit().data()); tcpsocket->waitForReadyRead(); //等待主机响应“ok” tcpsocket->write(ba); //把图像数据写入传输给主机 tcpsocket->waitForReadyRead(); }   4.1.2 客户端

接收消息封装成线程,重写run

void RecvThread::run() { isRunning = true; QTcpSocket tcpsocket; connect(&tcpsocket, SIGNAL(disconnected()), this, SIGNAL(disconnectSlot())); tcpsocket.connectToHost(ip, port); if (!tcpsocket.waitForConnected(3000)) { qDebug() vfd = ::open(deviceName.c_str(), O_RDWR); if (this->vfd < 0) { perror("open fail"); VideoException vexp("open fail"); //创建异常对象 //抛异常 throw vexp; } // 2.配置采集属性 struct v4l2_format vfmt; vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // vfmt.fmt.pix.width = WIDTH; vfmt.fmt.pix.height = HEIGHT; vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; //(设置视频输出格式,但是要摄像头支持4:2:2) //通过ioctl把属性写入设备 int ret = ioctl(this->vfd, VIDIOC_S_FMT, &vfmt); if (ret < 0) { perror("set fail"); // VideoException vexp("set fail");//创建异常对象 // throw vexp; } //通过ioctl从设备获取属性 memset(&vfmt, 0, sizeof(vfmt)); vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(this->vfd, VIDIOC_G_FMT, &vfmt); if (ret < 0) { perror("get fail"); // VideoException vexp("get fail");//创建异常对象 // throw vexp; } if (vfmt.fmt.pix.width == WIDTH && vfmt.fmt.pix.height == HEIGHT && vfmt.fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) { } else { // VideoException vexp("set error 2");//创建异常对象 // throw vexp; } } void V4l2Api::video_mmap() { // 1申请缓冲区队列 struct v4l2_requestbuffers reqbuffer; reqbuffer.count = this->count; //申请缓冲区队列长度 reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuffer.memory = V4L2_MEMORY_MMAP; int ret = ioctl(this->vfd, VIDIOC_REQBUFS, &reqbuffer); if (ret < 0) { perror("req buffer fail"); // VideoException vexp("req buffer fail");//创建异常对象 // throw vexp; } // 2.映射 for (int i = 0; i < this->count; i++) { struct VideoFrame frame; struct v4l2_buffer mapbuffer; mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; mapbuffer.index = i; mapbuffer.memory = V4L2_MEMORY_MMAP; //从队列中拿到内核空间 ret = ioctl(this->vfd, VIDIOC_QUERYBUF, &mapbuffer); if (ret < 0) { perror("query fail"); } //映射 frame.length = mapbuffer.length; frame.start = (char *)mmap(NULL, mapbuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, this->vfd, mapbuffer.m.offset); //空间放回队列中(内核空间) ret = ioctl(this->vfd, VIDIOC_QBUF, &mapbuffer); //把frame添加到容器framebuffers framebuffers.push_back(frame); } } bool V4l2Api::yuyv_to_rgb888(unsigned char *yuyvdata, unsigned char *rgbdata, int picw, int pich) { int i, j; unsigned char y1, y2, u, v; int r1, g1, b1, r2, g2, b2; //确保所转的数据或要保存的地址有效 if (yuyvdata == NULL || rgbdata == NULL) { return false; } int tmpw = picw / 2; for (i = 0; i < pich; i++) { for (j = 0; j < tmpw; j++) // 640/2 == 320 { // yuv422 // R = 1.164*(Y-16) + 1.159*(V-128); // G = 1.164*(Y-16) - 0.380*(U-128)+ 0.813*(V-128); // B = 1.164*(Y-16) + 2.018*(U-128)); //下面的四个像素为:[Y0 U0 V0] [Y1 U1 V1] -------------[Y2 U2 V2] [Y3 U3 // V3] //存放的码流为: Y0 U0 Y1 V1------------------------Y2 U2 Y3 V3 //映射出像素点为: [Y0 U0 V1] [Y1 U0 V1]--------------[Y2 U2 V3] [Y3 U2 // V3] //获取每个像素yuyv数据 YuYv y1 = *(yuyvdata + (i * tmpw + j) * 4); // yuv像素的Y u = *(yuyvdata + (i * tmpw + j) * 4 + 1); // yuv像素的U y2 = *(yuyvdata + (i * tmpw + j) * 4 + 2); v = *(yuyvdata + (i * tmpw + j) * 4 + 3); //把yuyv数据转换为rgb数据 r1 = y1 + 1.042 * (v - 128); g1 = y1 - 0.34414 * (u - 128); b1 = y1 + 1.772 * (u - 128); r2 = y2 + 1.042 * (v - 128); g2 = y2 - 0.34414 * (u - 128); b2 = y2 + 1.772 * (u - 128); if (r1 > 255) r1 = 255; else if (r1 < 0) r1 = 0; if (g1 > 255) g1 = 255; else if (g1 < 0) g1 = 0; if (b1 > 255) b1 = 255; else if (b1 < 0) b1 = 0; if (r2 > 255) r2 = 255; else if (r2 < 0) r2 = 0; if (g2 > 255) g2 = 255; else if (g2 < 0) g2 = 0; if (b2 > 255) b2 = 255; else if (b2 < 0) b2 = 0; //把rgb值保存于rgb空间 数据为反向 rgbdata[((pich - 1 - i) * tmpw + j) * 6] = (unsigned char)b1; rgbdata[((pich - 1 - i) * tmpw + j) * 6 + 1] = (unsigned char)g1; rgbdata[((pich - 1 - i) * tmpw + j) * 6 + 2] = (unsigned char)r1; rgbdata[((pich - 1 - i) * tmpw + j) * 6 + 3] = (unsigned char)b2; rgbdata[((pich - 1 - i) * tmpw + j) * 6 + 4] = (unsigned char)g2; rgbdata[((pich - 1 - i) * tmpw + j) * 6 + 5] = (unsigned char)r2; } } return true; } void V4l2Api::jpeg_to_rgb888(unsigned char *jpegData, int size, unsigned char *rgbdata) { //解码jpeg图片 // 1.定义解码对象struct jpeg_decompress_struct 错误处理对象struct // jpeg_error_mgr; struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr err; // 2.初始化错误jpeg_std_error(err),创建初始化解码对象jpeg_create_decompress(); cinfo.err = jpeg_std_error(&err); jpeg_create_decompress(&cinfo); // 3.加载源数据jpeg_mem_src() jpeg_mem_src(&cinfo, jpegData, size); // 4.获取jpeg图片头数据 jpeg_read_header(&cinfo, true); // 5.开始解码 jpeg_start_decompress(&cinfo); // 6.分配存储一行像素所需要的空间//---RGB数据 // 640--cinfo.output_width, 480--cinfo.output_height char *rowFrame = (char *)malloc(cinfo.output_width * 3); int pos = 0; // 7.一行一行循环读取(一次读取一行,要全部读完) while (cinfo.output_scanline < cinfo.output_height) { //读取一行数据--解码一行 jpeg_read_scanlines(&cinfo, (JSAMPARRAY)&rowFrame, 1); //把rgb像素显示在lcd上 mmp memcpy(rgbdata + pos, rowFrame, cinfo.output_width * 3); pos += cinfo.output_width * 3; } free(rowFrame); // 8.解码完成 jpeg_finish_decompress(&cinfo); // 9.销毁解码对象 jpeg_destroy_decompress(&cinfo); } void V4l2Api::run() { isRunning = true; char buffer[WIDTH*HEIGHT*3]; char rgbbuffer[WIDTH*HEIGHT*3]; int times = 0; int len; while(isRunning) { grapImage(buffer, &len); //yuyv_to_rgb888((unsigned char *)buffer, (unsigned char *)rgbbuffer); jpeg_to_rgb888((unsigned char *)buffer, len, (unsigned char *)rgbbuffer); //把RGB数据转为QImage QImage image((uchar*)rgbbuffer, WIDTH, HEIGHT, QImage::Format_RGB888); emit sendImage(image); qDebug()


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3