0% found this document useful (0 votes)
94 views

Programming For Engineers in Python: Recitation 12

The document provides an overview of image processing techniques in Python for engineers. It discusses: 1) Binary segmentation to convert images to black and white by thresholding pixel values. 2) Computing the image gradient to detect points of intensity change. 3) Morphological operations like erosion and dilation to modify images. 4) Denoising images using mean, median and bounded median filters. The techniques are demonstrated on sample images using the NumPy and SciPy libraries.

Uploaded by

Shahrzad Ghasemi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
94 views

Programming For Engineers in Python: Recitation 12

The document provides an overview of image processing techniques in Python for engineers. It discusses: 1) Binary segmentation to convert images to black and white by thresholding pixel values. 2) Computing the image gradient to detect points of intensity change. 3) Morphological operations like erosion and dilation to modify images. 4) Denoising images using mean, median and bounded median filters. The techniques are demonstrated on sample images using the NumPy and SciPy libraries.

Uploaded by

Shahrzad Ghasemi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 39

Programming for Engineers in Python

Recitation 12
Image Processing
Plan: Image Processing with numpy
 Binary segmentation

 Image gradient

 Image brightening

 Morphological operators
 Erosion
 Dilation
 Smoothing

 Denoising

 Ternary segmentation

2
A 2D table of values
(pixels), each in
Grayscale Image 0..255:
• 0 = Black
• 255 = White

105 114 116 116 160 121 97 90 124 119 188 144 112 116 78 32 19 61 40

3
Image processing – basic functions
 Reading an image from disk:
from scipy import misc
im = misc.imread('C:/Koala.jpg')
 Creating an “empty” image matrix:
im = numpy.zeros( (height,width), dtype=numpy.uint8 )

 Important: each pixel is in the range 0-255 (type np.uint8)


o Numerical operations cause overflow, e.g.:
numpy.uint8(200) + numpy.uint8(100) = 44 = 300 mod 256
o Therefore, before doing numerical operations on image pixels, convert the
whole image or a specific pixel to int 32-bit using numpy.int_ :
a = numpy.uint8(200) ; b = numpy.uint8(100)
numpy.int_(a) + numpy.int_(b) = 300
4
Image processing – more functions
>>> A = numpy.array([[1,7,2],[6,9,3]])
>>> A
array([[1, 7, 2],
[6, 9, 3]])
 Find the maximal / minimal value:
numpy.min(A) → 1
numpy.max(A) → 9
 Find the average / median value:
numpy.mean(A) → 4.66
numpy.median(A) → 4.5

5
Image processing – more functions
 We will need the following numpy function later:
numpy.concatenate( (A,B), axis=0)
array([[1, 2, 3],
>>> A = numpy.array([[1,2,3],[4,5,6]]) [4, 5, 6]])

>>> B = numpy.array([[7,8,9],[1,1,1]]) array([[7, 8, 9], array([[1, 2, 3],


[1, 1, 1]])
[4, 5, 6],
>>> numpy.concatenate((A,B), axis=0) [7, 8, 9],
[1, 1, 1]])
>>> C = numpy.array([[50,50]]) array([[50, 50]])
array([[50],
>>> C = C.T [50]])
array([[ 1, 2, 3, 50],
>>> numpy.concatenate((A,C), axis=1) [ 4, 5, 6, 50]])

6
Binary Segmentation
 Goal: grayscale image  black and white image
 pixel > threshold  change to white (255)
 pixel <= threshold  change to black (0)

 Motivation: for example, save space

Threshold = 50 Threshold = 128 Threshold = 200

7
Binary Segmentation: Code
>>> import matplotlib.pyplot as plt
>>> threshold = 128
>>> from scipy import misc
>>> binIm = (im > threshold) * 255
>>> im = misc.imread('C:/Koala.jpg')
>>> plt.figure()
>>> type(im)
>>> plt.imshow(im, cmap=plt.cm.gray)
<type 'numpy.ndarray'>
>>> plt.figure()
>>> im.shape, im.dtype
>>> plt.imshow(binIm, cmap=plt.cm.gray)
((598, 848), dtype('uint8'))
>>> plt.show()
>>> np.min(im), np.max(im)
(0, 255)

8
Image gradient
 Given an image we may want to locate points of major change in
intensity.
 In the gradient image, every pixel is the difference between the
corresponding pixel in the original image and a neighboring pixel.
 We can compute the gradient wrt each of the image dimensions.

9
Image vertical gradient: Code
 The idea: shift up and subtract images

zeroRow = np.zeros((1,im.shape[1]), dtype=np.uint8)


upShiftedIm = np.concatenate((im[1:], zeroRow), axis=0)
diff = np.int_(upShiftedIm) - np.int_(im)
imDy = np.abs(diff) array([[1, 2, 3],
[4, 5, 6],
plt.imshow(imDy, cmap=plt.cm.gray) [7, 8, 9]])
plt.show() =>
array([[4, 5, 6],
[7, 8, 9],
 Can also be computed directly using for loops ! [0, 0, 0]])

10
Brightening an image
 We can hardly see the gradient image.
o Solution: let’s brighten it.

>>> brImDy = np.minimum(np.int_(imDy) * 5, 255)

11
Image processing
 Many more operations can be performed on images, in particular:
o Geometric transformations: shifting, rotation, flipping
o Filtering: replace a pixel by a function of the neighboring pixels
 The neighborhood can be defined by a structuring element, e.g. square

 A good survey with numpy/scipy examples can be found in:


http://scipy-lectures.github.io/advanced/image_processing/

12
Morph by Neighbors
 All the operators we’ll see have the same basic idea:
Change a pixel according to its
neighborhood
The ‘neighborhood’ of a pixel in
the input image

nx,ny=3 nx,ny=1 nx,ny=2


13
http://luispedro.org/software/pymorph

Morphological Operators
 Morphology is a technique for the analysis and processing
of geometrical structures
 Erosion:
 Take binary image ; produce more black, less white.
 Edges of white areas (“foreground”) erode away.
 So black areas (“background”) grow.

14
Morphological Operators
 Dilation – inverse of erosion.
 Boundaries of black areas erode away.
 So white areas grow.

15
Morph – Common Code
The specific
Framework: morph. operator

def morphological(im, operator=np.min, nx=5, ny=5):


newIm = np.zeros(im.shape)
for x in range(im.shape[0]): current pixel
location
for y in range(im.shape[1]):
nlst = neighbours(im, x, y, nx, ny)
newIm[x,y] = operator(nlst)
return newIm
Size of
neighborhood
16
Get Neighbors – Code
def neighbours(im, x, y, nx=1, ny=1):
return im[max(x-nx, 0) : min(x+nx+1, im.shape[0]), \
max(y-ny, 0) : min(y+ny+1, im.shape[1])]

Default Neighborhood
17
Erosion and dilation using morph
def erosion(im, nx=5, ny=5):
return morphological(im, np.min, nx, ny)

• Black neighbor  change pixel to black

def dilation(im, nx=5, ny=5):


return morphological(im, np.max, nx, ny)

• White neighbor  change pixel to white

18
Morphological Operators - Run

erosion

dilation

19
Detect Edges with Dilation
 Idea:
 Thicken white edges a bit, using dilation
 Compute difference from original image

Dilated Original Difference

im1 = misc.imread('square1.bmp')
im2 = dilation(im1, 1, 1)
imDiff = im2 – im1
20
Denoising
 We want to “clean” these pictures from noise
 Suggestions?

21
Denoising - Mean
Take 1: Smoothing
Use the morphological framework with mean (average):
def denoise_mean(im, nx=5, ny=5):
return morphological(im, np.mean, nx, ny)

22
Denoising - Median
Take 2: Median
def denoise_median(im, nx=5, ny=5):
return morphological(im, np.median, nx, ny)

23
Denoising - Median
 Can we do better?
 We did not exploit what we know about the ‘noise’
 The ‘noise’ can only be
 Almost white
 Almost black

 So change only very bright or very dark pixels!


 What is very bright?
 Pixel>W
 What is very dark?
 Pixel<B
24
Denoising - operation
 Pixels not too bright or dark remain with no change.
 Otherwise, examine the pixel’s neighborhood nlst.
 too dark:
 Get the median of nlst, but ignore ‘too dark’ pixels
 too bright:
 Get the median of nlst, but ignore ‘too bright’ pixels

25
Denoising – Bounded Median
def my_median(nbrs, min_val=0, max_val=255): def fix_white(nbrs, W=200):
goodNbrs = \ center = (nbrs.shape[0]/2, nbrs.shape[1]/2)
nbrs[(nbrs >= min_val) & (nbrs <= max_val)] if nbrs[center] > W: # too bright
if goodNbrs.shape[0] > 0: return my_median(nbrs, max_val=W)
return np.median(goodNbrs) else:
else: return nbrs[center]
center = (nbrs.shape[0]/2, nbrs.shape[1]/2)
return nbrs[center]
def fix_black(nbrs, B=50):
center = (nbrs.shape[0]/2, nbrs.shape[1]/2)
if nbrs[center] < B: # too dark
return my_median(nbrs, min_val=B)
else:
return nbrs[center]

26
Denoising – Bounded Median
def denoise_bounded_median(im, nx=3, ny=3):
num_repeat = 3
for i in range(num_repeat):
im = morphological(im, fix_white, nx, ny)
im = morphological(im, fix_black, nx, ny)
return im

27
Denoising – Bounded Median
Iteration
number
1 2

3 4

28
Ternary segmentation

29
Example

Before After
30
Ternary segmentation
 Example: If the values of the image pixels are 1 to 90 then:
first threshold = 30; second threshold = 60
 Possible algorithm:
 Sort all n*m pixel values in the image into sorted_pxls
(one-dimensional list)
 Set first threshold = sorted_pxls[n*m/3]
 Set second threshold = sorted_pxls[2*n*m/3]
 Complexity: O(nm*log(nm))

 Let’s try to solve in O(nm)

31
Ternary segmentation
 We will break the problem into smaller problems:
1. Create a histogram.
2. Create a cumulative histogram.
3. Find 2 thresholds from cumulative histogram.
4. Transform the image using the thresholds.

 Step by step…
 Modularize the solution

32
Image histogram
 pic.png contains a 20x20 pixels image
 Each pixel is a gray level in 0..255

 Q1: Implement the function build_hist():


 Input: grayscale image.
 Output: histogram of gray levels in image.

 So our histogram will be a list with 256 values:


 Index = gray level
 Value = #pixels with that gray level
33
Image Histogram - Code
def build_hist(im):
hist = [0]*256
for x in range(im.shape[0]):
for y in range(im.shape[1]):
gray_level = im[x,y]
hist[gray_level] += 1
return hist

34
Cumulative histogram
 Q2: Implement the function cum_sum(lst):
 Input: list of numbers
 Output: list of the cumulative sums of lst

12 43 17 22

12 55 72 94

35
Cumulative sum – Code
def cum_sum(numbers):
res = []
for i in range(len(numbers)):
prevSum = res[i-1] if i > 0 else 0
res.append(prevSum + numbers[i])
return res

36
Ternary segmentation
 Q3: Given an image, find two thresholds th1, th2 such that:

37
Finding the thresholds
def find_thrds(im):
hist = build_hist(im)
c_lst = cum_sum(hist)
i=0
while float(c_lst[i]) / im.size < 1./3:
i += 1
th1 = i
while float(c_lst[i]) / im.size < 2./3:
i += 1
th2 = i
return (th1, th2)
38
Ternary segmentation
 Q4: Implement the ternary segmentation function itself:
o Input: a grayscale image
o Output: a new image where every pixel has one of 3 gray levels:
0, 128 or 255

def ternary_sementation(im):
th1, th2 = find_thrds(im)
newIm = np.zeros(im.shape)
newIm = newIm + ((im > th1) & (im <= th2)) * 128
newIm = newIm + (im > th2) * 255
return newIm

39

You might also like