27 votes

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

Je suis le développement d'une application de caméra basée sur Camera API 2 et j'ai trouvé plusieurs problèmes à l'aide de la libyuv. Je veux convertir YUV_420_888 images récupérées à partir d'un ImageReader, mais je vais avoir quelques problèmes avec la mise à l'échelle reprocessable surface.

En substance: les Images de sortir avec des tons de vert au lieu d'avoir le correspondant tons (je suis de l'exportation de la .yuv fichiers et à les vérifier à l'aide de http://rawpixels.net/).

Vous pouvez voir une entrée exemple ici: enter image description here

Et ce que je reçois après je effectuer la mise à l'échelle: enter image description here

Je pense que je suis en train de faire quelque chose de mal avec le progrès, ou de fournir un invalide format YUV (j'ai peut-être de transformer l'image dans un autre format?). Cependant, je ne vois pas où est l'erreur car je ne sais pas comment corréler la couleur verte à l'échelle de l'algorithme.

C'est la conversion de code, je suis en utilisant, vous pouvez ignorer le retour NULL comme il n'y a plus de traitement qui n'est pas lié au problème.

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

1voto

gmetax Points 2357

Vous pouvez essayer ce code qu'il utilise le 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;
    ...
 

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

1voto

Rama Points 2869

Vous avez un problème avec la taille de l'image:

Il devrait être:

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 une Image qui est en 6x4

Chanel y taille: 6*4 = 24

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

Chanel u taille: 3*2 = 6

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

Chanel v taille: 3*2 = 6

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

La Taille Du Tableau = 6*4+3*2+3*2 = 36
Mais la Taille de l'Image = canal y Taille = 36 * 2 / 3 = 24

0voto

Dave Points 2391

gmetax est presque correct.

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

La réponse de gmetax est fausse en ce qu'il a mis y_size à la place de out_size lors de la définition de la trame de sortie. Je pense que l'extrait de code correct ressemblerait à 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;
 

0voto

Pavel Points 1715

Vous essayez de redimensionner votre YUV422 image comme si elle était YUV420, pas étonnant que les couleurs sont tout foiré. Tout d'abord, vous devez comprendre ce qu'est exactement le format de votre YUV mémoire tampon d'entrée. À partir de la documentation de YUV_422_888 il ressemble, il peut représenter planes et entrelacés formats (si pixel foulée n'est pas 1). À partir de vos résultats, il ressemble à la source est planaire et de la transformation de Y plan est ok, mais ton erreur est dans la manipulation de U et V des avions. Pour obtenir de mise à l'échelle à droite:

  • Vous devez comprendre que si votre U et V des avions sont entrelacés ou planes. Très probablement, ils sont planes et des.
  • Utiliser ScalePlane de libyuv à l'échelle U et V séparément. Peut-être si vous entrez dans I420Scale il appelle ScalePlane pour les individuels les avions. Faire la même chose, mais l'utilisation correcte linesizes pour votre U et V les avions (chacun est deux fois plus grande que ce que I420Scale attend).

Quelques conseils sur la manière de déterminer si vous avez planes ou entrelacé U et V: essayez de sauter de mise à l'échelle de votre image et de l'enregistrer, pour s'assurer que vous obtenez le résultat correct (identique à la source). Puis essayez à zéro U cadre ou V cadre et voir ce que vous obtenez. Si U et V sont planes et vous memset U avion à zéro, vous devriez voir l'image entière à changer de couleur. Si ils sont entrelacés, vous aurez la moitié de l'image change et l'autre qui reste le même. Même façon, vous pouvez vérifier vos hypothèses sur les tailles, les linesizes, et les décalages de vos avions. Une fois que vous êtes sûr de votre format YUV et la mise en page, vous pouvez mettre à l'échelle individuelle avions si votre entrée est planaire, ou si vous avez entrelacés d'entrée vous devez d'abord deinterleave avions et ensuite les mettre à l'échelle.

Alternativement, vous pouvez utiliser libswscale de ffmpeg/libav et essayer différents formats de trouver le bon un, puis l'utiliser libyuv.

0voto

Le vert des images a été causé par l'un des avions plein de 0. Cela signifie que l'un des avions était vide. Ceci a été causé parce que je a été la conversion de YUV NV21 au lieu de YUV I420. Les images à partir du cadre de la caméra dans android vient comme I420 YUVs.

Nous avons besoin de les convertir en YUV I420 pour fonctionner correctement avec Libyuv. Après cela, nous pouvons commencer à utiliser les multiples activités que la bibliothèque vous offrir. Comme la rotation, échelle, etc.

Voici la ciselée sur la façon dont la mise à l'échelle de la méthode ressemble:

JNIEXPORT jint JNICALL
Java_com_aa_project_images_yuv_myJNIcl_scaleI420(JNIEnv *env, jclass type,
                                                 jobject srcBufferY,
                                                 jobject srcBufferU,
                                                 jobject srcBufferV,
                                                 jint srcWidth, jint srcHeight,
                                                 jobject dstBufferY,
                                                 jobject dstBufferU,
                                                 jobject dstBufferV,
                                                 jint dstWidth, jint dstHeight,
                                                 jint filterMode) {

    const uint8_t *srcY = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferY));
    const uint8_t *srcU = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferU));
    const uint8_t *srcV = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferV));
    uint8_t *dstY = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferY));
    uint8_t *dstU = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferU));
    uint8_t *dstV = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferV));

    return libyuv::I420Scale(srcY, srcWidth,
                             srcU, srcWidth / 2,
                             srcV, srcWidth / 2,
                             srcWidth, srcHeight,
                             dstY, dstWidth,
                             dstU, dstWidth / 2,
                             dstV, dstWidth / 2,
                             dstWidth, dstHeight,
                             static_cast<libyuv::FilterMode>(filterMode));
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X