2013-06-19 26 views

Odpowiedz

13

YUV 4: 2: 0 płaska wygląda następująco:

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

gdzie:

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

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

A subsamling używane tak:

420

Oznacza to, że każda wartość chroma-piksela jest dzielona między 4 luma-piksele.

Jednym ze sposobów jest usunięcie pikseli, upewniając się, że odpowiednia relacja Y-Cb-Cr jest zachowywana/przeliczana ponownie.

Coś bliskiego Nearest-neighbor interpolation, ale odwrócone.

Innym podejściem jest najpierw przekonwertować 4: 2: 0 subsampling do 4: 4: 4

444

Tutaj masz 1 do 1 mapowanie między luminancji i chrominancji danych.

To jest poprawny sposób interpolacji kolorów między 4: 2: 0 i 4: 2: 2 (luma ma już poprawną rozdzielczość) Kod w python, wykonaj html-link dla c-dito. Kod nie jest bardzo pythonic, tylko bezpośrednie tłumaczenie wersji 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 

Uruchom to dwukrotnie, aby uzyskać 4: 4: 4. To tylko kwestia usuwania wierszy i kolumn.

Możesz też czterokrotnie powiększyć chromosomaty z 4: 2: 0 do 4: 4: 4, usunąć wiersze i kolumny, a następnie średnią wartość 4 Cb/Cr na 1, aby wrócić do 4: 2: 0 znowu, wszystko zależy od tego, jak surowo musisz być :-)

+0

Nie musisz iść do 4: 4: 4 iz powrotem do 4: 2: 0. Ten ostatni krok spowoduje ponowne podpróbkowanie obrazu i dalsze obniżenie jego jakości. Po prostu podziel kolorowe kolory: http://stackoverflow.com/questions/17187193/resize-downsize-yuv420sp-image/30659193#30659193 –

7

Oto funkcja Java, której używam do zmniejszenia YUV 420 (lub NV21) o współczynnik równy dwa.

Funkcja pobiera obraz w tablicy bajtów wraz z szerokością i wysokością oryginalnego obrazu jako dane wejściowe i zwraca obraz w tablicy bajtów, która ma szerokość i wysokość równą połowie oryginalnej szerokości i wysokości .

Jako podstawę dla mojego kodu użyłem to: 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 ma Y w jednej płaszczyźnie i U & V w innym. Jeśli podzielisz U & V na oddzielne płaszczyzny, możesz następnie wykonać tę samą operację skalowania na każdej z 3 płaszczyzn po kolei bez konieczności przejścia od 4: 2: 0 -> 4: 4: 4.

Zobacz kod źródłowy dla libyuv; to tylko skaluje płaszczyzny: https://code.google.com/p/libyuv/source/browse/trunk/source/scale.cc