2013-06-19 6 views
7

Ich versuche, ein Bild zu skalieren (verkleinern), das im YUV420sp Format kommt. Ist es möglich, eine solche Bildgrößenanpassung durchzuführen, ohne sie in RGB zu konvertieren, so dass das YUV420sp-Pixel-Array direkt manipuliert wird? Wo kann ich einen solchen Algorithmus finden?Größe ändern (verkleinern) YUV420sp Bild

Dank

Antwort

13

YUV 4: 2: 0 planar sieht wie folgt aus:

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

wo:

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

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

Und die subsamling so benutzten:

420

Das bedeutet, dass jeder Chroma-Pixel-Wert von 4 Luma-Pixeln geteilt wird.

Ein Ansatz besteht darin, nur Pixel zu entfernen und sicherzustellen, dass die entsprechende Y-Cb-Cr-Beziehung beibehalten/neu berechnet wird.

Etwas in der Nähe der Nearest-neighbor interpolation aber umgekehrt.

Ein weiterer Ansatz besteht darin, zunächst das 4 zu konvertieren: 2: 0 Subsampling auf 4: 4: 4

444

Hier haben Sie eine 1 bis 1 Zuordnung zwischen Luma und Chroma-Daten.

Dies ist der richtige Weg, Chroma zwischen 4: 2: 0 und 4: 2: 2 zu interpolieren (Luma hat bereits die richtige Auflösung) Code in Python, folgen Sie HTML-Link für C-Dito. Code ist nicht sehr pythonisch, nur eine direkte Übersetzung der c-Version.

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 

Führen Sie dies zweimal aus, um 4: 4: 4 zu erhalten. Dann ist es nur eine Frage des Entfernens von Zeilen und Spalten.

Oder Sie können einfach die Chroma-Pixel vervierfachen, um von 4: 2: 0 bis 4: 4: 4 zu gehen, Zeilen und Spalten entfernen und dann 4 Cb/Cr-Werte in 1 um 4: 2 zu erhalten: 0 wieder, es hängt alles davon ab, wie streng Sie sein müssen :-)

+0

Sie müssen nicht zu 4: 4: 4 und zurück zu 4: 2: 0 gehen. In diesem letzten Schritt wird das Bild erneut unterabgetastet und die Qualität weiter verringert. Teilen Sie einfach die Colourplanes: http://stackoverflow.com/questions/17187193/resize-downsize-yuv420sp-image/30659193#30659193 –

7

Hier ist eine Java-Funktion, die ich verwenden, um eine YUV 420 (oder NV21) um einen Faktor von zwei zu verkleinern.

Die Funktion nimmt das Bild in einem Bytearray zusammen mit der Breite und Höhe des ursprünglichen Bildes als Eingabe und gibt ein Bild in einem Bytearray zurück, das Breite und Höhe gleich der Hälfte der ursprünglichen Breite und Höhe hat .

Als Grundlage für meinen Code I benutzen: 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 hat das Y in einer Ebene und die U & V in einem anderen. Wenn Sie die U & V in separate Ebenen aufteilen, können Sie die gleiche Skalierung für jede der 3 Ebenen der Reihe nach durchführen, ohne vorher von 4: 2: 0 -> 4: 4: 4 zu gehen.

Werfen Sie einen Blick auf den Quellcode für libyuv; Es skaliert nur die Ebenen: https://code.google.com/p/libyuv/source/browse/trunk/source/scale.cc