图像处理、显示中的行宽(linesize)、步长(stride)、间距(pitch)

本文深入解析图像处理中常被忽视的间距参数,解释其出现原因与内存对齐的关系,探讨不同场景如d3d显示、ffmpeg解码及格式转换中间距的应用,帮助读者理解并正确处理间距问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在图像数据传输和显示的过程中有一个不常用的参数:间距。

间距的名称:
它有很多的别名,在使用d3d显示的时候,它叫pitch;在用ffmpeg解码的时候,它叫linesize; 
在用ffmpeg转换格式的时候,它叫stride。这篇文章中统一以间距来表示。

间距为什么出现:
这个参数看起来似乎没什么用,因为它的值和图像的宽度一样。但是那是大多数情况下,一旦遇到它和宽度不一样的时候,如果你不了解它的含义,那么程序肯定要出问题。可是为什么有时候它等于宽度,有时候又不等于呢?这就和它的含义有关了。 
我们都知道现在计算机的cpu都是32位或者64位的cpu,他们一次最少读取4、8个字节,如果少于这些,反而要做一些额外的工作,会花更长的时间。所有会有一个概念叫做内存对齐,将结构体的长度设为4、8的倍数。 
间距也是因为同样的理由出现的。因为图像的操作通常按行操作的,如果图像的所有数据都紧密排列,那么会发生非常多次的读取非对齐内存。会影响效率。而图像的处理本就是一个分秒必争的操作,所以为了性能的提高就引入了间距这个概念。

间距的含义:
间距就是指图像中的一行图像数据所占的存储空间的长度,它是一个大于等于图像宽度的内存对齐的长度。这样每次以行为基准读取数据的时候就能内存对齐,虽然可能会有一点内存浪费,但是在内存充裕的今天已经无所谓了。

间距的值:
所以如果图像的宽度如果是内存对齐长度的整数倍,那么间距就会等于宽度,而现在的cpu通常一次读取都是4个字节,而我们通常见到的分辨率都是4的整数倍,所以我们通常发现间距和图像的宽度一样(这里通常指rgb32格式或者以通道表示的yuv420p格式的y通道)。但是如果遇到一些少见的分辨率时间距和图像的宽度就不一样。 
还有一种情况是显卡,因为显卡是独立工作的,所以显卡可能和cpu的内存对齐位数是不同的,此时间距就可能和cpu上的有很大差别,例如NVIDA显卡(它的内存对齐位数超大),通常在用d3d显示的时候会用到间距。所以如果你的d3d显示程序在Intel的显卡上显示正常,而在NVIDA显卡上显示不正常,先不要怀疑显卡驱动,先看看你有没有正确处理间距的问题(亲生经历)。

间距的处理:
那么对于间距和宽度不同的时候要如何处理呢?在不同的情况下,处理不同,但是只要把握一个核心—内存对齐,就能理解。

在使用d3d做图像显示的时候,在获取显示内存空间的时候通常会获取到一个参数pitch,就是我们的间距。显卡每次都将pitch长度的数据当做一行。我们将图像数据复制过去得时候要一行一行复制,每次下一行数据的目的起始位置都是上一行的起始位置加上间距。如果是yv12这种通道表示的数据,u、v通道要相应的将行距除2。间距导致的空间内容可以不用置空。
在ffmpeg解码的时候,解码后会获取到一个参数linesize,其实也是间距。从解码后的数据内存中将数据拷贝出来的时候,需要一行一行拷贝,每一行数据的起始位置都是上一行的起始位置加上间距,一行的真正的图像数据长度就是是图像宽度(通道类型要相应除倍数)。
在用ffmpeg进行图像格式转换的时候,需要传入一个参数stride,其实也是间距。只不过这次不需要复杂的处理,只需要知道传入ffmpeg进行转换的图像数据使用的间距,然后传入就行,ffmpeg会自动根据这个值进行相应的处理。
--------------------- 
作者:太上绝情 
来源:优快云 
原文:https://blog.youkuaiyun.com/bjrxyz/article/details/52690661 
版权声明:本文为博主原创文章,转载请附上博文链接!

### 图像处理中的YUV到RGB转换 在图像处理领域,颜色空间的转换是一项常见的操作。特别是从YUV到RGB的颜色空间转换,在视频编码、解码以及显示过程中扮演着重要角色。 #### 方法一:基于公式的逐像素计算 最基础的方法是依据标准定义的线性方程组来完成这种转变。对于每个单独的像素点而言,会应用如下所示的一套公式来进色彩分量间的映射[^1]: \[ R = Y + 1.402(V - 128) \] \[ G = Y - 0.344136(U - 128) - 0.714136(V - 128) \] \[ B = Y + 1.772(U - 128) \] 这种方法虽然直观易懂,但在实际应用场景下可能因为性能原因而不被优先考虑。当面对大规模的数据集或是实时性的需求时,简单的循环遍历每一个像素并执上述运算可能会显得效率低下。 为了提高这一过程的速度,开发者们探索出了多种优化策略,比如利用SIMD指令集加速矩阵乘法的操作,或者是采用查表法减少重复计算等手段。 #### 方法二:借助第三方库实现高效转换 除了手动编写代码外,还可以依赖成熟的多媒体框架简化开发流程的同时获得更好的运表现。例如FFmpeg就是一个广泛使用的开源项目,它内部封装了一系列高效的算法用于不同格式间相互转化的任务。 具体来说,在C/C++环境中可以通过调用`libswscale`库里的API——即`sws_scale()`函数轻松达成目的。此接口允许指定源图象(通常是已经过解码得到的YUV数据)及其属性参数,并指明期望获取的目标格式(此处为RGB),从而自动完成整个变换工作流[^2]。 下面给出了一段示范性质的小程序片段展示如何运用该功能模块进一次完整的由YUV至RGB的空间跃迁: ```c #include <libavutil/imgutils.h> #include <libswscale/swscale.h> // 初始化缩放器上下文对象 struct SwsContext *sws_ctx; int width, height; // 输入图片尺寸 uint8_t* yuv_data[AV_NUM_DATA_POINTERS]; // 存储输入yuv平面数组 int linesize_yuv[AV_NUM_DATA_POINTERS]; enum AVPixelFormat src_pix_fmt = AV_PIX_FMT_YUV420P; // 创建输出缓冲区保存rgb结果 uint8_t* rgb_buffer; int line_size_rgb; enum AVPixelFormat dst_pix_fmt = AV_PIX_FMT_RGB24; // 设置Swscale配置项... sws_ctx = sws_getContext(width, height, src_pix_fmt, width, height, dst_pix_fmt, SWS_BILINEAR, NULL, NULL, NULL); if (!sws_ctx){ fprintf(stderr,"Could not initialize the conversion context!\n"); } // 执转换操作 sws_scale(sws_ctx, (const uint8_t* const*)yuv_data, linesize_yuv, 0, height, &rgb_buffer, &line_size_rgb); ``` 这段示例展示了创建一个软件尺度调整(Software Scaling Context),设置好必要的参数之后就可以直接调用`sws_scale`来进批量化的像素级转换了。需要注意的是这里假设输入是一张遵循YUV420P采样模式的标准画面;而对于其他类型的媒体文件,则需相应修改读取方式及初始化部分的相关设定。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值