<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          PyTorch OCR模型的安卓端部署

          共 6323字,需瀏覽 13分鐘

           ·

          2020-11-29 04:27

          開發(fā)環(huán)境選擇

          1. 本文操作系統(tǒng)為Windows,因?yàn)閃indows上的安卓模擬器選擇較多,并且真機(jī)調(diào)試也比較方便;

          2. 交叉編譯在Windows和Ubuntu上都進(jìn)行了嘗試,都可行,但是如果是Ubuntu上交叉編譯之后再挪到Windows的話,容易出幺蛾子;

          3. 我目前使用的最穩(wěn)定的工具版本組合是:ndk18、androidstudio4.1、cmake3.10、gradle6.5、MinGW(CodeBlocks自帶)。

          1. PyTorch模型轉(zhuǎn)NCNN

          這一小節(jié)是介紹如何將自己重新訓(xùn)練過的PyTorch模型轉(zhuǎn)成ncnn,如果沒有重訓(xùn)練需求的話,可以直接跳過這一節(jié)。

          (1) 整體步驟

          理想情況下,從PyTorch轉(zhuǎn)到ncnn只需要下面兩步:

          • PyTorch轉(zhuǎn)ONNX
          torch.onnx._export(model,?x,?path,?opset_version=11)?
          • ONNX轉(zhuǎn)NCNN
          ./onnx2ncnn?model.onnx?model.param?model.bin?

          遇到問題的適合解決思路如下:

          convert.png

          下面介紹一下我在做ChineseOCRLite中的PSENet模型轉(zhuǎn)換的過程中遇到的問題。

          (2)實(shí)際操作的時(shí)候可能會(huì)遇到各種問題

          問題1:ReLU6不支持

          概述:ReLU6算子在轉(zhuǎn)換的時(shí)候容易出現(xiàn)不支持的情況,需要使用其他算子替代

          解決:使用torch.clamp替代(雖然ReLU6可以通過組合ReLU的方式實(shí)現(xiàn),但是組合得到的ReLU6在NCNN中容易轉(zhuǎn)換失敗,不建議使用。)

          def?relu6(x,inplace=True):
          ??return?torch.clamp(x,0,6)?

          問題2:Resize算子轉(zhuǎn)換問題

          概述:因?yàn)楦鱾€(gè)框架對(duì)Resize算子的支持都不盡相同,在轉(zhuǎn)換過程中總會(huì)出現(xiàn)一些問題,pytorch中的interpolate算子轉(zhuǎn)換成ONNX之后變成很多零散的算子,如cast、shape等,這些在ncnn里面不支持。你可以選擇手動(dòng)修改文件,也可以使用下面這個(gè)自動(dòng)的方法:

          解決:使用onnx_simplifier對(duì)onnx模型進(jìn)行簡(jiǎn)化,可以合并這些零散的算子。

          python?-m?onnxsim?model.onnx?model_sim.onnx?

          問題3:關(guān)于轉(zhuǎn)ONNX及使用onnx_simplifier過程中出現(xiàn)的一系列奇怪問題

          概述:使用不同版本的ONNX可能會(huì)遇到不同的問題,比如提示conv層無(wú)輸入等(具體錯(cuò)誤名稱記不清了)。

          解決:下載最新ONNX源碼編譯安裝(onnx_simplifier中出現(xiàn)的一些錯(cuò)誤也可以通過安裝最新ONNX來(lái)解決)

          git?clone?https://github.com/onnx/onnx.git
          sudo?apt-get?install?protobuf-compiler?libprotoc-dev?
          cd?ONNX?
          python?setup.py?install?

          問題4:模型輸出結(jié)果的尺寸固定

          概述:直接轉(zhuǎn)換得到的onnx模型的Resize算子都是固定輸出尺寸的,無(wú)論輸入多大的圖片都會(huì)輸出同樣大小的特征圖,這無(wú)疑會(huì)影響到模型的精度及靈活性。

          解決:修改NCNN模型的param文件,將Resize算子修改成按比例resize。

          直接轉(zhuǎn)換得到的param文件中的Interp算子是這樣的:

          Interp????913??????1?1?901?913?0=2?1=1.000000e+00?2=1.000000e+00?3=640?4=640?

          從下面的ncnn源碼中可以看到,0代表resize_type,1和2分別是高和寬的縮放比例,3和4分別是輸出的高和寬。

          int?Interp::load_param(const?ParamDict&?pd)?
          {
          ?????resize_type?=?pd.get(0,?0);
          ?????height_scale?=?pd.get(1,?1.f);
          ?????width_scale?=?pd.get(2,?1.f);
          ?????output_height?=?pd.get(3,?0);
          ?????output_width?=?pd.get(4,?0);
          ?????return?0;?
          }?

          我們只需將其修改成如下格式即可實(shí)現(xiàn)按比例resize:

          Interp??????913???????1?1?901?913?0=1?1=4.000000e+00?2=4.000000e+00?

          問題5:NCNN模型輸出結(jié)果與ONNX模型不同

          解決:逐層對(duì)比NCNN與onnx模型的輸出結(jié)果

          使用onnxruntime(Python)和NCNN(C++)分別提取每個(gè)節(jié)點(diǎn)的輸出,進(jìn)行對(duì)比。對(duì)于ncnn比較簡(jiǎn)單,可以使用

          extractor.extract(node_name,preds);?

          來(lái)提取不同節(jié)點(diǎn)的輸出。

          問題5衍生問題1:ONNX沒有提供提取中間層輸出的方法

          解決:給要提取的層添加一個(gè)輸出節(jié)點(diǎn),代碼如下:

          def?find_node_by_name(graph,?node_name):
          ??for?node?in?graph.node:
          ????if?node.output[0]?==?node_name:
          ??????return?node
          ??return?None??????????

          def?add_extra_output_node(model,target_node,?output_name):
          ??extra_output?=?helper.make_empty_tensor_value_info(output_name)
          ??target_output?=?target_node.output[0]
          ??identity_node?=?helper.make_node("Identity",inputs=[target_output],outputs=[output_name],name=output_name)
          ??model.graph.node.append(identity_node)
          ??model.graph.output.append(extra_output)
          ??return?model?

          修改模型之后再使用

          out?=?sess.run([output_name],{"input.1":img.astype(np.float32)})?

          就可以獲取到模型的中間層輸出了。

          問題5衍生問題2:發(fā)現(xiàn)最后一個(gè)Resize層的輸出有差異

          解決:參考chineseocr_lite里面的代碼把mode由bilinear改成了nearest(這里錯(cuò)誤的原因可能是wenmuzhou/PSENet.pytorch中的模型最后一個(gè)F.interpolate中的align_corners參數(shù)設(shè)置成了True。據(jù)說NCNN只實(shí)現(xiàn)了align_corners為False的情況)。

          這里修改之后的模型跟原模型之間是會(huì)有少許誤差的,如果誤差不可接受,就要重新訓(xùn)練才行。

          2. 交叉編譯opencv與ncnn

          交叉編譯工作可以在windows上進(jìn)行,使用的是MinGW + cmkae3.10 + AndroidNDK18??梢詤⒖糤indows下編譯OpenCV android(https://www.cnblogs.com/zhxmdefj/p/13094954.html)

          沒有windows C++環(huán)境的話,也可以選擇在linux上進(jìn)行。

          如果是在linux交叉編譯,然后復(fù)制到windows的話,需要修改一下opencv中cmake配置文件中的路徑。

          (1)android ndk下載

          最初選擇的是r20b,因?yàn)楹虲Make之間的兼容問題,切換到了18b。

          wget?https://dl.google.com/android/repository/android-ndk-r18b-linux-x86_64.zip?hl=zh_cn?
          mv?android-ndk-r18b-linux-x86_64.zip?hl=zh_cn?android-ndk-r18b-linux-x86_64.zip?
          unzip?android-ndk-r18b-linux-x86_64.zip

          (2)編譯opencv

          利用android中提供的android.toolchain.cmake 工具鏈可以快速的編譯opencv的arm版。

          這里選擇的arm平臺(tái)是armeabi-v7a,便于在老舊手機(jī)上運(yùn)行。

          folder="build_arm"
          if?[[?!?-d?"$folder"?]];?then
          ????echo?"$folder?not?found,?creating?folder..."
          ????mkdir?build_arm
          fi
          cd?build_arm
          cmake?\
          ????-DCMAKE_TOOLCHAIN_FILE=\
          ????/home/dai/soft/android-ndk-r18b/build/cmake/android.toolchain.cmake?\
          ????-DANDROID_NDK=/home/dai/soft/android-ndk-r18b?\
          ????-DCMAKE_BUILD_TYPE=Release??\
          ????-DBUILD_ANDROID_PROJECTS=OFF?\
          ????-DBUILD_ANDROID_EXAMPLES=OFF?\
          ????-DANDROID_ABI=armeabi-v7a?\
          ????-DANDROID_NATIVE_API_LEVEL=21??..
          make?-j4

          (3)編譯ncnn

          編譯選項(xiàng)參考ncnn wiki(https://github.com/Tencent/ncnn/wiki/how-to-build#build-for-arm-cortex-a-family-with-cross-compiling)

          folder="build_arm"
          if?[[?!?-d?"$folder"?]];?then
          ????echo?"$folder?not?found,?creating?folder..."
          ????mkdir?build_arm
          fi
          cd?build_arm
          cmake?\
          ????-DCMAKE_TOOLCHAIN_FILE=\
          ????/home/dai/soft/android-ndk-r18b/build/cmake/android.toolchain.cmake?\
          ????-DANDROID_ABI="armeabi-v7a"?\
          ????-DANDROID_ARM_NEON=ON?\
          ????-DANDROID_PLATFORM=android-14?\
          ????..
          make?-j4

          (4)chineseocr_lite的PC端測(cè)試

          與ncnn有關(guān)的代碼位于ncnn_project目錄下。在有opencv和ncnn庫(kù)的基礎(chǔ)上,可以先在pc端跑一下識(shí)別代碼。

          cd?ncnn_project/ocr
          mkdir?build_arm?
          cd?build_arm?
          cmake?..?
          make?-j4

          編譯完成之后

          ./TextRecognition?../test1.jpg

          可以看到輸出結(jié)果:

          psenet前向時(shí)間:0.462291s
          psenet?decode?時(shí)間:0.0604791s
          boxzie10
          預(yù)測(cè)結(jié)果:

          統(tǒng)
          ;名

          丹正珍
          類住

          有限責(zé)

          中山市
          角度檢測(cè)和文字識(shí)別總時(shí)間:1.52042s

          3. NCNN模型的安卓端部署

          因?yàn)榇a較長(zhǎng),這一部分只介紹把PC端代碼遷移到安卓端的思路,想看詳細(xì)代碼的同學(xué)請(qǐng)移步文末的Github地址。

          遷移的整體思路如下圖所示:

          android_flow.png

          下面一一介紹圖中內(nèi)容

          UI界面

          這個(gè)demo的UI界面中至少應(yīng)包含三個(gè)元件:

          Button——用于選擇相冊(cè)圖片

          ImageView——用于展示圖片及文本檢測(cè)框

          TextView——用于展示識(shí)別結(jié)果

          界面截圖如下(TextView在沒有文字的時(shí)候是不顯示的):

          UI.jpg

          界面res/layout/activity_main.xml文件修改。

          Java部分

          模型推理是在C++中完成的,Java部分的代碼主要是利用安卓的API實(shí)現(xiàn)圖片讀取、文本檢測(cè)框繪制和結(jié)果展示等功能。

          需要傳入到C++函數(shù)的內(nèi)容包括Bitmap圖片和AssetManager對(duì)象。

          從C++接收到的是一個(gè)包含文本框和識(shí)別結(jié)果的字符串。

          C++部分

          C++負(fù)責(zé)模型推理,推理代碼與PC端無(wú)異,只是安卓端的文件讀取與PC端不同,需要修改文件讀取代碼,比如crnn的模型加載代碼就需要改成下面的樣子:

          int?model::init(AAssetManager?*mgr,?const?std::string?crnn_param,?const?std::string?crnn_bin)
          {
          ????int?ret1?=?crnn.load_param(mgr,?crnn_param.c_str());
          ????int?ret2?=?crnn.load_model(mgr,?crnn_bin.c_str());
          ????LOGI("ret1?is?%d,?ret2?is?%d",?ret1,?ret2);
          ????return?(ret1||ret2);
          }

          另外還需要把Java部分傳過來(lái)的Bitmap轉(zhuǎn)換成cv::Mat,代碼如下:

          //?convert?bitmap?to?mat
          ????int?*data?=?NULL;
          ????AndroidBitmapInfo?info?=?{0};
          ????AndroidBitmap_getInfo(env,?bitmap,?&info);
          ????AndroidBitmap_lockPixels(env,?bitmap,?(void?**)?&data);

          ????//?這里偷懶只寫了RGBA格式的轉(zhuǎn)換
          ????LOGI("info?format?RGBA???%d",?info.format?==?ANDROID_BITMAP_FORMAT_RGBA_8888);
          ????cv::Mat?test(info.height,?info.width,?CV_8UC4,?(char*)data);?//?RGBA
          ????cv::Mat?img_bgr;
          ????cvtColor(test,?img_bgr,?CV_RGBA2BGR);

          最終識(shí)別結(jié)果

          最終得到的demo識(shí)別結(jié)果如下圖所示:

          result.jpg

          本項(xiàng)目完整代碼請(qǐng)移步github:

          https://github.com/Arctanxy/DeepLearningDeployment/tree/master/ocr



          歡迎關(guān)注GiantPandaCV, 在這里你將看到獨(dú)家的深度學(xué)習(xí)分享,堅(jiān)持原創(chuàng),每天分享我們學(xué)習(xí)到的新鮮知識(shí)。( ? ?ω?? )?

          有對(duì)文章相關(guān)的問題,或者想要加入交流群,歡迎添加BBuf微信:

          二維碼

          為了方便讀者獲取資料以及我們公眾號(hào)的作者發(fā)布一些Github工程的更新,我們成立了一個(gè)QQ群,二維碼如下,感興趣可以加入。

          公眾號(hào)QQ交流群


          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美成人在线免费播放 | 亚洲人成色777777精品音频 | 青青草原在线 | 内射红桃视频免费看 | 蜜桃传媒视频在线播放 |