# Image Pyramid Without Decimation

I have an image processing algorithm which uses Gaussian and Laplacian Pyramid Decomposition. At each level of the pyramid the algorithm decimates the previous level by a factor of 2. The LPF the algorithm uses is Gaussian.

I was wondering whether I could get the same results without the decimation step (By altering the LPF).

As far as I know, the decimation step is equivalent of cutting by half the bandwidth of the LPF.

What I tried, with no success, is to double the STD of the LPF at each step and skip the decimation.

Is there another / better way to do it? Thanks.

You can generate a scale volume from an image by convolving it with Gaussians of increasing $\sigma$. Or, equivalently, by convlolving it repeatedly with the same Gaussian. It just won't be a pyramid. :)

One reason to generate a pyramid by downsampling is to reduce the amount of computation, because once you apply a low-pass filter the full resolution becomes redundant.

Also, are you computing the Laplacian pyramid by using the difference of Gaussians? In that case, you need to downsample each subsequent scale level, and then upsample it before you take the difference with the previous level.

• Thank you for your answer. I implement the Pyramid as written here: web.mit.edu/persci/people/adelson/pub_pdfs/pyramid83.pdf. I know the reason for the decimation step as you mentioned (Another reason is mentioned above - Cut the LPF cutoff frequency by half). If I don't care about computation time, How could I achieve the same result in the synthesis step without the decimation (Needless to say with the appropriate synthesis)? – Royi Nov 18 '11 at 21:37
• @Drazick, I don't think it is possible to generate a Laplacian pyramid a la Burt and Adelson without down-sampling. And why would you want to? What is the actual problem you are trying to solve? If you really want a Laplacian scale volume of the same size as the image, you can just convolve the original with a Laplacian-of-Gaussian filter of varying bandwidth. – Dima Nov 21 '11 at 18:24
• I'd like to achieve the same effect without changing the dimensions of the matrix I'm working on. This could beneficial under some circumstances. I've tried (See below) just to change the bandwidth, something is missing there. It should be possible since it is done in the Multiscale Signal Processing. – Royi Nov 22 '11 at 8:16

In wavelet analysis, assuming you're operating on two-dimensional dyadic wavelets, in order to get the next (larger) wavelet, you convolve the current one with itself. Take you current LPF, convolve it with itself, and you'll get you next one. Proceed for however many iteration you need.

• I will give it a try and report. Though I think in the Gaussian case the result will be the same (Since I set the support of the LPF as 3 times the Std). Thanks. – Royi Nov 18 '11 at 19:30
• Nope, it won't give the same results (Or even close for that matter). – Royi Nov 18 '11 at 20:08
• Hm... can you post some code? Is it MATLAB? I'll play around with it. Been ages since I've done multiresolution. – Phonon Nov 18 '11 at 20:19
• Could you give your email? I'll send it over. It's a MATLAB Code. If anyone else would like to try, I'd be more than happy. Just leave your email. – Royi Nov 18 '11 at 20:26
• It's in my profile. – Phonon Nov 18 '11 at 20:33

For posterity, I'm going to add that you can build the pyramid in this way.

In other words, if you choose the correct standard deviation for the gaussians, you can do all the low-pass filtering to the original image first, and then downsample later to make identical results to if you had used the normal blur-downsample-blur-downsample method.

Here is python (opencv, numpy) code to show it.

import cv2
import numpy
import math
import sys

vidcap = cv2.VideoCapture( sys.argv[1] );
img = img[:,:,0]/3 + img[:,:,1]/3 + img[:,:,2]/3;
img = numpy.float32(img);
img = img * (1.0 / 255);

class size:
width=0;
height=0;
def __init__(self, w, h):
self.width=w;
self.height=h;

def pair(self):
return (self.width, self.height);

def getsize( img ):
s = size(img.shape[1], img.shape[0]);
return s;

#############################################################################################################################
## REV: "normal" method of blurring and then downsampling and using the downsampled as the input to the next level.
d1d = cv2.GaussianBlur( img, (0,0), sig1 );
d1 = cv2.resize( d1d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d2d = cv2.GaussianBlur( d1, (0,0), sig1 );
d2 = cv2.resize( d2d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d3d = cv2.GaussianBlur( d2, (0,0), sig1 );
d3 = cv2.resize( d3d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d4d = cv2.GaussianBlur( d3, (0,0), sig1 );
d4 = cv2.resize( d4d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d5d = cv2.GaussianBlur( d4, (0,0), sig1 );
d5 = cv2.resize( d5d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d6d = cv2.GaussianBlur( d5, (0,0), sig1 );
d6 = cv2.resize( d6d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d7d = cv2.GaussianBlur( d6, (0,0), sig1 );
d7 = cv2.resize( d7d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );
d8d = cv2.GaussianBlur( d7, (0,0), sig1 );
d8 = cv2.resize( d8d, None, fx = 0.5, fy = 0.5, interpolation = cv2.INTER_NEAREST );

#############################################################################################################################
## REV: "new" method -- i.e. apply correct width LPF directly to original image, then dowsample at correct skippage aferwards.
## REV: Apply "Direct" method filters to origial image "img"
## REV: Advantage is that blurs can be applied in parallel, instead of required serial.

#REV: First, build the standard deviations of the "direct" blurs (i.e. to blur without downsampling).
#REV: Gsig1 convolved with Gsig2 = Gsig, sig3^2 = sig1^2 + sig2^2, so sig3 = sqrt(sig1^2 + sig2^2). I could just directly convolve the filters, but this might be faster?
#REV: Also, this is a closed form solution...
sig1 = math.sqrt( 0*0     + 1**2 );
sig2 = math.sqrt( sig1**2 + 2**2 );
sig3 = math.sqrt( sig2**2 + 4**2 );
sig4 = math.sqrt( sig3**2 + 8**2 );
sig5 = math.sqrt( sig4**2 + 16**2 );
sig6 = math.sqrt( sig5**2 + 32**2 );
sig7 = math.sqrt( sig5**2 + 64**2 );
sig8 = math.sqrt( sig5**2 + 128**2 );

s1d = cv2.GaussianBlur( img, (0,0), sig1 );
s2d = cv2.GaussianBlur( img, (0,0), sig2 );
s3d = cv2.GaussianBlur( img, (0,0), sig3 );
s4d = cv2.GaussianBlur( img, (0,0), sig4 );
s5d = cv2.GaussianBlur( img, (0,0), sig5 );
s6d = cv2.GaussianBlur( img, (0,0), sig6 );
s7d = cv2.GaussianBlur( img, (0,0), sig7 );
s8d = cv2.GaussianBlur( img, (0,0), sig8 );

## REV: downsampling the straight-LPF applied images
s1 = cv2.resize( s1d, (d1.shape[1], d1.shape[0]), interpolation = cv2.INTER_NEAREST );
s2 = cv2.resize( s2d, (d2.shape[1], d2.shape[0]), interpolation = cv2.INTER_NEAREST );
s3 = cv2.resize( s3d, (d3.shape[1], d3.shape[0]), interpolation = cv2.INTER_NEAREST );
s4 = cv2.resize( s4d, (d4.shape[1], d4.shape[0]), interpolation = cv2.INTER_NEAREST );
s5 = cv2.resize( s5d, (d5.shape[1], d5.shape[0]), interpolation = cv2.INTER_NEAREST );
s6 = cv2.resize( s6d, (d6.shape[1], d6.shape[0]), interpolation = cv2.INTER_NEAREST );
s7 = cv2.resize( s7d, (d7.shape[1], d7.shape[0]), interpolation = cv2.INTER_NEAREST );
s8 = cv2.resize( s8d, (d8.shape[1], d8.shape[0]), interpolation = cv2.INTER_NEAREST );

key = cv2.waitKey( 0 );
while( key != 113): #REV: 113 == 'q'?
print( "Key", key )
key = cv2.waitKey(0);