I'm trying to use cv::setMouseCallback in my c++ project. I just don't get it.
let that I habe a class Stuff how can tell this class you got a frame and run the cv::setMouseCallback on this frame here is an example of what I'm trying to do :
class Stuff{
public:
Stuff();
void setFrame(cv::Mat);
void mouse (int,int, int, int,void*);
private :
cv::Mat frame;
int key;
};
Stuff::Stuff(){}
void Stuff::setFrame(cv::Mat framex){
frame = framex;
}
int main (){
Stuff obj;
cv::Mat frame = cv::imread ("examople.jpg");
char* name;
cv::imshow(name,frame);
cv::setMouseCallback(name,obj.mouse,&frame) // I' stop here because that's exactlly what just don't work
}
this the error message that get:
Stuff::mouse : function call missing argument list; use '&Stuff::mouse ' to create a pointer to member
the real program is too big to put its code here that why I'm trying to simplify the question
you must declare a mouse handler as static inside your class. For instance, I have a dragger with a member mouser, that I want to be called. I declare an helper static void mouser, that cast the void* received and calls the member:
class dragger {
void mouser(int event, int x, int y) {
current_img = original_img.clone();
Point P(x, y);
...
}
static void mouser(int event, int x, int y, int, void* this_) {
static_cast<dragger*>(this_)->mouser(event, x, y);
}
and instance in dragger constructor in this way
dragger(string w, Mat m) :
window_id(w), status(0), original_img(m), /*black(0, 0, 0),*/ K(5, 5)
{
...
setMouseCallback(w, mouser, this);
}
...
}
First of all you need to create a named window in the main function. namedWindow( "image", 0 ); or something similar will do the job.
The mouse callback function is not associated to the frame variable but it is associated to the window. In your case it would be:
char* name = "image";
cv::namedWindow( name, 0 );
cv::setMousCallback(name, obj.mouse,&frame);
The callbacks are functions that call other functions when an event happens on a window. For the mouse, an event can be the mouse movement, the left, right or middle clicks. Here you can find a list of them, as well as good explanations.
So when this "event" takes place in the window, opencv calls the function whose name was specified in the setMouseCallback as an argument, in your case Stuff::mouse. if you define the function like this:
Stuff::mouse( int event, int x, int y, int flags, void* params )
when it is called the event variable will be filled with the value of the trigger, the x and y with the positions off the mouse on the image etc.
If you want to pass the frame in the mouse function you use it as in this question, if you consider the correction of patxiska's answer.
So with a switch you can find out what kind of event it was:
switch( event ){
case CV_EVENT_LBUTTONDOWN:
//...
break;
case CV_EVENT_RBUTTONDOWN:
//...
break;
case CV_EVENT_FLAG_CTRLKEY:
//...
break;
}
and take your frame typecasting it from void* back to a cv::Mat.
Here you can find another example of Opencv's site on how to use a mouse callback.
Hope I helped, I haven't used opencv for a while and I don't have my sample source files now. Callbacks are simplified in the Opencv GUI but that's the logic of working with any GUI. Input such as mouse and keyboard trigger events and the callback functions pass the events to the functions of your implementation.
Related
I'm creating a GLFWKeyCallback and because of how simple it is I've decided to use a lambda. This callback modifies a member variable, so I have to pass this into the capture list. Here is what my code looks like so far:
glfwSetKeyCallback(window,
[this](GLFWwindow* window, int key, int scancode, int action, int mods)
{
if(action == GLFW_PRESS)
{
//use a mutex
//Modify member variable
}
});
The problem is that whenever I pass this into the capture list, Visual Studio 2019 displays the following error:
no suitable conversion function from "lambda [] void (GLFWwindow *window, int key, int scancode, int action, int mods)->void" to GLFWKeyfun" exists
Have I missed something or is this code just invalid?
The GLFW callbacks don't take lambdas, or function objects: they take plain old function pointers. A non-capturing lambda can be converted to a function pointer, but not a capturing one.
However, you can get a similar effect by using glfwSetUserPointer and glfwGetUserPointer. The lambda still can't be capturing, but you can recover the this pointer.
For example,
struct MyClass {
GLFWwindow* window;
MyClass(GLFWwindow* window) : window(window) {
glfwSetWindowUserPointer(window, static_cast<void*>(this));
glfwSetKeyCallback(window,
[](GLFWwindow* window, int key, int scancode, int action, int mods) {
auto self = static_cast<MyClass*>(glfwGetWindowUserPointer(window));
// can access member variables through `self`
});
}
// make sure that either the class will last as long as GLFW will
// or clean up the user pointer and callbacks in here
~MyClass() {
// clean up
}
// don't be able to copy, probably, or bad things will happen
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
// other things...
};
Adding onto the other answer.
I see glfwSetWindowUserPointer brought up a lot as a solution to this issue. It works fine (and I use it myself, since I don't know of any other solution), but it comes with a caveat that I haven't seen anyone mention:
You can only store one pointer per Window using this method. If some other code sets a different pointer to your window, all of a sudden your lambda won't work anymore. I can think of two workarounds here:
When you retrieve the pointer in your lambda body, set it to a static variable. This way, it will persist across calls to the lambda, even if someone else sets a different pointer. Note: the static variable won't initialize until the first call to the lambda, so you'd be best to call the lambda once, yourself, after defining it.
Define a object or map of pointers. Give GLFWSetUserPointer a pointer to that map. I can't think of any way to enforce this pattern, but if you have complete control over your app, you can store multiple pointers in associated with a Window this way.
As you know, glfw works with callbacks for inputs and callback functions have certain signatures that users need to match. Here is an example from the documentation:
void key_callback(GLFWwindow* window, int key, int scancode, int action, int
mods)
{
if (key == GLFW_KEY_E && action == GLFW_PRESS)
activate_airship();
}
Now, activate airship seems to be a global function here. What I really want to do is, to modify some object, possibly, at each input stage. So I want something like:
void key_callback(Airship a, GLFWwindow* window, int key, int scancode, int
action, int mods)
{
if (key == GLFW_KEY_W && action == GLFW_PRESS)
a.render_Wireframe();
}
As you can see, I want to pass the object I am trying to modify, Airship here. But this time, callback signature is distrupted. I can't use it anymore. What is the best way to achieve this? I am trying to come up with ideas but in the future, I might want to change this to work with not just airships but a new object I add as well. What should be the design here?
The thing is, with the ability to set one callback for whole program, I don't know how to achieve the following. Let's say I have two objects Airship and Battlesip. I want both of them to have their own input handling mechanism. Pressing W should do something if Airship is the picked object in the scene and something else if Battleship is the picked object.
So I want to have something like following in the end;
class Airship : public SceneObject
{
...
void input_handle(){
if(is_picked && pressed == GLFW_KEY_W)
launch_missile();
}
}
class Battleship : public SceneObject
{
...
void input_handle(){
if(is_picked && pressed == GLFW_KEY_W)
do_something_else();
}
}
And in my main loop, I have a vector of SceneObject and I call input_handle on each frame, for each object. I don't know how can I handle something like this with a single callback scheme of glfw. I can't pass those things as callback functions to a window even if I matched the signature because they are class members. Well nothing would change if I could pass class members, since I can only set one callback.
Here is how I ended up solving this issue in a simplified manner. I am not sure if this is the correct design but it might be of help to a future viewer. I have a scene graph like object, say SceneGraph that holds all the SceneObjects mentioned in the question. I ended up making this SceneGraph object a singleton. I have one scene graph per run so it seemed logical to me to make it a singleton.
class SceneGraph
{
// ...
// many more code
friend void key_callback(GLFWwindow* window, int key, int scancode,
int action, int mods)
{
// get the singleton instance, singleton is static and this is
// a friend function, so I can register this as a callback
SceneGraph* d = SceneGraph::handle();
d->input_handle();
}
void input_handle()
{
for(auto& s : objs)
s.input_handle()
}
private:
std::vector<SceneObject> objs;
}
Of course, you can pass button state etc. to your input routines. I just wanted show the bare minimum design that I went with. Object picking example that I talked about in the original question can also be handled now.
I am stuck trying to get a cv::Mat object from another class (third party code) into my main function.
I have a class:
FrameObserver.h
class FrameObserver : virtual public IFrameObserver
{
public:
FrameObserver( CameraPtr pCamera, FrameInfos eFrameInfos, ColorProcessing eColorProcessing );
cv::Mat m_ConvertImage;
// This is our callback routine that will be executed on every received frame
virtual void FrameReceivedLeft( const FramePtr pFrame );
virtual void FrameReceivedRight(const FramePtr pFrame);
void ProcessCvLeft(const FramePtr pFrame);
void ProcessCvRight(const FramePtr pFrame);
static cv::Mat FrameObserver::getCurrentFrame() { return currentFrame; }
private:
static cv::Mat currentFrame;
#ifdef WIN32
double m_dFrequency;
#endif //WIN32
};
}}} // namespace AVT::VmbAPI::Examples
FrameObserver.cpp
void FrameObserver::ProcessCvLeft(const FramePtr pFrame)
{
VmbUchar_t *pBuffer;
VmbUint32_t FrameWidth;
VmbUint32_t FrameHeight;
pFrame->GetWidth(FrameWidth);
pFrame->GetHeight(FrameHeight);
pFrame->GetImage(pBuffer);
ShowFrameInfos(pFrame);
IplImage *img1 = cvCreateImageHeader(cvSize(
FrameWidth,
FrameHeight),
IPL_DEPTH_8U,
1);
cvSetData(img1, pBuffer, img1->widthStep);
cv::Mat copyimageL = cv::cvarrToMat(img1);
currentFrame = copyimageL; //copies to currentFrame here
}
Then I have main.cpp:
cv::Mat Mainframe;
int main( int argc, char* argv[] )
{
//I need to pull the currentFrame Mat stream into the frame Mat above.
}
What is the best way to do this? I have tried:
FrameObserver fo;
cv::Mat test = fo.currentFrame;
But it gives me the following error:
no default constructor exists for class "FrameObserver"
Thank you.
As far as C++ goes, your mistake is that the class FrameObserver has no default constructor (i.e. a constructor that can be called without arguments). It's not defined explicitly, nor will it be generated, since another constructor has already been defined (see here). Thus, you can't write
FrameObserver fo;
Since this invokes the default constructor FrameObserver::FrameObserver() which (see above) is not present. Thus, you should create the FrameObserver object using the existing constructor with arguments FrameObserver::FrameObserver( CameraPtr pCamera, FrameInfos eFrameInfos, ColorProcessing eColorProcessing );
Now on to the actual problem. Since this class is a part of a supplied API, you shouldn't try to change it, rather learn to use it correctly. By the name of it, it appears to be an observer. Thus, you need to create an instance of it (a single one, at that), and carefully read the docs on how it's supposed to supply you frame data as a matrix. I don't know how exactly it works, but by common sense, since it's an "observer", it should automatically provide you with notifications about incoming frames. Again, have a close look at the documentation.
Important edit:
Here it is, look at the header:
// This is our callback routine that will be executed on every received frame
virtual void FrameReceivedLeft( const FramePtr pFrame );
virtual void FrameReceivedRight(const FramePtr pFrame);
By the looks of it, you are supposed to subclass FrameObserver and reimplement the two above functions. That means that the code to process or extract those frames must reside inside those routines in the subclass you're going to create.
Anyway, while the details may vary, the concept stays: the observer is going to call some methods on its own. You can only wait for it to call those methods and react accordingly. More precisely, you register your class object or function within the observer object, and then let the observer call those methods automatically. (It is said that you "subscribe to the notifications" provided by the observer).
I'm using the function setMouseCallback to extract the information about pixel coordinates at every mouse event. The program I created works perfectly if I use openCV windows. Precisely:
image is cv::Mat;
cv::namedWindow("Original", WINDOW_NORMAL);
cv::imshow("Original", image);
cv::setMouseCallback("Original", mouseWrapper, NULL);
where
void esempio::onMouse(int event, int x, int y, int flags, void *param)
{
//---------------------------------------------------------
// Code to read the mouse event in the identification of a point
//---------------------------------------------------------
if (event == CV_EVENT_LBUTTONDOWN)
{
std::cout << "1: " << x << "," << y << std::endl;
pp_m.x=x;
pp_m.y=y;
}
}
void mouseWrapper( int event, int x, int y, int flags, void* param )
{
esempio * mainWin = (esempio *)(param);
mainWin->onMouse(event,x,y,flags,0);
}
Now, I would like to use the same code in a QLabel created in my interface. I tried to use the function setWindowTitle to change the name of the QLabel in this way:
ui->label_show->setWindowTitle("Test");
cv::setMouseCallback("Test", mouseWrapper, NULL);
but this approach seems not adequate.
How can I indicate to the function setMouseCallback to work on the desired QLabel?
Thanks
This can be hard because:
I'm not sure that the OS is taking into account titles of child widgets, and I'm not sure that OpenCV will recognize window title of a non-top-level widget;
Qt handles child widgets internally and doesn't expose them to OS unless forced to do that;
Most importantly, OpenCV won't have a chance to call your callback because Qt manages the event loop.
I don't see why not use Qt's own means to react on mouse events.
Call ui->label_show->installEventFilter(this) in constructor of your form and implement virtual eventFilter function. In this function you can use event argument to retrieve event type and mouse coordinates (after casting to QMouseEvent).
See event filters.
I've recently begun to dip my toes into DerelictGLFW. I have two classes, one of them a Window class, and another an InputHandler class (a event manager for window events). In my cursor position callback, I take the window user pointer and try to set the position, but I get an Access Violation Error immediately upon attempting to set any value outside of the callback and GLFW. GLFW is initialized, and does not report any errors. Thank you for your time.
Class Window
{
private:
double cursorX;
...other stuffs...
#property
void cursorX(double x) nothrow
{
cursorX = x;
}
}
extern(C) void mousePosCallback(GLFWwindow* window, double x, double y)
{
Window* _window = window.userPointer
//userPointer is a static function that gets the window user pointer
//and casts it to a Window*
_window.cursorX = x;
}
static Window* userPointer(GLFWwindow* window)
{
return cast(Window*) glfwGetWindowUserPointer(window);
}
Edits:
Added extern(C) to callback, error persists.
Corrected "immediately upon entering the callback" to "immediately upon attempting to set any value outside of the callback and GLFW".
Added userPointer function to question
mousePosCallback must be declared in a extern(C) block. This is to make the calling convention match.
extern (C) void mousePosCallback(GLFWwindow* window, double x, double y)
{
Window* _window = window.userPointer
//userPointer is a static function that gets the window user pointer
//and casts it to a Window*
_window.cursorX = x;
}
It seems I have discovered the source of the error. During initialization of the window, I attempt to set the user pointer with this. I'm not sure why, but moving it into a different function that is not called from the constructor appears to remedy the problem. The problem is solved, but could anyone help me to understand why? It seems rather odd to me.