1

I am making an application that would process some images for me, using OpenCV. It should allow for processing different types of images (type specified by the user).

Most processing is done is a same manner, and only depends on the type of data held in the cv:Mat (used in functions such as cv::Mat.at<type>() to reference the data correctly). For this, I built a Base class ImageProcessor which would contain the common implementation, virtual interfaces for functions which need to know the type, and a Factory Method. I further built a templated Derived TypedProcessor<datatype> class, such that I can implement the data-access methods.

However, I am getting an odd compilation error whenever I use OpenCV templated access functions inside the Derived class. Here is the Minimal (non-)Working Example:

#include <iostream>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>

class ImageProcessor{
    public:
        static ImageProcessor *NewProcessor(char option, std::string imgPath);
        virtual ~ImageProcessor() {}
        virtual void printMaxElem() = 0;
    protected:
        ImageProcessor(const std::string &imgPath) : workingImagePath(imgPath) {
            this->workingImage = cv::imread(imgPath.c_str(), CV_LOAD_IMAGE_ANYDEPTH);
        }
        cv::Mat workingImage;
        std::string workingImagePath;
};


template <typename datatype>
class TypedProcessor : public ImageProcessor{
    public:
        TypedProcessor(std::string imgPath) : ImageProcessor(imgPath) {}
        virtual ~TypedProcessor() {}

        void printMaxElem();
        void preprocess();
    private:
        void calculateDisplayValue();
};

template <typename datatype>
void TypedProcessor<datatype>::printMaxElem(){
    std::cerr << (datatype)(*std::max_element(this->workingImage.begin<datatype>(), this->workingImage.end<datatype>())) << std::endl;
}

ImageProcessor* ImageProcessor::NewProcessor(char option, std::string imgPath){
    ImageProcessor *processor = NULL;
    switch (option){
        case 'h': // input correctly as CV_32F
            processor = new TypedProcessor<float>(imgPath);
            // GOAL: AVOIDING THIS:
            //std::cerr << (float)(*std::max_element(processor->workingImage.begin<float>(), processor->workingImage.end<float>())) << std::endl;
            break;
        case 'i': // input as CV_16U but should be interpreted as CV_16S
            processor = new TypedProcessor<short>(imgPath);
            processor->workingImage.flags = (processor->workingImage.flags & ~CV_MAT_TYPE_MASK) | CV_16S;
            // GOAL: AVOIDING THIS:
            //std::cerr << (short)(*std::max_element(processor->workingImage.begin<short>(), processor->workingImage.end<short>())) << std::endl;
            break;
        default:
            break;
    }

    processor->printMaxElem();

    return processor;
}


int main(void){
    ImageProcessor *myprocessor = ImageProcessor::NewProcessor('h', "user/will/input/this");
    return 0;
}

The errors I am getting are:

In member function ‘void TypedProcessor<datatype>::printMaxElem()’:
test.cpp:35:84: error: expected primary-expression before ‘>’ token
         std::cerr << (datatype)(*std::max_element(this->workingImage.begin<datatype>(), this->workingImage.end<datatype>())) << std::endl;
                                                                                    ^
test.cpp:35:86: error: expected primary-expression before ‘)’ token
     std::cerr << (datatype)(*std::max_element(this->workingImage.begin<datatype>(), this->workingImage.end<datatype>())) << std::endl;
                                                                                  ^

I also get equivalent errors for this->workingImage.end<datatype>(). A very similar error shows up if I replace datatype with short (not the functionality I want; just for testing purposes). The variable this->workingImage is normally visible inside the function as expected (and I can use any variables I declare as protected inside ImageProcessor), and the function call passes normally if I change the body.

The errors only arise when I am specifically using any templated access function from OpenCV (the begin and end from the example, at, etc...).

Even more curious, if I uncomment the lines marked by GOAL: AVOIDING THIS, they compile and execute perfectly. However, this is not an option as the whole point of this structure is to avoid this and implement either data-type-dependant or data-type-specific functions through the use of the templated Derived class.

Note: the functionalities as well as types of images supported in this example are not final. For now, there are only two types of images but this is likely to change in the future. Also, the processing functions will sometimes be the same (except the need to specify a different data type), sometimes they will have a common part and part dependent of the data/image type (e.g. preprocessing requires looping through all the elements, getting the min and max values (common), and then interpolating all the missing values (different approach for my float and short images)). Some functionalities will also be encapsulated directly in the Base class (if they do not require data specification, e.g. getting width/height of the image comes to mind).

Does anybody know how to help with correcting the code or achieving the desired functionality?

penelope
  • 8,251
  • 8
  • 45
  • 87
  • I think that you are over-complicating this, if your only goal is to know how to access OpenCV data... Any specific problem you want to solve? – Miki Jun 08 '16 at 16:04
  • @Miki The images are various radar responses (height, intensity, and more). Some example functionalities: preprocessing (common part looping through the data; interpolation for any missing elements will depend on the type of image, height and intensity interpolated differently). Displaying the image (so a human can interpret it): all Images normalized to 0-255 and then, depending on the type of data, same are shown in linear scale, others in logarithmic (e.g. intensity images have very few high points). Then there will be some common parts, like the max from the example (just type diff) – penelope Jun 08 '16 at 16:41
  • I would do in a simpler way... Build a `RadarResponseHeight`, `RadarResponseIntensity` etc... classes as `struct RadarResponseXXX { Mat1f mat; ... }` or whatever type you know your data will be. Then you can decide how to display each image according to its semantic meaning, not merely the data type... – Miki Jun 08 '16 at 16:44
  • BTW, for the matter of this case (i.e. find the max element), you can just use `minMaxLoc` ;D – Miki Jun 08 '16 at 16:45
  • (I can't check now why the template stuff doesn't work... sorry ;D) – Miki Jun 08 '16 at 16:47
  • @Miki Ty for the help, but I found this actually being a C++ error that is simply hard to search for. Answering it now... – penelope Jun 08 '16 at 16:58
  • Glad you make it work. I'm not sure this is the best approach... But, hey, if it's good for you ;D – Miki Jun 08 '16 at 17:27

1 Answers1

1

Uh, sorry everybody, seems like I just did not know how to search for the right problem.

The explanation on a smaller example is provided in this answer. Apparently, I need to prefix the function calls by template and it works like a charm. So, the function in question should be re-written as:

template <typename datatype>
void TypedProcessor<datatype>::printMaxElem(){
    std::cerr << (datatype)(*std::max_element(this->workingImage.template begin<datatype>(), this->workingImage.template end<datatype>())) << std::endl;
}
Community
  • 1
  • 1
penelope
  • 8,251
  • 8
  • 45
  • 87