Registration and Segmentation
Registration and Segmentation
tation
19 Histogram-based image segmenta-
tion
where µi is the mean value of the elements in Si . The k-means algorithm is iterative.
(1)
From a set of k initial elements {mi }i∈[1;k] (randomly selected), the algorithm
iterates the following (t) steps:
n o
(t) (t) (t)
Si = x j : xj − m i ≤ xj − mi∗ , ∀i∗ ∈ [1; k] (19.2)
(t+1) 1 X
mi = (t)
xj (19.3)
|Si | x (t)
j ∈Si
(t) (t)
where |Si | is the number of elements of Si .
We will use the python function randn from the module numpy.random.
• Use the python function sklearn . cluster . KMeans for separating the
points. The result is presented in Fig.19.1.
• Make the clustering of this 3-D map by using the K-means method.
When manually choosing a threshold value, one has to analysis the histogram (Fig.
19.2).
import numpy as np
2 import imageio
import matplotlib . pyplot as plt # plots
4 from skimage import filter # otsu thresholding
6 # read image
cells =imageio.imread(’ cells . png’) ;
8
# display histogram
10 fig =plt . figure () ;
plt . hist ( cells . flatten () , 256)
12 fig . show();
fig . savefig ( " histo . pdf" ) ;
4000
3000
2000
1000
0
0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
19.5 Color image segmentation using K-means: k = 3 in 3D 223
Segmentation
1 fig =plt . figure () ;
plt . subplot (1,2,1)
3 plt . imshow(cells , plt . cm.gray) ; plt . title ( ’ Original image’) ;
plt . subplot (1,2,2)
5 plt . imshow(cells>80, plt . cm.gray) ; plt . title ( ’Manual segmentation’) ;
fig . savefig ( "manual.pdf") ;
The results are displayed using the following code (Fig. 19.3):
1 # Automatic threshold
s_auto= autothresh ( cells ) ;
3
# Otsu thresholding
5 s_otsu = filter . threshold_otsu ( cells ) ;
7 plt . figure () ;
plt . subplot (1,2,1)
9 plt . imshow(cells>s_auto, plt . cm.gray) ; plt . title ( ’Automatic thresholding ’ )
plt . subplot (1,2,2) ;
11 plt . imshow(cells>s_otsu , plt . cm.gray) ; plt . title ( ’Otsu thresholding ’ ) ;
Histogram-based image segmentation 224
Figure 19.3: Automatic thresholding and thresholding by Otsu. Results are almost
identical because threshold values are 105.3 and 105, respectively.
(a) Automatic threshold. (b) Otsu threshold.
Different techniques can be found in the scikit documentation. A point cloud will
first be generated, from 3 clustered cloud points. The objective is then to segment
all the points into their original cluster.
Imports
1 import numpy as np
import matplotlib . pyplot as plt
3 import time
5 points1=generation (100, 0, 0) ;
points2=generation (100, 3,4) ;
7 points3=generation (100, - 5, - 3) ;
6
8 6 4 2 0 2 4 6
k-means clustering
10 # retrieve results
k_means_labels = k_means.labels_ ;
12 k_means_cluster_centers = k_means. cluster_centers_ ;
14 # plot
fig = plt . figure ()
16 colors = [ ’#4EACC5’, ’#FF9C34’, ’#4E9A06’]
18 # k−means
# zip agregates values two by two
20 for k, col in zip (range(n) , colors ) :
my_members = k_means_labels == k
22 cluster_center = k_means_cluster_centers[k]
24 # display points
plt . plot ( pts [my_members, 0], pts[my_members, 1], ’w’,
26 markerfacecolor=col , marker=’. ’ )
28 # display centroid
plt . plot ( cluster_center [0], cluster_center [1], ’o’ ,
30 markerfacecolor=col , markeredgecolor=’k’ ,
markersize=6)
32 plt . title ( ’KMeans’)
plt . show()
34 fig . savefig ( "kmeans.pdf") ;
19.5 Color image segmentation using K-means: k = 3 in 3D 227
KMeans
6
6
8 6 4 2 0 2 4 6
3D scatter plot
This is a method to display colors in the RGB cube. This method is really slow,
depending on your GPU.
Histogram-based image segmentation 228
5 fig = plt . figure ()
ax = fig . add_subplot (111, projection =’3d’ )
7
# Plot scatter points
9 for k, col in zip (range(n) , colors ) :
my_members = k_means_labels == k
11 cluster_center = k_means_cluster_centers[k]
ax. scatter ( data[my_members, 0], data[my_members, 1],
13 data[my_members, 2], c=col)
ax. scatter ( cluster_center [0], cluster_center [1],
15 cluster_center [2], s=30, c=col )
20 Segmentation by region growing
20.1 Introduction
The region growing segmentation method starts from a seed. The initial region
first contains this seed and then grows according to
|I(p) − ms | ≤ T
20.3.1 imports
1 import queue
from scipy import misc
3 import matplotlib . pyplot as plt
import numpy as np
20.3.2 Predicate
This function defines the agregation condition.
The following code is used to start the region growing from a pixel manually
clicked on an image.
# start of code
2 fig = plt . figure () ;
ax = fig . add_subplot (211) ;
4 ax. set_title ( ’ Click on a point ’ )
6 # load image
img = misc. ascent () ;
8 ax.imshow(img, picker=True,cmap=plt.gray () ) ;
10 fig . canvas.mpl_connect(’button_press_event ’ , onpick)
plt . show();
And here comes the main function for region growing. The result is illustrated
in Fig.20.1.
Segmentation by region growing 232
Notice that values −1 of the visited matrix avoid testing multiple times the same
pixel. In the predicate function, the visited matrix is used in case of adapting the
20.2 Region growing implementation 233
predicate to the current region. In the next case, the candidate pixel’s graylevel is
compared to the mean gray value of the region. The results are illustrated Fig.20.1.
Figure 20.1: Region growing illustration for pixel (189,136). The segmentation result
highly depend on the order used to populate the queue, on the predicate function
and on the seed pixel.
(a) Original image. (b) Segmented region.
This tutorial introduces the Hough transform. Line detection operators are
implemented.
21.1 Introduction
This tutorial deals with line detection in an image. For a given point of coordinates
(x, y) in R2 , there exists an infinite number of lines going by this point, with
different angles θ. These lines are represented by the following equation:
ρ = x · cos(θ) + y · sin(θ).
Thus, for each point (x, y) (Fig. 21.1a) corresponds a curve parametered by
[θ, ρ], where θ ∈ [0;2π] (Fig. 21.1b). The intersection of these curves represents a
line (in this case, y = x).
Figure 21.1: Representation of the Hough transform.
(a) Different points in R2 . (b) Hough transform of the 3 points.
Y ρ
y=x
(3,3)
•
(2,2)
•
(1,1)
•
X θ
(1, 1)
(2, 2)
(3, 3)
21.2 Algorithm
The (general and simple) method for line detection is then:
Hough transform and line detection 236
4. Get back in the Euclidean space and draw the lines on the image.
Code a function that will transform each point of a binary image into a
curve in the Hough space. For each curve, increment each pixel by one in
the Hough space.
21.4 Maxima detection 237
Use or code a function to detect maxima (regional maxima). For each maxi-
mum, keep only one point.
For each maximum, display the corresponding line above the original image.
Hough transform and line detection 238
This correction makes use of the python modules numpy, opencv, skimage and
matplotlib.
1 import numpy as np
import cv2
3 import matplotlib . pyplot as plt
from skimage. feature import peak_local_max
The contours are detected using the Canny edge detection method. In this code,
the method from OpenCV is employed, see Fig.21.3a.
Notice that OpenCV contains Hough fonctions: HoughLines and HoughLinesP. The
result (sinogram) of the image is presented Fig.21.3b.
21.5 Display lines 239
0
200
400
600
800
1000
1200
1400
1600
(a) Canny edge detection. (b) Representation of the sinogram and detec-
tion of the maxima in the Hough space.
Figure 21.3: The algorithm of the Hough line detection consists in detecting the
edges, then representing each pixel in the Hough space and finally detecting the
maximal value in the sinogram.
## Hough transform
2 # size of image
X = img.shape [0];
4 Y = img.shape [1];
8 # initialization of matrix H
rho_max = np.hypot(X,Y);
10 rho = np.arange(-rho_max, rho_max, 1) ;
theta = np.arange (0, np.pi , angular_sampling) ;
12 cosTheta = np.cos( theta ) ;
sinTheta = np. sin ( theta ) ;
14 H = np.zeros ([ rho. size , theta . size ]) ;
16 # Hough transform
# loop on all contour pixels
18 for i in range(X):
for j in range(Y) :
20 if (edges[ i , j ] != 0) :
R = i ∗cosTheta + j ∗sinTheta ;
22 R = np.round(R + rho. size /2) . astype ( int ) ;
H[R,range(theta . size ) ] += 1;
24
plt . imshow(H);
Hough transform and line detection 240
# Maxima detection
2 G = cv2.GaussianBlur(H, (5,5) , 5) ;
maxima = peak_local_max(H, 5, threshold_abs =150, num_peaks=5);
4 plt . figure () ;
plt . imshow(G);
6
# display maxima on Hough transform image G
8 plt . scatter (maxima [:,1], maxima [:,0], c=’r ’ ) ;
plt . show();
15 # display in window
cv2.imshow(’hough transform’, img);
17 # write resulting image
cv2.imwrite( ’cv_hough.png’, img);
21.5 Display lines 241
import cv2
2 import numpy as np
16 # display lines
for rho, theta in lines [0]:
18 print rho, theta
a = np.cos( theta )
20 b = np. sin ( theta )
x0 = a∗rho
22 y0 = b∗rho
x1 = int (x0 + 1000∗( -b) )
24 y1 = int (y0 + 1000∗( a) )
x2 = int (x0 - 1000∗( -b) )
26 y2 = int (y0 - 1000∗( a) )
print x1, y1, x2, y2
28 cv2. line (img,(x1,y1) ,( x2,y2) ,(0,0,255) ,2)
22 Active contours
This tutorials aims at introducing the active contours (a.k.a. snakes) method
as originally presented in [18]. It is a segmentation method based on the
optimization of a contour that will converge to a specific object.
22.1 Definition
A snake is a parametric curve v(s) with s∈[0; 1[. The energy functional is represented
by:
Z 1 Z 1
Esnake = Eint (v(s))ds + Eext (v(s))ds.
0 0
The internal energy is detailed in Eq.22.1. The first order derivation constrains
the length of the curve, the second order derivation constrains the curvature. The
parameters α and β a priori depend on s, but for simplicity, constant values will be
taken.
1
Eint (v(s)) = α(s)|v ′ (s)|2 + β(s)|v ′′ (s)|2 (22.1)
2 | {z } | {z }
length curvature
The snake will be attracted by some edges points from the external energy (the
image to be segmented). The, the energy functional will be in a local minimum: it
is shown that the Euler-Lagrange equation is satisfied:
with v (2) and v (4) denoting the 2nd and 4th order derivatives. To solve this equation,
the gradient descent method is employed: the snake is now transformed into a
function of the position s and the time t, with Fext = −∇Eext
∂s
= αv (2) − βv (4) + Fext
∂t
Active contours 244
Figure 22.1: Illustration of the active contours segmentation method. Two energies
are at stake: internal energies depend only on the snake shape and control points,
external energies are related to the image properties.
The forces fx and fy are the components of Fext . For example for an image I,
if ∗ denotes the convolution and Gσ a gaussian kernel of standard deviation σ:
Fext = −∇(k∇(Gσ ∗ I)k)
22.2 Numerical resolution 245
• Generate the initial contour as an ellipse, with the same center as the
disk.
• Program the iterations and visualize the results. For example, γ = 200
and 1000 iterations may give an idea of the parameters to use.
Active contours 246
1 import numpy as np
import matplotlib . pyplot as plt
3 import scipy
import scipy . ndimage. filters
5 from scipy import interpolate
import progressbar
step =.01;
2 x = n/2 + 400 ∗ np.cos(np.arange (0,2∗ np.pi+step , step ) ) ;
y = n/2 + 200 ∗ np. sin (np.arange (0,2∗ np.pi+step , step ) ) ;
1 k =.1;
alpha = .0001;
3 beta = 10;
gamma= 100;
5 iterations = 1000;
700
650
600
550
500
450
400
350
300
100 200 300 400 500 600 700 800 900 1000
N = x. size ;
2 X = np.array ([ -beta , alpha+4∗beta , - 2∗alpha- 6∗beta , alpha+4∗beta , -beta , -beta ,
֒→ alpha+4∗beta , -beta , alpha+4∗beta ])
A = scipy . sparse . diags (X, np.array ([ - 2, - 1, 0, 1, 2, N-2, N-1, -N+2, -N+1]), shape
֒→ =(N,N)) . toarray () ;
4 AA = np. identity (N)-gamma∗A;
invAA = np. linalg . inv(AA);
Active contours 248
imshow(I ,[])
2 hold on
plot ([ x;x (1) ], [y; y (1) ], ’g’ , ’ linewidth ’ , 3) ;
4 # display arrows for external forces
step =20;
6 subx = 1: step : size ( I ,1) ;
suby = 1: step : size ( I ,2) ;
8 [Xa, Ya] = meshgrid(subx, suby) ;
quiver (Xa, Ya, Fx(subx, suby) , Fy(subx,suby)) ;
1000
800
600
400
200
0
0 200 400 600 800 1000
22.2 Numerical resolution 249
Figure 22.5: Result of the snake converging toward the disk, after 1000 iterations
with the proposed parameters.
200
400
600
800
1000
0 200 400 600 800 1000
23 Watershed
This tutorial aims to study the watershed transform for image segmentation.
In image processing, an image can be considered as a topographic surface.
If we flood this surface from its minima and if we prevent the merging of
the water coming from different sources, we partition the image into two
different sets: the catchment basins separated by the watershed lines.
• Take the complementary of this distance map and visualize its minima.
• Smooth the original image with a low pass filter. To stay in the math-
ematical morphology field, you can use an alternate morphological
filter (opening followed by closing). A Gaussian filter is also a good
solution.
import numpy as np
2 from scipy import misc
import matplotlib . pyplot as plt
4 from scipy import ndimage as ndi
from scipy . ndimage.morphology import distance_transform_edt
6 from scipy . ndimage.morphology import distance_transform_cdt
from skimage import morphology
Regional maxima
1 def rmax(I) :
"""
3 Own version of regional maximum
This avoids plateaus problems of peak_local_max
5 I : original image, int values
returns : binary array , with True for the maxima
7 """
I = I . astype ( ’ float ’ ) ;
9 I = I / np.max(I) ∗ 2∗∗31;
I = I . astype ( ’ int32 ’ ) ;
11 h = 1;
rec = morphology.reconstruction ( I , I+h);
13 maxima = I + h - rec ;
return maxima>0
The local maxima will be used as a marker for the watershed operation, in order
to perform the separation of the grains, see Fig.23.1.
The gradient image amplifies the noise. Thus, the watershed operator directly ap-
plied to the gradient of the image produces an over-segmented image (see Fig.23.2).
23.3 Constrained watershed by markers 255
def sobel_mag(im):
2 """
Returns Sobel gradient magnitude
4 im: image array of type float
returns : magnitude of gradient (L2 norm)
6 """
dx = ndi . sobel (im, axis =1) # horizontal derivative
8 dy = ndi . sobel (im, axis =0) # vertical derivative
mag = np.hypot(dx, dy) # magnitude
10 return mag;
Figure 23.2: Performing the watershed on the gradient image is usually not a good
idea.
(b) Amplitude of the gradient (So-
(a) Original image. bel). (c) Watershed segmentation.
In fact, this method produces as many segments as there are minima in the
gradient image Fig.23.3.
Before evaluating the gradient, the image is filtered. The number of minima is
lower and this leads to a less over-segmented image (Fig.23.4).
Watershed 256
SE = morphology.disk(2) ;
2 O = morphology.opening(gel, selem=SE);
F = morphology.closing(O, selem=SE).astype ( ’ float ’ ) ;
4 g = sobel_mag(F) . astype ( ’ float ’ ) ;
Figure 23.4: Even if the gradient is performed on the filtered image, there is still a
high over-segmentation.
The watershed can be constrained by markers: the markers can provide the correct
number of regions. This method imposes both the background (external markers)
and the objects (internal markers). The results are illustrated in Fig.23.5. The
ultimate erosion of the internal markers is used to deconnect these markers from
the external markers.
local_maxi = rmax(255-F) ;
2 markers = ndi . label ( local_maxi , np.ones ((3, 3) ) ) [0]
W = morphology.watershed(F, markers, watershed_line=True)
4
markers2 = local_maxi | (W==0);
6 M = ndi. label (markers2, np.ones ((3, 3) ) ) [0]
segmentation = morphology.watershed(g, M, watershed_line=True);
8
gel [segmentation==0] = 255;
10 plt . imshow(gel);
plt . show();
12 imageio.imwrite( "segmentation.python.png", gel ) ;
23.3 Constrained watershed by markers 257
24.1 Vascularization
24.3 Quantification
import numpy as np
2 import matplotlib . pyplot as plt
from scipy import misc,ndimage
4 from skimage import morphology
24.4.1 Vascularization
Antrum segmentation
The first step consists in the segmentation of the antrum by thresholding the blue
component. Some post-processes are used to remove artifacts such as holes. This is
illustrated in Fig. 24.2.
Theca segmentation
The second step provides the segmentation of the theca which is extracted as a
spatial region (corona) adjacent to and outside the antrum. The width of the corona
is selected by the user (expert).
Segmentation of follicles 262
se40 = morphology.disk(40) ;
2 theca = morphology.binary_dilation (antrum, selem = se40) ;
theca = theca - antrum;
4 plt . imshow(theca); plt . title ( ’Theca’)
plt . show()
Vascularization segmentation
The final step extracts the vascularization of the considered follicle. Pixels belonging
to the vascularization are considered to have a low blue component and they should
also be included in the antrum of the follicle.
Results
First solution
The granulosa cells have a low contrast, so it is difficlut to use thresholding tech-
niques. But we know they are localized between the antrum and the vascularization.
Nevertheless the vascularization is not a closed region outside the antrum. There-
fore, the proposed solution consits in firt trying to close the vascularization region
and taking the corona between this region and the antrum. The result is shown in
Fig. 24.3.
Segmentation of follicles 264
1 se10 = morphology.disk(10) ;
dil = 1-morphology.binary_closing( vascularization , se10) ;
3 L = morphology.label( dil , connectivity =1) ;
dil = L == L [300,300];
5 granulosa = dil - antrum;
plt . imshow(granulosa);
7 plt . title ( ’ granulosa ’ )
plt . show()
Second solution
This first solution is not really robust. The closing of the vascularization region is
not really accurate. A more robust solution consists in using deformable models
to get the corona between the vascularization and the antrum. But this kind of
method is out of the scope of this tutorial.
24.4.3 Quantification
vascularization : 0.0428495233516
2 Granulosa: 0.0913707490403
25 Image Registration
This tutorial aims to implement the Iterative Closest Point (ICP) method
for image registration. More specifically, we are going to estimate a rigid
transformation (translation + rotation without scaling) between two images.
The different processes will be applied on T1-MR images of the brain in Fig.25.1.
25.1.1 Preliminaries
Pairs of points are first manually selected.
• Read and visualize the two MR images ’brain1’ and ’brain2’ (moving
and source images).
t̂ = q̄ − R̂.p̄ (25.1)
To simulate the mixing of the points, randomly shuffle them selected on the
first image and perform the registration with the previous method.
See np.random.permutation.
1. From the list of the characteristic points p of the image ’brain1’, find
the nearest neighbors q in the image ’brain2’. Be careful to the order
of the input arguments.
Replace the manual selection in the two previous parts of this tutorial.
Notice that the automatic points detection does not ensure to give the same
order in the points of the two images. More generally, other salient points detectors
do not give the same number of points, and thus the algorithms have to remove
outliers (non matching points). This case is not taken into account in this tutorial.
25.3 Automatic control points detection 271
If you want to display and save the fusion of both images, you can use this
function:
With openCV, there is not built-in function to make a manual selection of pairs
of control points. The following code uses a global variable I in order to manage
the display of the points, which are finally stored into the pts variable. First, the
callback function on_mouse is defined to handle mouse event.
pts = [];
2 def on_mouse(event, x, y, flags , param):
"""
4 callback method for detecting click on image
It draws a circle on the global variable image I
6 """
global pts , I ;
8 if event == cv2.EVENT_LBUTTONUP:
pts . append((x, y) ) ;
10 cv2. circle ( I ,( x,y) , 2, (0,0,255) , - 1)
def cpselect () :
2 """
method for manually selecting the control points
4 It waits until ’q’ key is pressed .
"""
6 cv2.namedWindow("image")
cv2.setMouseCallback("image", on_mouse)
8 print ( " press ’q’ when finished" )
# keep looping until the ’q’ key is pressed
10 while True:
# display the image and wait for a keypress
12 cv2.imshow("image", I )
key = cv2.waitKey(1) & 0xFF
14
# if the ’ c ’ key is pressed , break from the loop
16 if key == ord( "q" ) :
break
18
# close all open windows
20 cv2.destroyAllWindows()
return pts ;
Transformation estimation
The rigid transformation is estimated from the corresponding points by the follow-
ing function:
Image Registration 274
31 T = np.zeros ((2,3) ) ;
T [0:2,0:2] = R;
33 T [0:2,2] = t ;
return T;
Then, you can apply this function to the manually selected points.
25.3 Automatic control points detection 275
# 1st case , rigid registration , with pairs of points in the correct order
2 T = rigid_registration (A_points, B_points) ;
Figure 25.3: Result of the registration for the manually selected control points.
(a) Without registration. (b) With registration.
The result is good, because the manual selection of the points is good (the points
are given in the correct order for both images).
The following code randomly shuffles the points of the first vector. The result is of
course a wrongly registered image, see Fig.25.4.
Image Registration 276
Figure 25.4: Result of the registration for when the control points are not found in
the same order.
(a) Matching pairs of points. (b) Permulation of the points.
ICP
14 nb_loops=5;
tree = cKDTree( dataB ) ;
16
for loop in range(nb_loops) :
18 # search for closest points and reorganise array of points accordingly
d, inds = tree . query( data2A);
20
data2B = dataB[inds ,:];
22 # find rigid registration with reordered points
t_loop = rigid_registration (data2A, data2B) ;
24
T = composeTransform(t_loop, T) ;
26 # evaluates transform on control points , to make the iteration
data2A = applyTransform(dataA, T) ;
28
return T;
Generally, the points are automatically detected, and thus, there is no warranty
that they are found in the same order, nor that each pair of point correspond to
matching points (some points –called outliers– need to be eliminated to compute
the correct transformation). In this tutorial, we do not address the problem of
outliers. Please notice that with these parameters and images, by chance, the same
points are detected in the correct order.
Image Registration 278
Figure 25.5: Result of the registration for when the control points are not found in
the same order. The ICP algorithm reorders the points and gives a good result.
(a) Random shuffle of points and
direct rigid transformation estima- (b) ICP registration on the same
tion. points.
The Fig.25.6 displays the results with the automatic detection of corners (in this
case, the method from Shi and Tomasi). Notice that by chance, the points in both
images match. In other cases, one would have to remove outliers.
25.3 Automatic control points detection 279
Figure 25.6: Shi and Tomasi corners detection. By chance, the points correspond
and the ICP method gives a correct result.
(a) (b)