2011-03-11 65 views
30

我已經做了一些谷歌搜索,並找不到有關此格式的足夠信息。這是相機預覽的默認格式。任何人都可以提出有關它的良好信息來源以及如何從該照片格式中提取數據/預覽圖像?更具體地說,我需要提取黑白圖像。從安卓相機的NV21格式中提取黑白圖像

編輯:好像該格式也被稱爲420的YCbCr半平面

+0

從Android API級別17開始,最好的解決方案是使用內在的RenderScript - ScriptIntrinsicYuvToRGB。這個RenderScript是內置的,所以你不必編寫一行類C代碼。看到這個問題的詳細信息:[如何使用ScriptIntrinsicYuvToRGB轉換yuv到rgba](http://stackoverflow.com/q/20358803/377657) – rwong

回答

4

數據爲YUV420格式。
如果您只對單色通道感興趣,即「黑白」,那麼這是您已有的第一個width x height字節的數據緩衝區。
Y通道是第一個圖像平面。這正是灰色/強度/光度等渠道。

12

NV21基本上是YUV420,但是對於Y,U和V有獨立平面的平面格式,NV21對於Luma有1個平面,Chroma有2個平面。格式看起來像

YYYYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYYYYYYY 。 。 。 。

VUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVU VUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVU 。 。 。 。 。

+0

好的解釋 – WillC

79

我開發了以下代碼將NV21轉換爲RGB,並且它正在工作。

/** 
* Converts YUV420 NV21 to RGB8888 
* 
* @param data byte array on YUV420 NV21 format. 
* @param width pixels width 
* @param height pixels height 
* @return a RGB8888 pixels int array. Where each int is a pixels ARGB. 
*/ 
public static int[] convertYUV420_NV21toRGB8888(byte [] data, int width, int height) { 
    int size = width*height; 
    int offset = size; 
    int[] pixels = new int[size]; 
    int u, v, y1, y2, y3, y4; 

    // i percorre os Y and the final pixels 
    // k percorre os pixles U e V 
    for(int i=0, k=0; i < size; i+=2, k+=2) { 
     y1 = data[i ]&0xff; 
     y2 = data[i+1]&0xff; 
     y3 = data[width+i ]&0xff; 
     y4 = data[width+i+1]&0xff; 

     u = data[offset+k ]&0xff; 
     v = data[offset+k+1]&0xff; 
     u = u-128; 
     v = v-128; 

     pixels[i ] = convertYUVtoRGB(y1, u, v); 
     pixels[i+1] = convertYUVtoRGB(y2, u, v); 
     pixels[width+i ] = convertYUVtoRGB(y3, u, v); 
     pixels[width+i+1] = convertYUVtoRGB(y4, u, v); 

     if (i!=0 && (i+2)%width==0) 
      i+=width; 
    } 

    return pixels; 
} 

private static int convertYUVtoRGB(int y, int u, int v) { 
    int r,g,b; 

    r = y + (int)(1.402f*v); 
    g = y - (int)(0.344f*u +0.714f*v); 
    b = y + (int)(1.772f*u); 
    r = r>255? 255 : r<0 ? 0 : r; 
    g = g>255? 255 : g<0 ? 0 : g; 
    b = b>255? 255 : b<0 ? 0 : b; 
    return 0xff000000 | (b<<16) | (g<<8) | r; 
} 

這張圖片有助於瞭解。 YUV420 NV21

如果你想只是灰度圖像是easer。你可以丟棄所有的U和V信息,只取Y信息。該代碼將可以是這樣的:

/** 
* Converts YUV420 NV21 to Y888 (RGB8888). The grayscale image still holds 3 bytes on the pixel. 
* 
* @param pixels output array with the converted array o grayscale pixels 
* @param data byte array on YUV420 NV21 format. 
* @param width pixels width 
* @param height pixels height 
*/ 
public static void applyGrayScale(int [] pixels, byte [] data, int width, int height) { 
    int p; 
    int size = width*height; 
    for(int i = 0; i < size; i++) { 
     p = data[i] & 0xFF; 
     pixels[i] = 0xff000000 | p<<16 | p<<8 | p; 
    } 
} 

只創建位圖:

Bitmap bm = Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); 

像素哪裏是你的INT []數組。

+0

如何從byte []數據中提取Cr通道? –

+0

@YuriyChernyshov假設YUV = YCrCb,您可以提取Cr通道,調整方法convertYUV420_NV21toRGB8888,並將U值放在數組上。但爲什麼你只想要Cr?請注意,U和V是交錯的,圖片中的第一個U1與Y1,Y2,Y7,Y8一起使用。 – Derzu

+0

Derzu,我開始學習交通標誌檢測。第一步是採用Cr通道並開始處理它 - 使用兩個閾值自適應地閾值化,以提供對紅色場景組件的穩健隔離。 –

3

這裏的代碼只提取灰度圖像數據:

private int[] decodeGreyscale(byte[] nv21, int width, int height) { 
    int pixelCount = width * height; 
    int[] out = new int[pixelCount]; 
    for (int i = 0; i < pixelCount; ++i) { 
     int luminance = nv21[i] & 0xFF; 
     out[i] = Color.argb(0xFF, luminance, luminance, luminance); 
    } 
    return out; 
} 
+0

Sam我怎麼能從nv21中刪除紅色和藍色。我想讓我的形象變成綠色(夜視效果)。 – Nepster

+0

@Nepster,我不確定我的頭頂。通過在上面的代碼中做出這種改變,你可以使用亮度作爲綠色值:'out [i] = Color.argb(0xFF,0,luminance,0);'。或者,也許你可以改變全色實現的代碼來丟棄藍色和紅色的信息。我認爲你應該爲此創建一個新的StackOverflow問題。 – Sam

+0

它適用於山姆。非常感謝。但是,當我打開前攝像頭。它垂直旋轉圖像(正面朝下) – Nepster

1

當你只需要一個灰度相機預覽,你可以使用一個非常簡單的 的renderScript:

# pragma version(1) 
# pragma rs java_package_name(com.example.name) 
# pragma rs_fp_relaxed 

rs_allocation gIn; // Allocation filled with camera preview data (byte[]) 
int previewwidth; // camera preview width (int) 

// the parallel executed kernel 
void root(uchar4 *v_out, uint32_t x,uint32_t y){ 
    uchar c = rsGetElementAt_uchar(gIn,x+y*previewwidth); 
    *v_out = (uchar4){c,c,c,255}; 
} 

注:這是不是快於ScriptIntrinsicYuvToRGB(以及ScriptIntrinsicColorMatrix執行RGBA->灰色),但是 它使用API​​ 11+運行(其中Intrinsics需要Api 17+)。