简体   繁体   中英

How to demonstrate that the impulse response of the Gaussian Pyramid is Scale Invariant?

I built a Gaussian Pyramid from a 512x512 image with one Dirac pulse at the centre(256,256), then tried to follow the following procedure to prove that this pyramid is scale-invariant, and it has the same impulse response at each level, but the results doesn't seem to be very correct!

金字塔 脉冲响应 Can you please advise me how to do it?

Edit:

I edited the code to fix some bugs, thanks to @ CrisLuengo for his notes.

Code:

import numpy as np
import matplotlib.pyplot as plt
import cv2
import skimage.exposure as exposure
from math import sqrt, ceil

#=================
# Resize Function
#=================   
def _resize(image, downscale=2, step=0.5, minSize=(7, 7)):
    if(image.shape > minSize ):
        # newSize = (image.shape[0]// downscale, image.shape[1]//downscale)
        # newImage = cv2.resize(image, dsize=newSize, fx=step, fy=step) 
        newImage = cv2.resize(image, None, fx=step, fy=step) 
        return newImage
    else:
        return 0
#--------------------------------------------------------------
#===========================
# Gaussian Pyramid Function
#===========================
def pyramid(image, sigma_0=1):
    '''
    Function to create a Gaussian pyramid from an image for given standard deviation sigma_0

    Parameters:
    -----------
    @param: image: nd-array.
             The original image.
    @param: sigma_0: float.
            standard deviation of the Gaussian distribution.

    returns:
    List of images with different scales, the pyramid
    '''
    # Resize All input images into a standard size
    image = cv2.resize(image,(512,512))

    # level 0
    if ceil(6*sigma_0)%2 ==0 : 
        Gimage = cv2.GaussianBlur(image, (ceil(6*sigma_0)+1, ceil(6*sigma_0)+1), sigmaX=sigma_0, sigmaY=sigma_0)
    else:
        Gimage = cv2.GaussianBlur(image, (ceil(6*sigma_0)+2, ceil(6*sigma_0)+2), sigmaX=sigma_0, sigmaY=sigma_0)
    # sigma_k
    sigma_k = 4*sigma_0
    # sigma_k = sqrt(2)*sigma_0

    # Pyramid as list
    GaussPyr = [Gimage]

    # Loop  of other levels of the pyramid
    for k in range(1,6):

        if ceil(6*sigma_k)%2 ==0 :
            # smoothed = cv2.GaussianBlur(GaussPyr[k-1], (ceil(6*sigma_k)+1, ceil(6*sigma_k)+1), sigmaX=sigma_k, sigmaY=sigma_0)
            smoothed = cv2.GaussianBlur(GaussPyr[k-1], (ceil(6*sigma_k)+1, ceil(6*sigma_k)+1), sigmaX=sigma_k, sigmaY=sigma_k)
        else:
            # smoothed = cv2.GaussianBlur(GaussPyr[k-1], (ceil(6*sigma_k)+2, ceil(6*sigma_k)+2), sigmaX=sigma_k, sigmaY=sigma_0)
            smoothed = cv2.GaussianBlur(GaussPyr[k-1], (ceil(6*sigma_k)+2, ceil(6*sigma_k)+2), sigmaX=sigma_k, sigmaY=sigma_k)

        # Downscaled Image
        resized = _resize(smoothed ) # ,step=0.25*sigma_k
        GaussPyr.append(resized)
    return GaussPyr
#====================
# Impulse Response
#====================
# Zeros 512x512 Black Image
delta = np.zeros((512, 512), dtype=np.float32)
# Dirac
delta[255,255] = 255

# sigmas
sigma1 = 1
sigma2 = sqrt(2)

# Pyramids
deltaPyramid1 = pyramid(delta, sigma_0=sigma1)
deltaPyramid2 = pyramid(delta, sigma_0=sigma2)

# Impulse Response for each level
ImpResp1 = np.zeros((len(deltaPyramid1), 13),dtype=float)
ImpResp2 = np.zeros((len(deltaPyramid2), 13),dtype=float)
# sigma = 1
for idx, level in enumerate(deltaPyramid1):
    # # 1
    # level = cv2.resize(level, (512, 512))# , interpolation=cv2.INTER_AREA
    # ImpResp1[idx,:] = exposure.rescale_intensity(level[255, 249:262], in_range='image', out_range=(0,255)).astype(np.uint8)
    # ImpResp1[idx,:] = level[255, 249:262]
    
    # # 2
    centery = level.shape[0]//2
    centerx = level.shape[1]//2
    ImpResp1[idx,:] = exposure.rescale_intensity(level[centery, (centerx-7):(centerx+6)], out_range=(0,255), in_range='image').astype(np.uint8)
    # ImpResp1[idx,:] = level[centery, (centerx-7):(centerx+6)]
# sigma = sqrt(2)
for idx, level in enumerate(deltaPyramid2):
    # # 1
    # level = cv2.resize(level, (512, 512))# , interpolation=cv2.INTER_AREA
    # ImpResp2[idx,:] = exposure.rescale_intensity(level[255, 249:262], in_range='image', out_range=(0,255)).astype(np.uint8)
    # ImpResp2[idx,:] = level[255, 249:262]
    # # 2
    centery = level.shape[0]//2
    centerx = level.shape[1]//2
    ImpResp2[idx,:] = exposure.rescale_intensity(level[centery, (centerx-7):(centerx+6)], out_range=(0,255), in_range='image').astype(np.uint8)
    # ImpResp2[idx,:] = level[centery, (centerx-7):(centerx+6)]

#====================
# Visualize Results
#====================
labels = []
for c in range(13):
    label = 'C{}'.format(c+1)
    labels.append(label)

x = np.arange(len(labels))  # the label locations
width = 0.1  # the width of the bars

fig, ax = plt.subplots()
rects1 = []
for k in range(ImpResp1.shape[0]):
    rects1.append(ax.bar(x - 2*k*width, ImpResp1[k], width, label='K{}'.format(k)))

# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('values')
ax.set_title('sigma0=1')
ax.set_xticks(x)
ax.set_xticklabels(labels)
ax.legend()

fig.tight_layout()

fig2, ax2 = plt.subplots()
rects2 = []
for k in range(ImpResp1.shape[0]):
    rects2.append(ax2.bar(x + 2*k*width, ImpResp2[k], width, label='K{}'.format(k)))

# Add some text for labels, title and custom x-axis tick labels, etc.
ax2.set_ylabel('values')
ax2.set_title('sigma0=sqrt(2)')
ax2.set_xticks(x)
ax2.set_xticklabels(labels)
ax2.legend()

fig2.tight_layout()

plt.show()

First, let's simplify to a situation that is simple enough to see the scaling property of the Gaussian. Convolving a delta image with a Gaussian yields that Gaussian. A Gaussian B twice the size of a Gaussian A, and then scaled spatially by half, is identical to A (up to intensity scaling of course, B is 1/4 as high as A in 2D).

delta = <all zeros except one pixel in the middle>
A = GaussianBlur(delta, 1)
B = GaussianBlur(delta, 2)
B = resize(B, 1/2)
A == B * 2**2
C = GaussianBlur(delta, sigma=7.489)
C = resize(C, 1/7.489)
A == C * 7.489**2

Now, if we're chaining the blur operations, we obtain a stronger blur. The square of the output sigma is equal to the sum of squares of the sigmas applied:

A = GaussianBlur(delta, 1)
B = GaussianBlur(delta, 2)
C = GaussianBlur(A, sqrt(3))
B == C

That is, 1**2 + sqrt(3)**2 = 2**2 .

So, at each step in the pyramid, we need to compute how much blurring we've already applied, and apply the right amount to get to the necessary level of blurring. Every time we blur, we increase the blur by a given amount, every time we rescale we reduce the blur by a given amount.

If sigma0 is the initial smoothing, and sigma1 is the smoothing applied before downscaling, and downscaling is by a factor k>1, then this relationship:

sqrt(sigma0**2 + sigma1**2) / k == sigma0

will ensure that the downscaled delta image is the same as the original smoothed delta image (up to intensity scaling). We obtain:

sigma1 = sqrt((sigma0 * k)**2 - sigma0**2)

(if I did they right, here on my phone screen).

Since we're back to an image identical to the original, subsequent pyramid levels will use these same values.

An additional issue I noticed in your code is that you rescale the delta image “to a standard size” before starting to process. Don't do this, the delta image will no longer be a delta image, and the relationships above will no longer hold. The input must have exactly one pixel set to 1, the rest being 0.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM