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] );
success,img = vidcap.read();
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);