2013-06-19 6 views
7

J'essaye de redimensionner (redimensionner) une image qui vient au format YUV420sp. Est-il possible de faire un tel redimensionnement d'image sans le convertir en RVB, manipulant ainsi directement le réseau de pixels YUV420sp? Où puis-je trouver un tel algorithme?Redimensionner (downsize) YUV420sp image

Merci

Répondre

13

YUV 4: 2: 0 plan ressemble à ceci:

---------------------- 
|  Y  | Cb|Cr | 
---------------------- 

où:

Y = width x height pixels 
Cb = Y/4 pixels 
Cr = Y/4 pixels 

Total num pixels (bytes) = width * height * 3/2 

Et le subsamling utilisé comme ceci:

420

Cela signifie que chaque valeur chroma-pixel est partagée entre 4 luma-pixels. Une approche consiste simplement à supprimer des pixels, en s'assurant que la relation Y-Cb-Cr correspondante est conservée/recalculée.

Quelque chose proche du Nearest-neighbor interpolation mais inversé.

Une autre approche est d'abord convertir 4: 2: 0 à 4 sous-échantillonnage: 4: 4

444

Ici, vous avez 1 à 1 entre les données luma et chroma.

C'est la bonne façon d'interpoler la chrominance entre 4: 2: 0 et 4: 2: 2 (la luma est déjà à la résolution correcte) Code en python, suivez le lien html pour c-dito. Le code n'est pas très pythonique, juste une traduction directe de la version c.

def __conv420to422(self, src, dst): 
    """ 
    420 to 422 - vertical 1:2 interpolation filter 

    Bit-exact with 
    http://www.mpeg.org/MPEG/video/mssg-free-mpeg-software.html 
    """ 
    w = self.width >> 1 
    h = self.height >> 1 

    for i in xrange(w): 
     for j in xrange(h): 
      j2 = j << 1 
      jm3 = 0 if (j<3) else j-3 
      jm2 = 0 if (j<2) else j-2 
      jm1 = 0 if (j<1) else j-1 
      jp1 = j+1 if (j<h-1) else h-1 
      jp2 = j+2 if (j<h-2) else h-1 
      jp3 = j+3 if (j<h-3) else h-1 

      pel = (3*src[i+w*jm3] 
       -16*src[i+w*jm2] 
       +67*src[i+w*jm1] 
       +227*src[i+w*j] 
       -32*src[i+w*jp1] 
        +7*src[i+w*jp2]+128)>>8 

      dst[i+w*j2] = pel if pel > 0 else 0 
      dst[i+w*j2] = pel if pel < 255 else 255 

      pel = (3*src[i+w*jp3] 
       -16*src[i+w*jp2] 
       +67*src[i+w*jp1] 
       +227*src[i+w*j] 
       -32*src[i+w*jm1] 
       +7*src[i+w*jm2]+128)>>8 

      dst[i+w*(j2+1)] = pel if pel > 0 else 0 
      dst[i+w*(j2+1)] = pel if pel < 255 else 255 
    return dst 

Exécutez cette opération deux fois pour obtenir 4: 4: 4. Ensuite, il suffit de supprimer les lignes et les colonnes. Vous pouvez également quadrupler les chroma-pixels pour passer de 4: 2: 0 à 4: 4: 4, supprimer les lignes et les colonnes, puis calculer 4 valeurs Cb/Cr en 1 pour revenir à 4: 2: 0 encore une fois, tout dépend de la rigueur dont vous avez besoin :-)

+0

Vous n'avez pas besoin d'aller à 4: 4: 4 et de revenir à 4: 2: 0. Cette dernière étape ré-échantillonnera l'image et diminuera encore la qualité. Il suffit de diviser les couleurs: http://stackoverflow.com/questions/17187193/resize-downsize-yuv420sp-image/30659193#30659193 –

7

Voici une fonction Java que j'utilise pour réduire un YUV 420 (ou NV21) d'un facteur deux. La fonction prend l'image dans un tableau d'octets avec la largeur et la hauteur de l'image originale en entrée et renvoie une image dans un tableau d'octets dont la largeur et la hauteur sont égales à la moitié de la largeur et de la hauteur d'origine .

Comme base de mon code je ceci: Rotate an YUV byte array on Android

public static byte[] halveYUV420(byte[] data, int imageWidth, int imageHeight) { 
    byte[] yuv = new byte[imageWidth/2 * imageHeight/2 * 3/2]; 
    // halve yuma 
    int i = 0; 
    for (int y = 0; y < imageHeight; y+=2) { 
     for (int x = 0; x < imageWidth; x+=2) { 
      yuv[i] = data[y * imageWidth + x]; 
      i++; 
     } 
    } 
    // halve U and V color components 
    for (int y = 0; y < imageHeight/2; y+=2) { 
     for (int x = 0; x < imageWidth; x += 4) { 
      yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + x]; 
      i++; 
      yuv[i] = data[(imageWidth * imageHeight) + (y * imageWidth) + (x + 1)]; 
      i++; 
     } 
    } 
    return yuv; 
} 
1

YUV420sp a Y dans un plan et le U & V dans un autre. Si vous divisez le U & V en plans distincts, vous pouvez ensuite effectuer la même opération de mise à l'échelle sur chacun des trois plans, sans devoir d'abord passer de 4: 2: 0 -> 4: 4: 4.

Regardez le code source de libyuv; il ne fait que mettre à l'échelle les plans: https://code.google.com/p/libyuv/source/browse/trunk/source/scale.cc