opencv矩阵数据是不是保证是连续的?

Posted

技术标签:

【中文标题】opencv矩阵数据是不是保证是连续的?【英文标题】:Is opencv matrix data guaranteed to be continuous?opencv矩阵数据是否保证是连续的? 【发布时间】:2015-11-12 05:53:56 【问题描述】:

我知道,opencv 矩阵中包含的数据不能保证是连续的。为了清楚起见,这里有一段来自Opencv documentation:

Opencv 提供了一个名为 isContinuous() 的函数来测试给定矩阵的数据是否连续。我的问题是

(1) 如果我创建一个新矩阵如下

Mat img = cv::imread(img_name)

img中的数据是否保证连续?

(2) 我知道通过从现有矩阵中借用数据来创建新矩阵会导致数据不连续

cv::Mat small_mat = large_mat.col(0);

上述代码通过借用 large_mat 的第 0 列创建了一个新矩阵 small_mat ,导致 small_mat 中的数据不连续。那么问题来了,如果我创建一个全新的矩阵而不从现有矩阵中借用数据,那么这个全新的矩阵是否会有不连续的数据?

(3) 下面的代码是否保证创建一个包含连续数据的矩阵?

cv::Mat mat(nRows, nCols, CV_32FC1);

【问题讨论】:

我猜如果你使用像 IPP 这样的奇数图像尺寸的东西,它不能保证是连续的。因此,它可能取决于您特定的 OpenCV 构建。 【参考方案1】:

您可以在 OpenCV 文档中看到isContinuous:

如果矩阵元素在每一行的末尾没有间隙地连续存储,则该方法返回 true。否则,它返回 false。显然,1x1 或 1xN 矩阵总是连续的。 使用 Mat::create() 创建的矩阵总是连续的。 但是如果您使用 Mat::col()、Mat::diag() 等提取矩阵的一部分,或者构造外部分配数据的矩阵头,这样的矩阵可能不再有这个属性。

因此,只要您正在创建一个新矩阵(即您正在调用create),您的矩阵就会是连续的。

create 的作用类似于:

    如果当前数组形状和类型与新的匹配,则立即返回。否则,通过调用 Mat::release() 取消引用之前的数据。 初始化新标头。 分配total()*elemSize()字节的新数据。 分配新的、与数据关联的引用计数器并将其设置为 1。

这意味着当您(隐式)调用 create 时,矩阵将是连续的(第 3 步)。


您的问题

如果我用imread 创建一个新矩阵,数据保证是连续的

是的,因为imread在内部调用create

我知道通过从现有矩阵中借用数据来创建新矩阵会导致数据不连续。

正确,数据将不连续。要使新矩阵连续,可以调用clone(),它调用create来创建新矩阵。

如果我创建一个全新的矩阵而不从现有矩阵中借用数据,那么全新的矩阵是否会有不连续的数据?

是的,构造函数内部调用create

矩阵构造器保证创建数据连续的矩阵?

是的,构造函数内部调用create

这是一个总结的小例子:

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()

    // Read image
    Mat img = imread("path_to_image");
    cout << "img is continuous? " << img.isContinuous() << endl; 
    // Yes, calls create internally

    // Constructed a matrix header for externally allocated data
    Mat small_mat = img.col(0);
    cout << "small_mat is continuous? " << small_mat.isContinuous() << endl; 
    // No, you're just creating a new header.

    // Matrix (self) expression
    small_mat = small_mat + 2;
    cout << "small_mat is continuous? " << small_mat.isContinuous() << endl; 
    // No, you're not even creating a new header

    // Matrix expression
    Mat expr = small_mat + 2;
    cout << "expr is continuous? " << expr.isContinuous() << endl; 
    // Yes, you're creating a new matrix

    // Clone
    Mat small_mat_cloned = img.col(0).clone();
    cout << "small_mat_cloned is continuous? " << small_mat_cloned.isContinuous() << endl; 
    // Yes, you're creating a new matrix

    // Create
    Mat mat(10, 10, CV_32FC1);
    cout << "mat is continuous? " << mat.isContinuous() << endl; 
    // Yes, you're creating a new matrix

    return 0;

【讨论】:

【参考方案2】:

当您使用Mat::create 创建Mat 时,它是连续的。这样的操作比如显式Mat::create,或者隐式Mat::cloneMat::Mat(...)

【讨论】:

【参考方案3】:

存储在 OpenCV cv::Mats 中的数据在内存中并不总是连续的,这可以通过 API Mat::isContinuous() 进行验证。相反,它遵循以下规则:

    imread()clone()构造函数 创建的矩阵将始终是连续的。 矩阵不连续的唯一情况是它从现有矩阵借用数据(即从大矩阵的 ROI 创建),但借用的数据是连续的在大矩阵中,包括 借用一行; 借用多行但具有完整的原始宽度。

来自my blog 的以下代码以更好的方式演示了这一点(请参阅内联 cmets 以获得进一步的解释)。

std::vector<cv::Mat> mats(7);

// continuous as created using constructor
mats[0] = cv::Mat::ones(1000, 800, CV_32FC3);      

// NOT continuous as borrowed data is not continuous (multiple rows and not full original width)
mats[1] = mats[0](cv::Rect(100, 100, 300, 200));     

// continuous as created using clone()
mats[2] = mats[1].clone();               

// continuous for single row always    
mats[3] = mats[2].row(10);             

// NOT continuous as borrowed data is not continuous (multiple rows and not full original width)         
mats[4] = mats[2](cv::Rect(5, 5, 100, 2));     

// continuous as borrowed data is continuous (multiple rows with full original width)   
mats[5] = mats[2](cv::Rect(0, 5, mats[2].cols, 2));

// NOT continuous as borrowed data is not continuous (multiple rows and not full original width) 
mats[6] = mats[2].col(10);                            

【讨论】:

以上是关于opencv矩阵数据是不是保证是连续的?的主要内容,如果未能解决你的问题,请参考以下文章

提取一个连续的 OpenCV cuda::GpuMat?

opencv2 矩阵方式 resize图像缩放代码(转载)

OpenCV 错误:图像步长错误(矩阵不连续)

如何像矩阵元素一样访问opencv中的vector<vector<Point>>轮廓?

OpenCV isContinuous()函数简析

OpenCV isContinuous()函数简析