Taxa de proporção de imagem

Este tópico descreve dois conceitos relacionados: taxa de proporção da imagem e taxa de proporção de pixel. Em seguida, descreve como esses conceitos são expressos no Microsoft Media Foundation usando tipos de mídia.

Taxa de proporção de imagem

A taxa de proporção de imagem define a forma da imagem de vídeo exibida. A taxa de proporção de imagem tem notação X:Y, em que X:Y é a razão entre a largura e a altura da imagem. A maioria dos padrões de vídeo usa a taxa de proporção de imagem 4:3 ou 16:9. A taxa de proporção 16:9 é comumente chamada de widescreen. O cinema geralmente usa uma proporção de 1:85:1 ou 1:66:1. A taxa de proporção de imagem também é chamada de taxa de proporção do vídeo (DAR).

diagram showing 4:3 and 16:9 aspect ratios

Às vezes, a imagem do vídeo não tem a mesma forma que a área do vídeo. Por exemplo, um vídeo 4:3 pode ser exibido em uma televisão widescreen (16×9). No vídeo do computador, ele pode ser mostrado dentro de uma janela que tem um tamanho arbitrário. Nesse caso, existem três maneiras de fazer a imagem se ajustar dentro da área de vídeo:

  • Esticar a imagem ao longo de um eixo para ajustá-la à área do vídeo.
  • Dimensionar a imagem para se ajustar à área de vídeo, mantendo a taxa de proporção de imagem original.
  • Cortar a imagem.

Esticar a imagem para ajustá-la à área de vídeo quase sempre está errado, pois não preserva a proporção correta da imagem.

Letterboxing

O processo de redimensionar uma imagem widescreen para caber em uma tela 4:3 é chamado de letterboxing, como mostrado no próximo diagrama. As áreas retangulares resultantes na parte superior e inferior da imagem normalmente são preenchidas com a cor preta, embora outras cores também possam ser usadas.

diagram showing the correct way to letterbox

O caso inverso, dimensionar uma imagem 4:3 para se ajustar a uma tela widescreen, às vezes é chamado de pillarboxing. No entanto, o termo letterbox também é usado em um sentido geral, para significar o dimensionamento de uma imagem de vídeo para se ajustar a qualquer área de vídeo específica.

diagram showing pillarboxing

Pan-and-scan

Pan-and-scan é uma técnica pela qual uma imagem widescreen é cortada para uma área retangular 4×3, para ser exibida em um dispositivo de vídeo 4:3. A imagem resultante preenche toda a tela sem exigir áreas pretas de letterbox, mas partes da imagem original serão cortadas. A área cortada pode passar de quadro para quadro, conforme a área de interesse muda. O termo "pan", em pan-and-scan, refere-se ao efeito de panorâmica causado pelo movimento da área de pan-and-scan.

diagram showing pan-and-scan

Taxa de proporção de pixel

A proporção de pixel (PAR) mede a forma de um pixel.

Quando uma imagem digital é capturada, a imagem é amostrada tanto verticalmente quanto horizontalmente, resultando em uma matriz retangular de amostras quantizadas, chamadas de pixels ou pels. A forma da grade de amostragem determina a forma dos pixels na imagem digitalizada.

Aqui está um exemplo que usa números pequenos para facilitar a matemática. Suponha que a imagem original seja quadrada (ou seja, a taxa de proporção da imagem é 1:1); e suponha que a grade de amostragem contenha 12 elementos, organizados em uma grade de 4×3. A forma de cada pixel resultante será mais alta do que larga. Especificamente, a forma de cada pixel será 3×4. Pixels que não são quadrados são chamados de pixels não quadrados.

diagram showing a non-square sampling grid

A taxa de proporção de pixel também se aplica ao dispositivo de vídeo. A forma física do dispositivo de vídeo e a resolução física dos pixels (horizontal e vertical) determinam a taxa de proporção de pixels (PAR) do dispositivo de vídeo. Os monitores de computador geralmente usam pixels quadrados. Se a imagem PAR e o vídeo PAR não corresponderem, o tamanho da imagem deverá ser alterado em uma das dimensões, vertical ou horizontal, para ser exibido de forma correta. A fórmula a seguir relaciona PAR, DAR (taxa de proporção de vídeo) e tamanho da imagem em pixels:

DAR = (largura da imagem em pixels / altura da imagem em pixels) × PAR

Observe que a largura e a altura da imagem nesta fórmula referem-se à imagem na memória, não à imagem exibida.

Aqui está um exemplo do mundo real: o vídeo analógico NTSC-M contém 480 linhas de varredura na área de imagem ativa. ITU-R Rec. O BT.601 especifica uma taxa de amostragem horizontal de 704 pixels visíveis por linha, resultando em uma imagem digital com 704 x 480 pixels. A taxa de proporção de imagem pretendida é 4:3, gerando uma PAR de 10:11.

  • DAR: 4:3
  • Largura em pixels: 704
  • Altura em pixels: 480
  • PAR: 10/11

4/3 = (704/480) x (10/11)

Para exibir essa imagem corretamente em um dispositivo de vídeo com pixels quadrados, você deve dimensionar a largura em 10/11 ou a altura em 11/10.

Trabalhar com taxas de proporção

A forma correta de um quadro de vídeo é definida pela taxa de proporção de pixel (PAR) e pela área de exibição.

  • A PAR define a forma dos pixels em uma imagem. Os pixels quadrados têm uma proporção de 1:1. Qualquer outra taxa de proporção descreve um pixel não quadrado. Por exemplo, a televisão NTSC usa um PAR de 10:11. Supondo que você esteja apresentando o vídeo em um monitor de computador, o vídeo terá pixels quadrados (1:1 PAR). A PAR do conteúdo de origem é definida no atributo MF_MT_PIXEL_ASPECT_RATIO no tipo de mídia.
  • A área de vídeo é a região da imagem do vídeo que deve ser mostrada. Há duas áreas de vídeo relevantes que podem ser especificadas no tipo de mídia:
    • Abertura pan-and-scan. A abertura pan-and-scan é uma região de 4×3 do vídeo que deve ser exibida no modo pan/scan. Ela é usada para mostrar conteúdo widescreen em uma tela 4×3 sem letterboxing. A abertura pan-and-scan é definida no atributo MF_MT_PAN_SCAN_APERTURE e deve ser usada somente quando o atributo MF_MT_PAN_SCAN_ENABLED for TRUE.
    • Abertura do vídeo. Essa abertura é definida em alguns padrões de vídeo. Qualquer coisa fora da abertura do vídeo é a região de overscan e não deve ser exibida. Por exemplo, a televisão NTSC tem 720×480 pixels com uma abertura de vídeo de 704×480. A abertura de vídeo é definida no atributo MF_MT_MINIMUM_DISPLAY_APERTURE. Se estiver presente, ele deverá ser usado quando o modo pan-and-scan for FALSE.

Se o modo pan-and-can for FALSE e nenhuma abertura de vídeo estiver definida, todo o quadro de vídeo deverá ser exibido. Na verdade, este é o caso da maioria do conteúdo de vídeo que não é vídeo para televisão e DVD. A taxa de proporção de toda a imagem é calculada como (largura da área do vídeo / altura da área do vídeo) × PAR.

Exemplos de código

Localizar a área de vídeo

O código a seguir mostra como obter a área de vídeo do tipo de mídia.

MFVideoArea MakeArea(float x, float y, DWORD width, DWORD height);

HRESULT GetVideoDisplayArea(IMFMediaType *pType, MFVideoArea *pArea)
{
    HRESULT hr = S_OK;
    BOOL bPanScan = FALSE;
    UINT32 width = 0, height = 0;

    bPanScan = MFGetAttributeUINT32(pType, MF_MT_PAN_SCAN_ENABLED, FALSE);

    // In pan-and-scan mode, try to get the pan-and-scan region.
    if (bPanScan)
    {
        hr = pType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)pArea, 
            sizeof(MFVideoArea), NULL);
    }

    // If not in pan-and-scan mode, or the pan-and-scan region is not set, 
    // get the minimimum display aperture.

    if (!bPanScan || hr == MF_E_ATTRIBUTENOTFOUND)
    {
        hr = pType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)pArea, 
            sizeof(MFVideoArea), NULL);

        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            // Minimum display aperture is not set.

            // For backward compatibility with some components, 
            // check for a geometric aperture. 

            hr = pType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)pArea, 
                sizeof(MFVideoArea), NULL);
        }

        // Default: Use the entire video area.

        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);

            if (SUCCEEDED(hr))
            {
                *pArea = MakeArea(0.0, 0.0, width, height);
            }
        }
    }
    return hr;
}
MFOffset MakeOffset(float v)
{
    MFOffset offset;
    offset.value = short(v);
    offset.fract = WORD(65536 * (v-offset.value));
    return offset;
}
MFVideoArea MakeArea(float x, float y, DWORD width, DWORD height)
{
    MFVideoArea area;
    area.OffsetX = MakeOffset(x);
    area.OffsetY = MakeOffset(y);
    area.Area.cx = width;
    area.Area.cy = height;
    return area;
}

Converter entre taxas de proporção de pixel

O código a seguir mostra como converter um retângulo de uma taxa de proporção de pixel (PAR) para outra, preservando a taxa de proporção de imagem.

//-----------------------------------------------------------------------------
// Converts a rectangle from one pixel aspect ratio (PAR) to another PAR.
// Returns the corrected rectangle.
//
// For example, a 720 x 486 rect with a PAR of 9:10, when converted to 1x1 PAR,
// must be stretched to 720 x 540.
//-----------------------------------------------------------------------------

RECT CorrectAspectRatio(const RECT& src, const MFRatio& srcPAR, const MFRatio& destPAR)
{
    // Start with a rectangle the same size as src, but offset to (0,0).
    RECT rc = {0, 0, src.right - src.left, src.bottom - src.top};

    // If the source and destination have the same PAR, there is nothing to do.
    // Otherwise, adjust the image size, in two steps:
    //  1. Transform from source PAR to 1:1
    //  2. Transform from 1:1 to destination PAR.

    if ((srcPAR.Numerator != destPAR.Numerator) || 
        (srcPAR.Denominator != destPAR.Denominator))
    {
        // Correct for the source's PAR.

        if (srcPAR.Numerator > srcPAR.Denominator)
        {
            // The source has "wide" pixels, so stretch the width.
            rc.right = MulDiv(rc.right, srcPAR.Numerator, srcPAR.Denominator);
        }
        else if (srcPAR.Numerator < srcPAR.Denominator)
        {
            // The source has "tall" pixels, so stretch the height.
            rc.bottom = MulDiv(rc.bottom, srcPAR.Denominator, srcPAR.Numerator);
        }
        // else: PAR is 1:1, which is a no-op.

        // Next, correct for the target's PAR. This is the inverse operation of 
        // the previous.

        if (destPAR.Numerator > destPAR.Denominator)
        {
            // The destination has "wide" pixels, so stretch the height.
            rc.bottom = MulDiv(rc.bottom, destPAR.Numerator, destPAR.Denominator);
        }
        else if (destPAR.Numerator < destPAR.Denominator)
        {
            // The destination has "tall" pixels, so stretch the width.
            rc.right = MulDiv(rc.right, destPAR.Denominator, destPAR.Numerator);
        }
        // else: PAR is 1:1, which is a no-op.
    }
    return rc;
}

Calcular a área de letterbox

O código a seguir calcula a área de letterbox, considerando um retângulo de origem e destino. Supõe-se que ambos os retângulos tenham a mesma PAR.

RECT LetterBoxRect(const RECT& rcSrc, const RECT& rcDst)
{
    // Compute source/destination ratios.
    int iSrcWidth  = rcSrc.right - rcSrc.left;
    int iSrcHeight = rcSrc.bottom - rcSrc.top;

    int iDstWidth  = rcDst.right - rcDst.left;
    int iDstHeight = rcDst.bottom - rcDst.top;

    int iDstLBWidth;
    int iDstLBHeight;

    if (MulDiv(iSrcWidth, iDstHeight, iSrcHeight) <= iDstWidth) 
    {
        // Column letterboxing ("pillar box")
        iDstLBWidth  = MulDiv(iDstHeight, iSrcWidth, iSrcHeight);
        iDstLBHeight = iDstHeight;
    }
    else 
    {
        // Row letterboxing.
        iDstLBWidth  = iDstWidth;
        iDstLBHeight = MulDiv(iDstWidth, iSrcHeight, iSrcWidth);
    }

    // Create a centered rectangle within the current destination rect

    LONG left = rcDst.left + ((iDstWidth - iDstLBWidth) / 2);
    LONG top = rcDst.top + ((iDstHeight - iDstLBHeight) / 2);

    RECT rc;
    SetRect(&rc, left, top, left + iDstLBWidth, top + iDstLBHeight);
    return rc;
}

Tipos de mídia

Tipos de mídia de vídeo

MF_MT_MINIMUM_DISPLAY_APERTURE

MF_MT_PAN_SCAN_APERTURE

MF_MT_PAN_SCAN_ENABLED

MF_MT_PIXEL_ASPECT_RATIO