首页 > 编程语言 >(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序

(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序

时间:2022-12-26 17:03:12浏览次数:56  
标签:src QAndroidJniObject GTD QT point vertices OpenCV ui ptmp

一、综述



    如何采集图片?在windows环境下,我们可以使用dshow,在linux下,也有ffmpeg等基础类库,再不济,opencv自带的videocapture也是提供了基础的支撑。那么在andoird下,使用的肯定是Android自带的相关函数了。由于Android是基于java语言的,如果我们想要调用Android  的相关函数,那么必须通过JNI的方法。



    这里有可以分为两种,一种是直接在java中实现比较完整的函数,在qt中,只需要调用这个函数就可以;另一种就是使用qt自带的jni机制,比如下面这样,打开摄像头,并且采集图片。我们首先介绍第二种方法,让大家最快进入情况。



 



二、通过JNI打开摄像头



a、填加头文件和命名空间,定义公共变量和宏:



#include   <QtAndroid>



#include   <QDebug>



#include   <QAndroidJniEnvironment>



#include   <QAndroidActivityResultReceiver>



#include   <QDateTime>



#include   <QFile>



using   namespace   cv;



using   namespace   QtAndroid ;



 



QString   strFetchImage   =   "" ;



QString   selectedFileName   =   "" ;



 



#define   CHECK_EXCEPTION ()   \



if (env->ExceptionCheck())\



{\



qDebug ()   <<   "exception   occured" ;\



env->ExceptionClear();\



}



 



其中需要注意的是, CHECK_EXCEPTION 是用来检查Android系统是否有异常的。这一点在使用JNI的时候非常重要和必要。



 



b、填加回调类,主要就是在一系列异常判断后,获得imagepath。该类集成自 ResultReceiver :



class   ResultReceiver :   public   QAndroidActivityResultReceiver



{



     public :   ResultReceiver ( QString   imagePath ,   QLabel   * view )  :   m_imagePath ( imagePath ),   m_imageView ( view ) { }



     void   handleActivityResult ( int   receiverRequestCode , int   resultCode , const   QAndroidJniObject   &   data ) {



      qDebug ()   <<   "handleActivityResult,   requestCode   -   "   <<   receiverRequestCode <<   "   resultCode   -   "   <<   resultCode <<   "   data   -   "   <<   data . toString ();



     if ( resultCode   ==   - 1   &&   receiverRequestCode   ==   1 ) {



     qDebug ()   <<   "captured   image   to   -   "   <<   m_imagePath ;



     qDebug ()   <<   "captured   image   exist   -   "   <<   QFile :: exists ( m_imagePath );



     m_imageView -> setPixmap ( QPixmap ( m_imagePath )); }



    }



     QString   m_imagePath ;



     QLabel   * m_imageView ;



};



 



C、填加控件触发事件。一般来说我们选择pressed事件




(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android


d、编写拍照代码


//打开摄像头,采集图片


void MainWindow :: on_btn_capture_pressed ()


{


ui -> lbMain -> setScaledContents ( true ); //显示的图像自动缩放


b_canSave = false ;  //图片没有采集完成,目前不可以保存


//引用JNI


QAndroidJniEnvironment env ;


//创建用于打开摄像头的content


QAndroidJniObject action = QAndroidJniObject :: fromString ( "android.media.action.IMAGE_CAPTURE" ); QAndroidJniObject   ( intent ( "android/content/Intent" , "(Ljava/lang/String;)V" , action . object < jstring >());


//设定img路径


QString date = QDateTime :: currentDateTime (). toString ( "yyyyMMdd_hhmmss" );


QAndroidJniObject fileName = QAndroidJniObject :: fromString ( date + ".jpg" );


QAndroidJniObject savedDir = QAndroidJniObject :: callStaticObjectMethod ( "android/os/Environment" , "getExternalStorageDirectory" , "()Ljava/io/File;" );


//使用CHECK_EXCEPTION处理异常


CHECK_EXCEPTION ()


qDebug () << "savedDir - " << savedDir . toString ();


QAndroidJniObject savedImageFile ( "java/io/File" , "(Ljava/io/File;Ljava/lang/String;)V" , savedDir . object < jobject >(), fileName . object < jstring >());


CHECK_EXCEPTION ()


qDebug () << "savedImageFile - " << savedImageFile . toString ();


QAndroidJniObject savedImageUri = QAndroidJniObject :: callStaticObjectMethod ( "android/net/Uri" , "fromFile" , "(Ljava/io/File;)Landroid/net/Uri;" ,


savedImageFile . object < jobject >());


CHECK_EXCEPTION ()


 


//将输出路径传递过来


QAndroidJniObject mediaStoreExtraOutput = QAndroidJniObject :: getStaticObjectField ( "android/provider/MediaStore" , "EXTRA_OUTPUT" , "Ljava/lang/String;" );


CHECK_EXCEPTION ()


qDebug () << "MediaStore.EXTRA_OUTPUT - " << mediaStoreExtraOutput . toString ();


intent . callObjectMethod (


"putExtra" , "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;" , mediaStoreExtraOutput . object < jstring >(),


savedImageUri . object < jobject >());


 


//获得采集图片的绝对路径,并且显示出来


ResultReceiver * resultReceiver = new ResultReceiver ( savedImageFile . toString (), ui -> lbMain );


startActivity ( intent , 1 , resultReceiver );


//获得返回的绝对地址(注意这句话一定要写在 CHECK_EXCEPTION 中)


strFetchImage = savedImageFile . toString ();


}


最终采集到的图片地址保存在 strFetchImage   中


 


e、编写处理代码。由于我这里主要进行的是图像处理操作,所以必须结合OpenCV相关函数进行


//图像处理操作


void MainWindow :: on_btn_process_pressed ()


{


b_canSave = false ;


if (strFetchImage != "" )


{


ui -> lbMain -> setScaledContents ( false );


Mat src = imread (strFetchImage. toStdString ());


Mat src2 ;


Mat rotated ;


主要算法/


cv :: resize ( src , src2 , cv :: Size ( 720 , 1000 )); //标准大小


Mat src_gray ;


Mat src_all = src2 . clone ();


 


Mat threshold_output ;


vector<vector< Point > > contours , contours2 ;


vector< Vec4i > hierarchy ;


//预处理


cvtColor ( src2 , src_gray , CV_BGR2GRAY );


blur ( src_gray , src_gray , Size ( 3 , 3 ) ); //模糊,去除毛刺


threshold ( src_gray , threshold_output , 100 , 255 , THRESH_OTSU );


//添加提示


ui -> lb_info -> setText ( "开始寻找轮廓!" );


//寻找轮廓


//第一个参数是输入图像 2值化的


//第二个参数是内存存储器,FindContours找到的轮廓放到内存里面。


//第三个参数是层级,**[Next, Previous, First_Child, Parent]** 的vector


//第四个参数是类型,采用树结构


//第五个参数是节点拟合模式,这里是全部寻找


findContours ( threshold_output , contours , hierarchy , CV_RETR_TREE , CHAIN_APPROX_NONE , Point ( 0 , 0 ) );


//添加提示


if ( contours .size()<= 10 )


{


     ui -> lb_info -> setText ( "轮廓筛选错误,循环退出!请重新采集数据。" );


     return ;


}


else


{


     ui -> lb_info -> setText ( "开始寻找轮廓! 开始筛选轮廓!" );


}


 


//轮廓筛选


int c = 0 , ic = 0 , area = 0 ;


int parentIdx =- 1 ;


for ( int i = 0 ; i < contours .size(); i ++ )


{


//hierarchy[i][2] != -1 表示不是最外面的轮廓


if ( hierarchy [ i ][ 2 ] != - 1 && ic == 0 )


{


parentIdx = i ;


ic ++;


}


else if ( hierarchy [ i ][ 2 ] != - 1 )


{


ic ++;


}


//最外面的清0


else if ( hierarchy [ i ][ 2 ] == - 1 )


{


ic = 0 ;


parentIdx = - 1 ;


}


//找到定位点信息


if ( ic >= 2 )


{


contours2 .push_back( contours [ parentIdx ]);


ic = 0 ;


parentIdx = - 1 ;


}


}


 


//添加提示


if ( contours2 .size()< 3 )


{


ui -> lb_info -> setText ( "定位点选择错误,循环退出!请重新采集数据。" );


return ;


}


else


{


ui -> lb_info -> setText ( "开始寻找轮廓! 开始筛选轮廓!定位点选择正确!" );


}


 


//填充定位点,我们约定,必须要能够同时识别出4个点来


for ( int i = 0 ; i < contours2 .size(); i ++)


drawContours ( src_all , contours2 , i , CV_RGB ( 0 , 255 , 0 ) , - 1 );


 


//识别出来了关键区域,但是数量不对,显示当前识别结果,退出循环


if ( contours2 .size() != 4 )


{


QPixmap qpixmap = Mat2QImage ( src_all );


ui -> lbMain -> setPixmap ( qpixmap );


ui -> lb_info -> setText ( "定位点数量不为4!请重新采集数据。" );


return ;


}


else


{


//否则,进一步分割


Point point [ 4 ];


for ( int i = 0 ; i < contours2 .size(); i ++)


{


//筛选轮廓,


double d = contourArea ( contours2 [ i ]);


if ( d > 720 * 1000 / 4 )


{


ui -> lb_info -> setText ( "采集中有错误轮廓,请重新采集数据" );


QPixmap qpixmap = Mat2QImage ( src_all );


ui -> lbMain -> setPixmap ( qpixmap );


return ;


}


//定位重点,并重新排序


Point ptmp = Center_cal ( contours2 , i );


 


if ( ptmp . x < 720 / 4 && ptmp . y < 1000 / 4 )


{


point [ 0 ] = ptmp ;


}


else if ( ptmp . x < 720 / 4 && ptmp . y > 1000 / 4 )


{


point [ 2 ] = ptmp ;


}


else if ( ptmp . x > 720 / 4 && ptmp . y < 1000 / 4 )


{


point [ 1 ] = ptmp ;


}


else


{


point [ 3 ] = ptmp ;


}


}


 


//打印出来


for ( int i = 0 ; i < 3 ; i ++)


{


char cbuf [ 100 ];


sprintf ( cbuf , "%d" , i + 1 );


putText ( src_all , cbuf , point [ i ], FONT_HERSHEY_PLAIN , 5 , Scalar ( 0 , 0 , 0 ), 5 );


ui -> lb_info -> setText ( "结果识别正确,可以保存" );


}


 


//透视变换


cv :: Point2f src_vertices [ 4 ];


src_vertices [ 0 ] = point [ 0 ];


src_vertices [ 1 ] = point [ 1 ];


src_vertices [ 2 ] = point [ 2 ];


src_vertices [ 3 ] = point [ 3 ];


Point2f dst_vertices [ 4 ];


dst_vertices [ 0 ] = Point ( 0 , 0 );


dst_vertices [ 1 ] = Point ( 720 , 0 );


dst_vertices [ 2 ] = Point ( 0 , 1000 );


dst_vertices [ 3 ] = Point ( 720 , 1000 );


Mat warpMatrix = getPerspectiveTransform ( src_vertices , dst_vertices );


//执行透视变化


warpPerspective ( src2 , rotated , warpMatrix , rotated . size (), INTER_LINEAR , BORDER_CONSTANT );


}


//END 主要算法 END ///


// 将图片显示到label上


QPixmap qpixmap = Mat2QImage ( rotated );


ui -> lbMain -> setPixmap ( qpixmap );


matResult = rotated . clone ();


b_canSave = true ;


}


}


三、初步结果和继续研究需要解决的问题


按照设计,目前得到这样的结果



(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android_02



(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android_03


下一步注重解决以下问题


1、提高程序稳定性;


2、提高界面流程性和运行速度;


3、重构代码,进一步进行封装;


4、添加数据保存的相关功能。


感谢阅读至此,希望有所帮助!


 




​来自为知笔记(Wiz)​




一、综述


    如何采集图片?在windows环境下,我们可以使用dshow,在linux下,也有ffmpeg等基础类库,再不济,opencv自带的videocapture也是提供了基础的支撑。那么在andoird下,使用的肯定是Android自带的相关函数了。由于Android是基于java语言的,如果我们想要调用Android  的相关函数,那么必须通过JNI的方法。


    这里有可以分为两种,一种是直接在java中实现比较完整的函数,在qt中,只需要调用这个函数就可以;另一种就是使用qt自带的jni机制,比如下面这样,打开摄像头,并且采集图片。我们首先介绍第二种方法,让大家最快进入情况。


 


二、通过JNI打开摄像头


a、填加头文件和命名空间,定义公共变量和宏:


#include   <QtAndroid>


#include   <QDebug>


#include   <QAndroidJniEnvironment>


#include   <QAndroidActivityResultReceiver>


#include   <QDateTime>


#include   <QFile>


using   namespace   cv;


using   namespace   QtAndroid ;


 


QString   strFetchImage   =   "" ;


QString   selectedFileName   =   "" ;


 


#define   CHECK_EXCEPTION ()   \


if (env->ExceptionCheck())\


{\


qDebug ()   <<   "exception   occured" ;\


env->ExceptionClear();\


}


 


其中需要注意的是, CHECK_EXCEPTION 是用来检查Android系统是否有异常的。这一点在使用JNI的时候非常重要和必要。


 


b、填加回调类,主要就是在一系列异常判断后,获得imagepath。该类集成自 ResultReceiver :


class   ResultReceiver :   public   QAndroidActivityResultReceiver


{


     public :   ResultReceiver ( QString   imagePath ,   QLabel   * view )  :   m_imagePath ( imagePath ),   m_imageView ( view ) { }


     void   handleActivityResult ( int   receiverRequestCode , int   resultCode , const   QAndroidJniObject   &   data ) {


      qDebug ()   <<   "handleActivityResult,   requestCode   -   "   <<   receiverRequestCode <<   "   resultCode   -   "   <<   resultCode <<   "   data   -   "   <<   data . toString ();


     if ( resultCode   ==   - 1   &&   receiverRequestCode   ==   1 ) {


     qDebug ()   <<   "captured   image   to   -   "   <<   m_imagePath ;


     qDebug ()   <<   "captured   image   exist   -   "   <<   QFile :: exists ( m_imagePath );


     m_imageView -> setPixmap ( QPixmap ( m_imagePath )); }


    }


     QString   m_imagePath ;


     QLabel   * m_imageView ;


};


 


C、填加控件触发事件。一般来说我们选择pressed事件



(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android


d、编写拍照代码


//打开摄像头,采集图片


void MainWindow :: on_btn_capture_pressed ()


{


ui -> lbMain -> setScaledContents ( true ); //显示的图像自动缩放


b_canSave = false ;  //图片没有采集完成,目前不可以保存


//引用JNI


QAndroidJniEnvironment env ;


//创建用于打开摄像头的content


QAndroidJniObject action = QAndroidJniObject :: fromString ( "android.media.action.IMAGE_CAPTURE" ); QAndroidJniObject   ( intent ( "android/content/Intent" , "(Ljava/lang/String;)V" , action . object < jstring >());


//设定img路径


QString date = QDateTime :: currentDateTime (). toString ( "yyyyMMdd_hhmmss" );


QAndroidJniObject fileName = QAndroidJniObject :: fromString ( date + ".jpg" );


QAndroidJniObject savedDir = QAndroidJniObject :: callStaticObjectMethod ( "android/os/Environment" , "getExternalStorageDirectory" , "()Ljava/io/File;" );


//使用CHECK_EXCEPTION处理异常


CHECK_EXCEPTION ()


qDebug () << "savedDir - " << savedDir . toString ();


QAndroidJniObject savedImageFile ( "java/io/File" , "(Ljava/io/File;Ljava/lang/String;)V" , savedDir . object < jobject >(), fileName . object < jstring >());


CHECK_EXCEPTION ()


qDebug () << "savedImageFile - " << savedImageFile . toString ();


QAndroidJniObject savedImageUri = QAndroidJniObject :: callStaticObjectMethod ( "android/net/Uri" , "fromFile" , "(Ljava/io/File;)Landroid/net/Uri;" ,


savedImageFile . object < jobject >());


CHECK_EXCEPTION ()


 


//将输出路径传递过来


QAndroidJniObject mediaStoreExtraOutput = QAndroidJniObject :: getStaticObjectField ( "android/provider/MediaStore" , "EXTRA_OUTPUT" , "Ljava/lang/String;" );


CHECK_EXCEPTION ()


qDebug () << "MediaStore.EXTRA_OUTPUT - " << mediaStoreExtraOutput . toString ();


intent . callObjectMethod (


"putExtra" , "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;" , mediaStoreExtraOutput . object < jstring >(),


savedImageUri . object < jobject >());


 


//获得采集图片的绝对路径,并且显示出来


ResultReceiver * resultReceiver = new ResultReceiver ( savedImageFile . toString (), ui -> lbMain );


startActivity ( intent , 1 , resultReceiver );


//获得返回的绝对地址(注意这句话一定要写在 CHECK_EXCEPTION 中)


strFetchImage = savedImageFile . toString ();


}


最终采集到的图片地址保存在 strFetchImage   中


 


e、编写处理代码。由于我这里主要进行的是图像处理操作,所以必须结合OpenCV相关函数进行


//图像处理操作


void MainWindow :: on_btn_process_pressed ()


{


b_canSave = false ;


if (strFetchImage != "" )


{


ui -> lbMain -> setScaledContents ( false );


Mat src = imread (strFetchImage. toStdString ());


Mat src2 ;


Mat rotated ;


主要算法/


cv :: resize ( src , src2 , cv :: Size ( 720 , 1000 )); //标准大小


Mat src_gray ;


Mat src_all = src2 . clone ();


 


Mat threshold_output ;


vector<vector< Point > > contours , contours2 ;


vector< Vec4i > hierarchy ;


//预处理


cvtColor ( src2 , src_gray , CV_BGR2GRAY );


blur ( src_gray , src_gray , Size ( 3 , 3 ) ); //模糊,去除毛刺


threshold ( src_gray , threshold_output , 100 , 255 , THRESH_OTSU );


//添加提示


ui -> lb_info -> setText ( "开始寻找轮廓!" );


//寻找轮廓


//第一个参数是输入图像 2值化的


//第二个参数是内存存储器,FindContours找到的轮廓放到内存里面。


//第三个参数是层级,**[Next, Previous, First_Child, Parent]** 的vector


//第四个参数是类型,采用树结构


//第五个参数是节点拟合模式,这里是全部寻找


findContours ( threshold_output , contours , hierarchy , CV_RETR_TREE , CHAIN_APPROX_NONE , Point ( 0 , 0 ) );


//添加提示


if ( contours .size()<= 10 )


{


     ui -> lb_info -> setText ( "轮廓筛选错误,循环退出!请重新采集数据。" );


     return ;


}


else


{


     ui -> lb_info -> setText ( "开始寻找轮廓! 开始筛选轮廓!" );


}


 


//轮廓筛选


int c = 0 , ic = 0 , area = 0 ;


int parentIdx =- 1 ;


for ( int i = 0 ; i < contours .size(); i ++ )


{


//hierarchy[i][2] != -1 表示不是最外面的轮廓


if ( hierarchy [ i ][ 2 ] != - 1 && ic == 0 )


{


parentIdx = i ;


ic ++;


}


else if ( hierarchy [ i ][ 2 ] != - 1 )


{


ic ++;


}


//最外面的清0


else if ( hierarchy [ i ][ 2 ] == - 1 )


{


ic = 0 ;


parentIdx = - 1 ;


}


//找到定位点信息


if ( ic >= 2 )


{


contours2 .push_back( contours [ parentIdx ]);


ic = 0 ;


parentIdx = - 1 ;


}


}


 


//添加提示


if ( contours2 .size()< 3 )


{


ui -> lb_info -> setText ( "定位点选择错误,循环退出!请重新采集数据。" );


return ;


}


else


{


ui -> lb_info -> setText ( "开始寻找轮廓! 开始筛选轮廓!定位点选择正确!" );


}


 


//填充定位点,我们约定,必须要能够同时识别出4个点来


for ( int i = 0 ; i < contours2 .size(); i ++)


drawContours ( src_all , contours2 , i , CV_RGB ( 0 , 255 , 0 ) , - 1 );


 


//识别出来了关键区域,但是数量不对,显示当前识别结果,退出循环


if ( contours2 .size() != 4 )


{


QPixmap qpixmap = Mat2QImage ( src_all );


ui -> lbMain -> setPixmap ( qpixmap );


ui -> lb_info -> setText ( "定位点数量不为4!请重新采集数据。" );


return ;


}


else


{


//否则,进一步分割


Point point [ 4 ];


for ( int i = 0 ; i < contours2 .size(); i ++)


{


//筛选轮廓,


double d = contourArea ( contours2 [ i ]);


if ( d > 720 * 1000 / 4 )


{


ui -> lb_info -> setText ( "采集中有错误轮廓,请重新采集数据" );


QPixmap qpixmap = Mat2QImage ( src_all );


ui -> lbMain -> setPixmap ( qpixmap );


return ;


}


//定位重点,并重新排序


Point ptmp = Center_cal ( contours2 , i );


 


if ( ptmp . x < 720 / 4 && ptmp . y < 1000 / 4 )


{


point [ 0 ] = ptmp ;


}


else if ( ptmp . x < 720 / 4 && ptmp . y > 1000 / 4 )


{


point [ 2 ] = ptmp ;


}


else if ( ptmp . x > 720 / 4 && ptmp . y < 1000 / 4 )


{


point [ 1 ] = ptmp ;


}


else


{


point [ 3 ] = ptmp ;


}


}


 


//打印出来


for ( int i = 0 ; i < 3 ; i ++)


{


char cbuf [ 100 ];


sprintf ( cbuf , "%d" , i + 1 );


putText ( src_all , cbuf , point [ i ], FONT_HERSHEY_PLAIN , 5 , Scalar ( 0 , 0 , 0 ), 5 );


ui -> lb_info -> setText ( "结果识别正确,可以保存" );


}


 


//透视变换


cv :: Point2f src_vertices [ 4 ];


src_vertices [ 0 ] = point [ 0 ];


src_vertices [ 1 ] = point [ 1 ];


src_vertices [ 2 ] = point [ 2 ];


src_vertices [ 3 ] = point [ 3 ];


Point2f dst_vertices [ 4 ];


dst_vertices [ 0 ] = Point ( 0 , 0 );


dst_vertices [ 1 ] = Point ( 720 , 0 );


dst_vertices [ 2 ] = Point ( 0 , 1000 );


dst_vertices [ 3 ] = Point ( 720 , 1000 );


Mat warpMatrix = getPerspectiveTransform ( src_vertices , dst_vertices );


//执行透视变化


warpPerspective ( src2 , rotated , warpMatrix , rotated . size (), INTER_LINEAR , BORDER_CONSTANT );


}


//END 主要算法 END ///


// 将图片显示到label上


QPixmap qpixmap = Mat2QImage ( rotated );


ui -> lbMain -> setPixmap ( qpixmap );


matResult = rotated . clone ();


b_canSave = true ;


}


}


三、初步结果和继续研究需要解决的问题


按照设计,目前得到这样的结果



(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android_02



(GO_GTD_2)基于OpenCV和QT,建立Android图像处理程序_android_03


下一步注重解决以下问题


1、提高程序稳定性;


2、提高界面流程性和运行速度;


3、重构代码,进一步进行封装;


4、添加数据保存的相关功能。


感谢阅读至此,希望有所帮助!


 




​来自为知笔记(Wiz)​



标签:src,QAndroidJniObject,GTD,QT,point,vertices,OpenCV,ui,ptmp
From: https://blog.51cto.com/jsxyhelu2017/5969443

相关文章

  • 使用opencv自带的融合函数
    [wiki,blog]使用opencv自带的融合函数/*M///////IMPORTANT:READBEFOREDOWNLOADING,COPYING,INSTALLINGORUSING.////By......
  • 基于DJango和Pybind11,实现网络后端调用OpenCV算法
    本Blog来源于实际项目,所采用框架组合中,OpenCV实现算法主体、pybind11完成OpenCV的Python封装、django提供Web应用服务,三者协同配合,共同实现“网络后端调用OpenCV算法,解决特......
  • qt的其他窗口
    一、qt的其他类族  2.Qlabelui->setupUi(this);QFontfont;//确立一个字体对象font.setFamily("华文行楷");//字体font.setPointSize(20);//字体的......
  • PLC采集网关MQTT上云平台操作技巧
    金鸽MQTT的配置操作步骤:(1)双击“金鸽IoT”弹出金鸽MQTT配置框。(2)点击启用按钮,启用金鸽MQTT。默认:关闭。灰色表示:不启用,绿色表示:启用。(3)IP/域名:1883.dtuip.com,默认填写......
  • qt基础知识总结
    qt基础知识总结1、ctrl+r:快速运行2、两种模式的区别:一个是提供菜单栏的,一个不提供菜单栏3、界面讲解layouts:布局=水平布局+垂直布局+网格布局+表单布局spacers:垫子≈......
  • 用 Python 和 OpenCV 检测图片上的条形码()
    原文地址:http://python.jobbole.com/80448/假设我们要检测下图中的条形码:图1:包含条形码的示例图片现在让我们开始写点代码,新建一个文件,命名为detect_barcode.py,打开并编码:Py......
  • OpenCV中Denoising相关函数的简单介绍
    参考:http://wenhuix.github.io/research/denoise.html一、基本情况      (一)基本方法     Fast Non-Local MeansDenoising(FNLMD),论文为  ......
  • 《学习OpenCV3》第7章第4题-SVD奇异值分解的验算
    原文题目:中文翻译: 解题过程d.使用OpenCV编写代码/************************************************************************......
  • QT实现异步日志
    #include<QCoreApplication>#include<QDateTime>#include<QDir>#include<QFile>#include<QProcess>#include<QTextStream>#include<QTimer>#defineROLLSIZE1000*......
  • QT实现UDP广播和接收
    #include<QCoreApplication>#include<QNetworkDatagram>#include<QUdpSocket>structReceiveThread::ReceiveThreadPrivate{constquint16recvPort=6000;vola......