OpenCV中文网站

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 安装 配置
查看: 349|回复: 3

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

[复制链接]
发表于 2019-9-25 20:18:25 | 显示全部楼层 |阅读模式

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:n_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:n_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:ayer> class8_ab = net.getLayer("class8_ab");
class8_ab->blobs.push_back(pts_in_hull);
Ptr<dnn:ayer> 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修改了界面但是编译无反应的解决方法,请具体参考
Qt Creatror使用designer修改了界面但是编译无反应的解决方法

4、配置过程的参考建议。
        这里分为3个步骤,首先是使用QT编写Android程序,然后是实现Android+OpenCV,最后是Android+OpenCV+DNN,应该说是渐进方式的,每个步骤都有不同的参考资料。
        step1:配置QT编写Android程序
https://blog.csdn.net/yongheng0852/article/details/78875855
https://www.cnblogs.com/MakeView660/p/11206268.html
https://www.cnblogs.com/jsxyhelu/p/8286476.html
         step2:Android上运行OpenCV
  https://blog.csdn.net/u012230798/article/details/86620400
  https://www.cnblogs.com/jsxyhelu/p/8449222.html
  https://blog.csdn.net/tututuo/article/details/83419612

          step3:Android+OpenCV+DNN

https://blog.csdn.net/m0_38133212/article/details/88032546
https://blog.csdn.net/m0_38133212/article/details/87979923
https://blog.csdn.net/luoyayun361/article/details/84800539
5、各个软件下载地址,注意优先选择X86_64版本
  
Android SDK https://www.androiddevtools.cn/
  
Android NDK https://developer.android.google.cn/ndk/downloads/index.html
  
Qt+QTCreator https://www1.qt.io/download-open-source-access/
  
OpenCV4Android https://sourceforge.net/projects/opencvlibrary/files/4.1.0/opencv-4.1.0-android-sdk.zip/download
  
6、最后,提供完整的代码。但是你需要根据机器的实际情况进行修改
  链接:https://pan.baidu.com/s/1oYo4iTihkKuG8eneBV7tmQ
提取码:00k9
  


           总体感觉,开发基于Android的图像处理程序,是一件比较繁琐的事情,可能出现问题的地方比较多,特别是QT的资料相对较少;但是一旦配置成功、摸清楚其中的原理之后,就能够非常方便地将桌面图像程序算法移植过来,但是也需要注意算法移植过程中的一些小技巧。
  感谢阅读至此,希望有所帮助!


回复

使用道具 举报

发表于 2019-10-3 09:46:58 | 显示全部楼层
不错!
另外,代码的地方建议用tag标签,否则看起来会出现一大堆图标。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-10-9 21:44:22 | 显示全部楼层
OK, 现在论坛里面优质和原创资料太少,如何创建合理的环境吸引更多人将原创资料发布过来,值得思考。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|OpenCV中文网站  

GMT+8, 2019-10-23 10:55 , Processed in 0.059988 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表