-1

I have strange problem. I am taking a set of photos of a sequential event via webcam and OpenCV. Storing them in a vector variable. At the end of the capture function, all I got is the last capture.

I think I have a fundamental problem with vectors. Functions are below

void eventCapture()
{
    vector<cv::Mat> capturedAll;
    getCaptures(capturedAll, 10, 500);

    int csize = capturedAll.size();
    // Here gives always the last capture
    // It is not related with imshow
    // imwrite also saves the last capture as wrong
    for (int i = 0; i < 10; i++) {
        cv::imshow("Images", capturedAll[i]);
        string imgname = "imgcaps" + to_string(i) + ".jpg";
        cv::imwrite(imgname, capturedAll[i]);
        cv::waitKey(100);
    }
}

void getCaptures(vector<cv::Mat>& capStore, int count, int tdif)
{
    QElapsedTimer capTimer;
    capTimer.start();

    for (int i = 0; i < count; i++) {
        capTimer.restart();

        // get_capture takes a photo from webcam
        cv::Mat capMat = webCam.get_capture();
        capStore.push_back(capMat);

        string imgname = "localsave" + to_string(i) + ".jpg";
        // Saved image here is giving correct result
        cv::imwrite(imgname, capMat);

        while (!capTimer.hasExpired(tdif))
            qApp->processEvents();
    }
}

I also tried to use iterator but it has given same wrong result.

meakcey
  • 83
  • 1
  • 10
  • show the code of get_capture(). – eyllanesc Dec 30 '17 at 16:13
  • 1
    All I can do is guess, but I think that you should negate hasExpired in the while loop, cause currently you are getting 10 images microseconds away. No wonder they look the same... – bartop Dec 30 '17 at 16:17
  • get_capture is a pseudo code there. I have a different camera class but output of that function is correct. As I noted in getCaptures function, if I directly save the captured images I get the correct result. But if I assign it to a vector and try to get from there, I am getting last capture. – meakcey Dec 30 '17 at 16:23
  • they are not microseconds away actually. I could set time difference between captures in miliseconds. So 500 ms is highly long time difference and I am sure the scene in front of the camera is changing between captures. – meakcey Dec 30 '17 at 16:27
  • I have copied here wrong, excuse me. It is just a delay between captures. – meakcey Dec 30 '17 at 16:47
  • Possible duplicate of [Deep Copy of OpenCV cv::Mat](https://stackoverflow.com/questions/21659496/deep-copy-of-opencv-cvmat) – drescherjm Dec 30 '17 at 17:07
  • Also: https://stackoverflow.com/questions/13713625/is-cvmat-class-flawed-by-design – drescherjm Dec 30 '17 at 17:08
  • show code of webcam. Probably you just have to call return yourMat.clone() to not share memory between multiple captures. – Micka Dec 30 '17 at 20:49

2 Answers2

1

I think problem is about the pointers of cv::Mat. Changing below section solved the problem. But I don't understand exactly. Every time in the loop I am initializing capMat again.

    cv::Mat capMat = webCam.get_capture();
    capStore.push_back(capMat.clone());
meakcey
  • 83
  • 1
  • 10
  • Looking at the doucumentation for `Mat (Mat &)`: https://docs.opencv.org/trunk/d3/d63/classcv_1_1Mat.html#a294eaf8a95d2f9c7be19ff594d06278e makes a shallow copy you need to use clone to make a deep copy. – drescherjm Dec 30 '17 at 17:02
  • probably within webCam the data memory is reused for new images. Your capMat is only a header around that provided image data unless you deep-copy or create new Mat memory. – Micka Dec 30 '17 at 22:48
0

If you are working in Qt (as your label suggest) best way to avoid this altogether is to deep-copy the image to QImage to avoid creating copies of cv::Mat. This is safe way to create QImage from cv::Mat that also takes care of releasing memory when last QImage reference is removed:

cv::Mat* capture;
size_t bufferSize = capture->cols * capture->rows * capture->channels();
auto pixelFormat = toPixelFormat(capture->format);

auto outputBuffer = new uchar[bufferSize];

// This will not work for sparse mats - you have to copy row-by-row
memcpy(outputBuffer, capture->data, bufferSize);

// Have to pass stride since QImage is padding the rows for non power-of-two sizes
QImage image(outputBuffer, capture->cols, capture->rows, capture->cols * capture->channels(), pixelFormat, DeleteImageData, outputBuffer);

You will need to convert cv::Mat format to correct QImage::Format, e.g.:

QImage::Format ImageFilterPrivate::getOpenCVType(int openCvFormat) {
    switch(openCvFormat) {
        case CV_8UC4: return QImage::Format_ARGB32;
        case CV_8UC3: return QImage::Format_RGB888;
        // ... etc.
    }
}

Some of the OpenCV formats (e.g. 16bit grayscale doesn't have matching types in Qt). You also need to give a function that will delete allocated data after last reference of QImage is lost to avoid memory leaks (can be passed as lambda):

void DeleteImageData(void* data) {
  auto buffer = static_cast<unsigned char*>(data);
  delete buffer;
}
shrpq
  • 467
  • 3
  • 8