Problèmes lors de la mise à l’échelle d’une image YUV à l’aide de la bibliothèque libyuv

Je développe une application pour appareil photo basée sur Camera API 2 et j’ai constaté plusieurs problèmes d’utilisation de libyuv . Je souhaite convertir des images YUV_420_888 récupérées à partir d’un ImageReader, mais je rencontre des problèmes de mise à l’échelle dans une surface retraitable.

Essentiellement: les images apparaissent avec des tons de vert au lieu d’avoir les tons correspondants (j’exporte les fichiers .yuv et je les vérifie à l’aide de http://rawpixels.net/ ).

Vous pouvez voir un exemple d’entrée ici: entrez la description de l'image ici

Et ce que je reçois après avoir effectué la mise à l’échelle entrez la description de l'image ici

Je pense que je fais quelque chose de mal avec les progrès, ou de fournir un format YUV non valide (peut-être que je dois transformer l’image en un autre format?). Cependant, je ne peux pas savoir où se trouve l’erreur car je ne sais pas comment corréler la couleur verte à l’algorithme de mise à l’échelle.

C’est le code de conversion que j’utilise, vous pouvez ignorer le retour NULL car il existe un traitement supplémentaire qui n’est pas lié au problème.

 #include  #include  #include  #include  #include  #include  #define LOG_TAG "libyuv-jni" #define unused(x) UNUSED_ ## x __atsortingbute__((__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; } 

Vous pouvez essayer que le code utilise la y_size au lieu de la taille complète de votre tableau.

  ... //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; ... 

probablement votre code est basé sur ce https://github.com/begeekmyfriend/yasea/blob/master/library/src/main/libenc/jni/libenc.cc et vous devez utiliser le y_size

Vous avez un problème avec la taille d’entrée du cadre:

CA devrait etre:

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

Par exemple, si vous avez un cadre 6×4

Taille Chanel: 6 * 4 = 24

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

Taille Chanel: 3 * 2 = 6

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

Chanel taille v : 3 * 2 = 6

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

Taille du tableau = 6 * 4 + 3 * 2 + 3 * 2 = 36
Mais la taille de l’image réelle = taille du canal y = 36 * 2/3 = 24

gmetax est presque correct.

Vous utilisez la taille de l’ensemble du tableau où vous devriez utiliser la taille du composant Y, qui est src_width * src_height .

La réponse de gmetax est fausse car il a mis y_size à la place de out_size lors de la définition du cadre de sortie. Je crois que l’extrait de code correct ressemblera à ceci:

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

Vous essayez de redimensionner votre image YUV422 comme si c’était YUV420, il n’est donc pas étonnant que les couleurs soient toutes foirées. Tout d’abord, vous devez déterminer le format exact de votre tampon d’entrée YUV. D’après la documentation de YUV_422_888, il semble qu’il s’agisse de formats plans ou entrelacés (si la largeur de pixel n’est pas égale à 1). D’après vos résultats, il semble que votre source soit planaire et que le traitement du plan Y soit correct, mais votre erreur est liée à la gestion des plans U et V. Pour réussir la mise à l’échelle:

  • Vous devez déterminer si vos plans U et V sont entrelacés ou plans. Très probablement, ils sont également planes.
  • Utilisez ScalePlane de libyuv pour mettre à l’échelle U et V séparément. Peut-être que si vous I420Scale dans I420Scale il appelle ScalePlane pour des avions individuels. Faites de même, mais utilisez des lignes de lignes correctes pour vos plans U et V (chacune est deux fois plus grande que ce à quoi I420Scale s’attend).

Quelques conseils pour savoir si vous avez des U et V plans ou entrelacés: essayez de ne pas redimensionner votre image et de l’enregistrer, pour vous assurer d’obtenir un résultat correct (identique à la source). Ensuite, essayez de mettre à zéro le cadre U ou V et voyez ce que vous obtenez. Si U et V sont plans et que vous remettez à zéro le plan U , l’image entière changera de couleur. S’ils sont entrelacés, la moitié de l’image change et l’autre rest inchangée. De la même manière, vous pouvez vérifier vos hypothèses sur les tailles, les tailles de ligne et les décalages de vos avions. Une fois que vous êtes sûr de votre format et de votre mise en page YUV, vous pouvez mettre à l’échelle des plans individuels si votre entrée est plane, ou si vous avez d’abord une entrée entrelacée, vous devez désentrelacer les plans puis les redimensionner.

Sinon, vous pouvez utiliser libswscale à partir de ffmpeg / libav et essayer différents formats pour en trouver un, puis utiliser libyuv.