搭建Android+QT+OpenCV环境,实现“单色图片着色”效果

一世执手 提交于 2020-04-14 12:00:48

【推荐阅读】微服务还能火多久?>>>

 
         OpenCV是我们大家非常熟悉的图像处理开源类库;在其新版本将原本在Contrib分库中的DNN模块融合到了主库中,并且更新了相应文档。这样我们就能够非常方便地利用OpenCV实现一些属于DeepLearning范畴的效果,比如“超级分辨率”“单色图片着色”“色彩迁移”等。当我们想把软件处理的平台由PC机转移到嵌入式平台和手机上的时候,QT也是能和OpenCV配合地非常好的平台。在这里,我具体研究了如何搭建Android+QT+OpenCV环境,实现“单色图片着色”效果;并将相关内容整理如下,希望能够对有这方面需求的工程师提供帮助。
一、环境配置
        首先我们面临的问题是工具版本的选择,虽然我们已经确定了Android+QT+OpenCV的基本软件结构,但是在每一个环节都需要选择具体的版本。
        Android需要选择的是sdk和ndk的版本,我这里使用的是Android10(API29)+android-ndk-r20的组合,基本上是现在(2019年9月)最新的组合了;
         QT需要选择的是QT和QT Creator,我这里选择的是QT 5.13.1+QT Creator 4.10.0,同样是 现在(2019年9月)最新的组合;
        OpenCV用于Android的话,官方有Prebuild版本,我这里采用的是opencv-4.1.0-android-sdk。
 
1、SDK的下载和配置
       首先我们正确安装JDK并且将其目录放入PATH中,而后需要下载android-sdk、 android-ndk-r20、 OpenCV-android-sdk,并且分别解压放置在非中文无空格的目录中。(下载地址看文末)
 
2、QT的安装和配置
       下载QT在线安装程序直接安装,安装过程中除了默认选中的“Developer and designer Tools”以外,只需选中QT5.13.1下的“Android Armv7”即可。
完成后主要是做一个配置“工具”->"选项“中”设备“栏目"JDK""SDK""NDK"都配置到正确的目录下
这里容易出现各种错误,尽可能保证使用我推荐的软件版本,避免错误影响进度。
此时,你应该就已经可以在Android上运行QT自带的例子。
         任意选择一个例子,比如“Qt 3D: Audio Visualizer Example ”,选择之前配置好的Kits,连接好实体手机或者模拟器(参考文末链接),点击“运行”,稍等一会,即可出现效果。
        这里需要注意,第一次运行可能需要从网络上下载一些东西,所以请保证网络顺畅。
 
3、OpenCV环境的引入
 
        下面我们想办法将OpenCV环境引入进来,之前已经下载了“OpenCV-android-sdk",它的文件目录是这样:
这时,需要我们配置QT项目的.pro文件,最为重要的就是在.pro文件中添加这个模块
 
android {
ANDROID_OPENCV = D : /OpenCV -android -sdk /sdk /native
INCLUDEPATH += \
$$ANDROID_OPENCV /jni /include /opencv2 \
$$ANDROID_OPENCV /jni /include \


LIBS += \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_ml.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_objdetect.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_calib3d.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_video.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_features2d.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_highgui.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_flann.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_imgproc.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_dnn.a \
$$ANDROID_OPENCV /staticlibs /armeabi -v7a /libopencv_core.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /libcpufeatures.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /libIlmImf.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibjasper.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibjpeg -turbo.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibpng.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibprotobuf.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibtiff.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /liblibwebp.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /libquirc.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /libtbb.a \
$$ANDROID_OPENCV / 3rdparty /libs /armeabi -v7a /libtegra_hal.a \
$$ANDROID_OPENCV /libs /armeabi -v7a /libopencv_java4.so

}






























 
这里就是告诉QT到哪里去寻找OpenCV-android-sdk的include文件和libs文件,最后,还需要将libopencv_java4.so添加到项目中
 
 
 
方法是对于当前项目,点击“项目”->“详情”->"add"将这个libopencv_java4.so加进去,需要注意这里有bug,添加完成后,需要手动修
 
改.pro文件这个部分至正确:
 
 
contains(ANDROID_TARGET_ARCH,armeabi -v7a) {
ANDROID_EXTRA_LIBS = \
D : /OpenCV -android -sdk /sdk /native /libs /armeabi -v7a /libopencv_java4.so

 
4、DNN模型的引入
      
      由于所有的DNN模型都需要调用模型文件(.pb等),而这些文件都必须预先编译到APK中去。使用  Qt 如何来做了?还是在.pro文
件上下功夫。
 
        打开 Qt 工程文件pro,并添加如下代码
 
data.files += images / *. *
data.files += dnn / *.prototxt
data.files += dnn / *.caffemodel
data.path = /assets /dnn
INSTALLS += data



 
 
注意,这里只是我的示例写法,images和dnn是我手动添加的和工程文件 pro 同级目录的文件夹
里面分别包含了图片和模型文件:
 
再来看.pro中添加的这个部分
 
data.files  += images / *. *
data.files  += dnn / *.prototxt
data.files  += dnn / *.caffemodel
data.path  =  /assets /dnn
INSTALLS  += data



 
其中data字段是可以随便定义的,首先指定 data .files 文件目录,然后将images目录下所有的文件,dnn目录下所有的.prototxt和.caffemodel
 
全部加入其中。最后制定data.path为/assets/dnn
 
这样编译出来的 apk 中,加压后会发现已经生成一个assets文件夹,并且在改文件夹中存放了我们已经添加的文件。
 
 
我们可以通过winrar打开apk,发现这些文件。
 
那么到目前为止,所有需要准备的东西都已经停当,我们开始编码。
 
二、代码编写
这里我给出的例子是一个非常简单的widget程序(截图可能和题图有所不同,以这里的为准)
 
包含1个textbox和2个button按钮。我们直接按照从上到下的顺序来看代码。
 
首先是"读取Lena并显示“,这个按钮的功能比较存粹,就是 使用OpenCV 从前面保存的assets目录中读出lena.jpg,并且最终利用QT显示出来。
 
void MainWindow : :on_pushButton_2_clicked()
{
QFile : :copy( "assets:/dnn/lena.bmp", "lena.bmp");
Mat src = imread( "lena.bmp");
cvtColor(src,src,COLOR_BGR2GRAY);
QPixmap qpixmap = Mat2QImage(src);
// 将图片显示到label上
ui - >label - >setPixmap(qpixmap);
}







 
逐句来看,首先使用 QFile : : copy 函数将 "assets:/dnn/lena.bmp" 拷贝到根目录下的 "lena.bmp" 处,这样OpenCV就能够使用绝对路径来读取;
 
接下来读入这个图片,转换为灰度图片,这些都是基本OpenCV函数。然后我们使用了 Mat2QImage 函数将Mat格式的数据转换成为了 QPixmap 格式,并且使用label显示出来。
 
那么 Mat2QImage是一个我们自己实现的函数,功能就是将Mat格式转换为QImage格式,这样QT就能够显示。
 
//格式转换
QPixmap Mat2QImage(Mat src)
{
QImage img;
//根据QT的显示方法进行转换
if(src.channels() == 3)
{
cvtColor( src, tmp, COLOR_BGR2RGB );
img = QImage( ( const unsigned char *)(tmp.data), tmp.cols, tmp.rows, QImage : :Format_RGB888 );
}
else
{
img = QImage( ( const unsigned char *)(src.data), src.cols, src.rows, QImage : :Format_Grayscale8 );
}
QPixmap qimg = QPixmap : :fromImage(img) ;
return qimg;
}

















其次是“调用着色算法”,这个函数就是在前面的基础上添加了较多功能。
 
void MainWindow : :on_pushButton_clicked()
{
QFile : :copy( "assets:/dnn/lena.jpg", "lena.jpg");
QFile : :copy( "assets:/dnn/colorization_deploy_v2.prototxt", "colorization_deploy_v2.prototxt");
QFile : :copy( "assets:/dnn/colorization_release_v2.caffemodel", "colorization_release_v2.caffemodel");
Mat src = imread( "lena.jpg");
cvtColor(src,tmp,COLOR_BGR2GRAY);
cvtColor(tmp,src,COLOR_GRAY2BGR);


string modelTxt = "colorization_deploy_v2.prototxt";
string modelBin = "colorization_release_v2.caffemodel";
bool useOpenCL = true;

// fixed input size for the pretrained network
const int W_in = 224;
const int H_in = 224;
Net net = dnn : :readNetFromCaffe(modelTxt, modelBin);
if (useOpenCL)
net.setPreferableTarget(DNN_TARGET_OPENCL);

// setup additional layers:
int sz[] = { 2, 313, 1, 1 };
const Mat pts_in_hull( 4, sz, CV_32F, hull_pts);
Ptr <dnn : :Layer > class8_ab = net.getLayer( "class8_ab");
class8_ab - >blobs.push_back(pts_in_hull);
Ptr <dnn : :Layer > conv8_313_rh = net.getLayer( "conv8_313_rh");
conv8_313_rh - >blobs.push_back(Mat( 1, 313, CV_32F, Scalar( 2. 606)));

// extract L channel and subtract mean
Mat lab, L, input;
src.convertTo(tmp, CV_32F, 1. 0 / 255);
cvtColor(tmp, lab, COLOR_BGR2Lab);
extractChannel(lab, L, 0);
cv : :resize(L, input, Size(W_in, H_in));
input -= 50;

// run the L channel through the network
Mat inputBlob = blobFromImage(input);
net.setInput(inputBlob);
Mat result = net.forward();

// retrieve the calculated a,b channels from the network output
Size siz(result.size[ 2], result.size[ 3]);
Mat a = Mat(siz, CV_32F, result.ptr( 0, 0));
Mat b = Mat(siz, CV_32F, result.ptr( 0, 1));
cv : :resize(a, a, src.size());
cv : :resize(b, b, src.size());

// merge, and convert back to BGR
Mat color, chn[] = { L, a, b };
merge(chn, 3, lab);
cvtColor(lab, color, COLOR_Lab2BGR);

color.convertTo(tmp,CV_8UC3, 255);

QPixmap qpixmap = Mat2QImage(tmp);
// 将图片显示到label上
ui - >label - >setPixmap(qpixmap);
}




























































这个代码比较长,我们一块一块地来讲,首先仍然是将assets目录中的文件拷贝到绝对地址下面;
然后这个比较长的代码具体实现的功能就是调用模型实现着色效果,这里我整编一些之前写的东西。
 
目前使用的这个模型来自Richard Zhang,原始论文:
目前算法能够实现较好的灰度图片作色效果,他所采用的方法是基于大量图片的训练来“预测”灰色图片中对应的彩色效果。下图是论文中的对比。
 
为了程序的成功运行,需要先前往

http://eecs.berkeley.edu/~rich.zhang/projects/2016_colorization/files/demo_v2/colorization_release_v2.caffemodel

https://raw.githubusercontent.com/richzhang/colorization/master/colorization/models/colorization_deploy_v2.prototxt

下载caffeemodel和prototxt文件。整个过程中很多代码,只有几行是核心的:

 

其他的代码都是为了能够将各种文件转换成forward支持的格式。其中调用了一个大的常量:

static float hull_pts[] = {
- 90 ., - 90 ., - 90 ., - 90 ., - 90 ., - 80 ., - 80 ., - 80 ., - 80 ., - 80 ., - 80 ., - 80 ., - 80 ., - 70 ., - 70 ., - 70 ., - 70 ., - 70 ., - 70 ., - 70 ., - 70 .,
- 70 ., - 70 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 60 ., - 50 ., - 50 ., - 50 ., - 50 ., - 50 ., - 50 ., - 50 ., - 50 .,
- 50 ., - 50 ., - 50 ., - 50 ., - 50 ., - 50 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 40 ., - 30 .,
- 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 30 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 .,
- 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 20 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 ., - 10 .,
- 10 ., - 10 ., - 10 ., - 10 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 .,
10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 10 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 ., 20 .,
20 ., 20 ., 20 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 30 ., 40 ., 40 ., 40 ., 40 .,
40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 40 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 .,
50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 50 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 ., 60 .,
60 ., 60 ., 60 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 70 ., 80 ., 80 ., 80 .,
80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 80 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 .,
90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 90 ., 100 ., 100 ., 100 ., 100 ., 100 ., 100 ., 100 ., 100 ., 100 ., 100 ., 50 ., 60 ., 70 ., 80 ., 90 .,
20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 .,
60 ., 70 ., 80 ., 90 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 100 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 .,
30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 100 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 100 ., - 50 .,
- 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 100 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 .,
30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., 100 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 .,
100 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., - 80 ., - 70 ., - 60 ., - 50 .,
- 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 .,
0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., 90 ., - 100 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 .,
40 ., 50 ., 60 ., 70 ., 80 ., 90 ., - 100 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 .,
80 ., - 110 ., - 100 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., - 110 ., - 100 .,
- 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., 80 ., - 110 ., - 100 ., - 90 ., - 80 ., - 70 .,
- 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., - 110 ., - 100 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 .,
- 20 ., - 10 ., 0 ., 10 ., 20 ., 30 ., 40 ., 50 ., 60 ., 70 ., - 90 ., - 80 ., - 70 ., - 60 ., - 50 ., - 40 ., - 30 ., - 20 ., - 10 ., 0 .
};


























这些数据的产生都和作者原始采用的模型有密切关系,想要完全理解Dnn的代码就必须了解对应模型的训练过程。

 
参考和技巧
1、看到我这里使用“夜神”模拟器来调试Android程序是不是很感兴趣?具体使用起来是有技巧滴,请参考

如何使用”夜神“作为虚拟机来进行程序调试

 
2、在QTCreator的“工具->外部”选项下,可以配置一些外部程序,比如我把“夜神”和"SDK Manger"配置在这里,方便使用
 
            
3、发现 Qt Creatror使用designer修改了界面但是编译无反应的解决方法,请具体参考
 
4、 配置过程的参考建议。
        这里分为3个步骤,首先是使用QT编写Android程序,然后是实现Android+OpenCV,最后是Android+OpenCV+DNN,应该说是渐进方式的,每个步骤都有不同的参考资料。
 
        step1:配置QT编写Android程序
 
 
         step3: Android+OpenCV+DNN
        
5、各个软件下载地址,注意优先选择X86_64版本
 
 
 
 
 
6、最后,提供完整的代码。但是你需要根据机器的实际情况进行修改
链接:https://pan.baidu.com/s/1oYo4iTihkKuG8eneBV7tmQ 
提取码:00k9 
 
         总体感觉,开发基于Android的图像处理程序,是一件比较繁琐的事情,可能出现问题的地方比较多,特别是QT的资料相对较少;但是一旦配置成功、摸清楚其中的原理之后,就能够非常方便地将桌面图像程序算法移植过来,但是也需要注意算法移植过程中的一些小技巧。
            感谢阅读至此,希望有所帮助!
 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!