Android平臺(tái)第三方圖像處理算法集成問(wèn)題總結(jié)

之前工作大部分時(shí)間是在集成第三方圖像處理算法, 其中主要是雙攝虛化(Bokeh)相關(guān), 在此總結(jié)一下其中遇到的一些問(wèn)題和解決方法.

集成方式

第三方算法公司提供的SDK都是C/C++動(dòng)態(tài)庫(kù)(.so) + 頭文件的方式, 集成到手機(jī)中通常就兩種方式:

  1. 集成到單獨(dú)的App中
  2. 集成到Android系統(tǒng)源碼中

兩種方式優(yōu)缺點(diǎn)如下:

  1. 優(yōu)點(diǎn): 通用性較好, 針對(duì)不同平臺(tái)(QCOM/MTK)或者廠商不需要額外修改代碼, 缺點(diǎn):性能表現(xiàn)一般, 無(wú)法用到系統(tǒng)的一些獨(dú)特的硬件(GPU, DSP等)
  2. 優(yōu)點(diǎn): 性能較好, 最大限度利用硬件資源, 缺點(diǎn): 通用性很差, 每個(gè)平臺(tái)集成代碼都有些差異.

使用哪種集成方式一般取決于具體算法使用場(chǎng)景: 比如對(duì)應(yīng)Camera360這樣的通用應(yīng)用, 就必須集成到App中, 而對(duì)于一些作為系統(tǒng)亮點(diǎn)或者差異化功能的賣(mài)點(diǎn)的廠商, 自然是集成到Android系統(tǒng)中, 像我做過(guò)的雙攝手機(jī)項(xiàng)目, 由于特殊性, 就必須集成到系統(tǒng)中.

兩種集成方式代碼方面稍有差異, 詳細(xì)內(nèi)容請(qǐng)看: Android調(diào)用第三方C++算法庫(kù)

extern C 問(wèn)題

雖然C++號(hào)稱完全兼容C, 也想取代C, 但最終并沒(méi)有完全實(shí)現(xiàn), 不同算法公司或者Android系統(tǒng)中不同模塊, 使用的語(yǔ)言也不完全相同, 有的使用C, 有的用C++, 因此集成過(guò)程中會(huì)有一些兼容問(wèn)題, 主要表現(xiàn)如下:

  • 算法接口是C++, 調(diào)用的模塊是C代碼

    此類(lèi)問(wèn)題要一般都是要求算法提供商提供C接口(只改接口部分, 修改并不多), C代碼在Android系統(tǒng)中還是比較多的, 比較成熟的算法提供商一般默認(rèn)都是提供C接口的, 這樣就能兼容C和C++了

  • 算法是C接口, 調(diào)用用的C++

    因?yàn)镃++兼容C, 看起來(lái)沒(méi)問(wèn)題, 但是由于頭文件中沒(méi)有加extern C, 會(huì)導(dǎo)致以C++方式編譯后, 產(chǎn)生的函數(shù)名和算法中C接口的函數(shù)名不同, 所以會(huì)出現(xiàn)找不到調(diào)用的函數(shù)問(wèn)題, 這種情況在引入的頭文件中加入extern C即可.

undefined reference to xxx 快速定位

此類(lèi)錯(cuò)誤一般有兩種情況

  • 忘記在調(diào)用算法庫(kù)的模塊的Android.mk中加入LOCAL_SHARED_LIBRARIES := libxxx 如果 LOCAL_SHARED_LIBRARIES變量在當(dāng)前mk中定義過(guò), 則需使用LOCAL_SHARED_LIBRARIES += libxxx
  • 算法庫(kù)和頭文件不對(duì)應(yīng),缺少函數(shù)
    這種情況一般是算法提供商的問(wèn)題, 確定是否是這個(gè)問(wèn)題可以使用linux中提供的nm命令,如:
    nm -D xxx路徑/libxxx.so, 看看輸出信息中是否有你要找的那個(gè)個(gè)函數(shù)名稱, 沒(méi)有就說(shuō)明是算法庫(kù)和頭文件不對(duì)應(yīng).

注: nm -D輸出信息中, 如果是C算法庫(kù), 函數(shù)名和頭文件中是一樣的, 如果是C++算法庫(kù), 則稍有差異, C++由于有重載特性, 函數(shù)名編譯后會(huì)帶有參數(shù)類(lèi)型和返回值信息以及命名空間信息.
比如: 對(duì)于函數(shù)void startPreview(int flag), 如果是C方式編譯, 通過(guò)nm -D輸出的函數(shù)名字為startPreview , C++則是_Z12startPreviewi, 比如我們看下libandroid_runtime.so里面和Camera預(yù)覽相關(guān)的函數(shù)

$ nm -D system/lib/libandroid_runtime.so |grep -i preview
         U _ZN7android6Camera11stopPreviewEv
         U _ZN7android6Camera12startPreviewEv
         U _ZN7android6Camera14previewEnabledEv
         U _ZN7android6Camera16setPreviewTargetERKNS_2spINS_22IGraphicBufferProducerEEE
         U _ZN7android6Camera23setPreviewCallbackFlagsEi
         U _ZN7android6Camera24setPreviewCallbackTargetERKNS_2spINS_22IGraphicBufferProducerEEE

可以看到相關(guān)接口都是C++的.

can not load library

代碼運(yùn)行時(shí)找不到so庫(kù)的問(wèn)題一般也分為兩種情況:

  • 算法庫(kù)沒(méi)有打包到ROM包中

    這種情況一般對(duì)于初學(xué)者經(jīng)常發(fā)生, 通常會(huì)發(fā)生在將so庫(kù)預(yù)置到Android系統(tǒng)中, 比如我們通過(guò)如下代碼預(yù)置一個(gè)so庫(kù)

    include $(CLEAR_VARS)
    LOCAL_MODULE := libxxx
    LOCAL_MODULE_TAGS := optional
    LOCAL_SRC_FILES := libs/armeabi-v7a/$(LOCAL_MODULE).so
    LOCAL_MODULE_STEM := $(LOCAL_MODULE)
    LOCAL_MODULE_SUFFIX := $(suffix $(LOCAL_SRC_FILES))
    LOCAL_MULTILIB := 32
    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
    include $(BUILD_PREBUILT)
    

    如果只有這些代碼, 模塊編譯(mmm)也能正常編譯, so庫(kù)也會(huì)被編譯到/system/lib/下, 但make clean全編譯的時(shí)候, 這個(gè)libxxx.so是不會(huì)被編譯到out目錄中的, 因?yàn)槟阒皇穷A(yù)置到系統(tǒng)中, 確沒(méi)有告訴系統(tǒng)那個(gè)地方要用到這個(gè)so, 所以make的時(shí)候就會(huì)被默認(rèn)剔除, 可通過(guò)在要調(diào)用的模塊的Android.mk中加入LOCAL_SHARED_LIBRARIES += libxxx或者LOCAL_REQUIRED_MODULES := libxxx來(lái)解決此問(wèn)題, 表明有模塊在使用這個(gè)so. 另外使用LOCAL_SHARED_LIBRARIES會(huì)使當(dāng)前編譯模塊依賴于libxxx, 即其他地方加載當(dāng)前so文件中時(shí)會(huì)自動(dòng)去加載libxxx.so, 如果這個(gè)庫(kù)是JNI的so庫(kù), 也可通過(guò)LOCAL_JNI_SHARED_LIBRARIES := libxxx來(lái)表明依賴關(guān)系

  • 缺少依賴庫(kù)文件

    缺少依賴庫(kù)是指算法商提供的so庫(kù)可能依賴系統(tǒng)或者其他第三方庫(kù), 但你沒(méi)有直接看出來(lái), 如果錯(cuò)誤信息出現(xiàn)了can not load library libxxx,而且這個(gè)libxxx在系統(tǒng)或者算法商提供的列表里并沒(méi)有, 這種情況一般是算法商漏提供了一些so庫(kù)或者不同平臺(tái)公共庫(kù)不一樣(比如高通平臺(tái)OpenCL接口庫(kù)名稱為libOpenCL.so, MTK平臺(tái)則是libGLES_mali.so), 這種情況可以使用linux命令 readelf來(lái)查看依賴的庫(kù), 方法如下:

     $ readelf -d out/target/product/msm8909w/system/lib/hw/bluetooth.default.so
    Dynamic section at offset 0x19085c contains 40 entries:
    Tag        Type                         Name/Value
     0x00000001 (NEEDED)                     Shared library: [libcutils.so]
     0x00000001 (NEEDED)                     Shared library: [libdl.so]
     0x00000001 (NEEDED)                     Shared library: [liblog.so]
     0x00000001 (NEEDED)                     Shared library: [libz.so]
     0x00000001 (NEEDED)                     Shared library: [libpower.so]
     0x00000001 (NEEDED)                     Shared library: [libprotobuf-cpp-full.so]
     0x00000001 (NEEDED)                     Shared library: [libmedia.so]
     0x00000001 (NEEDED)                     Shared library: [libutils.so]
     0x0000000e (SONAME)                     Library soname: [bluetooth.default.so]
    

    上面是我截取輸出中的一部分, 可以看到有很多依賴的so, 比如你要打印LOG, 就要依賴Android系統(tǒng)中l(wèi)iblog.so這個(gè)庫(kù),通過(guò)此命令就很容易知道有沒(méi)有漏掉一些算法庫(kù).

YUV數(shù)據(jù)對(duì)齊(stride/scanline)

由于芯片硬件特性, 做硬件JPEG編碼時(shí), 圖片寬高如果都是處理器位數(shù)(32或者64, 也有可能是其他數(shù)值, 取決于芯片平臺(tái))的整數(shù)倍, 能得到更好的性能, 所以如果預(yù)覽或者拍照的圖片寬高相不是關(guān)數(shù)值的整數(shù)倍, 比如msm8953平臺(tái)是64位對(duì)齊, 拍照設(shè)置的圖片尺寸為2592x1944,在HAL層獲取的yuv數(shù)據(jù)實(shí)際寬高為2624x1984,如果用相關(guān)工具看這個(gè)yuv圖片, 圖片右邊和下邊有無(wú)效像素(綠邊或者黑邊或者對(duì)應(yīng)像素的延伸). 對(duì)齊后的寬在QCOM平臺(tái)寬稱為stride(步長(zhǎng)),即相鄰兩行圖像數(shù)據(jù)之間的間隔, 高稱之為scanline,即有多少行數(shù)據(jù)可以進(jìn)行讀取.

一個(gè)簡(jiǎn)單計(jì)算對(duì)齊后寬高的函數(shù)(只針對(duì)2冪次方對(duì)齊, 2,4,8,16...)

int align(int target, int align)
{
    return (target + align -1) & (~(align - 1));
}
//示例:
align(2592, 64);// 得到 2624

當(dāng)然一般平臺(tái)返回的yuv數(shù)據(jù)都有相關(guān)信息記錄stride和scanline, 高通平臺(tái)獲取方式請(qǐng)看:高通(QCOM)平臺(tái)HAL層獲取預(yù)覽/拍照/錄像YUV數(shù)據(jù) ,MTK平臺(tái)相關(guān)信息都在IImageBuffer這個(gè)類(lèi)中, 詳細(xì)信息可以看下這個(gè)類(lèi)的相關(guān)定義, 由于我這里沒(méi)有源碼(博客要及時(shí)寫(xiě), 不然后面再寫(xiě)想驗(yàn)證東西或者看代碼, 發(fā)現(xiàn)沒(méi)有相關(guān)環(huán)境了......), 就不說(shuō)明了.

YUV數(shù)據(jù)格式

由于平臺(tái)的差異QCOM/MTK, 使用的yuv數(shù)據(jù)格式也有差異, QCOM平臺(tái)預(yù)覽格式為yuv420sp,即NV21, 拍照HAL層格式也是NV21.

MTK平臺(tái)預(yù)覽默認(rèn)為yuv420p, 即YV21, 拍照HAL層默認(rèn)情況為yuv422的一種格式, 但MTK平臺(tái)提供了接口用于申請(qǐng)不同格式的YUV數(shù)據(jù), 我自己嘗試過(guò)在MT6750T平臺(tái)申請(qǐng)拍照的YV12數(shù)據(jù), 能正常得到相關(guān)數(shù)據(jù).

預(yù)覽格式可以通過(guò)dumpsys media.camera來(lái)查看, 一般支持多種格式, 可以通過(guò)設(shè)置參數(shù)的方式控制預(yù)覽格式, 拍照也一樣:

//查看支持的預(yù)覽格式, adb方式, 當(dāng)然也可以通過(guò)Camera的API
adb shell dumpsys media.camera |grep preview-format-values // linux
adb shell dumpsys media.camera |findstr preview-format-values //windows

注意:QCOM平臺(tái)更改預(yù)覽格式會(huì)導(dǎo)致exif中的縮略圖出現(xiàn)異常色塊, 這個(gè)問(wèn)題很多QCOM平臺(tái)都有, 原因是縮略圖是根據(jù)預(yù)覽的數(shù)據(jù)進(jìn)行JPEG編碼生成的, 但編碼時(shí)默認(rèn)用的NV21格式, 我們的預(yù)覽不是NV21格式就會(huì)有問(wèn)題, 是個(gè)bug.

加載系統(tǒng)so庫(kù)失敗

從Android 7.0開(kāi)始, 非系統(tǒng)App, 無(wú)法通過(guò)System.loadLibrary("libname");加載系統(tǒng)庫(kù),會(huì)出現(xiàn)如下錯(cuò)誤:
java.lang.UnsatisfiedLinkError: dlopen failed: library "/system/lib/libxxx.so" needed or dlopened by "/system/lib/libnativeloader.so" is not accessible for the namespace "classloader-namespace", Google之所以這樣做是為了加強(qiáng)系統(tǒng)安全性, 這種情況一般只有將App預(yù)置到系統(tǒng)中, 第三方安裝的App就沒(méi)法調(diào)用系統(tǒng)庫(kù)了, 有些人可能說(shuō)可以把系統(tǒng)庫(kù)pull出來(lái)放到App中, 但這不可行的, 原因有兩點(diǎn):

  1. 系統(tǒng)庫(kù)本身會(huì)依賴其他庫(kù), pull一個(gè)庫(kù)是無(wú)法運(yùn)行的, 都pull出來(lái)顯然不太現(xiàn)實(shí), 嚴(yán)重增加apk體積
  2. 不同系統(tǒng)庫(kù)在不同平臺(tái)會(huì)有差異, 導(dǎo)致表現(xiàn)會(huì)有差異

所以非系統(tǒng)App還是調(diào)用通用API吧, 當(dāng)然如果你的App只是針對(duì)一個(gè)平臺(tái)或者一個(gè)機(jī)型, 就當(dāng)我沒(méi)說(shuō)......

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秤涩,一起剝皮案震驚了整個(gè)濱河市疾忍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌书斜,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件华坦,死亡現(xiàn)場(chǎng)離奇詭異撮躁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)捺氢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)藻丢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人摄乒,你說(shuō)我怎么就攤上這事悠反。” “怎么了馍佑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵斋否,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拭荤,道長(zhǎng)茵臭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任穷劈,我火速辦了婚禮笼恰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歇终。我一直安慰自己社证,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布评凝。 她就那樣靜靜地躺著追葡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奕短。 梳的紋絲不亂的頭發(fā)上宜肉,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音翎碑,去河邊找鬼谬返。 笑死,一個(gè)胖子當(dāng)著我的面吹牛日杈,可吹牛的內(nèi)容都是我干的遣铝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼莉擒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼酿炸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起涨冀,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤填硕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后鹿鳖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扁眯,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡壮莹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恋拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垛孔。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖施敢,靈堂內(nèi)的尸體忽然破棺而出周荐,到底是詐尸還是另有隱情,我是刑警寧澤僵娃,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布概作,位于F島的核電站,受9級(jí)特大地震影響默怨,放射性物質(zhì)發(fā)生泄漏讯榕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一匙睹、第九天 我趴在偏房一處隱蔽的房頂上張望愚屁。 院中可真熱鬧,春花似錦痕檬、人聲如沸霎槐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)丘跌。三九已至,卻和暖如春唁桩,著一層夾襖步出監(jiān)牢的瞬間闭树,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工荒澡, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留报辱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓单山,卻偏偏與公主長(zhǎng)得像捏肢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饥侵,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容