5
$\begingroup$

I've been completely stuck on a portion of my assignment for a few days now. After plenty of searching around, I have been unsuccessful in discovering information that leads me to the correct solution. That said, this is for OpenCV in Python, using Numpy for matrix calculations.

The problem statement:

Construct the derivative of Gaussian kernels, 𝑔𝑥 and 𝑔𝑦 by convolving the above two kernels: 𝑔𝑥=𝑆𝑥∗𝑔𝜎; 𝑔𝑦=𝑆𝑦∗𝑔𝜎

Additionally, here's a screenshot of the problem in full form:

Screenshot

What I Have Attempted:

import cv2
import numpy as np

def my_Normalize(img):
if len(img.shape) != 2:
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #convert img to grayscale
img_float = cv2.normalize(img.astype('float'), None, 0.0, 1.0, cv2.NORM_MINMAX)
return img_float

def my_DerivativesOfGaussian(img, sigma):
#the two 3x3 Sobel kernels
#Sx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float)
#Sy = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], np.float)

sobelx = cv2.Sobel(img,cv2.CV_32F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_32F,0,1,ksize=3)

#the Gaussian kernel, taken from example program 9
halfSize = 3 * sigma
maskSize = 2 * halfSize + 1 
gKern = np.ones((maskSize,maskSize)) / (float)( 2 * np.pi * (sigma**2))
xyRange = np.arange(-halfSize, halfSize+1)
xx, yy = np.meshgrid(xyRange, xyRange)    
x2y2 = (xx**2 + yy**2)    
exp_part = np.exp(-(x2y2/(2.0*(sigma**2))))
gKern = gKern * exp_part

#convolve the two kernels
Gx = cv2.filter2D(sobelx, -1, gKern)

IxN = my_Normalize(Gx)
cv2.imshow('Gx Normalized', IxN)
return Gx, Ix 

img1 = cv2.imread('TestImg1.jpg')
Ix, Iy = my_DerivativesOfGaussian(img1, 1)

cv2.waitKey(0)
cv2.destroyAllWindows()

Here is a side-by-side comparison of the result I was given for a test image (left image), compared with the result I obtained from the above execution (right image):

enter image description here

Where I am confused:

The resulting image that was obtained is close to the target image, except blurred slightly. The blurring leads me to believe there's an error with the Gaussian kernel. I also obtain a drastically different image when I switch the order of the Gaussian and Sobel kernels in the cv2.filter2D() function. However, this is counter-intuitive for me, as the association property states that convolution should be equivalent, regardless of the order in which the convolution occurs.

Specific Questions:

  • Is the cv2.filter2D() function able to convolve two kernels?
  • Assuming the answer to ^ is false, do you know of a function that is able to perform such an operation?
  • Where does my logic/understanding begin to fail? (i.e. what topics/concepts am I completely not understanding?)

Thanks so much for your time, any help is appreciated. This is the first time I have been absolutely stumped on an assignment, and can not think of a way to solve this problem without brute-forcing the convolution formula via nested loops.

Here are the images supplied for verifying correctness of the convolution, in their native formats:

Base input test image input test image

Normalized Ix Ix normalized

Normalized Iy Iy normalized

$\endgroup$
  • $\begingroup$ Lookign at your result, it does not seem to be too unreasonable. The Gaussian will introduce some smoothing in any case I think. Can you also provide the original image? $\endgroup$ – Maximilian Matthé Mar 17 '17 at 5:51
  • $\begingroup$ I've edited the original image and the two verification images that were supplied into the question now, thank you. Earlier I was trying to get a better understanding of what was done to produce these images, and the supplied Normalized Ix has different shape from the input image: img3 size: 1067346 img3 shape: (591L, 602L, 3L) img4 size: 1080000 img4 shape: (600L, 600L, 3L) - which adds to my confusion. @MaximilianMatthé $\endgroup$ – manapaws Mar 17 '17 at 6:26
  • $\begingroup$ I actually have the feeling that the images Ix and Iy you show are only the application of Sobel to the original image. Doing an additional Gaussian will smooth the result and yield the image that comes out of your code. $\endgroup$ – Maximilian Matthé Mar 17 '17 at 6:56
  • $\begingroup$ It is strange (to use a kind word) to derive the Gaussian derivative kernel this way. It is trivial to compute the analytic derivative of a Gaussian and sample that as the convolution kernel. The error introduced by using a Sobel derivative is unnecessary. A true Gaussian derivative yields a much better estimate of local edge orientation than Sobel kernels do. $\endgroup$ – Cris Luengo Nov 3 '18 at 20:36
1
$\begingroup$

To answer your specifiy questions:

Is the cv2.filter2D() function able to convolve two kernels?

Yes, but by default, it actually computes the correlation, not the convolution. However, the only difference is that the filter kernel (your second kernel) needs to be flipped. See OpenCV documentation for filter2D.

Where does my logic/understanding begin to fail? (i.e. what topics/concepts am I completely not understanding?)

I think one more issue is the size of the resulting kernel. The filter2D function returns an "output image of the same size and the same number of channels as src". However, the combined kernel increases is size (the combined size should be f1-size + f2-size - 1). So before you apply filter2D, you should add a black border to the first kernel.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.