同行1800多公里,跟着货车司机跑长途(人民眼·货车司机
图①:山西临汾经济技术开发区兴荣供应链有限公司的货车整装待发。资料图片 图②:司机王勇平驾驶货车行驶在
Windows版本: Win10 X64
OpenCV版本: 2.4.13.6
(资料图片仅供参考)
QT版本: 5.12
OpenCV官网下载地址: https://opencv.org
目前官网OpenCV最新的版本是4.2.0 ,Windows版本的OpenCV在3.X版本后就不带X86的库,只有X64的库,如果需要X86的库,需要自己下载源码去重新编译。
由于我的QT软件在安装时没有安装64位的编译器,又不想重新安装64位编译器,就选择了2.X的版本完成开发测试,3.X的版本在添加库的时候非常方便,就一个库。
库下载之后,双击运行,解压到指定的目录就行。
接下来要把OpenCV的动态库目录加到系统环境变量里,否则程序运行时找不到依赖库会崩溃。
程序功能: 在子线程里打开摄像头,获取摄像头的数据,通过信号与槽的方式,将摄像头数据传递给主UI界面实时显示,在采用定时器每100ms取一次标签上的数据进行人脸检测处理,将处理的数据再显示到另一个标签上。人脸检测分类器采用OpenCV自带的分类器,程序主要目的是介绍OpenCV配合QT如何进行开发。
OpenCV自带的人脸检测分类器路径:C:/OpenCV_2.4/opencv/sources/data/haarcascades_GPU/haarcascade_frontalface_alt2.xml
四、实现效果
xxx.pro工程文件代码:
QT += core guiQT += multimediawidgetsQT += xmlQT += multimediaQT += networkQT += widgetsQT += serialportgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use# any Qt feature that has been marked deprecated (the exact warnings# depend on your compiler). Please consult the documentation of the# deprecated API in order to know how to port your code away from it.DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.# In order to do so, uncomment the following line.# You can also select to disable deprecated APIs only up to a certain version of Qt.#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \widget.cppHEADERS += \widget.hFORMS += \widget.ui# Default rules for deployment.qnx: target.path = /tmp/${TARGET}/binelse: unix:!android: target.path = /opt/${TARGET}/bin!isEmpty(target.path): INSTALLS += target#linu平台的路径设置linux {message("运行linu版本")#添加opencv头文件的路径,需要根据自己的头文件路径进行修改INCLUDEPATH+=/home/wbyq/work_pc/opencv-3.4.9/_install/install/include\/home/wbyq/work_pc/opencv-3.4.9/_install/install/include/opencv\/home/wbyq/work_pc/opencv-3.4.9/_install/install/include/opencv2LIBS+=/home/wbyq/work_pc/opencv-3.4.9/_install/install/lib/libopencv_*}win32{message("运行win32版本")#添加opencv头文件的路径,需要根据自己的头文件路径进行修改INCLUDEPATH+=C:/OpenCV_2.4/opencv/build/include \C:/OpenCV_2.4/opencv/build/include/opencv \C:/OpenCV_2.4/opencv/build/include/opencv2LIBS+=C:/OpenCV_2.4/opencv/build/x86/vc14/lib/opencv_*}RESOURCES += \image.qrc
widget.cpp文件代码:
#include \"widget.h\"#include \"ui_widget.h\"class VideoAudioEncode videoaudioencode_0;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget){ui->setupUi(this);//驾驶室摄像头//工作对象videoRead_WorkClass_0=new VideoReadThread_0;videoRead_Workthread_0=new QThread;//连接摄像头采集信号,在主线程实时显示视频画面connect(videoRead_WorkClass_0,SIGNAL(VideoDataOutput(QImage )),this,SLOT(VideoDataDisplay_0(QImage )));//摄像头初始化函数connect(this,SIGNAL(Init_VideoAudio_WorkClass_0()),videoRead_WorkClass_0,SLOT(run()));//停止视频采集connect(this,SIGNAL(Stop_AudioVideo0_work_0()),videoRead_WorkClass_0,SLOT(stop()));//将工作对象移动到子线程里工作videoRead_WorkClass_0->moveToThread(videoRead_Workthread_0);//更新设备列表UpdateVideoAudiodDevice(ui->comboBox_video_0,ui->plainTextEdit_log_0);//timer.start(100);connect(&timer,SIGNAL(timeout()), this, SLOT(update()));timer.start(100);}Widget::~Widget(){delete ui;}//分类器的路径#define source_xml_addr \"C:/OpenCV_2.4/opencv/sources/data/haarcascades_GPU/haarcascade_frontalface_alt2.xml\"//将要检测的图片路径#define source_pix_addr \"D:/linux-share-dir/1.jpg\"//人脸检测代码void Widget::opencv_face(QImage qImage){QTime time;time.start();static CvMemStorage* storage = 0;static CvHaarClassifierCascade* cascade = 0;const char*cascade_name =source_xml_addr;//加载分类器cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );if( !cascade ){Log_Display_0(\"分类器加载错误.
\");return ;}//创建内存空间storage = cvCreateMemStorage(0);//加载需要检测的图片//const char* filename =source_pix_addr;//IplImage* img = cvLoadImage(filename, 1 );IplImage* img = QImageToIplImage(&qImage);if(img ==nullptr ){Log_Display_0(\"图片加载错误.
\");return;}double scale=1.2;static CvScalar colors[] = {{{0,0,255}},{{0,128,255}},{{0,255,255}},{{0,255,0}},{{255,128,0}},{{255,255,0}},{{255,0,0}},{{255,0,255}}};//Just some pretty colors to draw withIplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1);IplImage* small_img=cvCreateImage(cvSize(cvRound(img->width/scale),cvRound(img->height/scale)),8,1);cvCvtColor(img,gray, CV_BGR2GRAY);cvResize(gray, small_img, CV_INTER_LINEAR);cvEqualizeHist(small_img,small_img); //直方图均衡cvClearMemStorage(storage);double t = (double)cvGetTickCount();CvSeq* objects = cvHaarDetectObjects(small_img,cascade,storage,1.1,2,0/*CV_HAAR_DO_CANNY_PRUNING*/,cvSize(30,30));t = (double)cvGetTickCount() - t;//遍历找到对象和周围画盒for(int i=0;i<(objects->total);++i){CvRect* r=(CvRect*)cvGetSeqElem(objects,i);cvRectangle(img, cvPoint(r->x*scale,r->y*scale), cvPoint((r->x+r->width)*scale,(r->y+r->height)*scale), colors[i%8]);}for( int i = 0; i < (objects? objects->total : 0); i++ ){CvRect* r = (CvRect*)cvGetSeqElem( objects, i );CvPoint center;int radius;center.x = cvRound((r->x + r->width*0.5)*scale);center.y = cvRound((r->y + r->height*0.5)*scale);radius = cvRound((r->width + r->height)*0.25*scale);cvCircle(img, center, radius, colors[i%8], 3, 8, 0 );}show_face(img); //显示检测的结果cvReleaseImage(&gray);cvReleaseImage(&small_img);//释放图片cvReleaseImage(&img);Log_Display_0(tr(\"耗时:%1 ms
\").arg(time.elapsed()));}/*将QImage图片转为opecv的qimage格式*/IplImage *Widget::QImageToIplImage(const QImage * qImage){int width = qImage->width();int height = qImage->height();CvSize Size;Size.height = height;Size.width = width;IplImage *IplImageBuffer = cvCreateImage(Size, IPL_DEPTH_8U, 3);for (int y = 0; y < height; ++y){for (int x = 0; x < width; ++x){QRgb rgb = qImage->pixel(x, y);CV_IMAGE_ELEM( IplImageBuffer, uchar, y, x*3+0 ) = qBlue(rgb);CV_IMAGE_ELEM( IplImageBuffer, uchar, y, x*3+1 ) = qGreen(rgb);CV_IMAGE_ELEM( IplImageBuffer, uchar, y, x*3+2 ) = qRed(rgb);}}return IplImageBuffer;}/*将opecv的图片转为qimage格式*/QImage *Widget::IplImageToQImage(IplImage *img){QImage *qmg;uchar *imgData=(uchar *)img->imageData;qmg = new QImage(imgData,img->width,img->height,QImage::Format_RGB888);*qmg=qmg->rgbSwapped(); //BGR格式转RGBreturn qmg;}//显示检测的结果void Widget::show_face(IplImage* img){uchar *imgData=(uchar *)img->imageData;QImage my_image=QImage(imgData,img->width,img->height,QImage::Format_RGB888);my_image=my_image.rgbSwapped(); //BGR格式转RGBQPixmap my_pix; //创建画图类my_pix.convertFromImage(my_image);/*在控件上显示*/ui->label_display->setPixmap(my_pix);}//开始采集void Widget::on_pushButton_Start_clicked(){//设置当前选择的摄像头videoaudioencode_0.camera=video_dev_list.at(ui->comboBox_video_0->currentIndex());Stop_VideoAudioEncode_0(true);Start_VideoAudioEncode_Thread_0();}//析构函数VideoReadThread_0::~VideoReadThread_0(){}//停止视频采集void VideoReadThread_0::stop(){qDebug()<<\"停止视频采集--stop\";if(camera){camera->stop();delete camera;camera=nullptr;}if(m_pProbe){delete m_pProbe;m_pProbe=nullptr;}}//执行线程void VideoReadThread_0::run(){stop();Camear_Init();qDebug()<<\"摄像头开始采集数据\";}void VideoReadThread_0::Camear_Init(){/*创建摄像头对象,根据选择的摄像头打开*/camera = new QCamera(videoaudioencode_0.camera);m_pProbe = new QVideoProbe;if(m_pProbe != nullptr){m_pProbe->setSource(camera); // Returns true, hopefully.connect(m_pProbe, SIGNAL(videoFrameProbed(QVideoFrame)),this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::QueuedConnection);}/*配置摄像头捕 QCamera *camera;QVideoProbe *m_pProbe;获模式为帧捕获模式*///camera->setCaptureMode(QCamera::CaptureStillImage); //如果在Linux系统下运行就这样设置camera->setCaptureMode(QCamera::CaptureVideo);//如果在android系统下运行就这样设置/*启动摄像头*/camera->start();/*设置摄像头的采集帧率和分辨率*/QCameraViewfinderSettings settings;settings.setPixelFormat(QVideoFrame::Format_YUYV); //设置像素格式 Android上只支持NV21格式settings.setResolution(QSize(VIDEO_WIDTH,VIDEO_HEIGHT)); //设置摄像头的分辨率camera->setViewfinderSettings(settings);}/*** NV21是android相机默认格式* @param data* @param rgb* @param width* @param height*/void NV21_TO_RGB24(unsigned char *yuyv, unsigned char *rgb, int width, int height){const int nv_start = width * height ;int index = 0, rgb_index = 0;uint8_t y, u, v;int r, g, b, nv_index = 0,i, j;for(i = 0; i < height; i++){for(j = 0; j < width; j ++){//nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;nv_index = i / 2 * width + j - j % 2;y = yuyv[rgb_index];u = yuyv[nv_start + nv_index ];v = yuyv[nv_start + nv_index + 1];r = y + (140 * (v-128))/100; //rg = y - (34 * (u-128))/100 - (71 * (v-128))/100; //gb = y + (177 * (u-128))/100; //bif(r >255) r = 255;if(g >255) g = 255;if(b >255) b = 255;if(r < 0) r = 0;if(g < 0) g = 0;if(b < 0) b = 0;index = rgb_index % width + (height - i - 1) * width;//rgb[index * 3+0] = b;//rgb[index * 3+1] = g;//rgb[index * 3+2] = r;//颠倒图像//rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;//rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;//rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;//正面图像rgb[i * width * 3 + 3 * j + 0] = b;rgb[i * width * 3 + 3 * j + 1] = g;rgb[i * width * 3 + 3 * j + 2] = r;rgb_index++;}}}/*函数功能: 将YUV数据转为RGB格式函数参数:unsigned char *yuv_buffer: YUV源数据unsigned char *rgb_buffer: 转换之后的RGB数据int iWidth,int iHeight : 图像的宽度和高度*/void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight){int x;int z=0;unsigned char *ptr = rgb_buffer;unsigned char *yuyv= yuv_buffer;for (x = 0; x < iWidth*iHeight; x++){int r, g, b;int y, u, v;if (!z)y = yuyv[0] << 8;elsey = yuyv[2] << 8;u = yuyv[1] - 128;v = yuyv[3] - 128;r = (y + (359 * v)) >>8;g = (y - (88 * u) - (183 * v)) >>8;b = (y + (454 * u)) >>8;*(ptr++) = (r >255) ? 255 : ((r < 0) ? 0 : r);*(ptr++) = (g >255) ? 255 : ((g < 0) ? 0 : g);*(ptr++) = (b >255) ? 255 : ((b < 0) ? 0 : b);if(z++){z = 0;yuyv += 4;}}}void VideoReadThread_0::slotOnProbeFrame(const QVideoFrame &frame){QVideoFrame cloneFrame(frame);cloneFrame.map(QAbstractVideoBuffer::ReadOnly);//qDebug()<<\"height:\"<label_ImageDisplay_0->setPixmap(my_pixmap);}//驾驶室:日志显示void Widget::Log_Display_0(QString text){Log_Text_Display(ui->plainTextEdit_log_0,text);}/*日志显示*/void Widget::Log_Text_Display(QPlainTextEdit *plainTextEdit_log,QString text){plainTextEdit_log->insertPlainText(text);//移动滚动条到底部QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();if(scrollbar){scrollbar->setSliderPosition(scrollbar->maximum());}}//驾驶室:开启所有采集线程void Widget::Start_VideoAudioEncode_Thread_0(){videoRead_Workthread_0->start(); //开启视频采集线程Init_VideoAudio_WorkClass_0(); //发送初始化信号}//驾驶室:退出所有采集线程void Widget::Stop_VideoAudioEncode_0(bool flag){if(flag==true){Stop_AudioVideo0_work_0(); //发送信号停止摄像头QThread::msleep(10);//退出视频采集videoRead_Workthread_0->quit(); //告诉线程的事件循环以return 0(成功)退出videoRead_Workthread_0->wait(); //等待线程退出}}void Widget::on_pushButton_update_clicked(){UpdateVideoAudiodDevice(ui->comboBox_video_0,ui->plainTextEdit_log_0);}/*刷新本机可以的音频设备列表*/void Widget::UpdateVideoAudiodDevice(QComboBox *comboBox_video,QPlainTextEdit *plainTextEdit_log){/*2. 获取摄像头列表*/video_dev_list.clear();comboBox_video->clear();video_dev_list=QCameraInfo::availableCameras();for(int i=0;iaddItem(video_dev_list.at(i).deviceName());}/*如果没有可用的摄像头设备,按钮不可用*/if(video_dev_list.size()==0){Log_Text_Display(plainTextEdit_log,\"未查询到可用的摄像头设备.
\");}}//停止采集void Widget::on_pushButton_stop_clicked(){Stop_VideoAudioEncode_0(true);}void Widget::update(){if(ui->label_ImageDisplay_0->pixmap())opencv_face(ui->label_ImageDisplay_0->pixmap()->toImage());}
widget.h文件代码:
#ifndef WIDGET_H#define WIDGET_H#include //opencv include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACEnamespace Ui { class Widget; }QT_END_NAMESPACEclass Widget : public QWidget{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);void opencv_face(QImage qImage);~Widget();QListvideo_dev_list;void show_face(IplImage* img);class VideoReadThread_0 *videoRead_WorkClass_0; //视频工作类QThread *videoRead_Workthread_0; //视频线程/*驾驶室摄像头*/void Stop_VideoAudioEncode_0(bool flag);//停止线程void Start_VideoAudioEncode_Thread_0(); //启动线程void Log_Text_Display(QPlainTextEdit *plainTextEdit_log,QString text);void UpdateVideoAudiodDevice(QComboBox *comboBox_video, QPlainTextEdit *plainTextEdit_log);IplImage *QImageToIplImage(const QImage * qImage);QImage *IplImageToQImage(IplImage *img);QTimer timer;signals:void Init_VideoAudio_WorkClass_0();void Stop_AudioVideo0_work_0();private slots:void update();void on_pushButton_Start_clicked();void Log_Display_0(QString text);void VideoDataDisplay_0(QImage );void on_pushButton_update_clicked();void on_pushButton_stop_clicked();private:Ui::Widget *ui;};class VideoReadThread_0:public QObject{Q_OBJECTpublic:QCamera *camera;QVideoProbe *m_pProbe;VideoReadThread_0(QObject* parent=nullptr):QObject(parent){camera=nullptr;m_pProbe=nullptr;}~VideoReadThread_0();void Camear_Init(void);public slots:void stop();void run();void slotOnProbeFrame(const QVideoFrame &frame);signals:void VideoDataOutput(QImage); //输出信号};//视频音频编码类class VideoAudioEncode{public:QCameraInfo camera; //当前选择的摄像头};//视频输出尺寸#define VIDEO_WIDTH 640#define VIDEO_HEIGHT 480extern class VideoAudioEncode videoaudioencode_0;#endif // WIDGET_H
main.cpp文件代码:
#include \"widget.h\"#include int main(int argc, char *argv[]){QApplication a(argc, argv);Widget w;w.show();return a.exec();}
UI界面文件:
【领 QT开发教程 学习资料, 点击下方链接莬费领取↓↓ ,先码住不迷路~】
点击这里:
标签:
图①:山西临汾经济技术开发区兴荣供应链有限公司的货车整装待发。资料图片 图②:司机王勇平驾驶货车行驶在
2022年北京冬奥会的筹办过程,为中国冰雪运动发展提供了巨大动力。科技创新,成为中国冰雪运动前进道路上嘹亮的号角。在科学技术部社会发展
游客在银川市黄河横城旅游度假区观看花灯展(2月5日摄)。春节假期,“2022黄河横城冰雪彩灯艺术节”在宁夏银川市
新华社香港2月6日电题:狮子山下的舞狮人新华社记者韦骅“左眼精,右眼灵,红光万象,富贵繁荣!”“口食八方财,
正在进行围封或强制检测的葵涌邨居民在登记(资料照片)。新华社发新华社香港2月6日电 题:凝聚香港社会共克时艰
2月6日,航拍青海省西宁市雪后美景。受较强冷空气影响,2月5日至6日,青海迎来大范围降雪天气过程,古城西宁银装
[ 相关新闻 ]