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?
Related
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:
cv::add - this is basically what operator+ does
cv::Mat::type
cv::Mat::zeros
cv::Mat::empty
cv::line
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.
My apology for the confusing title. Basically I want to use mouse cursor to display the pixel value of image before applying the colormap. Is it possible? I know how to get the (x,y) coordinates from here
Here is my code:
cv::Mat falseColorsMap;
void onMouse( int event, int x, int y, int, void* );
void showImage( cv::Mat frameMat){
double min;
double max;
cv::minMaxLoc(frameMat, &min, &max);
std::cout << min << "," << max << "\n";
cv::Mat adjMap;
frameMat.convertTo(adjMap,CV_8UC1, 255 / (max-min), -min); // expand your range to 0..255. Similar to histEq();
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_JET);
namedWindow( "Out", CV_WINDOW_NORMAL );// Create a window for display.
resizeWindow("Out", 800, 600);
setMouseCallback( "Out", onMouse, 0 );
cv::imshow("Out", falseColorsMap);
waitKey(0);
}
// Function onMouse displays cursor values
void onMouse( int event, int x, int y, int, void* )
{
if ( event != CV_EVENT_LBUTTONDOWN )
return;
Point pt = Point(x,y);
std::cout<<"("<<pt.x<<", "<<pt.y<<") ...... "<<(falseColorsMap.at<int>(y,x)) << '\n';
}
How to get pixel value of frameMat when using the mouse cursor? Thanks for your help!
Please see the changes below to show original frames pixel values before color map conversions. Here color channel based condition is added. If it is three channel (BGR) image it will show 3 channels pixel value and if it is single channel image it will show the pixel value as well:
cv::Mat falseColorsMap;
cv::Mat framesMap;
void onMouse( int event, int x, int y, int, void* );
void showImage( cv::Mat frameMat){
double min;
double max;
cv::minMaxLoc(frameMat, &min, &max);
std::cout << min << "," << max << "\n";
framesMap = frameMat.clone();
cv::Mat adjMap;
frameMat.convertTo(adjMap,CV_8UC1, 255 / (max-min), -min); // expand your range to 0..255. Similar to histEq();
applyColorMap(adjMap, falseColorsMap, cv::COLORMAP_JET);
namedWindow( "Out", CV_WINDOW_NORMAL );// Create a window for display.
resizeWindow("Out", 800, 600);
setMouseCallback( "Out", onMouse, 0 );
cv::imshow("Out", falseColorsMap);
waitKey(0);
}
// Function onMouse displays cursor values
void onMouse( int event, int x, int y, int, void* )
{
if ( event != CV_EVENT_LBUTTONDOWN )
return;
Point pt = Point(x,y);
if(framesMap.channels()==3)
{
std::cout<<"("<<pt.x<<", "<<pt.y<<") ...... [blue] "<<framesMap.at<Vec3b>(y,x)[0] << " [green] "<<framesMap.at<Vec3b>(y,x)[1] << " [red] "<<framesMap.at<Vec3b>(y,x)[2]<<'\n';
}
else
{
std::cout<<"("<<pt.x<<", "<<pt.y<<") ...... "<<framesMap.at<uchar>(y,x) <<'\n';
}
}
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);
}
}
I show a Mat matrix on the screen as an image.
I want to click a location on this image and I want obtain that pixel value on screen.
How can be done with OpenCV, C++?
I use this code below for IplImage, but the result should be the same when you use Mat.
void my_mouse_callback(int event, int x, int y, int flags, void* param){
IplImage* image = (IplImage*) param;
switch(event) {
case CV_EVENT_LBUTTONDOWN:
std::cout << "x: " << x << std::endl;
std::cout << "y: " << y << std::endl;
break;
default:
break;
}
}
int main() {
IplImage* image = cvLoadImage("picture_file_name_here");
cvNamedWindow("Test");
cvSetMouseCallback("Test", my_mouse_callback, (void*) image);
cvShowImage("Test", image);
cvWaitKey(0);
cvReleaseImage(&image);
return 0;
}
Hope this helps.
for cv::Mat it would look like:
cv::Mat mat; // load img, etc
cv::setMouseCallback("Test", my_mouse_callback, (void*) &mat);
// ...
void my_mouse_callback(int event, int x, int y, int flags, void* param){
cv::Mat mat = *((cv::Mat*)param); // so, 1st cast, then deref
}