[TOC]

图像数据是否做字节对齐,可能影响到逐像素的遍历操作。OpenCV中的图像是HWC维度顺序存储,我们需要注意WC维度是否做4字节对齐。

这里使用的测试图如下:

注意它的宽度是471像素,是不能被4整除的。

1. IplImage的data字段,是char*类型,是4字节对齐。

const char* im_pth = "image.png";

IplImage* im = cvLoadImage(im_pth);

img->widthStep等于align_up(im->width*im->channels) //向上取整到最近的4的倍数

e.g. width=471, channels=3, widthStep=1416

2. 手动创建的Mat通常是没有字节对齐的

e.g.

FcImage fc_im = fc_load_image(im_pth);

int im_h = fc_im.h;

int im_w = fc_im.w;

int im_c = fc_im.c;

cv::Mat image = cv::Mat();

image.create(im_h, im_w, CV_8UC3);

cout << image.step << endl; //1413

来看看不对齐是什么效果,这里使用了fc image,提供的是对齐的图像数据,然后拷贝给手动创建的Mat,也就是不做对齐的:

FcImage fc_im = fc_load_image(im_pth);

int im_h = fc_im.h;

int im_w = fc_im.w;

int im_c = fc_im.c;

cv::Mat image = cv::Mat();

image.create(im_h, im_w, CV_8UC3);

for (int h = 0; h < im_h; h++) {

for (int w = 0; w < im_w; w++) {

for (int c = 0; c < im_c; c++) {

int src_idx = h * im_w*im_c + w * im_c + c;

int dst_idx = h * im_w*im_c + w * im_c + c;

image.data[dst_idx] = fc_im.data[src_idx];

}

}

}

std::cout << image.step << std::endl;

cv::imshow("image", image);

cv::waitKey(0);

3. 从IplImage转过来的Mat,是字节对齐的

早期的opencv2可以这样写:

const char* im_pth = "image.png";

IplImage* ipl = cvLoadImage(im_pth);

Mat mat(ipl); //或Mat mat(ipl, true)表示拷贝数据

后来的opencv不让这么写了,需要用cvarrayToMat函数:

IplImage* ipl = cvLoadImage(im_pth);

int im_h = ipl->height;

int im_w = ipl->width;

cv::Mat mat = cv::cvarrToMat(ipl);

cv::imshow("image", mat);

cv::waitKey(0);

4. 总结

如果需要逐像素操作,那么最好是定义并使用linebytes属性来替代每一处使用到的im_width*im_channel,因为linebytes=align_up(im_width*im_channel, 4)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐