I am printing the pixel location on mouse left button click like this:
void CallBackFunc(int event, int x, int y, int flags, void* values)
{
if ( event == EVENT_LBUTTONDOWN )
{
cout << "Left button clicked at (" << x << ", " << y << ")" << endl;
}
}
int main(int argc, char** argv)
{
Mat img = imread("yava.bmp");
namedWindow(" Window", 1);
setMouseCallback(" Window", CallBackFunc, NULL);
position(x,y); //to be implemneted
imshow(" Window", img);
waitKey(0);
return 0;
}
Now I want to get this location, i.e x and y from CallBackFunc to back in main(), which will be processed by my position(x,y) function.
How do i do this?
First of all, read the documentation on setMouseCallback, etc.
This is what I would do (this is the typical example) without changing your code too much:
vector<Point> points; // Store the points as global variables
void CallBackFunc(int event, int x, int y, int flags, void* values)
{
if ( event == EVENT_LBUTTONDOWN )
{
points.push_back(Point(x,y));
}
}
int main(int argc, char** argv)
{
Mat img = imread("yava.bmp");
namedWindow(" Window", 1);
setMouseCallback(" Window", CallBackFunc, NULL); // Add the pointer to the callback
while (1)
{
//Draw your points
for (int i=0;i<points.size();i++) circle(img,points.at(i),3,CV_RGB(255,0,0),-1);
imshow(" Window", img);
waitKey(10);// -> This is when your callback is called everytime you click
}
return 0;
}
For a better solution, consider avoiding global variables and use a class for storing those "clicks".
Related
I've been trying to get information about pixels that I click on. I've been using function SetMouceCallback and honestly I don't know exactly how it works.
void startGUI()
{
VideoCapture video;
Mat frame1;
video.open("/Users/Dominik/Desktop/Legia22.mov");
video.read(frame1);
Mat img = frame1.clone();
namedWindow("My Window", 1);
setMouseCallback("My Window", onMouse, &img);
imshow("My Window", img);
//********* I want to use leftClicks[] here
waitKey(0);
}
static void onMouse(int event, int x, int y, int, void* param)
{
Mat &img = *((Mat*)param);
static int counter=0;
Point leftClicks[4];
if ( event == EVENT_LBUTTONDOWN && counter<4)
{
Vec3b val = img.at< Vec3b >(y,x);
cout << "left button clicked pos - (" << x << ", " << y << ")" <<"\t colour bgr: "<< val << endl;
leftClicks[counter].x = x;
leftClicks[counter].y = y;
circle(img, Point(x,y), 4, SCALAR_BLUE, -1);
imshow("My Window", img);
counter++;
}
}
The following code works fine. Window is shown and on the first frame I can draw 4 circles. The points are printed in the terminal.
So basically what I'm trying to do is to get the array leftClicks[4] out of the function for further processing in other functions, eg. where the ****** are.
The problem is that the type of function onMouse has to be void and the arguments can't be changed.
I noticed that the function setMouseCallback has pretty specified what onMouse function has to be and no changes to the function onMouse could be made...
So how can I get the array leftClicks[] out of the function onMouse?
So this is my attempt at setting MouseEvent in openCV, my point is to draw some thing at given point according to mouse event. Any idea what's wrong?
I followed this tutorials: https://docs.opencv.org/3.3.0/db/d5b/tutorial_py_mouse_handling.html
#include<opencv2\core\core.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\ml\ml.hpp>
#include<iostream>
#include<conio.h>
using namespace std;
using namespace cv;
int radius = 5;
Mat img;
int ix = 1;
int iy = 1;
//mouse callback function
void drawCircle(int event, int x, int y, int, void* param) {
if (event == CV_EVENT_LBUTTONDOWN)
{
cout << "x = " << x
<< "\t y = " << y
<< endl;
circle(img, Point(x, y), 10, Scalar(255,255,255), 10);
}
//line(img, Point(10, 10), Point(x, y), Scalar(0, 0, 255), 3);
}
int main() {
img = imread("image.png");
if (img.empty()) {
cout << "\nerror reading image" << endl;
_getch();
return -1;
}
namedWindow("Image",1);
imshow("Image", img);
setMouseCallback("Image", drawCircle);
waitKey(0);
return 0;
}
Why doesn't it draw circle or line on my image? Thanks!
You probably need to force a redraw of the window to reflect the changes. I.e. replace your waitKey(0) by something like:
while (waitKey(20) != 27) // wait until ESC is pressed
{
imshow("Image", img);
}
Alternatively, you may be able to just add a call to imshow to the drawCircle callback.
So, after following the advise from the stackexchange users about mouse event, I was able to understand and implement some simple task using mouse clicks. So, the next goal was to draw a simple line using the mouse left click and mouse right click. Unfortunately, I can't see any line after I implemented my program.
int x,y;
Point p(0,0);
Point q(0,0);
Mat xal;
void drawimage()
{
a = q.x - p.x; //i am just checking out the values of a and b to see if the drawimagefunction is being called in the rightmouse click event
b = q.y - p.y;
cout<<" a is :"<<a<<endl;
cout<<"b is:"<<b<<endl;
line(xal,Point(p.x,p.y),Point(q.x,q.y),Scalar(0,0,255),2,8);
}
void onMouse( int event, int x, int y, int f, void* )
{
switch (event)
{
case EVENT_LBUTTONDOWN:
cout<<"Left button was pressed"<<x<<" "<<y<<" "<<endl;
{
p.x = x;
p.y = y;
cout<<"p is:"<<p.x<<p.y<<endl;
}
break;
case EVENT_RBUTTONDOWN:
cout<<"Right button was pressed at :"<<x <<" "<<y<<endl;
{
q.x = x;
q.y = y;
drawimage();//no line is being drawn though i can see that i get the values of a and b in the drawimage function.
}
break;
default:
break;
}
}
int main()
{
xal = imread("pic.JPG);
namedWindow("Picture",1);
setMouseCallback("Picture",onMouse,NULL);
imshow("Picture",xal);
cvwaitkey(0);
}
Add the following after your "line(..)" call in your drawLine() function:
imshow("Picture", xal);
The problem is that you are writing the line to the xal matrix, but you have not updated the image on the screen, which is what the imshow(..) call will do.
Try this one code. It is useful for you.
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace std;
using namespace cv;
void drawimage()
{
line(xal,Point(p->x,p->y),Point(q->x,q->y),Scalar(0,0,255),2,8);
}
void CallBackFunc(int event, int x, int y, int flags, void *ptr )
{
if ( event == EVENT_LBUTTONDOWN )
{
Point*p = (Point*)ptr;
p->x = x;
p->y = y;
drawimage();
}
else if ( event == EVENT_RBUTTONDOWN )
{
Point*q = (Point*)ptr;
q->x = x;
q->y = y;
drawimage();
}
}
int main(int argc, char** argv)
{
// Read image from file
Point p;
Point q;
Mat xal = imread("MyPic.JPG");
//if fail to read the image
if ( xal.empty() )
{
cout << "Error loading the image" << endl;
return -1;
}
//Create a window
namedWindow("My Window", 1);
//set the callback function for any mouse event
setMouseCallback("My Window", CallBackFunc,&p);
setMouseCallback("My Window", CallBackFunc,&q);
//show the image
imshow("My Window", xal);
// Wait until user press some key
waitKey(0);
return 0;
}
The following program would give the location of left mouse button click.
void onMouse(int evt, int x, int y, int flags, void* param) {
if(evt == CV_EVENT_LBUTTONDOWN) {
cv::Point* ptPtr = (cv::Point*)param;
ptPtr->x = x;
ptPtr->y = y;
}
}
int main() {
cv::Point2i pt(-1,-1);//assume initial point
cv::namedWindow("Output Window");
Mat frame = cv::imread("chhhha.png");
cv::setMouseCallback("Output Window", onMouse, (void*)&pt);
int X, Y;
while(1)
{
cv::imshow("Output Window", frame);
X=pt.x;
Y=pt.y;
cout<<"X and Y coordinates are given below"<<endl;
cout<<X<<'\t'<<Y<<endl;
waitKey(10);
}
getch();
}
I want to draw a line connecting the two points user clicks. I know line can be drawn by the function:
C++: void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
But the problem is I have to provide two points to this function, but my previous point is lost as can be seen in the following code:
void onMouse(int evt, int x, int y, int flags, void* param) {
if(evt == CV_EVENT_LBUTTONDOWN) {
cv::Point* ptPtr = (cv::Point*)param;
ptPtr->x = x;
ptPtr->y = y;
}
}
int main() {
cv::Point2i pt(-1,-1);
cv::namedWindow("Output Window");
Mat frame = cv::imread("chhhha.png");
cv::setMouseCallback("Output Window", onMouse, (void*)&pt);
int X, Y;
while(1)
{
cv::imshow("Output Window", frame);
X=pt.x;
Y=pt.y;
cout<<"X and Y coordinates are given below"<<endl;
cout<<X<<'\t'<<Y<<endl;
line(frame, pt1, pt2, 'r', 1, 8, 0); //here I am having only one point. This is the issue
waitKey(10);
}
getch();
}
Edit
So is there anyway to store the coordinates of the point clicked by user. So let's say user clicks two points on the image, and we would store x coordinates of the two clicks in X[0] and X[1], and similarly Y[0] and Y[1] for y coordinates.
Then I can easily use the function for drawing a line. Kindly help me proceed in this direction.
Thanks in advance for your suggestions.
My latest code
using namespace cv;
using namespace std;
void onMouse(int evt, int x, int y, int flags, void* param) {
if(evt == CV_EVENT_LBUTTONDOWN) {
std::vector<cv::Point>* ptPtr = (std::vector<cv::Point>*)param;
ptPtr->push_back(cv::Point(x,y));
}
}
int main()
{
std::vector<Point> points;
cv::namedWindow("Output Window");
Mat frame = cv::imread("chhha.png");
cv::setMouseCallback("Output Window", onMouse, (void*)&points);
int X=0, Y=0;
while(1)
{
cv::imshow("Output Window", frame);
X=points[0].x;
Y=points[0].y;
cout<<"First X and Y coordinates are given below"<<endl;
cout<<X<<'\t'<<Y<<endl;
waitKey(10);
}
getch();
}
This has two major issues:
1- This would compile well, but during run time it gives Debug Assertion Failed! error,
at the following lines when I debugged by putting breakpoints:
X=points[0].x;
Y=points[0].y;
It further says:
Expression: Vector subscript out of range
2- How do I come out of the while loop?
In other similar programs I noticed that it remains in while loop forever.
I suggest that instead of passing to a cv::Point*, pass a std::vector<cv::Point>*. Since cv::Point has a copy constructor, you can store points in there by push_back.
The code as I mean it:
std::vector<cv:Point> points;
cv::namedWindow("Output Window");
Mat frame = cv::imread("chhhha.png");
cv::setMouseCallback("Output Window", onMouse, (void*)&points);
int X, Y;
while(1)
{
cv::imshow("Output Window", frame);
if (points.size() > 2) //we have 2 points
{
for (auto it = points.begin(); it != points.end(); ++it)
{
cout<<"X and Y coordinates are given below"<<endl;
cout<<(*it).x<<'\t'<<(*it).y<<endl;
}
//draw points
}
...
and in the callback:
void onMouse(int evt, int x, int y, int flags, void* param) {
if(evt == CV_EVENT_LBUTTONDOWN) {
std::vector<cv::Point>* ptPtr = (std::vector<cv::Point>*)param;
ptPtr->push_back(cv::Point(x,y));
}
}
Later on, you can access the first 2 points with points[0] and points[1].
EDIT: updated the code with a check of the vector size. You should note that this method might require locking, if the mouse callback is executed in a different thread.
I have an image (24 bit bmp) such as the following:
The user draws a line (shown here in red) using mouse. This line can be any where with any angle. Then he clicks right or left mouse button, and the image pixel values across the lines are stored in a file in addition to displaying on the console.
I have used setMouseCallback() for showing the position of the mouse (shown below ). But I need a little more help in understanding an elegant way for finding and storing the pixel values across the line. Kindly help!
void CallBackFunc(int event, int x, int y, int flags, void* userdata)
{
if ( event == EVENT_LBUTTONDOWN )
{
cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
}
else if ( event == EVENT_RBUTTONDOWN )
{
cout << "Right button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;
}
else if ( event == EVENT_MOUSEMOVE )
{
cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;
}
}
int main(int argc, char** argv)
{
Mat img = imread("C:\\Users\\Acme\\Desktop\\image-processing\\2.bmp");
namedWindow(" Window", 1);
setMouseCallback(" Window", CallBackFunc, NULL);
imshow(" Window", img);
waitKey(0);
return 0;
}
Extract the line by warping it to a 1 x (linelength) or (linelength) x 1, vertical or horizontal Mat. Then you can easily read down or across pixel values.
The specific details depend upon your program, but values is populated once two points are clicked. What you do after that is up to you.
cv::Point g_points[2];
int g_pointIndex;
std::vector<cv::Vec3b> values;
bool g_allGood = false;
void onMouse(int e, int x, int y, int d, void* ud)
{
if (e != CV_EVENT_LBUTTONDOWN || g_pointIndex >= 2)
return;
g_points[g_pointIndex++] = cv::Point(x, y);
}
void main()
{
// load image as greyscale
Mat img = imread("C:\\temp\\2.png", CV_8UC1);
namedWindow("img", 1);
setMouseCallback("img", onMouse);
while (1)
{
// all points collected
if (g_pointIndex == 2 && !g_allGood)
{
/*
to save processing, i suggest you remove the mouse callback once all points
are collected. do this with: setMouseCallback("img", NULL,NULL);
*/
// create line iterator, and add pixel values to values vector
cv::LineIterator it(img, g_points[0], g_points[1]);
for (int i = 0; i < it.count; i++, ++it)
values.push_back((Vec3b)*it);
// you now have all pixel values in values;
g_allGood = true;
}
imshow("img", img);
waitKey(100);
}
}