4

As an educational excercise for myself I'm writing an application that can average a bunch of images. This is often used in Astrophotography to reduce noise.

The library I'm using is Magick++ and I've succeeded in actually writing the application. But, unfortunately, its slow. This is the code I'm using:

for(row=0;row<rows;row++)
{
    for(column=0;column<columns;column++)
    {
        red.clear(); blue.clear(); green.clear();
        for(i=1;i<10;i++)
        {
            ColorRGB rgb(image[i].pixelColor(column,row));
            red.push_back(rgb.red());
            green.push_back(rgb.green());
            blue.push_back(rgb.blue());
        }
        redVal = avg(red);
        greenVal = avg(green);
        blueVal = avg(blue);
        redVal = redVal*MaxRGB; greenVal = greenVal*MaxRGB; blueVal = blueVal*MaxRGB;
        Color newRGB(redVal,greenVal,blueVal);
        stackedImage.pixelColor(column,row,newRGB);
    }
}

The code averages 10 images by going through each pixel and adding each channel's pixel intensity into a double vector. The function avg then takes the vector as a parameter and averages the result. This average is then used at the corresponding pixel in stackedImage - which is the resultant image. It works just fine but as I mentioned, I'm not happy with the speed. It takes 2 minutes and 30s seconds on a Core i5 machine. The images are 8 megapixel and 16 bit TIFFs. I understand that its a lot of data, but I have seen it done faster in other applications.

Is it my loop thats slow or is pixelColor(x,y) a slow way to access pixels in an image? Is there a faster way?

Andrea
  • 11,801
  • 17
  • 65
  • 72
saad
  • 1,225
  • 15
  • 31

3 Answers3

4

Why use vectors/arrays at all?

Why not

double red=0.0, blue=0.0, green=0.0;
for(i=1;i<10;i++)
{
   ColorRGB rgb(image[i].pixelColor(column,row));
   red+=rgb.red();
   blue+=rgb.blue();
   green+=rgb.green();
}
red/=10;
blue/=10;
green/=10;

This avoids 36 function calls on vector objects per pixel.

And you may get even better performance by using a PixelCache of the whole image instead of the original Image objects. See the "Low-Level Image Pixel Access" section of the online Magick++ documentation for Image

Then the inner loop becomes

PixelPacket* pix = cache[i]+row*columns+column;
red+= pix->red;
blue+= pix->blue;
green+= pix->green;

Now you have also removed 10 calls to PixelColor, 10 ColorRGB constructors, and 30 accessor functions per pixel.

Note, This is all theory; I haven't tested any of it

AShelly
  • 34,686
  • 15
  • 91
  • 152
  • 1
    Yes, that is how I was doing it initially. But you see, averaging is the simplest stacking method. One can also find the median (which is great for rejecting outlier pixels [such as hot-pixels]) or Sigma Clip. There are several such methods and my goal is to implement them all. As I'm sure you can see, a method such as Median will need to look at the entire data set and I can't calculate it on the fly like an average. So I do need to store it somewhere. – saad Mar 16 '11 at 18:42
  • I just implemented everything you suggested (minus the calculating the avg on the fly suggestion), and now its finishing in about 30s! Thank you!! – saad Mar 16 '11 at 19:01
  • glad to hear it. I'd also suggest using the `PixelCache` to write to your 'stackedImage', if you haven't already. You should get some savings there too. And if you took @Benoit's suggestion about using plain arrays, don't forget that you can remove the `.clear()` call or equivalent, since you know you are going to overwrite the old items in the inner loop. – AShelly Mar 17 '11 at 18:19
1

Comments:

  • Why do you use vectors for red, blue and green? Because using push_back can perform reallocations, and bottleneck processing. You could instead allocate just once three arrays of 10 colors.
  • Couldn't you declare rgb outside of the loops in order to relieve stack of unnecessary constructions and destructions?
  • Doesn't Magick++ have a way to average images?
Benoit
  • 76,634
  • 23
  • 210
  • 236
  • Thanks for the response. You're right about the first two factors and I've just changed them in my code. Regarding the third, as mentioned, I'm doing this as an educational excercise. Secondly, I aim to implement further stacking methods, like median and Sigma Clipping etc - which I doubt Magick++ supports. – saad Mar 16 '11 at 18:39
0

Just in case anyone else wants to average images to reduce noise, and doesn't feel like too much "educational exercise" ;-)

ImageMagick can do averaging of a sequence of images like this:

convert image1.tif image2.tif ... image32.tif -evaluate-sequence mean result.tif

You can also do median filtering and others by changing the word mean in the above command to whatever you want, e.g.:

convert image1.tif image2.tif ... image32.tif -evaluate-sequence median result.tif

You can get a list of the available operations with:

identify -list evaluate

Output

Abs
Add
AddModulus
And
Cos
Cosine
Divide
Exp
Exponential
GaussianNoise
ImpulseNoise
LaplacianNoise
LeftShift
Log
Max
Mean
Median
Min
MultiplicativeNoise
Multiply
Or
PoissonNoise
Pow
RightShift
RMS
RootMeanSquare
Set
Sin
Sine
Subtract
Sum
Threshold
ThresholdBlack
ThresholdWhite
UniformNoise
Xor
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432