Bildseitenverhältnis

In diesem Thema werden zwei verwandte Konzepte beschrieben: Bildseitenverhältnis und Pixelseitenverhältnis. Anschließend wird beschrieben, wie diese Konzepte in Microsoft Media Foundation mithilfe von Medientypen ausgedrückt werden.

Bildseitenverhältnis

Das Bildseitenverhältnis definiert die Form des angezeigten Videobilds. Das Bildseitenverhältnis wird mit X:Y angegeben, wobei X:Y das Verhältnis von Bildbreite zu Bildhöhe darstellt. Die meisten Videostandards verwenden entweder das Bildseitenverhältnis von 4:3 oder 16:9. Das Seitenverhältnis von 16:9 wird häufig als Breitbild bezeichnet. Kinofilm verwendet häufig ein Seitenverhältnis von 1:85:1 oder 1:66:1. Das Bildseitenverhältnis wird auch als Anzeigeseitenverhältnis (Display Aspect Ratio, DAR) bezeichnet.

diagram showing 4:3 and 16:9 aspect ratios

Manchmal hat das Videobild nicht dieselbe Form wie der Anzeigebereich. Beispielsweise kann ein 4:3-Video auf einem Breitbildfernseher (16×9) angezeigt werden. In Computervideos wird das Video möglicherweise in einem Fenster angezeigt, das eine beliebige Größe aufweist. In diesem Fall gibt es drei Möglichkeiten, das Bild an den Anzeigebereich anzupassen:

  • Strecken Sie das Bild entlang einer Achse, um es an den Anzeigebereich anzupassen.
  • Skalieren Sie das Bild so, dass es in den Anzeigebereich passt, wobei das ursprüngliche Bildseitenverhältnis beibehalten wird.
  • Schneiden Sie das Bild zu.

Das Bild so zu strecken, dass es in den Anzeigebereich passt, ist fast immer falsch, weil dabei das richtige Bildseitenverhältnis nicht erhalten bleibt.

Letterboxing

Das Skalieren eines Breitbilds auf eine 4:3-Anzeige wird als Letterboxing bezeichnet, wie im nächsten Diagramm dargestellt. Die resultierenden rechteckigen Bereiche am oberen und unteren Rand des Bilds werden normalerweise mit Schwarz gefüllt, obwohl andere Farben verwendet werden können.

diagram showing the correct way to letterbox

Der umgekehrte Fall, die Skalierung eines Bilds von 4:3 auf eine Breitbildanzeige, wird manchmal als Pillarboxing bezeichnet. Der Begriff Letterbox wird jedoch auch im allgemeinen Sinne für das Skalieren eines Videobilds verwendet, sodass es in einen bestimmten Anzeigebereich passt.

diagram showing pillarboxing

Schwenken-und-Scannen

Schwenken-und-Scannen ist eine Technik, bei der ein Breitbild auf einen rechteckigen Bereich von 4×3 zugeschnitten wird, um es auf einem 4:3-Anzeigegerät anzuzeigen. Das resultierende Bild füllt die gesamte Anzeige, ohne dass schwarze Letterboxbereiche erforderlich sind, aber Teile des Originalbilds werden aus dem Bild geschnitten. Der Bereich, der zugeschnitten wird, kann von Frame zu Frame verschoben werden, wenn sich der Bereich verschiebt, der von Interesse ist. Der Begriff „Schwenken“ in Schwenken-und-Scannen bezieht sich auf den Verschiebungseffekt, der durch Verschieben des Schwenk- und Scanbereichs verursacht wird.

diagram showing pan-and-scan

Pixelseitenverhältnis

Das Pixelseitenverhältnis (Pixel Aspect Ration, PAR) misst die Form eines Pixels.

Wenn ein digitales Bild aufgenommen wird, wird das Bild sowohl vertikal als auch horizontal abgetastet, was zu einer rechteckigen Anordnung quantisierter Samplingwerte führt, die Pixel oder Pels genannt werden. Die Form des Samplingrasters bestimmt die Form der Pixel im digitalisierten Bild.

Hier ist ein Beispiel, das kleine Zahlen verwendet, um die Mathematik einfach zu halten. Angenommen, das ursprüngliche Bild ist quadratisch (d. a. das Seitenverhältnis des Bilds ist 1:1); und angenommen, das Samplingraster enthält 12 Elemente, die in einem 4×3-Raster angeordnet sind. Die Form jedes resultierenden Pixels ist höher als breit. Genauer gesagt, die Form jedes Pixels ist 3×4. Pixel, die nicht quadratisch sind, werden als nicht quadratische Pixel bezeichnet.

diagram showing a non-square sampling grid

Das Pixelseitenverhältnis gilt auch für das Anzeigegerät. Die physikalische Form des Anzeigegeräts und die physikalische Pixelauflösung (quer und längs) bestimmen den PAR-Wert des Anzeigegeräts. Computermonitore verwenden in der Regel quadratische Pixel. Wenn der Bild-PAR-Wert und der Anzeige-PAR-Wert nicht übereinstimmen, muss das Bild in einer Dimension skaliert werden, entweder vertikal oder horizontal, um korrekt angezeigt zu werden. Die folgende Formel setzt PAR, das Anzeige-Seitenverhältnis (DAR) und die Bildgröße in Pixeln in Beziehung:

DAR = (Bildbreite in Pixel / Bildhöhe in Pixel) × PAR

Beachten Sie, dass sich die Bildbreite und die Bildhöhe in dieser Formel auf das Bild im Arbeitsspeicher und nicht auf das angezeigte Bild beziehen.

Hier ist ein Beispiel aus der Praxis: Ein analoges NTSC-M-Video enthält 480 Bildzeilen im aktiven Bildbereich. ITU-R Rec. BT.601 gibt eine horizontale Samplingrate von 704 sichtbaren Pixeln pro Linie an, was zu einem digitalen Bild mit 704 x 480 Pixeln führt. Das beabsichtigte Bildseitenverhältnis ist 4:3 und ergibt einen PAR von 10:11.

  • DAR: 4:3
  • Breite in Pixel: 704
  • Höhe in Pixel: 480
  • PAR: 10/11

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

Um dieses Bild auf einem Anzeigegerät mit quadratischen Pixeln korrekt anzuzeigen, müssen Sie entweder die Breite um 10/11 oder die Höhe um 11/10 skalieren.

Arbeiten mit Seitenverhältnissen

Die richtige Form eines Videoframes wird durch das Pixelseitenverhältnis (PAR) und den Anzeigebereich definiert.

  • Der PAR-Wert definiert die Form der Pixel in einem Bild. Quadratische Pixel weisen ein Seitenverhältnis von 1:1 auf. Jedes andere Seitenverhältnis beschreibt ein nicht quadratisches Pixel. Beispielsweise verwendet NTSC-Fernsehen einen PAR-Wert von 10:11. Wenn Sie das Video auf einem Computermonitor präsentieren, hat die Anzeige quadratische Pixel (1:1 PAR). Der PAR-Wert des Quellinhalts wird im Attribut MF_MT_PIXEL_ASPECT_RATIO für den Medientyp angegeben.
  • Der Anzeigebereich ist der Bereich des Videobilds, der angezeigt werden soll. Es gibt zwei relevante Anzeigebereiche, die im Medientyp angegeben werden können:
    • Schwenken-und-Scannen-Blende. Die Schwenken-und-Scannen-Blende ist ein 4×3-Bereich des Videos, der im Schwenk-/Scanmodus angezeigt werden soll. Sie wird verwendet, um Breitbildinhalte auf einer 4×3-Anzeige ohne Letterboxing anzuzeigen. Die Schwenken-und-Scannen-Blende wird im Attribut MF_MT_PAN_SCAN_APERTURE angegeben und sollte nur verwendet werden, wenn das Attribut MF_MT_PAN_SCAN_ENABLEDTRUE ist.
    • Anzeigeblende. Diese Blende ist in einigen Videostandards definiert. Alles außerhalb der Anzeigeblende liegt der Überscanbereich, der nicht angezeigt werden sollte. Ein NTSC-Fernsehgerät hat zum Beispiel 720×480 Pixel mit einer Bildschirmblende von 704×480. Die Anzeigeblende wird im Attribut MF_MT_MINIMUM_DISPLAY_APERTURE angegeben. Wenn vorhanden, sollte sie verwendet werden, wenn der Schwenken-und-Scannen-Modus FALSE ist.

Wenn der Schwenken-und-Scannen-Modus FALSE ist und keine Blende für die Anzeige definiert ist, sollte der gesamte Videoframe angezeigt werden. In der Tat ist dies für die meisten Videoinhalte außer Fernsehen und DVD-Video der Fall. Das Seitenverhältnis des gesamten Bildes wird berechnet als (Breite des Anzeigebereichs / Höhe des Anzeigebereichs) × PAR.

Codebeispiele

Suchen des Anzeigebereichs

Der folgende Code zeigt, wie der Anzeigebereich aus dem Medientyp abgerufen wird.

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;
}

Konvertieren zwischen Pixelseitenverhältnissen

Der folgende Code zeigt, wie Sie ein Rechteck aus einem Pixelseitenverhältnis (PAR) in ein anderes konvertieren und dabei das Seitenverhältnis des Bilds beibehalten.

//-----------------------------------------------------------------------------
// 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;
}

Berechnen des Letterbox-Bereichs

Mit dem folgenden Code wird der Letterbox-Bereich anhand eines Quell- und Zielrechtecks berechnet. Es wird angenommen, dass beide Rechtecke den gleichen PAR-Wert aufweisen.

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;
}

Medientypen

Videomedientypen

MF_MT_MINIMUM_DISPLAY_APERTURE

MF_MT_PAN_SCAN_APERTURE

MF_MT_PAN_SCAN_ENABLED

MF_MT_PIXEL_ASPECT_RATIO