Search code examples
c++opencvvideoroi

Unable to draw rectangle over webcam video OpenCV C++


Im trying to create a rectangle ROI over webcam video. But this code is crashing

#include "opencv2/opencv.hpp"
#include <iostream>
#include <bits/stdc++.h> 
using namespace std;
using namespace cv;

int main(){
  VideoCapture cap(0); 
  if(!cap.isOpened()){
    cout << "Error opening video stream or file" << endl;
    return -1;
  }
Rect Roi(1,1,100,200);

 while(1){

    Mat frame;
    cap >> frame;
    frame.copyTo(frame(Roi));
    // If the frame is empty, break immediately
    if (frame.empty())
        break;
    // Display the resulting frame
    imshow( "Frame", frame );
    moveWindow("Frame",500,100);

    // Press  ESC on keyboard to exit
    char c=(char)waitKey(25);
    if(c==27)
      break;
  }
  cap.release();
  destroyAllWindows();
  return 0;
}

// g++ cam_roi.cpp pkg-config --cflags --libs opencv4; ./a.out

ERROR ==> [ WARN:0] global ../modules/videoio/src/cap_gstreamer.cpp (961) open OpenCV | GStreamer warning: Cannot query video position: status=0, value=-1, duration=-1 terminate called after throwing an instance of 'cv::Exception' what(): OpenCV(4.5.1) ../modules/core/src/matrix_wrap.cpp:1188: error: (-215:Assertion failed) !fixedSize() || ((Mat*)obj)->size.operator()() == Size(_cols, _rows) in function 'create'

Aborted


Solution

  • the core issue is

    (-215:Assertion failed) !fixedSize() || ((Mat*)obj)->size.operator()() == Size(_cols, _rows) in function 'create'
    

    (the gstreamer line is just a warning)

    this error comes from that line (it's not obvious but copyto involves a Mat::create call):

        frame.copyTo(frame(Roi));
    

    copyTo gets the destination Mat as the argument. it can resize the destination in some circumstances.

    frame(Roi) is a "view" into frame, and views can't be resized, i.e. they are "fixed size".

    you seem to want to take that subregion and put it back into frame.

    if that line of code worked, it would do the opposite, i.e. put the data from the whole frame into a subregion of itself...

    simply do this:

    frame = frame(Roi);
    

    this will keep the view. you can use it as usual. the whole data of frame will live as long as the view.

    the view's data will not be contiguous, if that's a concern.

    to "fix" that, you can create a copy that'll be contiguous:

    frame = frame(Roi).clone();