什么是Image Stride?

简介: 什么是Image Stride?

perface


当视频图像存储在内存中时,内存缓冲区可能在每行像素后包含额外的填充字节。填充字节会影响图像在内存中的存储方式,但不会影响图像的显示方式。

stride 是从内存中的一行像素到内存中的下一行像素的字节数。Stride 也叫pitch。如果存在填充字节,则**Stride **比图像的宽度宽,如下图所示。

包含具有相同维度的视频帧的两个缓冲区可以具有两个不同的strides。

如果你处理一个视频图像,你必须考虑strides。

此外,有两种方式可以将图像排列在存储器中。在自上而下的图像中,图像中最上面的一行像素首先出现在内存中。

在自下而上的图像中,最后一行像素首先出现在内存中。下图显示了自上而下的图像和自下而上的图像之间的区别

自下而上的图像具有negative stride因为stride被定义为相对于显示的图像向下移动一行像素所需的字节数

YUV图像应该始终是自上而下的,并且Direct3D曲面中包含的任何图像都必须是自上而下的。

系统内存中的RGB图像通常是自下而上的。

视频转换尤其需要处理步幅不匹配的缓冲区,因为输入缓冲区可能与输出缓冲区不匹配。

例如,假设您要转换源图像并将结果写入目标图像。假设两个图像具有相同的宽度和高度,但可能不具有相同的像素格式或相同的图像步幅。

下面的示例代码显示了编写此类函数的通用方法。这不是一个完整的工作示例,因为它抽象了许多具体的细节。

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;
    }
}
  • 此函数采用六个参数:
  • 指向目标图像中扫描行0起点的指针。
  • 目标图像的stride 。
  • 指向源图像中扫描行0起点的指针。
  • 源图像的stride 。
  • 图像的宽度(以像素为单位)。
  • 图像的高度(以像素为单位)。

一般的想法是一次处理一行,对行中的每个像素进行迭代。

假设SOURCE_PIXEL_TYPEDEST_PIXEL_TYPE分别是表示源图像和目的图像的像素布局的结构。

(例如,32位RGB使用RGBQUAD结构。并非每个像素格式都有预定义的结构。)将数组指针投射到结构类型可以访问每个像素的RGB或YUV组件

在每一行的开头,函数存储一个指向该行的指针。在行的末尾,它将指针增加图像步幅的宽度,从而使指针前进到下一行。

此示例为每个像素调用一个名为TransformPixelValue的假设函数。 这可以是从源像素计算目标像素的任何函数。当然,具体细节将取决于具体任务。

例如,如果使用平面YUV格式,则必须独立于亮度平面访问色度平面;对于隔行扫描视频,您可能需要单独处理字段;等等

为了给出更具体的示例,以下代码将32位RGB图像转换为AYUV图像。RGB像素使用RGBQUAD结构访问,AYUV像素使用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;
    }
}

下一个示例将32位RGB图像转换为YV12图像。此示例显示如何处理平面YUV格式。

(YV12是平面4:2:0格式。)在本例中,函数为目标图像中的三个平面保留三个单独的指针。但是,基本方法与前面的示例相同。

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

在所有这些示例中,假设应用程序已经确定了图像Stride。您有时可以从媒体缓冲区获取这些信息。

否则,必须根据视频格式进行计算。有关计算图像Stride和使用视频媒体缓冲区的更多信息,请参阅未压缩的视频缓冲区。

目录
相关文章
|
2月前
|
计算机视觉
halcon系列基础之Scale_image_range
halcon系列基础之Scale_image_range
|
6月前
Limits: MIN_INFLATE_RATIO: 0.010000, Entry: word/media/image5.png
Limits: MIN_INFLATE_RATIO: 0.010000, Entry: word/media/image5.png
|
8月前
|
计算机视觉 Python
cv2 resize 与reshape的区别
cv2 resize 与reshape的区别
|
9月前
|
存储 API
sws_scale():bad dst image pointers
sws_scale():bad dst image pointers
131 0
sws_scale():bad dst image pointers
|
11月前
|
数据格式
详解torch.size
详解torch.size
117 0
详解torch.size
|
12月前
The size of tensor a (4) must match the size of tensor b (3) at non-singletonThe size of
The size of tensor a (4) must match the size of tensor b (3) at non-singletonThe size of
653 0
|
算法 计算机视觉 C++
积分图像(Integral image)
积分图算法由Crow在1984年首次提出,是为了在多尺度透视投影中提高渲染速度。积分图算法是一种快速计算图像区域和以及图像区域平方和的算法。它的核心思想就是对每一个图像建立起自己的积分图查找表,在图像处理的阶段就可以根据预先建立积分图查找表直接查找从而实现对均值卷积的线性时间计算。做到了卷积执行的时间与窗口大小无关。之前介绍的NL-means算法就可以采用积分图算法进行优化加速。
119 0
积分图像(Integral image)
|
计算机视觉
|
Python