Search code examples
c++opencvline

How to draw a line on frame from live video feed in opencv c++


I want to draw a line on opencv frame in c++. For this I have below code where I am using setMouseCallback(winName, onMouse, NULL);. In below code I am using image:

Mat src;
const char* winName = "Image";
int start_x = 0;
int start_y = 0;
bool run_once = false;

void onMouse(int event, int x, int y, int f, void*) 
{

    if (cv::EVENT_LBUTTONDOWN)
    {
        if (f == 1)
        {
            if (run_once == false)
            {
                start_x = x;
                start_y = y;
                cout << "start x,y is : " << x << y << endl;
                run_once = true;

            }
        }
        if (f == 3)
        {
            cout << "start x,y is : " << start_x << start_y << endl;
            int end_x = x;
            int end_y = y;
            cout << "end x,y  is : " << end_x << end_y << endl;
            cv::line(src, cv::Point(start_x, start_y), cv::Point(end_x, end_y), Scalar(255), 2, 8, 0);
            imshow(winName, src);
            run_once = false;
        }


    }
}

int main()
{
    src = imread(<img path>, 1);

    namedWindow(winName, WINDOW_NORMAL);
    setMouseCallback(winName, onMouse, NULL);
    imshow(winName, src);
    while(1)
    {


    }

}

Using above code, if I left click using mouse on frame, it records start_x start_y. I drag my mouse to left/right and then right click and it records end_x end_y and simply draws a line and display it. This works fine but I want to achieve this functionality in live video frame.

The issue which I am facing in live video frame is that, in live video feed we are constantly displaying the frame in while(1) loop thus the line which is drawn is removed in next frame

void onMouse(int event, int x, int y, int f, void*)
{
   /*
    * SAME AS ABOVE
    */
}

int main(int, char**)
{
    VideoCapture cap(1); // open the default camera
    if (!cap.isOpened())  // check if we succeeded
        return -1;
    namedWindow(winName, WINDOW_NORMAL);
    setMouseCallback(winName, onMouse, NULL);


    for (;;)
    {

        cap >> src; // get a new frame from camera
        imshow(winName, src);
        if (waitKey(30) >= 0) break;
    }
    // the camera will be deinitialized automatically in VideoCapture destructor
    return 0;
}

In above code, we have onMouse function where we are using imshow to show the line drawn on frame but we also have imshow in while(1) loop thus the drawn line is not shown.

Is there anyway I can draw line on live video feed frame. Please help. Thanks


Solution

  • As @Sunreef suggested, you can create separate cv::Mat to keep picture with lines only and display src combined with this picture

    // #0 NEW - add declaration of lines here so this Mat is visible in both onMouse and main scope
    cv::Mat src, lines;
    
    void onMouse(int event, int x, int y, int f, void*)
    {        
        if (f == 3)
        {
            cout << "start x,y is : " << start_x << start_y << endl;
            int end_x = x;
            int end_y = y;
            cout << "end x,y  is : " << end_x << end_y << endl;
    
            // #1 NEW - draw line into lines instead of src
            cv::line(lines, cv::Point(start_x, start_y), cv::Point(end_x, end_y), Scalar(255), 2, 8, 0);
    
            // #2 NEW - remove unnecessary imshow here
    
            run_once = false;
         }
    }
    
    int main(int, char**)
    {
        for (;;)
        {
            cap >> src; 
    
            // #3 NEW - init lines once, to be the same size, same type as src filled with zeros
            if(lines.empty()) lines = cv::Mat::zeros(src.size(), src.type());
    
            // #4 NEW - show lines combined with lines            
            imshow(winName, lines + src);
    
            if (waitKey(30) >= 0) break;
        }
        return 0;
    }
    

    This way only lines image is updated in onMouse (#1). No need to show it in onMouse event (#2) since it will be displayed anyway in the main loop (#4). But before you actualy display lines you overlay (add) them to the src image. The only tricky part is to initialize lines as soon as you know the size and type of src (#3). And remember to declare the lines first (#0) so that the image is visible globally, just like src.

    I also suggest getting familiar with: