Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use cv::setMouseCallback in class?

Tags:

c++

opencv

I want to use cv::setMouseCallback inside my settings class to select an area of a picture. This is my code:

void Settings::on_buttonXML_clicked(){
    cv::VideoCapture webcam;
    webcam.open(INDEX);    
    webcam.read(src);
    color = Scalar(0,0,255);
    coor_num = 0;
    xmlPath="C:/myregion.xml";
    cv::namedWindow("imageWindow", CV_WINDOW_AUTOSIZE );
    cv::imshow("imageWindow", src);
    cv::setMouseCallback( "imageWindow", onMouse, 0 );
    cv::waitKey(0);

}

void Settings::onMouse(int event, int x, int y, int, void* ) {
    if (event == CV_EVENT_LBUTTONUP) {
        Point2f p(x, y);
        coor.push_back(p);
        line(src,p,p,color);
        if(coor.size()>1)
            line(src, p, coor[coor.size()-2], color);
        imshow("imageWindow", src);
    }
    else if (event == CV_EVENT_RBUTTONUP && coor.size()>2){
        line(src, coor[0], coor[coor.size()-1], color);
        getPointsInContour(coor);
        imshow("imageWindow", src);
        waitKey(2000);
        exit(0);
    }
}
void Settings::savePointsAsXML(vector<Point2f> & contour){
    TiXmlDocument doc;
    TiXmlDeclaration decl("1.0", "", "");
    doc.InsertEndChild(decl);
    for(int i = 0; i < contour.size(); i++)
    {
        TiXmlElement point("point");
        point.SetAttribute("x",contour[i].x);
        point.SetAttribute("y",contour[i].y);
        doc.InsertEndChild(point);
    }
    if(doc.SaveFile(xmlPath.c_str()))
        cout << "file saved succesfully.\n";
    else
        cout << "file not saved, something went wrong!\n";
}

void Settings::getPointsInContour(vector<Point2f> & contour){
    vector<Point2f> insideContour;
    for(int j = 0; j < src.rows; j++){
        for(int i = 0; i < src.cols; i++){
            Point2f p(i,j);
            if(cv::pointPolygonTest(contour,p,false) >= 0) // yes inside
                insideContour.push_back(p);
        }
    }
    cout << "# points inside contour: " << insideContour.size() << endl;
    savePointsAsXML(insideContour);
}

I'm getting tons of undefined reference to Settings:coor, Settings:src, Settings:color. I'm having trouble understanding what needs to be static for it to work. This is my header:

class Settings
{
private:        
    static void onMouse(int event, int x, int y, int, void* );
    static void savePointsAsXML(std::vector<cv::Point2f> & contour);
    static void getPointsInContour(std::vector<cv::Point2f> & contour);
    static cv::Scalar color;
    static std::vector<cv::Point2f> coor;
    static int coor_num;
    static std::string xmlPath;
    static cv::Mat src;

What am I missing in my code?

like image 720
testus Avatar asked Sep 09 '14 15:09

testus


1 Answers

Since OpenCV has a C like interface it does not take a member function as the callback but you can use standard means to overcome this and pass the class instance as the userdata parameter, then cast it back to the instance and call the member method. Here is a snippet:

void Settings::on_buttonXML_clicked(){
    cv::VideoCapture webcam;
    webcam.open(INDEX);    
    webcam.read(src);
    color = Scalar(0,0,255);
    coor_num = 0;
    xmlPath="C:/myregion.xml";
    cv::namedWindow("imageWindow", CV_WINDOW_AUTOSIZE );
    cv::imshow("imageWindow", src);
    cv::setMouseCallback( "imageWindow", onMouse, this ); // Pass the class instance pointer here
    cv::waitKey(0);
}

// In you header make a static and a member version of onMouse
void onMouse(int event, int x, int y);
static void onMouse(int event, int x, int y, int, void* userdata);

// Implement it to call the member function
void Settings::onMouse(int event, int x, int y, int, void* userdata)
{
    // Check for null pointer in userdata and handle the error
    ...
    Settings* settings = reinterpret_cast<Settings*>(userdata);
    settings->onMouse(event, x, y);
}

Hope this explains the idea, I wrote it inline and so I'm sorry for any typos.

like image 193
Rudolfs Bundulis Avatar answered Oct 04 '22 08:10

Rudolfs Bundulis