2014-11-22 144 views
5

我遇到一個奇怪的問題,使用Android模擬器& OpenCV CameraBridgeViewBaseOpenCV Android - 顏色問題使用CameraBridgeViewBase

使用onCameraFrame我得到的圖片,看起來像它不能正確解碼。

public Mat onCameraFrame(CvCameraViewFrame inputFrame) { 
    return inputFrame.rgba(); 
} 

使用「inputFrame.gray()」我得到什麼的期待 - 沒有任何瑕疵或其他問題的黑白圖像。

這就是我得到:

First picture

One more picture (a bigger one)

我試過到目前爲止:

  1. 不同的API級別(15至21)。
  2. 不同的模擬器:Genymotion &谷歌Android模擬器。
  3. 不同的平臺架構 - 包括ARM和Intel x86。
  4. 在Linux的不同筆記本電腦上啓動模擬器:它按預期工作,問題不復存在!
  5. 從Play商店下載使用OpenCV啓動應用程序。他們工作! 但是:
    1. 啓動按預期工作的應用程序,然後關閉它。
    2. 啓動您的應用程序(或OpenCV教程之一),然後關閉它。
    3. 從5.1再次啓動應用程序我發現它受到相同錯誤的影響!
  6. 不同的OpenCV版本(2.4.9和2.4.10)。
  7. 不同版本OpenCV的經理(一個從Play商店和OpenCV的包2.4.9 & 2.4.10)。
  8. 最後,正如我在5.2注意到,來自OpenCV的包裝預編譯教程的apk文件是由問題也受到影響。

一切都按預期在我的真實Android設備上工作。

查看CameraBridgeViewBase和Java/Native相機類的來源後,我決定在解碼圖像時出現問題。平臺相關的輸出格式(YUV,NV21)可能存在問題。然而,奇怪的是.gray()給出了一個合適的圖像(沒有工件)。

我使用的是Mac OS X的10.10優勝美地和MacBook Air用 「的Facetime HD」 的相機,如果該事項。

任何想法如何克服這個問題&幫助找到問題的根源,非常感謝!

回答

7

所以,鑽入問題後,我已經找到了問題的根源。

讓我們來看看OpenCV JavaCameraView類和它的CameraBridgeViewBase基類。 問題在於,在onPreviewFrame方法中收到的byte[]數組中的相機幀被錯誤地解碼。

的代碼的確切地方,解碼過程發生是Mat rgba()法在內部JavaCameraFrameJavaCameraView的實現:

public Mat rgba() { 
     Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4); 
     return mRgba; 
    } 

正如我們看到的,Imgproc.cvtColor(...)方法用於將幀從YUV轉換爲RGBA。 NV21 YUV -> RGBA轉換髮生在那裏。在初始化過程中,我們將格式設置爲NV21,所以這應該是正確的。此外,every Android device should support NV21。另外,我們可以檢查設備是否接受使用調試器的格式爲:

protected boolean initializeCamera(int width, int height) { 
    ... 
    params.setPreviewFormat(ImageFormat.NV21); 
    ... 
    mCamera.setParameters(params); 
    ... 
    params = mCamera.getParameters(); 
    Log.d(TAG, String.format("Actual preview format is 0x%X", params.getPreviewFormat())); 
} 

兩個手機(HTC感覺)和模擬器報道爲使用確實NV21。但是,如果我們將COLOR_YUV2RGBA_NV21更改爲COLOR_YUV2RGB_I420(YV12和I420是相同的東西,只需將Y和V倒置;),我們將看到仿真器最終將獲得適當的顏色空間。在params.setPreviewFormat(ImageFormat.NV21);中將NV21更改爲YV12我們會得到類似的結果。看起來像是在Imgproc.cvtColor或Android中有bug。

這是解決方案。 更改public Mat rgba()方式如下:

public Mat rgba() { 
     if (previewFormat == ImageFormat.NV21) { 
      Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4); 
     } 
     else if (previewFormat == ImageFormat.YV12) { 
      Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGB_I420, 4); // COLOR_YUV2RGBA_YV12 produces inverted colors 
     } 
     return mRgba; 
    } 

previewFormat是一個新的int變量,它是這樣聲明:

private int previewFormat = ImageFormat.NV21; 

添加以下更改初始化:

protected boolean initializeCamera(int width, int height) { 
     ... 
       params.setPreviewFormat(ImageFormat.NV21); 
       // "generic" = android emulator 
       if (Build.BRAND.equalsIgnoreCase("generic")) { 
        params.setPreviewFormat(ImageFormat.YV12); 
       } 
       ... 
       mCamera.setParameters(params); 
       params = mCamera.getParameters(); 
       previewFormat = params.getPreviewFormat(); 
     ... 
} 

重要:
請注意:這只是一個臨時解決方案,以使OpenCV在我的情況下可以與仿真器一起使用。應該進一步研究。檢查設備是否在onPreviewFrame中使用正確的圖像格式很容易。當我有一些時間時,我會回到這個。

+1

使用Android Studio 1.5.1並模擬API 23時,上述僅在''generic''被交換到'「android」'時才起作用。儘管如此,改變之後的工作仍然完美無瑕。 – 2016-01-26 19:55:20

+0

我只是試過這個,它工作正常。雖然,我不確定這是否是首選的方法,或者應該以不同的方式修復。我在OpenCV存儲庫中創建了一個問題,如果接受它,可以將其作爲請求提交。 – 2017-02-09 23:24:10

+1

爲了可追溯性:https://github.com/opencv/opencv/issues/8166 – 2017-02-14 01:26:16

0

對於那些使用genymotion的人,BRAND不會被稱爲通用。 爲了解決這個只是簡單的代碼更改爲

... 
if (Build.BRAND.equalsIgnoreCase("generic") | Build.BRAND.equalsIgnoreCase("Android")) { 
... 

,它應該工作,或只是打印輸出您正在使用的品牌,取代它的「Android」的

PS第二個條件將包括案件你使用的genymotion也是一個模擬器。

乾杯

+0

也許我應該進一步解釋,如果您使用的genymotion是仿真器並且通常由某些人使用,則第二個「或條件」將會覆蓋。 – PDragon 2016-02-20 03:47:53

+0

請注意,條件應爲||而不是| – 2017-02-09 22:20:55