1. 程式人生 > >搭建Android+QT+OpenCV環境,實現“單色圖片著色”效果

搭建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修改了介面但是編譯無反應的解決方法,請具體參考

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的資料相對較少;但是一旦配置成功、摸清楚其中的原理之後,就能夠非常方便地將桌面影象程式演算法移植過來,但是也需要注意演算法移植過程中的一些小技巧。             感謝閱讀至此,希望有所幫助!  

&n