2017-03-22 86 views
27

Zajmuję się tworzeniem aplikacji kamery opartej na Camera API 2 i znalazłem kilka problemów przy użyciu libyuv. Chcę konwertować obrazy YUV_420_888 pobrane z ImageReadera, ale mam pewne problemy ze skalowaniem na powierzchni podlegającej powtórnemu przetworzeniu.Problemy podczas skalowania obrazu YUV przy użyciu biblioteki libyuv

W skrócie: obrazy wychodzą z tonami zielonymi zamiast odpowiadających im dźwięków (eksportuję pliki .yuv i sprawdzam je za pomocą http://rawpixels.net/).

można zobaczyć przykład wejściowego tutaj: enter image description here

i co ja dostać po tym, jak wykonać skalowanie: enter image description here

Chyba robię coś złego z krokami, lub dostarczanie nieprawidłowy format YUV (może ja trzeba przekształcić obraz na inny format?). Nie mogę jednak ustalić, gdzie jest błąd, ponieważ nie wiem, jak skorelować zielony kolor z algorytmem skalowania.

To jest kod konwersji, którego używam, możesz zignorować zwrot NULL, ponieważ jest dalsze przetwarzanie, które nie jest związane z problemem.

#include <jni.h> 
#include <stdint.h> 
#include <android/log.h> 
#include <inc/libyuv/scale.h> 
#include <inc/libyuv.h> 
#include <stdio.h> 


#define LOG_TAG "libyuv-jni" 

#define unused(x) UNUSED_ ## x __attribute__((__unused__)) 
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS_) 

struct YuvFrame { 
    int width; 
    int height; 
    uint8_t *data; 
    uint8_t *y; 
    uint8_t *u; 
    uint8_t *v; 
}; 

static struct YuvFrame i420_input_frame; 
static struct YuvFrame i420_output_frame; 

extern "C" { 

JNIEXPORT jbyteArray JNICALL 
Java_com_android_camera3_camera_hardware_session_output_photo_yuv_YuvJniInterface_scale420YuvByteArray(
     JNIEnv *env, jclass /*clazz*/, jbyteArray yuvByteArray_, jint src_width, jint src_height, 
     jint out_width, jint out_height) { 

    jbyte *yuvByteArray = env->GetByteArrayElements(yuvByteArray_, NULL); 

    //Get input and output length 
    int input_size = env->GetArrayLength(yuvByteArray_); 
    int out_size = out_height * out_width; 

    //Generate input frame 
    i420_input_frame.width = src_width; 
    i420_input_frame.height = src_height; 
    i420_input_frame.data = (uint8_t *) yuvByteArray; 
    i420_input_frame.y = i420_input_frame.data; 
    i420_input_frame.u = i420_input_frame.y + input_size; 
    i420_input_frame.v = i420_input_frame.u + input_size/4; 

    //Generate output frame 
    free(i420_output_frame.data); 
    i420_output_frame.width = out_width; 
    i420_output_frame.height = out_height; 
    i420_output_frame.data = new unsigned char[out_size * 3/2]; 
    i420_output_frame.y = i420_output_frame.data; 
    i420_output_frame.u = i420_output_frame.y + out_size; 
    i420_output_frame.v = i420_output_frame.u + out_size/4; 
    libyuv::FilterMode mode = libyuv::FilterModeEnum::kFilterBilinear; 

    int result = I420Scale(i420_input_frame.y, i420_input_frame.width, 
          i420_input_frame.u, i420_input_frame.width/2, 
          i420_input_frame.v, i420_input_frame.width/2, 
          i420_input_frame.width, i420_input_frame.height, 
          i420_output_frame.y, i420_output_frame.width, 
          i420_output_frame.u, i420_output_frame.width/2, 
          i420_output_frame.v, i420_output_frame.width/2, 
          i420_output_frame.width, i420_output_frame.height, 
          mode); 
    LOGD("Image result %d", result); 
    env->ReleaseByteArrayElements(yuvByteArray_, yuvByteArray, 0); 
    return NULL; 
} 
+0

Dla tego, co jest warte, pełny zielony obraz YUV prawdopodobnie oznacza, że ​​twoje "byteArray" jest pełne 0. –

+0

to nie jest, dane z 3 wejściowych i wyjściowych tetrad mają info: –

+0

@FranciscoDurdinGarcia czy próbowałeś mojej odpowiedzi? – gmetax

Odpowiedz

1

Można spróbować tego kodu, że używa y_size zamiast pełnego rozmiaru tablicy.

... 
    //Get input and output length 
    int input_size = env->GetArrayLength(yuvByteArray_); 
    int y_size = src_width * src_height; 
    int out_size = out_height * out_width; 

    //Generate input frame 
    i420_input_frame.width = src_width; 
    i420_input_frame.height = src_height; 
    i420_input_frame.data = (uint8_t *) yuvByteArray; 
    i420_input_frame.y = i420_input_frame.data; 
    i420_input_frame.u = i420_input_frame.y + y_size; 
    i420_input_frame.v = i420_input_frame.u + y_size/4; 

    //Generate output frame 
    free(i420_output_frame.data); 
    i420_output_frame.width = out_width; 
    i420_output_frame.height = out_height; 
    i420_output_frame.data = new unsigned char[out_size * 3/2]; 
    i420_output_frame.y = i420_output_frame.data; 
    i420_output_frame.u = i420_output_frame.y + out_size; 
    i420_output_frame.v = i420_output_frame.u + out_size/4; 
    ... 

prawdopodobnie Twój kod jest oparty na tym https://github.com/begeekmyfriend/yasea/blob/master/library/src/main/libenc/jni/libenc.cc i według tego kodu trzeba użyć y_size

0

gmetax jest prawie poprawne.

Używasz rozmiaru całej tablicy, w której powinieneś używać rozmiaru komponentu Y, który jest src_width * src_height.

Odpowiedź gmetax jest błędna, ponieważ wstawił y_size zamiast out_size podczas definiowania ramki wyjściowej.Kod poprawny fragment, wierzę, wyglądałby następująco:

//Get input and output length 
int input_size = env->GetArrayLength(yuvByteArray_); 
int y_size = src_width * src_height; 
int out_size = out_height * out_width; 

//Generate input frame 
i420_input_frame.width = src_width; 
i420_input_frame.height = src_height; 
i420_input_frame.data = (uint8_t *) yuvByteArray; 
i420_input_frame.y = i420_input_frame.data; 
i420_input_frame.u = i420_input_frame.y + y_size; 
i420_input_frame.v = i420_input_frame.u + y_size/4; 

//Generate output frame 
free(i420_output_frame.data); 
i420_output_frame.width = out_width; 
i420_output_frame.height = out_height; 
i420_output_frame.data = new unsigned char[out_size * 3/2]; 
i420_output_frame.y = i420_output_frame.data; 
i420_output_frame.u = i420_output_frame.y + out_size; 
i420_output_frame.v = i420_output_frame.u + out_size/4; 
+0

faktycznie masz zaksięgowaną odpowiedź 37 minut po tym, jak edytowałem część, w której się pomyliłem :) – gmetax

1

masz problem z wielkości wejściowej ramce:

Powinno być:

int input_array_size = env->GetArrayLength(yuvByteArray_); 
int input_size = input_array_size * 2/3; //This is the frame size 

Na przykład Jeśli masz klatkę 6x4

Chanel y rozmiar: 6 * 4 = 24

1 2 3 4 5 6 
_ _ _ _ _ _ 
|_|_|_|_|_|_| 1 
|_|_|_|_|_|_| 2 
|_|_|_|_|_|_| 3 
|_|_|_|_|_|_| 4 

Chanel U rozmiar 3 * 2 = 6

1 2 3 
_ _ _ _ _ _ 
| | | | 
|_ _|_ _|_ _| 1 
| | | | 
|_ _|_ _|_ _| 2 

Chanel V rozmiar 3 * 2 = 6

1 2 3 
_ _ _ _ _ _ 
| | | | 
|_ _|_ _|_ _| 1 
| | | | 
|_ _|_ _|_ _| 2 

Tablica Rozmiar = 6 * 4 + 3 * 2 + 3 * 2 = 36
Ale rzeczywisty rozmiar klatki = kanał y Rozmiar = 36 * 2/3 = 24

+0

, więc sugerujesz mu, aby użyj tego samego rozmiaru (y rozmiar), co już zasugerowaliśmy (ja i ​​Dave). – gmetax

+0

@ gmetax Zauważyłem, że ważne jest, aby pamiętać, że należy użyć rzeczywistego rozmiaru klatki, i aby zapewnić metodę obliczenia jej z rozmiaru tablicy . Metoda, którą zamierzasz obliczyć, jest inna, ale jakie jest prawidłowe rozwiązanie, zależy od potrzeb PO. Bądź cierpliwy. – Rama

0

Próbujesz przeskalować swój obraz YUV422 tak, jakby był YUV420, nic dziwnego, że wszystkie kolory są pomieszane. Przede wszystkim musisz dowiedzieć się, jaki dokładnie format twojego bufora wejściowego YUV. Z dokumentacji YUV_422_888 wygląda na to, że może ona reprezentować zarówno format płaski, jak i przepleciony (jeśli piksel nie jest równy 1). Z twoich wyników wygląda na to, że twoje źródło jest planarne i przetwarzanie płaszczyzny Y jest w porządku, ale twój błąd dotyczy obsługi samolotów U i V. Aby uzyskać skalowanie rację:

  • Musisz dowiedzieć się, czy U i V samoloty są przeplatane lub płaska. Najprawdopodobniej są również płaskie.
  • Użyj ScalePlane z libyuv do skali U i V oddzielnie. Być może , jeśli wkroczysz w numer I420Scale, wywoła on ScalePlane dla indywidualnych samolotów . Zrób to samo, ale używaj poprawnych linii dla swoich samolotów U i V (każda jest dwukrotnie większa niż oczekiwana przez I420Scale).

Kilka wskazówek, jak dowiedzieć się, jeśli masz płaskie lub przeplatane U i V: spróbować pominąć skalowanie obrazu i zapisanie go, aby upewnić się, że masz poprawny wynik (identyczny jak w źródle). Następnie spróbuj wyzerować ramkę U lub ramkę V i zobaczyć, co otrzymasz. Jeśli U i V są płaskie i wyzerujesz płaszczyznę U, powinieneś zobaczyć cały obraz zmieniający kolor. Jeśli są one przeplatane, otrzymasz połowę zdjęć zmieniających się, a druga pozostanie taka sama. W ten sam sposób możesz sprawdzić swoje założenia dotyczące rozmiarów, linii i przesunięć swoich samolotów. Po upewnieniu się co do formatu i układu YUV można skalować poszczególne płaszczyzny, jeśli dane wejściowe są planarne, lub jeśli dane wejściowe są przeplatane, należy rozplatać samoloty, a następnie przeskalować je.

Można również użyć libswscale z ffmpeg/libav i wypróbować różne formaty, aby znaleźć poprawny, a następnie użyć libyuv.