O que é imagem Stride?

superfície

Já escrevi um artigo sobre formato de imagem: [Formato de imagem: formato de imagem comum RAW, RGB, análise de formato de imagem YUV&&, conversão de formato e software de visualização de imagem
]

Quando as imagens de vídeo são armazenadas na memória, o buffer de memória pode conter bytes de preenchimento extras após cada linha de pixels. Os bytes de preenchimento afetam como a imagem é armazenada na memória, mas não afeta como a imagem é exibida.

passo é o número de bytes de uma linha de pixels na memória até a próxima linha de pixels na memória . A passada também é chamada de pitch. Se houver bytes de preenchimento, o Stride será mais largo que a largura da imagem, conforme mostrado na imagem abaixo.

O que é imagem Stride

Dois buffers contendo quadros de vídeo com as mesmas dimensões podem ter dois avanços diferentes.

Se você estiver lidando com uma imagem de vídeo, deverá considerar os avanços.

Além disso, existem duas maneiras de organizar as imagens na memória. Em uma imagem de cima para baixo, a linha superior de pixels da imagem aparece primeiro na memória.

Em uma imagem de baixo para cima, a última linha de pixels aparece primeiro na memória. A imagem abaixo mostra a diferença entre imagens de cima para baixo e de baixo para cima .


As imagens de baixo para cima têm avanço negativo porque o avanço é definido como o número de bytes necessários para mover uma linha de pixels para baixo em relação à imagem exibida .

As imagens YUV devem sempre ser de cima para baixo e todas as imagens contidas nas superfícies Direct3D devem ser de cima para baixo.

As imagens RGB na memória do sistema geralmente são de baixo para cima.

A conversão de vídeo precisa especialmente lidar com buffers incompatíveis , uma vez que o buffer de entrada pode não corresponder ao buffer de saída.

Por exemplo, suponha que você queira converter uma imagem de origem e gravar o resultado em uma imagem de destino . Supõe-se que duas imagens tenham a mesma largura e altura, mas podem não ter o mesmo formato de pixel ou o mesmo avanço da imagem.

O código de exemplo abaixo mostra uma abordagem geral para escrever tal função. Este não é um exemplo prático completo, pois abstrai muitos dos detalhes 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 função leva seis parâmetros:
    • Ponteiro para o ponto inicial da linha de varredura 0 na imagem de destino.
    • O passo da imagem alvo.
    • Ponteiro para o ponto inicial da linha de varredura 0 na imagem de origem.
    • O passo da imagem de origem.
    • A largura da imagem em pixels.
    • A altura da imagem em pixels.

A ideia geral é processar uma linha por vez, iterando cada pixel da linha.

Suponha que SOURCE_PIXEL_TYPE e DEST_PIXEL_TYPE sejam estruturas que representam o layout de pixels da imagem de origem e da imagem de destino, respectivamente.

(Por exemplo, RGB de 32 bits usa a estrutura RGBQUAD. Nem todo formato de pixel tem uma estrutura predefinida.) A conversão de um ponteiro de matriz para um tipo de estrutura dá acesso aos componentes RGB ou YUV de cada pixel .

No início de cada linha, a função armazena um ponteiro para essa linha . No final da linha, aumenta o ponteiro pela largura do passo da imagem , avançando assim o ponteiro para a próxima linha.

Este exemplo chama uma função hipotética chamada TransformPixelValue para cada pixel. Pode ser qualquer função que calcule o pixel de destino a partir do pixel de origem. É claro que os detalhes dependerão da missão específica.

Por exemplo, se você usar um formato YUV planar, deverá acessar o plano croma independentemente do plano luma; para vídeo entrelaçado, pode ser necessário processar os campos separadamente; etc.

Para dar um exemplo mais específico, o código a seguir converte uma imagem RGB de 32 bits em uma imagem AYUV. Os pixels RGB são acessados ​​usando a estrutura RGBQUAD e os pixels AYUV são acessados ​​usando a estrutura 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;
    }
}

O próximo exemplo converte uma imagem RGB de 32 bits em uma imagem YV12. Este exemplo mostra como lidar com o formato YUV plano.

(YV12 é um formato planar 4:2:0.) Neste exemplo, a função mantém três ponteiros separados para os três planos na imagem de destino. No entanto, a abordagem básica é a mesma do exemplo 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);
    }
}

Em todos esses exemplos, presume-se que o aplicativo identificou a imagem Stride. Às vezes você pode obter essas informações do buffer de mídia.

Caso contrário, os cálculos deverão ser feitos com base no formato do vídeo. Para obter mais informações sobre como calcular o avanço da imagem e usar buffers de mídia de vídeo, consulte Buffers de vídeo não compactados.

Acho que você gosta

Origin blog.csdn.net/weixin_45264425/article/details/132783654
Recomendado
Clasificación