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

转自:http://www.cnblogs.com/jsxyhelu/p/8286475.html

一、综述

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

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

 

二、通过JNI打开摄像头

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

#include 

#include 

#include 

#include 

#include 

#include 

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事件

基于OpenCV和QT,建立Android图像处理程序_第1张图片

d、编写拍照代码

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

voidMainWindow::on_btn_capture_pressed()

{

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

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

//引用JNI

QAndroidJniEnvironmentenv;

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

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

//设定img路径

QStringdate=QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss");

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

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

//使用CHECK_EXCEPTION处理异常

CHECK_EXCEPTION()

qDebug()<<"savedDir-"<(),fileName.object());

CHECK_EXCEPTION()

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

CHECK_EXCEPTION()



//将输出路径传递过来

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

CHECK_EXCEPTION()

qDebug()<<"MediaStore.EXTRA_OUTPUT-"<(),

savedImageUri.object());



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

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

startActivity(intent,1,resultReceiver);

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

strFetchImage=savedImageFile.toString();

}

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

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

//图像处理操作

voidMainWindow::on_btn_process_pressed()

{

b_canSave=false;

if(strFetchImage!="")

{

ui->lbMain->setScaledContents(false);

Matsrc=imread(strFetchImage.toStdString());

Matsrc2;

Matrotated;

////////////////////////////主要算法/////////////////////////////

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

Matsrc_gray;

Matsrc_all=src2.clone();



Matthreshold_output;

vector>contours,contours2;

vectorhierarchy;

//预处理

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("开始寻找轮廓!开始筛选轮廓!");

}



//轮廓筛选

intc=0,ic=0,area=0;

intparentIdx=-1;

for(inti=0;i=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(inti=0;ilbMain->setPixmap(qpixmap);

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

return;

}

else

{

//否则,进一步分割

Pointpoint[4];

for(inti=0;i720*1000/4)

{

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

QPixmapqpixmap=Mat2QImage(src_all);

ui->lbMain->setPixmap(qpixmap);

return;

}

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

Pointptmp=Center_cal(contours2,i);



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

{

point[0]=ptmp;

}

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

{

point[2]=ptmp;

}

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

{

point[1]=ptmp;

}

else

{

point[3]=ptmp;

}

}



//打印出来

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

{

charcbuf[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::Point2fsrc_vertices[4];

src_vertices[0]=point[0];

src_vertices[1]=point[1];

src_vertices[2]=point[2];

src_vertices[3]=point[3];

Point2fdst_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);

MatwarpMatrix=getPerspectiveTransform(src_vertices,dst_vertices);

//执行透视变化

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

}

//////////////////////////END主要算法END///////////////////////

//将图片显示到label上

QPixmapqpixmap=Mat2QImage(rotated);

ui->lbMain->setPixmap(qpixmap);

matResult=rotated.clone();

b_canSave=true;

}

}

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

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

基于OpenCV和QT,建立Android图像处理程序_第2张图片

基于OpenCV和QT,建立Android图像处理程序_第3张图片

下一步注重解决以下问题

1、提高程序稳定性;

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

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

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

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

你可能感兴趣的:(Qt,OpenCV)