OpenCV里IplImage的widthStep参数 和width参数

OpenCV里IplImage的widthStep参数 和width参数前者是表示图像的每行像素数,后者指表示存储一行像素需要的字节数。在OpenCV里边,widthStep必须是4的倍数,从而实现字节对齐,有利于提高运算速度。如果8U单通道图像宽度为3,那么widthStep是4,加一个字节补齐。这个图像的一行需要4个字节,只使用前3个,最后一个空着。也就是一个宽3高3的图像的imageData数据大小为4*3=12字节。

大家好,又见面了,我是你们的朋友全栈君。

      一直以为IplImage结构体中的widthStep元素大小等于width*nChannels,大错特错!(为了快速访问,要内存对齐啊)查看OpenCV2.1的源码,在src/cxcore/cxarray.cpp文件中,找到cvInitImageHeader函数,函数中对widthStep大小赋值如下:

  image->widthStep = (((image->width * image->nChannels *(image->depth & ~IPL_DEPTH_SIGN) + 7)/8)+ align - 1) & (~(align - 1));                                           

     其中IPL_DEPTH_SIGN的定义可以在cxtypes.h中找到,定义为:#define IPL_DEPTH_SIGN 0x80000000, align的大小为CV_DEFAULT_IMAGE_ROW_ALIGN,其大小在cxmisc.h中定义为:#define  CV_DEFAULT_IMAGE_ROW_ALIGN 4,depth取8位深度。

     根据(1)式,已知IPL_DEPTH_SIGN、align、depth 的大小,分别手动计算如下图像的widthStep:

        图像宽度     图像通道数              计算得到的widthStep

                           3                             12

                           1                             4

                           3                            16

                           1                             8

                           3                             24

                           1                             8

                           3                             12

                           1                             4

        为了进一步验证手算的正确性,我们编程实现输出widthStep的大小,程序如下:


IplImage *image_33 = cvCreateImage(cvSize(3, 3), 8, 3);
          IplImage *image_31 = cvCreateImage(cvSize(3, 3), 8, 1);
          IplImage *image_53 = cvCreateImage(cvSize(5, 3), 8, 3);
          IplImage *image_51= cvCreateImage(cvSize(5, 3), 8, 1);
          IplImage *image_73 = cvCreateImage(cvSize(7, 3), 8, 3);
          IplImage *image_71 = cvCreateImage(cvSize(7, 3), 8, 1);

          printf("%d, %d, %d, %d, %d, %d", image_33->widthStep,image_31->widthStep,
     image_53->widthStep,image_51->widthStep,image_73->widthStep,image_71->widthStep);

           运行结果为:12, 4, 16, 8, 24, 8, 与手动计算结果相同。

          从网上查阅资料,OpenCV分配的内存按4字节对齐,这样我们对上述计算的结果可以有个合理的解释,如宽度为3、通道数为3的图像,每一行需要的 实际内存长度为3*3,为了内存对齐,OpenCV会在每行末尾自动补上3个字节的内存,内存初始化都为0,所以widthStep变为了12。

widthStep大小对IplImage极为重要,在cxarray.cpp中,我们可以找到如下代码行:

image->imageSize = image->widthStep * image->height;

img->imageData = img->imageDataOrigin =(char*)cvAlloc( (size_t)img->imageSize );

      可见widthStep直接影响到imageData的数据长度。在操作imageData时,我们要避开对OpenCV自动补齐的内存进行操作,如直方图计算等。

写到这里,可能有人会问,我们平常都用widthStep = width * nChannels,怎么就没出错?我之前也一直在疑惑,合理的解释是,一般在实际应用中,图像的宽度一般为128, 256, 240, 320, 356,704等,刚好这些数字都能被4整除,widthStep刚好等于width * nChannels, 所以OpenCV并没有为这些图像分配多的内存,因此我们在对imageData做顺序操作也没出错。但是,请问谁能保证图像的宽度一定会是4的倍数? 


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/141997.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 批处理 %~dp0是什么意思「建议收藏」

    批处理 %~dp0是什么意思「建议收藏」cd/D%~dp0的意思如下:更改当前目录为批处理本身的目录比如你有个批处理a.bat在D:\qq文件夹下a.bat内容为cd/d%~dp0在这里cd/d%~dp0的意思就是cd/dd:\qq%0代表批处理本身d:\qq\a.bat~dp是变量扩充d既是扩充到分区号d:p就是扩充到路径\qqdp就是扩充到分区号路径d:\qq…

    2026年1月30日
    5
  • 常用Eclipse快捷方式

    Ctrl+1快速修复Ctrl+D:删除当前行Ctrl+Alt+↓复制当前行到下一行(复制增加)Ctrl+Alt+↑复制当前行到上一行(复制增加)Alt+↓当前行和下面一行交互位置(特

    2021年12月26日
    45
  • verilog同步fifo_verilog 异步复位

    verilog同步fifo_verilog 异步复位写在前面在上篇文章:同步FIFO的两种Verilog设计方法(计数器法、高位扩展法)中我们介绍了FIFO的基本概念,并对同步FIFO的两种实现方法进行了仿真验证。而异步FIFO因为读写时钟不一致,显然无法直接套用同步FIFO的实现方法,所以在本文我们将用Verilog实现异步FIFO的设计。1、什么是异步FIFO异步FIFO有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。在现代…

    2022年8月13日
    10
  • 【教程】使用gitee搭建免费的图床

    【教程】使用gitee搭建免费的图床前几天七牛云的免费图床测试域名回收,导致我上传的图片都不能访问!要配置自定义域名,域名还要绑定主机。没有云主机的我开始想你们搞一个免费的图床,并且数据也不会丢失呢 ?​ 想到之前自己在GitHub上搭建过一个GitHub Page ,里面可以上传图片并且图片也可以访问,这不是很好的图床吗,本地可以保存一份,并且上传到GitHub上云端也可以保存一份,还可以使…

    2022年2月27日
    60
  • vs2019中scanf返回值被忽略_vs2017scanf

    vs2019中scanf返回值被忽略_vs2017scanf一、问题再现如下是笔者在学习数据结构中的二叉树时遇到的问题:报错警告:返回值被忽略:”scanf”。二、问题分析百度一下之后大致原因是:scanf()函数在读取时不检查边界,所以可能会造成内存泄漏。有一定的安全隐患。微软公司觉得其不安全,所以在VS编译器中提供了scanf_s()函数,这是VS编译器特有的函数。强制用户提升安全性,如果想要在VS中使用scanf()函数,则可以通过一下4中方法解决。三、解决方案1、把scanf改成:scanf_s把所有的scanf()改

    2025年6月8日
    4
  • springboot2.0整合kafka_spring与mybatis整合

    springboot2.0整合kafka_spring与mybatis整合首先引入POM依赖 <!–nosql数据库–> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependenc…

    2022年9月26日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号