OpenCV源代码赏析: Mat对象step属性含义及使用深入分析

一、引子

有个CSDN粉丝博友“CP猫”前2天和我联系,说他也在研究CLAHE算法,遇到了OpenCV Mat对象的step属性访问的问题,问为啥一个象step这样的数组可以强制转为为一个整数输出,且输出值为数组的第一个元素,为此他昨天还专门写了篇博文《为什么OpenCV图像Mat矩阵的step属性能转换为整数输出?》。正好这近两个月来我一直断断续续的在研究CLAHE算法,在初始阶段恰好也有他这样的疑问,后来通过深入分析代码终于理解了,结合CP猫博主的疑问,老猿就此深入分析一下。

二、关于step属性的含义

CP猫博友的文章《为什么OpenCV图像Mat矩阵的step属性能转换为整数输出?》介绍了一下step属性,原文是这么介绍的:“step是个数组,用于存储每一维元素的大小(单位字节),如step[0]就是一维元素的个数,具体到图像来说,step[0]就是图像每行像素占用的字节数,step[1]就是每列像素占用的字节数”。

以上说法不全对,其中有几个说法有问题:

  1. step不是一个数组,而是一个结构化的类型,提供了通过类似数组下标方式访问的方法,在访问上可以姑且认为是个数组。这个方法挺有用,随后详细介绍,大家可以好好品味一下;
  2. step中通过下标能访问的数据如果认为是一个数组,这个数组的每个元素并不是存储每一维元素的大小,step[1]更不是每列像素占用的字节数。

怎么解释step的含义呢?在此老猿引用csdn d_a_r_k博友转载的博文
Mat中step的含义》的介绍,之所以不引用原文是因为 d_a_r_k博友转载博文给出的原文链接已经不可访问。

step这里指出的是图像在各个梯级上的字节数大小,而这里的梯级指的是构成图像的各层次。

以三维的Mat数据布局为例:
在这里插入图片描述
三维图像由一个一个平面(第一级)构成,每一个平面由一行一行(第二级)像素构成,每行由一个一个像素点(第三级)构成。因此三维图像中step[0]是面占用空间的大小,step[1]是行占用空间的大小,step[2]是像素点占用空间的大小。

同理:二维图像由一行一行(第一级)构成,而每一行又由一个一个点(第二级)构成。step[0]是行占用空间的大小,step[1]是像素点占用空间的大小。

因此Mat中的step[0]就是每个图像构成要素的第一级在内存中占据的字节数量。例如,二维图像中step[0]就是每一行(第一级)在矩阵内存中,占据的字节的数量。也就是说step[i]就是第i+1级在矩阵内存中占据的字节的数量。

所以在CP猫博友的文章《为什么OpenCV图像Mat矩阵的step属性能转换为整数输出?》的代码案例中,输出信息为:

图像的分辨率为:1023×681
step =28FC40,stepCast =3069,step[0]=3069,step[1]=3

三、为什么step对象可以转换成整型呢?

弄清楚了step的含义,我们来分析CP猫博友的问题。要弄清楚这个问题,必须阅读OpenCV相关的源码。

3.1、MatStep相关源代码

Mat对象的step属性实际上是一个MatStep对象,MatStep的定义在源码的build\include\opencv2\core\Mat.hpp文件下,具体定义如下:

struct CV_EXPORTS MatStep
{
    MatStep();
    explicit MatStep(size_t s);
    const size_t& operator[](int i) const;
    size_t& operator[](int i);
    operator size_t() const;
    MatStep& operator = (size_t s);

    size_t* p;
    size_t buf[2];
protected:
    MatStep& operator = (const MatStep&);
};

其相关实现代码在build\include\opencv2\core\Mat.inl.hpp内,具体代码如下:

/ MatStep 

inline
MatStep::MatStep()
{
    p = buf; p[0] = p[1] = 0;
}

inline
MatStep::MatStep(size_t s)
{
    p = buf; p[0] = s; p[1] = 0;
}

inline
const size_t& MatStep::operator[](int i) const
{
    return p[i];
}

inline
size_t& MatStep::operator[](int i)
{
    return p[i];
}

inline MatStep::operator size_t() const
{
    CV_DbgAssert( p == buf );
    return buf[0];
}

inline MatStep& MatStep::operator = (size_t s)
{
    CV_DbgAssert( p == buf );
    buf[0] = s;
    return *this;
}


扯一下,说实话,这代码看懂容易,但看了这C++代码让老猿觉得自己真low了,第一次知道结构可以象类一样的定义属性,也可以这样定义访问方法。

3.2、源代码分析

针对CP猫博友的问题,我们来看几个关键的源代码:

  1. MatStep的两个operator[]方法,这个是决定了MatStep对象可以下标访问的实现方法,就是重载下标访问符号"[]";
  2. MatStep的数据实际上就是放在类型为size_t大小为2的buf缓冲区中,因此它实际上是只能支持二维图像的处理;
  3. 类型转换没那么直观,实际上是通过重载operator size_t()重载size_t来访问的,size_t实际上vc定义的标准数据类型,其类型实际上就是无符号整型。具体定义如下:
#ifdef _WIN64
    typedef unsigned __int64 size_t;
 #else
    typedef unsigned int     size_t;
 endif

因此CP猫博友在将step使用 static_cast<int>(img.step)转换成int型时,实际上就调用了重载的size_t()操作符,因此返回了缓冲区的第一个元素。

备注:这个地方老猿没有细研究为什么int强制类型转换会触发size_t()操作,按理是触发int()操作,虽然size_t与int是有关系,但关系没有这么直接,但个人觉得不重要,就不进一步研究了。

三、小结

本文详细介绍了OpenCV Mat对象step属性含义,并基于OpenCV关于MatStep类型的源代码对step数据的访问机制进行了深入分析,从而解答了博友关于数组为什么强制类型转换会变为一个返回数组第一个元素的问题。

文章中列举的这样简单紧凑的源码,实现了数据的简捷高效访问,看了之后是否深受启发?

更多图像处理的内容请参考专栏《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》、《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》及《图像处理基础知识》的介绍。

写博不易,敬请支持:

如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

如对文章内容存在疑问,可在博客评论区留言,或关注:老猿Python 微信公号发消息咨询,也可以扫描博客首页左边的二维码关注公众号。

关于老猿的付费专栏

  1. 付费专栏《https://blog.csdn.net/laoyuanpython/category_9607725.html 使用PyQt开发图形界面Python应用》专门介绍基于Python的PyQt图形界面开发基础教程,对应文章目录为《 https://blog.csdn.net/LaoYuanPython/article/details/107580932 使用PyQt开发图形界面Python应用专栏目录》;
  2. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10232926.html moviepy音视频开发专栏 )详细介绍moviepy音视频剪辑合成处理的类相关方法及使用相关方法进行相关剪辑合成场景的处理,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/107574583 moviepy音视频开发专栏文章目录》;
  3. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10581071.html OpenCV-Python初学者疑难问题集》为《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的伴生专栏,是笔者对OpenCV-Python图形图像处理学习中遇到的一些问题个人感悟的整合,相关资料基本上都是老猿反复研究的成果,有助于OpenCV-Python初学者比较深入地理解OpenCV,对应文章目录为《https://blog.csdn.net/LaoYuanPython/article/details/109713407 OpenCV-Python初学者疑难问题集专栏目录
  4. 付费专栏《https://blog.csdn.net/laoyuanpython/category_10762553.html Python爬虫入门 》站在一个互联网前端开发小白的角度介绍爬虫开发应知应会内容,包括爬虫入门的基础知识,以及爬取CSDN文章信息、博主信息、给文章点赞、评论等实战内容。

前两个专栏都适合有一定Python基础但无相关知识的小白读者学习,第三个专栏请大家结合《https://blog.csdn.net/laoyuanpython/category_9979286.html OpenCV-Python图形图像处理 》的学习使用。

对于缺乏Python基础的同仁,可以通过老猿的免费专栏《https://blog.csdn.net/laoyuanpython/category_9831699.html 专栏:Python基础教程目录)从零开始学习Python。

如果有兴趣也愿意支持老猿的读者,欢迎购买付费专栏。

老猿Python,跟老猿学Python!

☞ ░ 前往老猿Python博文目录 https://blog.csdn.net/LaoYuanPython
  • 27
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LaoYuanPython

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值