# Replace a chain of image blurs with one blur

In this question I asked how to implement a chain of blurs in one single step.

Then I found out from the gaussian blur page of Wikipedia that:

Applying multiple, successive gaussian blurs to an image has the same effect as applying a single, larger gaussian blur, whose radius is the square root of the sum of the squares of the blur radii that were actually applied. For example, applying successive gaussian blurs with radii of 6 and 8 gives the same results as applying a single gaussian blur of radius 10, since sqrt {6^{2}+8^{2}}=10.

So I thought that blur and singleBlur were the same in the following code:

cv::Mat firstLevel;
float sigma1, sigma2;
//intialize firstLevel, sigma1 and sigma2
cv::Mat blur = gaussianBlur(firstLevel, sigma1);
blur = gaussianBlur(blur, sigma2);
float singleSigma = std::sqrt(std::pow(sigma1,2)+std::pow(sigma2,2));
cv::Mat singleBlur = gaussianBlur(firstLevel, singleSigma);
cv::Mat diff = blur != singleBLur;
// Equal if no elements disagree
assert( cv::countNonZero(diff) == 0);


But this assert fails (and actually, for example, the first row of blur is different from the first one of singleBlur).

Why?

UPDATE:

What I'm trying to do is to parallelize this code. In particular, I'm focusing now on computing all the blurs at all levels in advance. The serial code (which works correctly) is the following:

   vector<Mat> blurs ((par.numberOfScales+3)*levels, Mat());
cv::Mat octaveLayer = firstLevel;
int scaleCycles = par.numberOfScales+2;

//compute blurs at all layers (not parallelizable)
for(int i=0; i<levels; i++){
blurs[i*scaleCycles+1] = octaveLayer.clone();
for (int j = 1; j < scaleCycles; j++){
float sigma = par.sigmas[j]* sqrt(sigmaStep * sigmaStep - 1.0f);
blurs[j+1+i*scaleCycles] = gaussianBlur(blurs[j+i*scaleCycles], sigma);
if(j == par.numberOfScales)
octaveLayer = halfImage(blurs[j+1+i*scaleCycles]);
}
}


I'm sorry for the horrible indexes above, but I tried to respect the original code system (which is horrible, like starting counting from 1 instead of 0). The code above has scaleCycles=5 and levels=6, so 30 blurs are generated in total.

This is the "single blur" version, where first I compute the sigmas for each blur that has to be computed (following Wikipedia's formula) and then I apply the blur (notice that this is still serial and not parallelizable):

   vector<Mat> singleBlurs ((par.numberOfScales+3)*levels, Mat());
vector<float> singleSigmas(scaleCycles);
float acc = 0;
for (int j = 1; j < scaleCycles; j++){
float sigma = par.sigmas[j]* sqrt(sigmaStep * sigmaStep - 1.0f);
acc += pow(sigma, 2);
singleSigmas[j] = sqrt(acc);
}

octaveLayer = firstLevel;
for(int i=0; i<levels; i++){
singleBlurs[i*scaleCycles+1] = octaveLayer.clone();
for (int j = 1; j < scaleCycles; j++){
float sigma = singleSigmas[j];
std::cout<<"j="<<j<<" sigma="<<sigma<<std::endl;
singleBlurs[j+1+i*scaleCycles] = gaussianBlur(singleBlurs[j+i*scaleCycles], sigma);
if(j == par.numberOfScales)
octaveLayer = halfImage(singleBlurs[j+1+i*scaleCycles]);
}
}


Of course the code above generates 30 ...

edit retag close merge delete

Maybe it is because of bounding effects on the image boundaries. It will be great if you just visualize the diff by the cv::imshow(...). Can you paste it here?

@pi-null-mezon thanks for your interest. I posted part of diff and the original code that I use

He means create diff and then display it on the screen as an image using imshow. Then save it and post that image here.

@Tetragramm I updated the question with many other details. Please give a look at it.

Sort by » oldest newest most voted Ok, you don't have to worry. What you need to look at is not that there is a difference, but the amount of difference.

Running your code shows the same thing, lots of difference, but if instead you find diff by using the absdiff function, you see that the amount of difference is very small. The largest difference is 27 counts, and that's right at the edge of the image. If you look at the difference image, it's totally black. In fact, there isn't a difference larger than 1 except right at the edge, where the border behaves differently.

The differences of 1 are just slightly different coefficients causing rounding effects differently. When you do it twice, you round to integer values between blurs, so the second blur isn't as accurate. A single blur doesn't have that problem.

more

@Tetragramm I updated the question with many other details. Please give a look at it.

Note again: You are multiplying the differences by 255 when you convert to CV_8U. In none of the code I see here, or at the link do you divide by 255. This means you take those images and divide by 255 to see the true difference.

I'm not sure that's the problem, but based on what you've posted, it looks like it. You need to double check that the magnitude of the input is the same as the output.

@Tetragramm but these images looks the same even by using simple imshow, where I don't convert anything.

Right. But that's because the float version of imshow uses a range of 0-1, but your input images are still 0-255. So that's still the problem.

Your images are range 0-255. Then your blurs are in the range 0-255. Then your absdiff is in range 0-255, then you multiply by 255 to show. So that's your problem.

@Tetragramm I'm sorry, I'm missing your point. By "your problem" you mean how images are shown/saved, or why the single blur formula doesn't work? I'm sorry if I didn't understand your answer, if you can give me more details it would be much appreciated

What I mean is that there is no actual problem. You are displaying the images in a way that exaggerates the error by a factor of 255. If you simply look at the actual images for the two methods, you will see they appear identical.

@Tetragramm from my understanding of your answer, you say that the blurs obtained from the chain version and the ones obtained with one shot are the same...But then why the number of produced keypoints is different? The only explanation is that the produced blurs are different.

There are very small differences between the images, but they are fundamentally the same. Remember that the gaussian blur function performs an approximation of a true gaussian blur. If you could have a "true" blur, then there would be absolutely no differences between them, but you can't. As it is, the differences are very small, and not worth worrying about.

BUT if the difference was so small, my algorithm would generate almost the same number of keypoints, right? Instead using the code with chain of blurs (the correct, original version) generates 1760 keypoints, instead with one blur 2397! Something's is wrong here and I'm really stucked trying to understand what XD

Official site

GitHub

Wiki

Documentation