7

I'm using the following code to add some noise to an image (straight out of the OpenCV reference, page 449 -- explanation of cv::Mat::begin):

void
simulate_noise(Mat const &in, double stddev, Mat &out)
{
    cv::Size s = in.size();
    vector<double> noise = generate_noise(s.width*s.height, stddev);

    typedef cv::Vec<unsigned char, 3> V4;
    cv::MatConstIterator_<V4> in_itr = in.begin<V4>();
    cv::MatConstIterator_<V4> in_end = in.end<V4>();
    cv::MatIterator_<V4> out_itr = out.begin<V4>();
    cv::MatIterator_<V4> out_end = out.end<V4>();

    for (; in_itr != in_end && out_itr != out_end; ++in_itr, ++out_itr)
    {
        int noise_index = my_rand(noise.size());
        for (int j = 0; j < 3; ++j)
            (*out_itr)[j] = (*in_itr)[j] + noise[noise_index];
    }
}

Nothing overly complicated:

  • in and out are allocated cv::Mat objects of the same dimensions and type
  • iterate over the input image in
  • at each position, pick a random value from noise (my_rand(int n) returns a random number in [0..n-1]
  • sum the pixel from in with the random noise value
  • put the summation result into out

I don't like this code because the following statement seems unavoidable:

typedef cv::Vec<unsigned char, 3> V4;

It has hard-coded two things:

  1. The images have 3 channels
  2. The channel depth is 8bpp

If I get this typedef wrong (e.g. wrong channel depth or wrong number of channels), then my program segfaults. I originally used typedef cv::Vec<unsigned char, 4> V4 to handle images with an arbitrary number of channels (the max OpenCV supports is 4), but this caused a segfault.

Is there any way I can avoid hard-coding the two things above? Ideally, I want something that's as generic as:

typedef cv::Vec<in.type(), in.size()> V4;
mpenkov
  • 21,621
  • 10
  • 84
  • 126

3 Answers3

5

I know this comes late. However, the real solution to your problem is to use OpenCV functionality to do what you want to do.

  1. create noise vector as you do already (or use the functions that OpenCV provides hint!)
  2. shuffle noise vector so you don't need individual noise_index for each pixel; or create vector of randomised noise beforehand
  3. build a matrix header around your shuffled/random vector: cv::Mat_<double>(noise);
  4. use matrix operations for computation: out = in + noise; or cv::add(in, noise, out);
  5. PROFIT!

Another advantage of this method is that OpenCV might employ multithreading, SSE or whatever to speed-up this massive-element operation, which you do not. Your code is simpler, cleaner, and OpenCV does all the nasty type handling for you.

ypnos
  • 50,202
  • 14
  • 95
  • 141
2

The problem is that you need determine to determine type and number of channels at runtime, but templates need the information at compile time. You can avoid hardcoding the number of channels by either using cv::split and cv::merge, or by changing the iteration to

for(int row = 0; row < in.rows; ++row) {
    unsigned char* inp  = in.ptr<unsigned char>(row);
    unsigned char* outp = out.ptr<unsigned char>(row);
    for (int col = 0; col < in.cols; ++col) {
        for (int c = 0; c < in.channels(); ++c) {
            *outp++ = *inp++ + noise();
        }
    }
}

If you want to get rid of the dependance of the type, I'd suggest putting the above in a templated function and calling that from your function, depending on the type of the matrix.

etarion
  • 16,935
  • 4
  • 43
  • 66
  • Thanks for the reply. I may end up using row pointers like you mentioned, but I'm a bit said to see the iterators go because this is the perfect place to use them. Also, even if I template the function, I still have to specify its typename at compile time, which really just moves the problem to another point in the code. Perhaps I'm being a bit cynical, but to me it seems like specifying this information at compile time defeats the point of having templates and polymorphism in the first place. – mpenkov Jan 20 '11 at 10:12
  • It doesn't "just" move the problem - if you went without the template function, you'd have to replicate the whole code for the type. Also, your underlying problem is that you are trying to operate on an image without knowing what it contains, and that is doomed to fail (for example, you need different noise for integer-valued images than for real-valued images. In your method, if generate_noise returns gaussian noise with mean 0, the noise will have a bias to make the image darker (because integers round _down_). – etarion Jan 20 '11 at 10:27
  • And no, having to specify this information at compile-time doesn't defeat the point of having templates, they are made for this exact thing - and polymorphism is another issue entirely. – etarion Jan 20 '11 at 10:29
  • You have a point. Do you understand why OpenCV templates the iterators and accessor methods of `cv::Mat`, as opposed to templating the whole `cv::Mat` class itself? For example, when using the STL I only have to specify the contained typename at declaration (`std::vector vec`). With OpenCV, I don't state the contained typename at declaration, but need to do it every time I use an accessor. Compared to STL, it's inconvenient. Does it have something to do with compatibility with the old C version of the library? – mpenkov Jan 21 '11 at 05:35
  • 4
    There is a template that inherits from `cv::Mat` - it's `cv::Mat_` and has premade typedefs, e.g. `cv::Mat3b` for a 3-channel image with one byte per channel. – etarion Jan 21 '11 at 09:12
1

They are hardcoded because performance is better that way.

In OpenCV1.x there is cvGet2D() , which can be used here since Mat can be casted as an IplImage. But it's slow since each time you access a pixel the function will find out the type, size, etc. Specially inefficient in loops.

nacho4d
  • 43,720
  • 45
  • 157
  • 240
  • 1
    Thanks for the suggestion, but I'd prefer to stay with the C++ interface as it's much cleaner (aside from this problem, I'm very happy with it). Also, I don't understand why `cv::Mat` isn't a templated class -- that would avoid having to template the iterators/accessor functions. Is that for efficiency reasons as well? – mpenkov Jan 20 '11 at 10:00