1

I am fairly new to C/C++ and have the following problem. As part of an exectuable, I want to draw some rectangles on a picture using OpenCV. For this, I have defined a separate header file to keep the .cpp executable as short as possible. It looks like this:

typedef struct Rectangle {
    cv::Point startPoint;
    cv::Point endPoint;

};

class drawSpaces {

private:
    Mat img;
    int ix = 1;
    int iy = 1;
    std::list<Rectangle> rectList;

public:
//mouse callback function
    void drawRect(int event, int x, int y, int, void *param) {
        if (event == CV_EVENT_LBUTTONDOWN) {
           //Save first point of rect
            ix = x;
            iy = y;
        } else if (event == CV_EVENT_LBUTTONUP) {
            //Save 2nd point of rect
            cv::rectangle(img, Point(ix, iy), Point(x, y), cv::Scalar(0, 255, 0));
            Rectangle rect;
            rect.startPoint = Point(ix, iy);
            rect.endPoint = Point(x, y);
            rectList.push_back(rect);
            }
        }
    }


    int draw(Mat image) {
        img = image;
        if (img.empty()) {
            cout << "\nerror reading image" << endl;
            return -1;
        }
        namedWindow("Image", 1);
        imshow("Image", img);
        setMouseCallback("Image", drawRect);
        while (waitKey(20) != 27)  // wait until ESC is pressed
        {
            imshow("Image", img);
        }
        //save image with rectangles
        imwrite( "../pics/new_Image.jpg", img );
        return 0;
    }

};

I now want to create an object of class drawSpaces and a Mat image in my main and run draw on it to get the new image. However, upon building, I get the Error Message error: invalid use of non-static member function setMouseCallback("Image", drawRect); with the compiler pointing at the drawRect funtion.

I have looked at other answers at this question and the majority seem to suggest to change drawRect to static. But I don't want my drawRect function to have the functionality of a static function, i.e. being able to be called without an actual drawSpaces object being present.

Any help, also on the style of coding, is appreciated!

EDIT: Using

setMouseCallback("Image", drawSpaces::drawRect);

does not help either.

emilaz
  • 1,722
  • 1
  • 15
  • 31
  • I omitted that i set the namespaces cv and std, as I omitted the inclusions. Sorry for the confusion, wanted to keep it as short as possible. – emilaz Dec 22 '17 at 14:08
  • The error description is the error. You cannot directly use the member function as the parameter. Try drawSpaces::drawRect – leyanpan Dec 22 '17 at 14:11
  • Thanks for your suggestion. I did try that, but it makes no difference to the error being shown. – emilaz Dec 22 '17 at 14:13
  • Sorry, I previously though the function was static. Now I think you cannot do this directly. The program won't create a copy of the function definition in the memory for each instance of the class like ordinary member variables. So the "setMouseCallback" function would not be able to know what object the "drawRect" function should be called upon. You can, however, do this if you know the object that should be called. See here: https://isocpp.org/wiki/faq/pointers-to-members – leyanpan Dec 22 '17 at 14:32
  • Thanks, that provided some insight. So as far as I understood it, I cannot use a non-static member function in another member function because if no object of that class is invoked, the non-static function is meaningless. This is why I need a static function, because a static function can be used even if no object of that class is present. Is that right? I have now changed it to a static function and at least there is no compiler error any more. I'll have to see if the change to static (along with almost all member variables) impairs the functionality of the code, but so far I solved the issue – emilaz Dec 22 '17 at 15:01
  • Yes. But the problem is you cannot access non-static members ix and iy in a static function – leyanpan Dec 22 '17 at 15:06

2 Answers2

3

Env: g++5.4(c++11) + OpenCV 3.3

The callback function of setMouseCallback("Image", callback); should always be static. So you should change the function to:

static void drawRect(int event, int x, int y, int, void *param)

Then you also should modify you class definition (some knowledge related to C++, you should learn by yourself) to compile it successfully.


My result:

enter image description here


//! 2017.12.22 23:13:58 CST
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

struct Rectangle {
    cv::Point startPoint;
    cv::Point endPoint;

};

class drawSpaces {
private:
    static Mat img;
    static int ix;
    static int iy;
    static std::list<Rectangle> rectList;

public:

    static void drawRect(int event, int x, int y, int, void *param) {
        if (event == CV_EVENT_LBUTTONDOWN) {
           //Save first point of rect
            ix = x;
            iy = y;
        } else if (event == CV_EVENT_LBUTTONUP) {
            //Save 2nd point of rect
            cv::rectangle(img, Point(ix, iy), Point(x, y), cv::Scalar(0, 255, 0), 1, LINE_AA);
            Rectangle rect;
            rect.startPoint = Point(ix, iy);
            rect.endPoint = Point(x, y);
            rectList.push_back(rect);
            }
        }

    int draw(Mat image) {
        img = image;
        if (img.empty()) {
            cout << "\nerror reading image" << endl;
            return -1;
        }
        namedWindow("Image", 1);
        imshow("Image", img);
        setMouseCallback("Image", drawRect);
        while (waitKey(20) != 27) {
            imshow("Image", img);
        }
        //save image with rectangles
        imwrite( "new_Image.jpg", img );
        return 0;
    }

};

int drawSpaces::ix =0;
int drawSpaces::iy =0;
Mat drawSpaces::img = Mat() ;
std::list<Rectangle> drawSpaces::rectList;



int main(){
    Mat img = imread("test.png");
    drawSpaces obj;
    obj.draw(img);

}
Kinght 金
  • 17,681
  • 4
  • 60
  • 74
  • if your callback function needs object members you can pass the object pointer via void *param parameter and cast it back to your object class. For example you could instead add a static drawRectStatic(...) and then call ((drawSpaces *)param)->drawRect(...) but maybe better with a dynamic cast ;) – Micka Dec 22 '17 at 18:32
-1

You are trying to pass a pointer to a member function as an argument. Non-static member functions need a instance to be called on or another signature.

This has already been discussed here: Passing a member function as an argument in C++

T. N.
  • 66
  • 7
  • Your comment is not helping me. The gist I take from the link you provided is simply using the drawSpaces:: signature, as leyanpan suggested. That does not solve the issue. – emilaz Dec 22 '17 at 14:37
  • Can you post the signature of "setMouseCallback"? – T. N. Dec 22 '17 at 14:44
  • It is the cv signature. – emilaz Dec 22 '17 at 14:49
  • 1
    I didn't know its a API function. "setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata=0)" Since u can't change the signature of that function, you will need to pass a non-member / static function. If you can't make drawRect static, you may use "userdata" to pass your instance to the callback handler. – T. N. Dec 22 '17 at 14:58