Intervalo de imagen

Cuando se almacena una imagen de vídeo en la memoria, el búfer de memoria puede contener bytes de relleno adicionales después de cada fila de píxeles. Los bytes de relleno afectan a cómo se almacena la imagen en la memoria, pero no afectan a cómo se muestra la imagen.

Stride es el número de bytes de una fila de píxeles en memoria a la siguiente fila de píxeles en memoria. Stride también se denomina pitch. Si hay bytes de relleno presentes, stride es más ancho que el ancho de la imagen, como se muestra en la ilustración siguiente.

diagram showing an image plus padding.

Dos búferes que contienen fotogramas de vídeo con dimensiones iguales pueden tener dos strides diferentes. Si procesa una imagen de vídeo, debe tener en cuenta el stride.

Además, hay dos maneras de organizar una imagen en memoria. En una imagen de arriba abajo, la fila superior de píxeles de la imagen aparece primero en la memoria. En una imagen de abajo arriba, la última fila de píxeles aparece primero en la memoria. En la ilustración siguiente se muestra la diferencia entre una imagen de arriba abajo y una imagen de abajo arriba.

diagram showing top-down and bottom-up images.

Una imagen de abajo arriba tiene un stride negativo, ya que el stride se define como el número de bytes necesarios para bajar una fila de píxeles, en relación con la imagen mostrada. Las imágenes YUV siempre deben ser de arriba abajo y cualquier imagen contenida en una superficie de Direct3D debe ser de arriba abajo. Las imágenes RGB en la memoria del sistema suelen ser de abajo arriba.

Las transformaciones de vídeo en particular necesitan controlar los búferes con strides no coincidentes, ya que es posible que el búfer de entrada no coincida con el búfer de salida. Por ejemplo, supongamos que desea convertir una imagen de origen y escribir el resultado en una imagen de destino. Supongamos que ambas imágenes tienen el mismo ancho y alto, pero es posible que no tengan el mismo formato de píxel o el mismo stride de imagen.

El código de ejemplo siguiente muestra un enfoque generalizado para escribir este tipo de función. Este no es un ejemplo funcional completo, ya que abstrae muchos de los detalles específicos.

void ProcessVideoImage(
    BYTE*       pDestScanLine0,     
    LONG        lDestStride,        
    const BYTE* pSrcScanLine0,      
    LONG        lSrcStride,         
    DWORD       dwWidthInPixels,     
    DWORD       dwHeightInPixels
    )
{
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        SOURCE_PIXEL_TYPE *pSrcPixel = (SOURCE_PIXEL_TYPE*)pSrcScanLine0;
        DEST_PIXEL_TYPE *pDestPixel = (DEST_PIXEL_TYPE*)pDestScanLine0;

        for (DWORD x = 0; x < dwWidthInPixels; x +=2)
        {
            pDestPixel[x] = TransformPixelValue(pSrcPixel[x]);
        }
        pDestScanLine0 += lDestStride;
        pSrcScanLine0 += lSrcStride;
    }
}

Esta función toma seis parámetros:

  • Puntero al inicio de la línea de digitalización 0 en la imagen de destino.
  • Stride de la imagen de destino.
  • Puntero al inicio de la línea de digitalización 0 en la imagen de origen.
  • Stride de la imagen de origen.
  • Ancho de la imagen en píxeles.
  • Alto de la imagen en píxeles.

La idea general es procesar una fila cada vez, iterando en cada píxel de la fila. Supongamos que SOURCE_PIXEL_TYPE y DEST_PIXEL_TYPE son estructuras que representan la distribución de píxeles para las imágenes de origen y destino, respectivamente. (Por ejemplo, RGB de 32 bits usa la estructura RGBQUAD. No todos los formatos de píxel tienen una estructura predefinida). Convertir el puntero de matriz al tipo de estructura le permite acceder a los componentes RGB o YUV de cada píxel. Al principio de cada fila, la función almacena un puntero a la fila. Al final de la fila, incrementa el puntero por el ancho del stride de la imagen, que avanza el puntero a la fila siguiente.

En este ejemplo se llama a una función hipotética denominada TransformPixelValue para cada píxel. Esto podría ser cualquier función que calcule un píxel de destino a partir de un píxel de origen. Por supuesto, los detalles exactos dependerán de la tarea concreta. Por ejemplo, si tiene un formato YUV planar, debe acceder a los planos cromáticos independientemente del plano luma; con vídeo entrelazado, es posible que tenga que procesar los campos por separado; y así sucesivamente.

Para dar un ejemplo más concreto, el código siguiente convierte una imagen RGB de 32 bits en una imagen AYUV. Se accede a los píxeles RGB mediante una estructura RGBQUAD y se accede a los píxeles AYUV mediante una estructura DXVA2_AYUVSample8.

//-------------------------------------------------------------------
// Name: RGB32_To_AYUV
// Description: Converts an image from RGB32 to AYUV
//-------------------------------------------------------------------
void RGB32_To_AYUV(
    BYTE*       pDest,
    LONG        lDestStride,
    const BYTE* pSrc,
    LONG        lSrcStride,
    DWORD       dwWidthInPixels,
    DWORD       dwHeightInPixels
    )
{
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        RGBQUAD             *pSrcPixel = (RGBQUAD*)pSrc;
        DXVA2_AYUVSample8   *pDestPixel = (DXVA2_AYUVSample8*)pDest;
        
        for (DWORD x = 0; x < dwWidthInPixels; x++)
        {
            pDestPixel[x].Alpha = 0x80;
            pDestPixel[x].Y = RGBtoY(pSrcPixel[x]);   
            pDestPixel[x].Cb = RGBtoU(pSrcPixel[x]);   
            pDestPixel[x].Cr = RGBtoV(pSrcPixel[x]);   
        }
        pDest += lDestStride;
        pSrc += lSrcStride;
    }
}

En el ejemplo siguiente se convierte una imagen RGB de 32 bits en una imagen YV12. En este ejemplo se muestra cómo controlar un formato YUV planar. (YV12 es un formato planar 4:2:0). En este ejemplo, la función mantiene tres punteros independientes para los tres planos de la imagen de destino. Sin embargo, el enfoque básico es el mismo que el ejemplo anterior.

void RGB32_To_YV12(
    BYTE*       pDest,
    LONG        lDestStride,
    const BYTE* pSrc,
    LONG        lSrcStride,
    DWORD       dwWidthInPixels,
    DWORD       dwHeightInPixels
    )
{
    assert(dwWidthInPixels % 2 == 0);
    assert(dwHeightInPixels % 2 == 0);

    const BYTE *pSrcRow = pSrc;
    
    BYTE *pDestY = pDest;

    // Calculate the offsets for the V and U planes.

    // In YV12, each chroma plane has half the stride and half the height  
    // as the Y plane.
    BYTE *pDestV = pDest + (lDestStride * dwHeightInPixels);
    BYTE *pDestU = pDest + 
                   (lDestStride * dwHeightInPixels) + 
                   ((lDestStride * dwHeightInPixels) / 4);

    // Convert the Y plane.
    for (DWORD y = 0; y < dwHeightInPixels; y++)
    {
        RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
        
        for (DWORD x = 0; x < dwWidthInPixels; x++)
        {
            pDestY[x] = RGBtoY(pSrcPixel[x]);    // Y0
        }
        pDestY += lDestStride;
        pSrcRow += lSrcStride;
    }

    // Convert the V and U planes.

    // YV12 is a 4:2:0 format, so each chroma sample is derived from four 
    // RGB pixels.
    pSrcRow = pSrc;
    for (DWORD y = 0; y < dwHeightInPixels; y += 2)
    {
        RGBQUAD *pSrcPixel = (RGBQUAD*)pSrcRow;
        RGBQUAD *pNextSrcRow = (RGBQUAD*)(pSrcRow + lSrcStride);

        BYTE *pbV = pDestV;
        BYTE *pbU = pDestU;

        for (DWORD x = 0; x < dwWidthInPixels; x += 2)
        {
            // Use a simple average to downsample the chroma.

            *pbV++ = ( RGBtoV(pSrcPixel[x]) +
                       RGBtoV(pSrcPixel[x + 1]) +       
                       RGBtoV(pNextSrcRow[x]) +         
                       RGBtoV(pNextSrcRow[x + 1]) ) / 4;        

            *pbU++ = ( RGBtoU(pSrcPixel[x]) +
                       RGBtoU(pSrcPixel[x + 1]) +       
                       RGBtoU(pNextSrcRow[x]) +         
                       RGBtoU(pNextSrcRow[x + 1]) ) / 4;    
        }
        pDestV += lDestStride / 2;
        pDestU += lDestStride / 2;
        
        // Skip two lines on the source image.
        pSrcRow += (lSrcStride * 2);
    }
}

En todos estos ejemplos, se supone que la aplicación ya ha determinado el stride de la imagen. A veces puede obtener esta información del búfer multimedia. De lo contrario, debe calcularla en función del formato de vídeo. Para obtener más información sobre cómo calcular el stride de imágenes y trabajar con búferes multimedia para vídeo, consulte Búferes de vídeo sin comprimir.

Tipos de medios de vídeo

Tipos de medios