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
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:
operator+
does