2

继续我的计算机视觉内容,我正在计算 N 个摄像机中的一个补丁的描述符。问题是当我计算描述符时,OpenCV 中的函数是

descriptor.compute(image, vecKeypoints, matDescriptors);

其中vecKeypoints是一个向量,cv::KeyPointsmatDescriptors一个向量cv::Mat,根据 OpenCV 的文档,它会填充计算的描述符。

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

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

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

据我所知,当我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();
        }
    }
}
4

3 回答 3

3

Mat 是像素的某种智能指针,因此 Mat a=b 将共享 a 和 b 的像素。push_back() 的类似情况

如果您需要“深拷贝”,请使用 Mat::clone()

于 2013-10-22T17:13:08.007 回答
2

cv::Mat implicitly shares data, so any time you copy it using the assignment operator or the copy constructor, used by push_back in your code, the data is not copied but shared with the new object. Any changes to the data in one will be reflected in the other. In fact, as you have observed, the pointers are the same.

In this case you just need to create a new cv::Mat for every iteration:

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

The answer I was searching for, is if you want to pre-allocate the matrices, here is the way to do it without copies, temporaries, or accidental sharing:

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);
}

Compare this to two efficient looking but subtly wrong ways to do this, that look like common C++ code when used with non-shared data structures:

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

or

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);
}

In both these cases the data will be shared behind the scenes between all elements of the vector, which is not at all what we want when creating a vector!

于 2018-03-21T21:54:29.357 回答
0

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

于 2016-03-02T17:32:26.197 回答