OpenCV 通过引用或指向回调函数的指针传递值

OpenCV passing values by reference or pointer to callback functions

我试图避免在 openCV 项目中使用全局变量(毫无疑问,我会让我的主管教我为什么它们不好 rah rah rah :) - 但目前它们似乎是我可以从鼠标和轨迹栏回调函数中获取信息的唯一方法。

使用鼠标示例 - 目前我有全局变量:

vector<Point2d> vectorOfPoints;
int clickCount = 0;

目前我的主线是这样的:

setMouseCallback("test",onMouse, NULL);  

然后上面的主要内容:

void onMouse(int event, int x, int y, int f, void* ){           
    if(event == CV_EVENT_LBUTTONDOWN){  
        vectorOfPoints.push_back(Point(x,y));
        clickCount++;
    }
}

它正在工作,但是在不使用全局变量的情况下,在回调函数中获取对 vectorOfPoints 和 clickCount 的 read/write 访问权限的语法是什么?

我在网上发现了这个问题几次,但答案对我来说不清楚或无法解决。评论中有关于如何操作的提示,但我到目前为止无法正确解释行话。

我希望有一些像我用来传递变量作为对方法的引用的语法一样简单的东西...

void referenceExampleMethod(vector<Point2d>& referenceExample){
    //do something with referenceExample...
} 

...越简单越好

我不敢问(行话过多!)但也许它 100% 相关 - 什么是 void* ??

感谢任何帮助

setMouseCallback 的最后一个参数在调用 onMouse 时传回给您。这就是 void *onMouse 中的含义:您传递给 setMouseCallback 的指针。 void * 是指向未指定类型的指针。您可以将其视为指向内存中某个位置的通用指针。

在您的情况下,您可能会传递一个结构的地址,该结构包含或指向您希望在 onMouse.

内部访问的变量
#include <utility>

...

typedef std::pair<std::vector<Point2d>&, int&> my_pair;

...
my_pair *p = new my_pair(vectorOfPoints, clickCount);  // TODO: needs to be deallocated eventually
setMouseCallback("test", onMouse, p);

上面假设 vectorOfPointsclickCount 已经分配到其他地方(例如 - 动态,静态,在主线程的堆栈上等)并且在您的持续时间内保持不变回调。那么,

void onMouse(int event, int x, int y, int f, void *pptr)
{
    my_pair *p = (my_pair*) pptr;

    if(event == CV_EVENT_LBUTTONDOWN){  
        p->first.push_back(Point(x,y));
        p->second++;
    }
}

我同意 @jschultz410 关于指向内存中某个位置的指针的回答的第一部分。但是,我不同意在野外使用原始指针。

你应该定义你自己的数据类型,保存你所有的数据,它可以是structclass,或std::pair,或std::tuple,随便什么,选择权在你。

然后创建该类型的 object,并在 setMouseCallback 的最后一个参数中使用其地址。

您必须确保的主要事情 - object 的生命周期必须涵盖 window 的生命周期。也就是说,object 必须在第一次调用 onMouse 之前创建,并在最后一次调用之后销毁。您可以通过在 main 的开头声明变量来做到这一点。然后 object 将在程序启动后的早期创建,并在接近完成时由编译器自动销毁。这是例子。

typedef std::pair<vector<Point2d>, int> data_holder_type; // note the absence of references, this pair holds std::vector and int

void onMouse(int event, int x, int y, int f, void* ptr){
    if(event == CV_EVENT_LBUTTONDOWN){  
        data_holder_type *dholder = static_cast<data_holder_type *>(ptr);

        dholder->first.push_back(Point(x,y));
        dholder->second.clickCount++;
    }
}
....

int main(void)  {
    data_holder_type dholder;

    // add code to initialize your dholder;

    ...
    setMouseCallback("test", onMouse, &dholder);
    ...

    cv::waitKey(); // wait until the window is closed

    // read values from dholder and process them

} //dhloder is deleted somewhere here

另一个重要的是并发访问这个object。 onMouse 是从单独的 parallel thread, and if your dholder is read or modified both in main, and onMouse simultaneously while the window is open, race conditions 中调用的。一般来说,它们通常会导致不可预测且很难捕获的错误。

一切正常,而您的 main 在 window 关闭之前无法访问 dholder

关于您关于 void * 的问题。请注意 onMouseif 内的行。 ptr 指向类型 void 的 object。 object 没有任何成员,firstsecond 或任何其他成员。如果您尝试使用 ptr(例如 ptr->first)访问它们,则会出现编译器错误。因此,您必须将此指针转换为指向另一种类型的指针,该类型包含有关它指向的 object 的一些信息,在本例中为 data_holder_type *

任何指针类型都可以转换为 void *,而 void * 可以转换为任何其他指针类型。这允许您为不同的 windows 设置多个不同的回调。

小心错误的转换!编译器不做任何检查。

此示例展示了如何为 windows 设置 3 个不同的鼠标回调和不同的标题。

typedef blah-blah-blah1 data_holder_type1; 
typedef blah-blah-blah2 data_holder_type2; 
typedef blah-blah-blah3 data_holder_type3; 

void onMouse1(int event, int x, int y, int f, void* ptr){
    if(event == CV_EVENT_LBUTTONDOWN){  
        data_holder_type1 *dholder = static_cast<data_holder_type1 *>(ptr);

        dholder->first.push_back(Point(x,y));
        dholder->second.clickCount++;
    }
}

void onMouse2(int event, int x, int y, int f, void* ptr){
    if(event == CV_EVENT_LBUTTONDOWN){  
        data_holder_type2 *dholder = static_cast<data_holder_type2 *>(ptr);

        // processing, related to another data type
    }
}

void onMouse3(int event, int x, int y, int f, void* ptr){
    if(event == CV_EVENT_LBUTTONDOWN){  
        data_holder_type3 *dholder = static_cast<data_holder_type3 *>(ptr);

        // process
    }
}

int main(void)  {
    data_holder_type1 dholder1;
    data_holder_type2 dholder2;
    data_holder_type3 dholder3;

    // add code to initialize your dholders;
    ...
    setMouseCallback("test1", onMouse1, &dholder1);
    setMouseCallback("test2", onMouse2, &dholder2);
    setMouseCallback("test3", onMouse3, &dholder3);
    ...
}