标准::cv的矢量::垫子

std::vector of cv::Mat

本文关键字:垫子 cv 标准      更新时间:2023-10-16

继续我的计算机视觉工作,我到了为N个相机中的一个补丁计算描述符的地步。问题是当我计算描述符时,OpenCV 中的函数是

descriptor.compute(image, vecKeypoints, matDescriptors);

其中 vecKeypointscv::KeyPoints 的向量,matDescriptors 是一个cv::Mat,根据 OpenCV 的文档,它填充了计算的描述符。

由于我有 N 个摄像机,因此我为每个摄像机

计算多个描述符,因此我为每个 N 个摄像机存储 K 个描述符。因此,我创建了一个描述符向量(即矩阵)

std::vector<cv::Mat> descriptors;

在每次迭代中,我都会计算一个新的 matDescriptor 并将其推送到向量descriptors。我看到的问题是,每个 mat描述符的数据存储地址对于向量描述符中的每个元素都是相同的

据我所知,当我vector.push_back(arg)制作 arg 的副本并将其存储在向量中时,为什么我有相同的地址?&(descriptors[0].data)不应该和&(descriptors[1].data)不同吗?

下面是代码的一般视图

std::vector<Pixel> patchPos;
std::vector<Pixel> disparityPatches;
//cv::Ptr<cv::DescriptorExtractor> descriptor = cv::DescriptorExtractor::create("ORB");
cv::ORB descriptor(0, 1.2f, 8, 0);
std::vector<cv::Mat> camsDescriptors;
std::vector<cv::Mat> refsDescriptors;
uint iPatchV = 0;
uint iPatchH = 0;
// FOR EACH BLOCK OF PATCHES (there are 'blockSize' patches in one block)
for (uint iBlock = 0; iBlock < nBlocks; iBlock++)
{
    // FOR EACH PATCH IN THE BLOCK
    for(uint iPatch = iBlock*blockSize; iPatch < (iBlock*blockSize)+blockSize; iPatch++)
    {
        // GET THE POSITION OF THE upper-left CORNER(row, col) AND
        // STORE THE COORDINATES OF THE PIXELS INSIDE THE PATCH
        for (uint pRow = (iPatch*patchStep)/camRef->getWidth(), pdRow = 0; pRow < iPatchV+patchSize; pRow++, pdRow++)
        {
            for (uint pCol = (iPatch*patchStep)%camRef->getWidth(), pdCol = 0; pCol < iPatchH+patchSize; pCol++, pdCol++)
            {
                patchPos.push_back(Pixel(pCol, pRow));
            }
        }
        // KEYPOINT TO GET THE DESCRIPTOR OF THE CURRENT PATCH IN THE REFERENCE CAMERA
        std::vector<cv::KeyPoint> refPatchKeyPoint;
        //          patchCenter*patchSize+patchCenter IS the index of the center pixel after 'linearizing' the patch
        refPatchKeyPoint.push_back(cv::KeyPoint(patchPos[patchCenter*patchSize+patchCenter].getX(),
                                                patchPos[patchCenter*patchSize+patchCenter].getY(), patchSize));
        // COMPUTE THE DESCRIPTOR OF THE PREVIOUS KEYPOINT
        cv::Mat d;
        descriptor.compute(Image(camRef->getHeight(), camRef->getWidth(), CV_8U, (uchar*)camRef->getData()),
                           refPatchKeyPoint, d);
        refsDescriptors.push_back(d); // This is OK, address X has data of 'd'
        //FOR EVERY OTHER CAMERA
        for (uint iCam = 0; iCam < nTotalCams-1; iCam++)
        {
            //FOR EVERY DISPARITY LEVEL
            for (uint iDispLvl = 0; iDispLvl < disparityLevels; iDispLvl++)
            {
                ...
                ...
                //COMPUTE THE DISPARITY FOR EACH OF THE PIXEL COORDINATES IN THE PATCH
                for (uint iPatchPos = 0; iPatchPos < patchPos.size(); iPatchPos++)
                {
                    disparityPatches.push_back(Pixel(patchPos[iPatchPos].getX()+dispNodeX, patchPos[iPatchPos].getY()+dispNodeY));
                }
            }
            // KEYPOINTS TO GET THE DESCRIPTORS OF THE 50.DISPAIRED-PATCHES IN CURRENT CAMERA
            ...
            ...
            descriptor.compute(Image(camList[iCam]->getHeight(), camList[iCam]->getWidth(), CV_8U, (uchar*)camList[iCam]->getData()),
                               camPatchKeyPoints, d);
            // First time this executes is OK, address is different from the previous 'd'
            // Second time, the address is the same as the previously pushed 'd'
            camsDescriptors.push_back(d);
            disparityPatches.clear();
            camPatchKeyPoints.clear();
        }
    }
}

Mat 是像素的某种智能指针,因此 Mat a=b 将具有 a 和 b 的共享像素push_back。

如果您需要"深层副本",请使用 Mat::clone()

cv::Mat 隐式共享数据,因此每当使用赋值运算符或复制构造函数(由代码中的push_back使用)复制数据时,数据不会复制,而是与新对象共享。对其中一个中的数据所做的任何更改都将反映在另一个中。事实上,正如你所观察到的,指针是相同的。

在这种情况下,你只需要为每次迭代创建一个新的cv::Mat:

for (...) {
    cv::Mat d;
    descriptor.compute(..., d);
    camsDescriptors.push_back(d);
}

我一直在寻找的答案是,如果你想预先分配矩阵,这里有一种方法可以在没有副本、临时或意外共享的情况下做到这一点:

std::vector v;
v.reserve(N);
for (size_t i = 0; i < N; i++) {
    // Uninitialized Mat of specified size, header constructed in place
    v.emplace_back(height, width, CV_8UC1);
}

将此与两种看起来有效但略微错误的方法进行比较,当与非共享数据结构一起使用时,它们看起来像常见的C++代码:

std::vector v;
v.resize(N, cv::Mat(height, width, CV_8UC1));

std::vector v;
v.reserve(N);
cv::Mat temp(height, width, CV_8UC1);
for (size_t i = 0; i < N; i++) {
    // Uninitialized Mat of specified size, header copied from temp
    v.push_back(temp);
}

这两种情况下,数据都将在幕后在向量的所有元素之间共享,这根本不是我们在创建向量时想要的!

在每个循环中,请确保在附加到向量之前调用函数 cv::Mat::release()。