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


     
             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的资料相对较少;但是一旦配置成功、摸清楚其中的原理之后,就能够非常方便地将桌面图像程序算法移植过来,但是也需要注意算法移植过程中的一些小技巧。
                感谢阅读至此,希望有所帮助!
     
  • 相关阅读:
    特性和属性
    装箱和拆箱的问题(NET1.1+)
    poj 1013 Counterfeit Dollar(模拟)
    poj 3686 The Windy's( KM算法 )
    二分匹配(匈牙利算法)
    poj 3273 Monthly Expense(二分)
    poj 2115 C Looooops(扩展欧几里德)
    poj 2400 Supervisor, Supervisee
    poj 2195 Going Home (KM算法)
    poj 2513 Colored Sticks(trie树 + 并查集 + 欧拉图)
  • 原文地址:https://www.cnblogs.com/jsxyhelu/p/11587046.html
Copyright © 2020-2023  润新知