No description has been provided for this image

Image Derivatives¶

Faisal Qureshi
Professor
Faculty of Science
Ontario Tech University
Oshawa ON Canada
http://vclab.science.ontariotechu.ca

Copyright information¶

© Faisal Qureshi

License¶

Creative Commons Licence
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Lesson Plan¶

  • Why do we care about image gradients?
  • Computing image derivatives
  • Sobel filters
  • Gradient magnitude and directions
  • Visualizing image gradients

Image edges and image deriatives¶

In [1]:
import numpy as np
import matplotlib.pyplot as plt
In [2]:
def sigmoid(x, s=1.0, t=0.0):
    return 1./(1.+np.exp(-s*(x-t)))
In [3]:
x = np.linspace(0,10,100)
plt.plot(x, sigmoid(x, s=3, t=5))
Out[3]:
[<matplotlib.lines.Line2D at 0x1147aaad0>]
No description has been provided for this image
In [4]:
# im = np.zeros([100,200])
# for i in range(100):
#     im[i,:] = np.hstack([sigmoid(x, s=5, t=5), 1-sigmoid(x, s=2.5, t=5)])

# The following does exactly what the for-loop shown above does
import numpy.matlib as matlab
im = matlab.repmat(np.hstack([sigmoid(x, s=5, t=5), 1-sigmoid(x, s=2.5, t=5)]), 100,1)    
    
plt.figure(figsize=(10,7))
plt.imshow(im, cmap='gray')
plt.xlabel('Width ($x$)')
plt.ylabel('Height ($y$)')
Out[4]:
Text(0, 0.5, 'Height ($y$)')
No description has been provided for this image
In [5]:
plt.figure(figsize=(10,12))
plt.subplot(211)
plt.imshow(im, cmap='gray')
plt.title('Image')
plt.plot([1,199],[40,40],'r-')
plt.xlabel(r'Width ($x$)')
plt.ylabel(r'Height ($y$)')
plt.subplot(212)
plt.title('Row 40')
plt.plot(im[40,1:199],'r')
plt.xlabel(r'Width ($x$)')
plt.ylabel(r'$I(x)$')
Out[5]:
Text(0, 0.5, '$I(x)$')
No description has been provided for this image
In [6]:
Hx = [1,-1]
dx = np.convolve(im[0,:], Hx)
d2x = np.convolve(dx, Hx)
plt.figure(figsize=(12,15))
plt.subplot(311)
plt.title('Intensity')
plt.plot(im[0,:],'r')
plt.xticks([])
#plt.xlabel(r'Width ($x$)')
plt.ylabel(r'$I(x)$')
#plt.plot([100,100],[0,1],'k--')
plt.plot([150,150],[0,1],'k--')
plt.plot([50,50],[0,1],'k--')
plt.subplot(312)
#plt.xlabel(r'Width ($x$)')
plt.ylabel(r"$I'(x)$")
plt.title('Derivative')
plt.plot(dx)
plt.xticks([])
#plt.plot([100,100],[-0.04,0.08],'k--')
plt.plot([50,50],[-0.06,0.13],'k--')
plt.plot([150,150],[-0.06,0.13],'k--')
plt.subplot(313)
plt.xlabel(r'Width ($x$)')
plt.ylabel(r"$I''(x)$")
plt.title('Second Derivative')
plt.plot(d2x)
#plt.plot([100,100],[-0.01,0.01],'k--')
plt.plot([50,50],[-0.03,0.03],'k--')
plt.plot([150,150],[-0.03,0.03],'k--')
Out[6]:
[<matplotlib.lines.Line2D at 0x115b20550>]
No description has been provided for this image

Computing image derivatives¶

Image gradients capture local changes in the image. These are a fundamental image processing operation that is required for a number of subsequent image analysis procedures, such as edge detection, segmentation, feature construction, etc.

Option 1: Reconstruct a continuous function $f$, then compute partial derivatives as follows

$$ \frac{\partial f(x,y)}{\partial x} = \lim_{\epsilon \to 0} \frac{f(x+\epsilon,y) - f(x,y)}{\epsilon} $$

$$ \frac{\partial f(x,y)}{\partial y} = \lim_{\epsilon \to 0} \frac{f(x,y+\epsilon) - f(x,y)}{\epsilon} $$

Option 2: We can use finite difference approximation to estimate image derivatives $$ \frac{\partial f(x,y)}{\partial x} \approx \frac{f[x+1,y] - f[x,y]}{1} $$

$$ \frac{\partial f(x,y)}{\partial y} \approx \frac{f[x,y+1] - f[x,y]}{1} $$

Observation: We can compute image derivatives using convolutions (linear filtering)

Kernel for computing image derivative w.r.t. $x$ (width)¶

$$ H_x = \left[ \begin{array}{rr} 1 & -1 \\ \end{array} \right] $$

Kernel for computing image derivative w.r.t. $y$ (height)¶

$$ H_y = \left[ \begin{array}{r} 1 \\ -1 \\ \end{array} \right] $$

Finite difference filters¶

  • Sobel filters

$$ H_x = \left[\begin{array}{rrr} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{array}\right] \mathrm{\ and\ } H_y = \left[\begin{array}{rrr} 1 & 2 & 1 \\ 0 & 0 & 0 \\ -1 & -2 & -1 \end{array}\right] $$

  • Prewire

$$ H_x = \left[\begin{array}{rrr} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{array}\right] \mathrm{\ and\ } H_y = \left[\begin{array}{rrr} 1 & 1 & 1 \\ 0 & 0 & 0 \\ -1 & -1 & -1 \end{array}\right] $$

  • Roberts

$$ H_x = \left[\begin{array}{rr} 0 & 1 \\ -1 & 0 \end{array}\right] \mathrm{\ and\ } H_y = \left[\begin{array}{rr} 1 & 0 \\ 0 & -1 \end{array}\right] $$

Image derviatives highlights edge pixels¶

No description has been provided for this image
  • X derivative is computed by convolving image with $H_x$, and Y derivative is computed by convolving image with $H_y$.
  • Pixels sitting on vertical edges are highlighted in X derivative.
  • Pixels sitting on horizontal edges are highlighted in Y derivative.

Image gradient¶

  • Image gradient $\nabla I$ points to the direction of most rapid change in intensity.

$$ \nabla I = \left[ \begin{array}{rr}\frac{\partial I}{\partial x} & \frac{\partial I}{\partial y} \end{array}\right]^T $$

  • Gradient magnitude (can be used to identify edge pixels)

$$ \| \nabla I \| = \sqrt{ \left( \frac{\partial I}{\partial x} \right)^2 + \left( \frac{\partial I}{\partial y} \right)^2 } $$

  • Gradient direction (is perpendicular to the underlying edge)

$$ \theta = \tan^{-1} \left( \frac{\partial I}{\partial y} / \frac{\partial I}{\partial x} \right) $$

No description has been provided for this image
No description has been provided for this image

Image Derivatives are highly sensitive to noise¶

No description has been provided for this image
No description has been provided for this image

Use smoothing first to get rid of the high-frequency components as seen below

No description has been provided for this image

Speeding up¶

No description has been provided for this image
No description has been provided for this image

Code Example¶

In [7]:
import cv2
import numpy as np
import scipy as sp
from scipy import signal
import matplotlib.pyplot as plt
%matplotlib inline

Kernel for computing image gradients¶

In [8]:
# flipped since convolution is flips

Hx = np.array([[1,-1]], dtype='float32')
Hy = np.array([[1],[-1]], dtype='float32')

plt.imshow(Hx, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
Out[8]:
([], [])
No description has been provided for this image
In [9]:
plt.imshow(Hy, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
Out[9]:
([], [])
No description has been provided for this image

Square¶

In [10]:
square = np.zeros((32,32), dtype='float32')
square[8:8+16, 8:8+16] = 1.0

plt.imshow(square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
Out[10]:
([], [])
No description has been provided for this image
In [11]:
Hx_square = sp.signal.convolve2d(square, Hx, 'same')

plt.imshow(Hx_square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
Out[11]:
([], [])
No description has been provided for this image
In [12]:
Hy_square = sp.signal.convolve2d(square, Hy, 'same')

plt.imshow(Hy_square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
Out[12]:
([], [])
No description has been provided for this image

Gradient magnitude¶

In [13]:
plt.figure(figsize=(15,5))
plt.subplot(131)
plt.imshow(square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Square')
plt.subplot(132)
plt.imshow(Hx_square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('X derivative')
plt.subplot(133)
plt.imshow(Hy_square, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Y derivative')
Out[13]:
Text(0.5, 1.0, 'Y derivative')
No description has been provided for this image
In [14]:
square_grad_mag = np.sqrt(np.square(Hx_square) + np.square(Hy_square))
plt.figure(figsize=(10,10))
plt.imshow(square_grad_mag, cmap='gray', interpolation='none')

plt.xticks([])
plt.yticks([])
plt.title('Gradient magnitude')
Out[14]:
Text(0.5, 1.0, 'Gradient magnitude')
No description has been provided for this image

Gradient direction¶

In [15]:
square_grad_angle = np.arctan2(Hy_square, Hx_square)

plt.figure(figsize=(10,10))
x_c, y_c = np.linspace(0,31,32), np.linspace(0,31,32)
xx, yy = np.meshgrid(x_c, y_c)
plt.quiver(xx, yy, Hx_square, Hy_square, scale=25, color='red', width=.005)
plt.imshow(square, cmap='gray')
plt.title('Gradient')
plt.xticks([])
plt.yticks([])
Out[15]:
([], [])
No description has been provided for this image
In [16]:
plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(square_grad_mag, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Gradient magnitude')
plt.subplot(122)
plt.quiver(xx, yy, Hx_square, Hy_square, scale=25, color='red', width=.005)
plt.imshow(square, cmap='gray')
plt.xticks([])
plt.yticks([])
plt.title('Gradient');
No description has been provided for this image

Circle¶

In [17]:
s = 128
circle = np.zeros((s,s), dtype='float32')

import math
cx, cy = 128./2, 128./2
for i in range(0, s-1):
    for j in range(0, s-1):
        if math.sqrt( (cx - i)**2 + (cy - j)**2 ) < 128./4:
            circle[i,j] = 1.0
            
plt.imshow(circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image
In [18]:
Hx = np.array([[1,0,-1],[2,0,-2],[1,0,-1]])

Hx_circle = sp.signal.convolve2d(circle, Hx, 'same')

plt.imshow(Hx_circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image
In [19]:
Hy = np.array([[1,2,1],[0,0,0],[-1,-2,-1]])

Hy_circle = sp.signal.convolve2d(circle, Hy, 'same')

plt.imshow(Hy_circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image
In [20]:
plt.figure(figsize=(15,5))
plt.subplot(131)
plt.imshow(circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Circle')
plt.subplot(132)
plt.imshow(Hx_circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('X derivative')

plt.subplot(133)
plt.imshow(Hy_circle, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Y derivative');
No description has been provided for this image

Gradient magnitude¶

In [21]:
circle_grad_mag = np.sqrt(np.square(Hx_circle) + np.square(Hy_circle))
plt.figure(figsize=(10,10))
plt.imshow(circle_grad_mag, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Gradient magnitude');
No description has been provided for this image

Gradient direction¶

In [22]:
circle_grad_angle = np.arctan2(Hy_circle, Hx_circle)

plt.figure(figsize=(10,10))
x_c, y_c = np.linspace(0,127,128), np.linspace(0,127,128)
xx, yy = np.meshgrid(x_c, y_c)
plt.quiver(xx, yy, Hx_circle, Hy_circle, scale=500, color='red')
#plt.imshow(circle, cmap='gray')
plt.title('Gradient')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image
In [23]:
plt.figure(figsize=(30,10))
plt.subplot(131)
plt.imshow(circle_grad_mag, cmap='gray', interpolation='none')
plt.xticks([])
plt.yticks([])
plt.title('Gradient magnitude')

plt.plot([64,104],[64,64], 'r')
plt.plot([64,104],[64-40,64-40], 'r')
plt.plot([64,64],[64,64-40], 'r')
plt.plot([104,104],[64,64-40], 'r')

plt.subplot(132)
# x_c, y_c = np.linspace(0,127,128), np.linspace(0,127,128)
# xx, yy = np.meshgrid(x_c, y_c)
# plt.quiver(xx, yy, Hx_circle, Hy_circle, scale=500, color='red')
# plt.imshow(circle, cmap='gray')
# plt.title('Gradient')
# plt.xticks([])
# plt.yticks([])
# plt.subplot(133)
x_c, y_c = np.linspace(64,104,40), np.linspace(64,104,40)
xx, yy = np.meshgrid(x_c, y_c)
plt.quiver(xx, yy, Hx_circle[64:104,64:104], Hy_circle[64:104,64:104], scale=150, color='red')
#plt.imshow(circle[64:104,64:104], cmap='gray')
plt.title('Gradient')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image

Using color to visualize image gradients¶

In [24]:
hsv = np.zeros([circle.shape[0],circle.shape[0],3], dtype=np.uint8)
hsv[..., 1] = 255

mag, ang = cv2.cartToPolar(Hx_circle[...], Hy_circle[...])
# print(hsv.shape)
# print(mag.shape)
# print(ang.shape)
hsv[..., 0] = ang * 180 / np.pi / 2
hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

plt.figure(figsize=(20,10))
plt.subplot(121)
plt.imshow(bgr)
plt.xticks([])
plt.yticks([])

plt.subplot(122)
width = 1000
x = np.linspace(width, -width, width*2+1)
y = np.linspace(width, -width, width*2+1)
xx, yy = np.meshgrid(x, y)

hsv = np.zeros([x.shape[0], y.shape[0], 3], dtype='uint8')
hsv[..., 1] = 255

mag, ang = cv2.cartToPolar(xx[...], yy[...])
# print(hsv.shape)
# print(mag.shape)
# print(ang.shape)
hsv[..., 0] = ang * 180 / np.pi / 2
hsv[..., 2] = cv2.normalize(mag, None, 0, 255, cv2.NORM_MINMAX)
bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
plt.imshow(bgr)
plt.xticks([])
plt.yticks([]);
No description has been provided for this image

Effects of noise - code¶

In [25]:
# 1D kernel for computing derivatives
Hx = np.array([1,-1], dtype='float32')
In [26]:
np.random.seed(0)

x = np.linspace(0,100,101)
y = np.ones(x.shape)*10
y[x <= 50] = 0
plt.figure(figsize=(20,5))
plt.plot(x, y, 'r.')
plt.plot(x, y, 'b')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image
In [27]:
plt.figure(figsize=(20,5))
dy = np.convolve(y, Hx, 'same')
plt.plot(x, dy, 'b')
plt.xticks([])
plt.yticks([])
Out[27]:
([], [])
No description has been provided for this image

Creating a noisy signal¶

In [28]:
x = np.linspace(0,1000,1001)
yn = np.ones(x.shape)*9
yn[x <= 500] = 0
yn = yn + np.random.rand(yn.shape[0])*8

plt.figure(figsize=(20,5))
plt.plot(x, yn, 'r.')
plt.plot(x, yn, 'b')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image

Edge finding in noisy signal¶

In [29]:
dyn = np.convolve(yn, Hx, 'same')
plt.figure(figsize=(20,5))
plt.plot(x, dyn, 'b')
plt.xticks([])
plt.yticks([]);
No description has been provided for this image

1D Gaussian - recap¶

In [30]:
s = 4
ticks = np.linspace(-s,s,2*s+1)

gxx = np.linspace(-40,40,801)
mu = 0
sigma = 5
g = np.exp(- (gxx-mu)**2 / (2*(sigma**2)) ) / ( 2 * np.pi * sigma)

plt.figure(figsize=(20,5))
plt.plot(gxx, g, 'k', linewidth=5)
plt.xlim([-s*sigma, s*sigma])
plt.yticks([])
plt.title(r'Gaussian.  ($\mu=0, \sigma=5$)');
No description has been provided for this image

1D Gaussian - First Derivative¶

In [31]:
dg = np.convolve(g, Hx, 'same')

plt.figure(figsize=(20,5))
plt.plot(gxx, dg, 'k', linewidth=5)
plt.xlim([-s*sigma, s*sigma])
plt.yticks([])
plt.title(r'Gaussian 1st derivative.  ($\mu=0, \sigma=5$)');
No description has been provided for this image

1D Guassian - Second Derviative¶

In [32]:
ddg = np.convolve(dg, Hx, 'same')

plt.figure(figsize=(20,5))
plt.plot(gxx, ddg, 'k', linewidth=5)
plt.xlim([-s*sigma, s*sigma])
plt.yticks([])
plt.title(r'Gaussian 2nd derivative.  ($\mu=0, \sigma=5$)');
No description has been provided for this image

Setting up Gaussian and Gaussian derivative¶

In [33]:
gxx = np.linspace(-20,20,41)
mu = 0
sigma = 7
g = np.exp(- (gxx-mu)**2 / (2*(sigma**2)) ) / ( 2 * np.pi * sigma)
plt.figure(figsize=(20,5))
plt.plot(gxx, g)
plt.xticks([])
plt.yticks([])
plt.title('Gaussian');
No description has been provided for this image
In [34]:
dg = np.convolve(g, Hx, 'same')
plt.figure(figsize=(20,5))
plt.plot(gxx, dg)
plt.xticks([])
plt.yticks([])
plt.title('First derivative of Gaussian');
No description has been provided for this image

Gaussian smoothing¶

In [35]:
gyn = np.convolve(yn, g, 'same')
plt.figure(figsize=(20,5))
plt.plot(x[5:-15], gyn[5:-15])
plt.plot(x[5:-15], gyn[5:-15],'r.')
plt.xticks([])
plt.yticks([])
plt.title('Signal smoothed with Gaussian');
No description has been provided for this image

Compute derivative of smoothed signal¶

In [36]:
dgyn = np.convolve(gyn, Hx, 'same')
plt.figure(figsize=(20,5))
plt.plot(x[5:-15], dgyn[5:-15], 'b')
plt.xticks([])
plt.yticks([])
plt.title('Derivative of smoothed signal');
No description has been provided for this image

Smooth signal with derivative of Gaussian¶

Convolve noisy signal with derivative of Gaussian (saving one convolution operation)

In [37]:
dgyn2 = np.convolve(dg, yn, 'same')
plt.figure(figsize=(20,5))
plt.plot(x[5:-15], dgyn2[5:-15], 'b')
plt.xticks([])
plt.yticks([])
plt.title('Signal convolution with first derivative of Gaussian');
No description has been provided for this image
No description has been provided for this image
In [ ]: