Подтвердить что ты не робот

Извлечь черно-белое изображение из камеры андроида NV21

Я сделал некоторые google-ing вокруг и не смог найти достаточно информации об этом формате. Это формат по умолчанию для предварительного просмотра камеры. Может ли кто-нибудь предлагать хорошие источники информации об этом и как извлекать данные из изображения фото/предварительного просмотра с этим форматом? Чтобы быть более конкретным, мне нужно получить черно-белое изображение.

EDIT: Похоже, этот формат также называется полуплоскостью YCbCr 420

4b9b3361

Ответ 1

Я разработал следующий код для преобразования 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

Если вы хотите, чтобы только изображение в оттенках серого было более простым. Вы можете отбросить всю информацию 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 [].

Ответ 2

NV21 в основном YUV420, но вместо планарного формата, где Y, U и V имеют независимые плоскости, NV21 имеет 1 плоскость для Luma и 2-й плоскости для Chroma. Формат выглядит как

YYYYYYYYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYYYYYYY , , , .

VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU VUVUVUVUVUVUVUVUVUVUVUVUVUVUVU , , , , .

Ответ 3

У меня также было много головной боли из-за этого формата предварительного просмотра.

Лучшее, что я могу найти, это:

http://www.fourcc.org/yuv.php#NV21

http://v4l2spec.bytesex.org/spec/r5470.htm

Кажется, что Y-компонент - это первая ширина * высота байтов int, которую вы получаете.

Несколько дополнительных информационных ссылок:

http://msdn.microsoft.com/en-us/library/ms867704.aspx#yuvformats_yuvsampling

http://msdn.microsoft.com/en-us/library/ff538197(v=vs.85).aspx

Надеюсь, что это поможет.

Ответ 4

Данные находятся в формате YUV420.
Если вас интересует только монохромный канал, т.е. "Черный и белый", то это первые width x height байты буфера данных, который у вас уже есть.
Канал Y - это первая плоскость изображения. Это ровно канал серой/интенсивности/яркости и т.д.

Ответ 5

Здесь код просто извлекает данные изображения в шкале серого:

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;
}

Ответ 6

Когда вам нужен только предварительный просмотр камеры в оттенках серого, вы можете использовать очень простой 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 +).