日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android上实现MobileSSD 实时摄像头检测 - NCNN

發(fā)布時間:2023/12/20 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android上实现MobileSSD 实时摄像头检测 - NCNN 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

NCNN

ncnn 是騰訊提供的移動端框架 非常時候在手機玩在研究了一段時間的 ncnn后 準(zhǔn)備做一個安卓的demo 以便更好的熟悉和使用 NCNN

先上APP和效果

app地址 :https://pan.baidu.com/s/1MX8GnM3xR4u0dcyw7iVSRg


第二張圖沒框準(zhǔn)是因為人在動 ,計算耗時還是比較長 大約需要1.幾秒

安卓程序編寫過程

程序本身并不復(fù)雜 但是搞安卓的過程中發(fā)現(xiàn) 攝像頭獲取下視頻真的是累的要去死

程序框架

啟動過程

---------- ----------------- -------------|Main窗口| -->|Camera Fragment| 獲取圖像 ----> |Surface顯示| <------------ | ----------------- | ------------- || | || -------------- | -------------- |-->|JNI 加載模型| --->|ncnn推理計算|----------------- --------------

數(shù)據(jù)流程

JAVA ----------- ------------------ -------------|CAMERAYUV| -->|YUV 轉(zhuǎn)RGB Bitmap| --> |Surface顯示|----------- | ------------------ -------------| | JNI | -------------- ----------- | ->|YUV Y 轉(zhuǎn) RGB| -->|Mobilenet| -->|-------------- -----------

編寫過程

攝像頭權(quán)限問題

android 7以后的程序需要獲取攝像頭的需要主動申請權(quán)限 一開始沒弄各種不行

后參考使用camera2 官方demo解決

private void requestCameraPermission() {if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {new ConfirmationDialog().show(getChildFragmentManager(), FRAGMENT_DIALOG);} else {requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);}}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {if (requestCode == REQUEST_CAMERA_PERMISSION) {if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {ErrorDialog.newInstance(getString(R.string.request_permission)).show(getChildFragmentManager(), FRAGMENT_DIALOG);}} else {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}}

引入ncnn庫的問題

其實官方說明已經(jīng)很詳細了(~ ~! 并沒有找到啊~~)

實際引入需要對cmakefile進行一些調(diào)整

首先參照AS的向?qū)Ыdk工程

然后將github上release中的 ncnn庫文件 和lib拷貝到 cpp目錄下

這里我使用的是 ncnn-android-lib 目錄 如圖

導(dǎo)入后需要對路徑進行配置 并鏈接 openmp 否則會出現(xiàn)錯誤

修改CMakeLists.txt

# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html# Sets the minimum version of CMake required to build the native library.cmake_minimum_required(VERSION 3.4.1)FIND_PACKAGE( OpenMP REQUIRED) if(OPENMP_FOUND)message("OPENMP FOUND")set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") endif()# Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK.include_directories(./ncnn-android-lib/include)add_library( # Sets the name of the library.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).mobilenetssd.cpp)# Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build.find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log)# Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries.target_link_libraries( # Specifies the target library.native-lib${CMAKE_SOURCE_DIR}/ncnn-android-lib/${ANDROID_ABI}/libncnn.a# Links the target library to the log library# included in the NDK.${log-lib})

然后就可以正常使用 NCNN了

jni調(diào)用的參數(shù)傳遞

為了提高效率 我并沒有對圖像數(shù)組進行拷貝

而是使用了 臨界區(qū)映射的方法 GetPrimitiveArrayCritical 這樣避免了拷貝但是出現(xiàn)了一個巨坑 各種百度不到

extern "C" JNIEXPORT jdoubleArray JNICALL Java_com_chenty_testncnn_CameraNcnnFragment_detectyonly(JNIEnv *env, jobject thiz, jbyteArray frame, jint src_width,jint src_height, jdoubleArray detect) {char *argb_frame = (char*)env->GetPrimitiveArrayCritical(frame, NULL);int size = env->GetArrayLength(frame);int objectcnt = 0;int i;//shift argb to rgbachar *rgba = (char *)malloc(size);memcpy(rgba, argb_frame + 1, size - 1);env->ReleasePrimitiveArrayCritical(frame, argb_frame, JNI_ABORT);struct ai_object_t *obj = cpp_mobilenet_aidetect((const unsigned char *)rgba, src_width, src_height, &objectcnt);free(rgba);double *detect_out = (double *)env->GetPrimitiveArrayCritical(detect, NULL);if(objectcnt <= 0)objectcnt = 1;for(i = 0 ; i < objectcnt ; i++){detect_out[i*6 + 0] = obj->label;detect_out[i*6 + 1] = obj->prob;detect_out[i*6 + 2] = obj->x;detect_out[i*6 + 3] = obj->y;detect_out[i*6 + 4] = obj->xe;detect_out[i*6 + 5] = obj->ye;obj++;}env->ReleasePrimitiveArrayCritical(detect, detect_out, JNI_ABORT);return detect; }

問題是 本身函數(shù)設(shè)計是 輸入圖像數(shù)組 長 寬 輸出 判定結(jié)果
然后在里面死活new 不了double數(shù)組

后來想想應(yīng)該就是這個臨界區(qū)不能new對象的原因

最后喪心病狂 直接java new一個數(shù)組傳進來算了。這就是目前的實現(xiàn)了

YUV數(shù)據(jù)的獲取

安卓默認(rèn)獲取攝像頭數(shù)據(jù) 是直接寫入到SURFACE的 效率很高 無法直接獲取

目前有2個方法獲取這個數(shù)據(jù)

  • 先繪制到SURFACE,然后從SURFACEDUMP RGB數(shù)據(jù)(OpenCV的做法)
  • 建立一個拍照Session 手動獲取圖片
  • 這里使用第二種方法 這個魔改demo的地方實在是太多了 大家自己看吧
    基本上就是建立session 獲取YUV 其中的坑很多 但是都可以百度解決還行吧。

    數(shù)據(jù)進入計算都完成了

    現(xiàn)在已經(jīng)可以在Log中看到SSD出來的目標(biāo)了,很好,然后是數(shù)據(jù)繪制

    數(shù)據(jù)繪制

    我們截斷了數(shù)據(jù)的獲取過程,當(dāng)需要顯示的時候又很糾結(jié)了。
    首先發(fā)現(xiàn)安卓 后攝像頭默認(rèn)是橫著的 WTF。。。 繪制圖像需要旋轉(zhuǎn)90度。。。
    直接鎖定應(yīng)用只能橫屏顯示(機智~~)

    繪制需要使用Surface 并且縮放,so Ctrl+C Ctrl+V了一堆代碼 實現(xiàn)了。。

    顯示和計算分離

    經(jīng)過上面的過程,已經(jīng)可以顯示出大體的效果了,但是每次計算耗時需要1秒,圖像像幻燈片一樣 這這么行。
    于是做了一個簡單的自鎖和計算顯示分離

    //split process function to prevent img reflash stopif(!mDetect_isbusy){mDetect_isbusy = true;new Thread(new Runnable() {public void run() {mDetect_result = ncnnprocess_imgyuv(yuv, width, height);mDetect_isbusy = false;}}).start();}if(mDetect_result != null) {mSurfaceView.Draw(yuv, width, height, mDetect_result, 90);}

    至此 功能部分已經(jīng)完整OK了

    mDetect_isbusy = false;}}).start();}if(mDetect_result != null) {mSurfaceView.Draw(yuv, width, height, mDetect_result, 90);} 至此 功能部分已經(jīng)完整OK了Code:https://github.com/chentyjpm/MobileNetSSD_Demo_AndoridNCNN

    總結(jié)

    以上是生活随笔為你收集整理的Android上实现MobileSSD 实时摄像头检测 - NCNN的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。