├── EyeImages
├── 02463d1928.tiff
└── 04327d1305.tiff
├── README.md
├── EyelashRemoval.m
├── morphsnakes.py
└── GrabCutIris_LevelSets_Ellipse.py
/EyeImages/02463d1928.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sbanerj1/IrisSeg/HEAD/EyeImages/02463d1928.tiff
--------------------------------------------------------------------------------
/EyeImages/04327d1305.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sbanerj1/IrisSeg/HEAD/EyeImages/04327d1305.tiff
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IrisSeg
2 | Python code for iris segmentation from near infra-red eye images using geodesic active contours and GrabCut [1]. The morphological GAC function is from [2].
3 | The code works well but is slow.
4 | Matlab code for eyelash removal is also included.
5 |
6 | # References
7 | 1. S. Banerjee and D. Mery. Iris Segmentation using Geodesic Active Contours and GrabCut. In Workshop on 2D & 3D Geometric Properties from Incomplete Data at PSIVT (PSIVT Workshops), 2015.
8 | 2. P. Márquez-Neila, L. Baumela and L. Álvarez. A Morphological Approach to Curvature-based Evolution of Curves and Surfaces. In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), 2013.
9 |
--------------------------------------------------------------------------------
/EyelashRemoval.m:
--------------------------------------------------------------------------------
1 | % Eyelash removal using bottom hat filter (Sandipan Banerjee)
2 |
3 | %load image
4 | filename = 'EyeImages/02463d1928.tiff';
5 | name1 = strsplit(filename,'/');
6 | image = im2double(imread(filename));
7 | mediatedImage = medfilt2(image);
8 |
9 | %get hairs using bottomhat filter
10 | se = strel('disk',5);
11 | hairs = imbothat(mediatedImage,se);
12 | hairs1 = hairs > 0.07;
13 |
14 | % remove noise
15 | hairs2 = medfilt2(hairs1,[4 4]);
16 |
17 | % label connected components
18 | lab_mask = bwlabel(hairs2);
19 | stats = regionprops(lab_mask, 'MajorAxisLength', 'MinorAxisLength');
20 |
21 | %identifies long, thin objects
22 | Aaxis = [stats.MajorAxisLength];
23 | Iaxis = [stats.MinorAxisLength];
24 | idx = find((Aaxis ./ Iaxis) > 1); % Selects regions that meet logic check
25 | out_mask = ismember(lab_mask, idx);
26 | mask2 = imdilate(out_mask,ones(5,5));
27 | I2 = roifill(image,mask2);
28 |
29 | %save result
30 | newF = 'EyelashRemoved';
31 | if ~exist(newF,'dir')
32 | mkdir(newF);
33 | end
34 | strng = strcat(newF,'/',char(name1(2)));
35 | imwrite(I2,strng);
--------------------------------------------------------------------------------
/morphsnakes.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | """
4 | morphsnakes
5 | ===========
6 |
7 | This is a Python implementation of the algorithms introduced in the paper
8 |
9 | Márquez-Neila, P., Baumela, L., Álvarez, L., "A morphological approach
10 | to curvature-based evolution of curves and surfaces". IEEE Transactions
11 | on Pattern Analysis and Machine Intelligence (PAMI), 2013.
12 |
13 | This implementation is intended to be as brief, understandable and self-contained
14 | as possible. It does not include any enhancement to make it fast or efficient.
15 |
16 | Any practical implementation of this algorithm should work only over the
17 | neighbor pixels of the 0.5-levelset, not over all the embedding function,
18 | and perhaps should feature multi-threading or GPU capabilities.
19 |
20 | The classes MorphGAC and MorphACWE provide most of the functionality of this
21 | module. They implement the Morphological Geodesic Active Contours and the
22 | Morphological Active Contours without Edges, respectively. See the
23 | aforementioned paper for full details.
24 |
25 | See test.py for examples of usage.
26 | """
27 |
28 | __author__ = "P. Márquez Neila
"
29 |
30 | from itertools import cycle
31 |
32 | import numpy as np
33 | from scipy import ndimage
34 | from scipy.ndimage import binary_dilation, binary_erosion, \
35 | gaussian_filter, gaussian_gradient_magnitude
36 |
37 | class fcycle(object):
38 |
39 | def __init__(self, iterable):
40 | """Call functions from the iterable each time it is called."""
41 | self.funcs = cycle(iterable)
42 |
43 | def __call__(self, *args, **kwargs):
44 | f = self.funcs.next()
45 | return f(*args, **kwargs)
46 |
47 |
48 | # SI and IS operators for 2D and 3D.
49 | _P2 = [np.eye(3), np.array([[0,1,0]]*3), np.flipud(np.eye(3)), np.rot90([[0,1,0]]*3)]
50 | _P3 = [np.zeros((3,3,3)) for i in xrange(9)]
51 |
52 | _P3[0][:,:,1] = 1
53 | _P3[1][:,1,:] = 1
54 | _P3[2][1,:,:] = 1
55 | _P3[3][:,[0,1,2],[0,1,2]] = 1
56 | _P3[4][:,[0,1,2],[2,1,0]] = 1
57 | _P3[5][[0,1,2],:,[0,1,2]] = 1
58 | _P3[6][[0,1,2],:,[2,1,0]] = 1
59 | _P3[7][[0,1,2],[0,1,2],:] = 1
60 | _P3[8][[0,1,2],[2,1,0],:] = 1
61 |
62 | _aux = np.zeros((0))
63 | def SI(u):
64 | """SI operator."""
65 | global _aux
66 | if np.ndim(u) == 2:
67 | P = _P2
68 | elif np.ndim(u) == 3:
69 | P = _P3
70 | else:
71 | raise ValueError, "u has an invalid number of dimensions (should be 2 or 3)"
72 |
73 | if u.shape != _aux.shape[1:]:
74 | _aux = np.zeros((len(P),) + u.shape)
75 |
76 | for i in xrange(len(P)):
77 | _aux[i] = binary_erosion(u, P[i])
78 |
79 | return _aux.max(0)
80 |
81 | def IS(u):
82 | """IS operator."""
83 | global _aux
84 | if np.ndim(u) == 2:
85 | P = _P2
86 | elif np.ndim(u) == 3:
87 | P = _P3
88 | else:
89 | raise ValueError, "u has an invalid number of dimensions (should be 2 or 3)"
90 |
91 | if u.shape != _aux.shape[1:]:
92 | _aux = np.zeros((len(P),) + u.shape)
93 |
94 | for i in xrange(len(P)):
95 | _aux[i] = binary_dilation(u, P[i])
96 |
97 | return _aux.min(0)
98 |
99 | # SIoIS operator.
100 | SIoIS = lambda u: SI(IS(u))
101 | ISoSI = lambda u: IS(SI(u))
102 | curvop = fcycle([SIoIS, ISoSI])
103 |
104 | # Stopping factors (function g(I) in the paper).
105 | def gborders(img, alpha=1.0, sigma=1.0):
106 | """Stopping criterion for image borders."""
107 | # The norm of the gradient.
108 | gradnorm = gaussian_gradient_magnitude(img, sigma, mode='constant')
109 | return 1.0/np.sqrt(1.0 + alpha*gradnorm)
110 |
111 | def glines(img, sigma=1.0):
112 | """Stopping criterion for image black lines."""
113 | return gaussian_filter(img, sigma)
114 |
115 | class MorphACWE(object):
116 | """Morphological ACWE based on the Chan-Vese energy functional."""
117 |
118 | def __init__(self, data, smoothing=1, lambda1=1, lambda2=1):
119 | """Create a Morphological ACWE solver.
120 |
121 | Parameters
122 | ----------
123 | data : ndarray
124 | The image data.
125 | smoothing : scalar
126 | The number of repetitions of the smoothing step (the
127 | curv operator) in each iteration. In other terms,
128 | this is the strength of the smoothing. This is the
129 | parameter µ.
130 | lambda1, lambda2 : scalars
131 | Relative importance of the inside pixels (lambda1)
132 | against the outside pixels (lambda2).
133 | """
134 | self._u = None
135 | self.smoothing = smoothing
136 | self.lambda1 = lambda1
137 | self.lambda2 = lambda2
138 |
139 | self.data = data
140 |
141 | def set_levelset(self, u):
142 | self._u = np.double(u)
143 | self._u[u>0] = 1
144 | self._u[u<=0] = 0
145 |
146 | levelset = property(lambda self: self._u,
147 | set_levelset,
148 | doc="The level set embedding function (u).")
149 |
150 | def step(self):
151 | """Perform a single step of the morphological Chan-Vese evolution."""
152 | # Assign attributes to local variables for convenience.
153 | u = self._u
154 |
155 | if u is None:
156 | raise ValueError, "the levelset function is not set (use set_levelset)"
157 |
158 | data = self.data
159 |
160 | # Determine c0 and c1.
161 | inside = u>0
162 | outside = u<=0
163 | c0 = data[outside].sum() / float(outside.sum())
164 | c1 = data[inside].sum() / float(inside.sum())
165 |
166 | # Image attachment.
167 | dres = np.array(np.gradient(u))
168 | abs_dres = np.abs(dres).sum(0)
169 | #aux = abs_dres * (c0 - c1) * (c0 + c1 - 2*data)
170 | aux = abs_dres * (self.lambda1*(data - c1)**2 - self.lambda2*(data - c0)**2)
171 |
172 | res = np.copy(u)
173 | res[aux < 0] = 1
174 | res[aux > 0] = 0
175 |
176 | # Smoothing.
177 | for i in xrange(self.smoothing):
178 | res = curvop(res)
179 |
180 | self._u = res
181 |
182 | def run(self, iterations):
183 | """Run several iterations of the morphological Chan-Vese method."""
184 | for i in xrange(iterations):
185 | self.step()
186 |
187 |
188 | class MorphGAC(object):
189 | """Morphological GAC based on the Geodesic Active Contours."""
190 |
191 | def __init__(self, data, smoothing=1, threshold=0, balloon=0):
192 | """Create a Morphological GAC solver.
193 |
194 | Parameters
195 | ----------
196 | data : array-like
197 | The stopping criterion g(I). See functions gborders and glines.
198 | smoothing : scalar
199 | The number of repetitions of the smoothing step in each
200 | iteration. This is the parameter µ.
201 | threshold : scalar
202 | The threshold that determines which areas are affected
203 | by the morphological balloon. This is the parameter θ.
204 | balloon : scalar
205 | The strength of the morphological balloon. This is the parameter ν.
206 | """
207 | self._u = None
208 | self._v = balloon
209 | self._theta = threshold
210 | self.smoothing = smoothing
211 |
212 | self.set_data(data)
213 |
214 | def set_levelset(self, u):
215 | self._u = np.double(u)
216 | self._u[u>0] = 1
217 | self._u[u<=0] = 0
218 |
219 | def set_balloon(self, v):
220 | self._v = v
221 | self._update_mask()
222 |
223 | def set_threshold(self, theta):
224 | self._theta = theta
225 | self._update_mask()
226 |
227 | def set_data(self, data):
228 | self._data = data
229 | self._ddata = np.gradient(data)
230 | self._update_mask()
231 | # The structure element for binary dilation and erosion.
232 | self.structure = np.ones((3,)*np.ndim(data))
233 |
234 | def _update_mask(self):
235 | """Pre-compute masks for speed."""
236 | self._threshold_mask = self._data > self._theta
237 | self._threshold_mask_v = self._data > self._theta/np.abs(self._v)
238 |
239 | levelset = property(lambda self: self._u,
240 | set_levelset,
241 | doc="The level set embedding function (u).")
242 | data = property(lambda self: self._data,
243 | set_data,
244 | doc="The data that controls the snake evolution (the image or g(I)).")
245 | balloon = property(lambda self: self._v,
246 | set_balloon,
247 | doc="The morphological balloon parameter (ν (nu, not v)).")
248 | threshold = property(lambda self: self._theta,
249 | set_threshold,
250 | doc="The threshold value (θ).")
251 |
252 | def step(self):
253 | """Perform a single step of the morphological snake evolution."""
254 | # Assign attributes to local variables for convenience.
255 | u = self._u
256 | gI = self._data
257 | dgI = self._ddata
258 | theta = self._theta
259 | v = self._v
260 |
261 | if u is None:
262 | raise ValueError, "the levelset is not set (use set_levelset)"
263 |
264 | res = np.copy(u)
265 |
266 | # Balloon.
267 | if v > 0:
268 | aux = binary_dilation(u, self.structure)
269 | elif v < 0:
270 | aux = binary_erosion(u, self.structure)
271 | if v!= 0:
272 | res[self._threshold_mask_v] = aux[self._threshold_mask_v]
273 |
274 | # Image attachment.
275 | aux = np.zeros_like(res)
276 | dres = np.gradient(res)
277 | for el1, el2 in zip(dgI, dres):
278 | aux += el1*el2
279 | res[aux > 0] = 1
280 | res[aux < 0] = 0
281 |
282 | # Smoothing.
283 | for i in xrange(self.smoothing):
284 | res = curvop(res)
285 |
286 | self._u = res
287 |
288 | def run(self, iterations):
289 | """Run several iterations of the morphological snakes method."""
290 | for i in xrange(iterations):
291 | self.step()
292 |
293 |
294 | def evolve_visual(msnake, levelset=None, num_iters=20, background=None):
295 | """
296 | Visual evolution of a morphological snake.
297 |
298 | Parameters
299 | ----------
300 | msnake : MorphGAC or MorphACWE instance
301 | The morphological snake solver.
302 | levelset : array-like, optional
303 | If given, the levelset of the solver is initialized to this. If not
304 | given, the evolution will use the levelset already set in msnake.
305 | num_iters : int, optional
306 | The number of iterations.
307 | background : array-like, optional
308 | If given, background will be shown behind the contours instead of
309 | msnake.data.
310 | """
311 | from matplotlib import pyplot as ppl
312 |
313 | if levelset is not None:
314 | msnake.levelset = levelset
315 |
316 | # Prepare the visual environment.
317 | fig = ppl.gcf()
318 | fig.clf()
319 | ax1 = fig.add_subplot(1,2,1)
320 | if background is None:
321 | ax1.imshow(msnake.data, cmap=ppl.cm.gray)
322 | else:
323 | ax1.imshow(background, cmap=ppl.cm.gray)
324 | ax1.contour(msnake.levelset, [0.5], colors='r')
325 |
326 | ax2 = fig.add_subplot(1,2,2)
327 | ax_u = ax2.imshow(msnake.levelset)
328 | ppl.pause(0.001)
329 |
330 | # Iterate.
331 | for i in xrange(num_iters):
332 | # Evolve.
333 | msnake.step()
334 |
335 | # Update figure.
336 | del ax1.collections[0]
337 | ax1.contour(msnake.levelset, [0.5], colors='r')
338 | ax_u.set_data(msnake.levelset)
339 | fig.canvas.draw()
340 | #ppl.pause(0.001)
341 |
342 | # Return the last levelset.
343 | return msnake.levelset
344 |
345 | def evolve_visual3d(msnake, levelset=None, num_iters=20):
346 | """
347 | Visual evolution of a three-dimensional morphological snake.
348 |
349 | Parameters
350 | ----------
351 | msnake : MorphGAC or MorphACWE instance
352 | The morphological snake solver.
353 | levelset : array-like, optional
354 | If given, the levelset of the solver is initialized to this. If not
355 | given, the evolution will use the levelset already set in msnake.
356 | num_iters : int, optional
357 | The number of iterations.
358 | """
359 | from mayavi import mlab
360 | import matplotlib.pyplot as ppl
361 |
362 | if levelset is not None:
363 | msnake.levelset = levelset
364 |
365 | fig = mlab.gcf()
366 | mlab.clf()
367 | src = mlab.pipeline.scalar_field(msnake.data)
368 | mlab.pipeline.image_plane_widget(src, plane_orientation='x_axes', colormap='gray')
369 | cnt = mlab.contour3d(msnake.levelset, contours=[0.5])
370 |
371 | @mlab.animate(ui=True)
372 | def anim():
373 | for i in xrange(num_iters):
374 | msnake.step()
375 | cnt.mlab_source.scalars = msnake.levelset
376 | print "Iteration %s/%s..." % (i + 1, num_iters)
377 | yield
378 |
379 | anim()
380 | mlab.show()
381 |
382 | # Return the last levelset.
383 | return msnake.levelset
384 |
--------------------------------------------------------------------------------
/GrabCutIris_LevelSets_Ellipse.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import cv2
3 | import sys
4 | import time
5 | import morphsnakes
6 | from cv2 import cv
7 | from scipy.misc import imread
8 | from matplotlib import pyplot as ppl
9 | import urllib2
10 | import math
11 | import random
12 | import os
13 | #import cv2.cv as cv
14 |
15 | print '*** Iris segmentation using GAC and GrabCut (PSIVT Workshops 2015) ***'
16 | print '*** Authors - Sandipan Banerjee & Domingo Mery ***'
17 | print '*** Usage - python GrabCutIris_LevelSets_Ellipse.py *** \n'
18 |
19 | segF = 'SegResults'
20 | if not os.path.exists(segF):
21 | os.makedirs(segF)
22 | #f1 = open('resultsFinal.txt','a+')
23 | lvl_left = -1
24 | lvl_right = -1
25 | lvl_up = -1
26 | lvl_down = -1
27 |
28 | p_left = -1
29 | p_right = -1
30 | p_up = -1
31 | p_down = -1
32 |
33 | BLUE = [255,0,0] # rectangle color
34 | RED = [0,0,255] # PR BG
35 | GREEN = [0,255,0] # PR FG
36 | BLACK = [0,0,0] # sure BG
37 | WHITE = [255,255,255] # sure FG
38 |
39 | DRAW_BG = {'color' : GREEN, 'val' : 0}
40 | DRAW_FG = {'color' : RED, 'val' : 1}
41 | DRAW_PR_FG = {'color' : BLACK, 'val' : 3}
42 | DRAW_PR_BG = {'color' : WHITE, 'val' : 2}
43 |
44 | temp_det = []
45 | lastcx = -1
46 | lastcy = -1
47 | lastr = -1
48 | #print __doc__
49 |
50 | # Loading images
51 | if len(sys.argv) == 2:
52 | filename = sys.argv[1] # for drawing purposes
53 | else:
54 | print 'Image not found!'
55 |
56 | #f1.write(filename)
57 | #f1.write(',')
58 |
59 | img = cv2.imread(filename)
60 | img2 = img.copy() # copies of the original image
61 | img3 = img.copy()
62 | cimg = img.copy()
63 | #img4 - img.copy()
64 | eyeball_bw = np.zeros(img.shape,np.uint8)
65 | iris_bw = np.zeros(img.shape,np.uint8)
66 | iter = 0
67 | #img2 = img.copy()
68 |
69 | # Stage 1 - Intensity profiling
70 |
71 | h,w,d = img.shape
72 | h3 = h/3
73 | w3 = w/3
74 |
75 | lft = 1*w3
76 | rt = 2*w3
77 | up = 1*h3
78 | down = 2*h3
79 |
80 | hor_l = [0]*(int(down-up)/5 + 1)
81 | ver_l = [0]*(int(rt-lft)/5 + 1)
82 | temp_l = []
83 | hor_list = []
84 | ver_list = []
85 | min_val = 100
86 | ellipse_size = 0
87 | min_x = 0
88 | min_y = 0
89 | maxf = 0
90 | maxs = 0
91 | eoc = 0
92 |
93 | i = lft
94 | j = up
95 | while i <= rt:
96 | j = up
97 | while j <= down:
98 | if int(img[j][i][0]) < min_val:
99 | min_val = int(img[j][i][0])
100 | j += 1
101 | i += 1
102 |
103 | m = 0
104 | n = up
105 | k = 0
106 | max_blah = 0
107 | while n <= down:
108 | m = lft
109 | while m <= rt:
110 | temp = int(img[n][m][0])
111 | if temp < (min_val + 20):
112 | hor_l[k] += 1
113 | img3[n][m] = (0,255,0)
114 | temp_l.append([m,n])
115 | else:
116 | img3[n][m] = (255,255,255)
117 | m += 1
118 | if hor_l[k] > max_blah:
119 | max_blah = hor_l[k]
120 | hor_list = temp_l
121 | temp_l = []
122 | n += 5
123 | k += 1
124 |
125 | for i in range(len(hor_list)):
126 | img3[int(hor_list[i][1])][int(hor_list[i][0])] = (0,0,255)
127 |
128 | max_t = max_blah
129 |
130 | m = 0
131 | n = lft
132 | k = 0
133 | max_blah = 0
134 | temp_l = []
135 | while n <= rt:
136 | m = up
137 | while m <= down:
138 | temp = int(img[m][n][0])
139 | if temp < (min_val + 20):
140 | ver_l[k] += 1
141 | img3[m][n] = (0,255,0)
142 | temp_l.append([n,m])
143 | else:
144 | img3[m][n] = (255,255,255)
145 | m += 1
146 | if ver_l[k] > max_blah:
147 | max_blah = ver_l[k]
148 | ver_list = temp_l
149 | temp_l = []
150 | n += 5
151 | k += 1
152 |
153 | for i in range(len(ver_list)):
154 | img3[int(ver_list[i][1])][int(ver_list[i][0])] = (255,0,0)
155 |
156 | if max_blah > max_t:
157 | max_t = max_blah
158 |
159 | cx = 0
160 | cy = 0
161 | hlst = []
162 | vlst = []
163 | sumh = 0
164 | sumv = 0
165 |
166 | i = lft
167 |
168 | while i <= rt:
169 | j = up
170 | while j <= down:
171 | if int(img[j][i][0]) < (min_val + 20):
172 | hlst.append(i)
173 | sumh += i
174 | vlst.append(j)
175 | sumv += j
176 | j += 1
177 | i += 1
178 |
179 | cx = int(sumh/len(hlst))
180 | cy = int(sumv/len(vlst))
181 | cx1 = 0
182 | cy1 = 0
183 |
184 | for i in range(len(hor_list)):
185 | for j in range(len(ver_list)):
186 | if (hor_list[i][0] == ver_list[j][0]) and (hor_list[i][1] == ver_list[j][1]):
187 | cx1 = hor_list[i][0]
188 | cy1 = hor_list[i][1]
189 | break
190 |
191 | img3[cy][cx] = (255,255,255)
192 |
193 | # Stage 2 - Contour estimation with GAC
194 |
195 | # setting up flags
196 | rect = (0,0,1,1)
197 | drawing = False # flag for drawing curves
198 | rectangle = False # flag for drawing rect
199 | rect_over = False # flag to check if rect drawn
200 | rect_or_mask = 100 # flag for selecting rect or mask mode
201 | value = DRAW_FG # drawing initialized to FG
202 | thickness = 3 # brush thickness
203 | output_file = []
204 | iteration = 1
205 |
206 |
207 | def contour_iterator(contour):
208 | while contour:
209 | yield contour
210 | contour = contour.h_next()
211 |
212 | class FitEllipse:
213 |
214 | def __init__(self, source_image, slider_pos):
215 | self.source_image = source_image
216 | cv.CreateTrackbar("Threshold", "Result", slider_pos, 255, self.process_image)
217 | self.process_image(slider_pos)
218 |
219 | def process_image(self, slider_pos):
220 | global cimg, source_image1, ellipse_size, maxf, maxs, eoc, lastcx,lastcy,lastr
221 | """
222 | This function finds contours, draws them and their approximation by ellipses.
223 | """
224 | stor = cv.CreateMemStorage()
225 |
226 | # Create the destination images
227 | cimg = cv.CloneImage(self.source_image)
228 | cv.Zero(cimg)
229 | image02 = cv.CloneImage(self.source_image)
230 | cv.Zero(image02)
231 | image04 = cv.CreateImage(cv.GetSize(self.source_image), cv.IPL_DEPTH_8U, 3)
232 | cv.Zero(image04)
233 |
234 | # Threshold the source image. This needful for cv.FindContours().
235 | cv.Threshold(self.source_image, image02, slider_pos, 255, cv.CV_THRESH_BINARY)
236 |
237 | # Find all contours.
238 | cont = cv.FindContours(image02,
239 | stor,
240 | cv.CV_RETR_LIST,
241 | cv.CV_CHAIN_APPROX_NONE,
242 | (0, 0))
243 |
244 | maxf = 0
245 | maxs = 0
246 | size1 = 0
247 |
248 | for c in contour_iterator(cont):
249 | if len(c) > ellipse_size:
250 | PointArray2D32f = cv.CreateMat(1, len(c), cv.CV_32FC2)
251 | for (i, (x, y)) in enumerate(c):
252 | PointArray2D32f[0, i] = (x, y)
253 |
254 |
255 | # Draw the current contour in gray
256 | gray = cv.CV_RGB(100, 100, 100)
257 | cv.DrawContours(image04, c, gray, gray,0,1,8,(0,0))
258 |
259 | if iter == 0:
260 | strng = segF + '/' + 'contour1.png'
261 | cv.SaveImage(strng,image04)
262 | color = (255,255,255)
263 |
264 | (center, size, angle) = cv.FitEllipse2(PointArray2D32f)
265 |
266 | # Convert ellipse data from float to integer representation.
267 | center = (cv.Round(center[0]), cv.Round(center[1]))
268 | size = (cv.Round(size[0] * 0.5), cv.Round(size[1] * 0.5))
269 |
270 | if iter == 1:
271 | if size[0] > size[1]:
272 | size2 = size[0]
273 | else:
274 | size2 = size[1]
275 |
276 | if size2 > size1:
277 | size1 = size2
278 | size3 = size
279 |
280 | # Fits ellipse to current contour.
281 | if eoc == 0 and iter == 2:
282 | rand_val = abs((lastr - ((size[0]+size[1])/2)))
283 | if rand_val > 20 and float(max(size[0],size[1]))/float(min(size[0],size[1])) < 1.5:
284 | lastcx = center[0]
285 | lastcy = center[1]
286 | lastr = (size[0]+size[1])/2
287 |
288 | if rand_val > 20 and float(max(size[0],size[1]))/float(min(size[0],size[1])) < 1.4:
289 | cv.Ellipse(cimg, center, size,
290 | angle, 0, 360,
291 | color,2, cv.CV_AA, 0)
292 | cv.Ellipse(source_image1, center, size,
293 | angle, 0, 360,
294 | color,2, cv.CV_AA, 0)
295 |
296 | elif eoc == 1 and iter == 2:
297 | (int,cntr,rad) = cv.MinEnclosingCircle(PointArray2D32f)
298 | cntr = (cv.Round(cntr[0]), cv.Round(cntr[1]))
299 | rad = (cv.Round(rad))
300 | if maxf == 0 and maxs == 0:
301 | cv.Circle(cimg, cntr, rad, color, 1, cv.CV_AA, shift=0)
302 | cv.Circle(source_image1, cntr, rad, color, 2, cv.CV_AA, shift=0)
303 | maxf = rad
304 | elif (maxf > 0 and maxs == 0) and abs(rad - maxf) > 30:
305 | cv.Circle(cimg, cntr, rad, color, 2, cv.CV_AA, shift=0)
306 | cv.Circle(source_image1, cntr, rad, color, 2, cv.CV_AA, shift=0)
307 | maxs = len(c)
308 | if iter == 1:
309 | temp3 = 2*abs(size3[1] - size3[0])
310 | if (temp3 > 40):
311 | eoc = 1
312 |
313 |
314 | def rgb2gray(img):
315 | """Convert a RGB image to gray scale."""
316 | return 0.2989*img[:,:,0] + 0.587*img[:,:,1] + 0.114*img[:,:,2]
317 |
318 | def circle_levelset(shape, center, sqradius, scalerow=1.0):
319 | """Build a binary function with a circle as the 0.5-levelset."""
320 | grid = np.mgrid[map(slice, shape)].T - center
321 | phi = sqradius - np.sqrt(np.sum((grid.T)**2, 0))
322 | u = np.float_(phi > 0)
323 | return u
324 |
325 | def test_iris():
326 | global lvl_up,lvl_down,lvl_left,lvl_right
327 | # Load the image.
328 | img_lvl = imread(filename)/255.0
329 |
330 | # g(I)
331 | gI = morphsnakes.gborders(img_lvl, alpha=2200, sigma=5.48)
332 |
333 | # Morphological GAC. Initialization of the level-set.
334 | mgac = morphsnakes.MorphGAC(gI, smoothing=1, threshold=0.31, balloon=1)
335 | mgac.levelset = circle_levelset(img_lvl.shape, (cy, cx), (int(max_t/2) + 30))
336 |
337 | # Visual evolution.
338 | ppl.figure()
339 | ij = morphsnakes.evolve_visual(mgac, num_iters=120, background=img_lvl)
340 | #print ij.shape
341 |
342 | x_list = []
343 | y_list = []
344 |
345 | for i in range(w-1):
346 | for j in range(h-1):
347 | if ij[j][i] == 0:
348 | eyeball_bw[j][i] = (255,0,0)
349 | else:
350 | x_list.append(i)
351 | y_list.append(j)
352 | eyeball_bw[j][i] = (0,0,255)
353 |
354 | lvl_down = max(y_list)
355 | lvl_up = min(y_list)
356 | lvl_right = max(x_list)
357 | lvl_left = min(x_list)
358 |
359 | test_iris()
360 |
361 | def test_pupil():
362 | global p_up,p_down,p_left,p_right
363 | # Load the image.
364 | img_lvl = imread(filename)/255.0
365 |
366 | # g(I)
367 | gI = morphsnakes.gborders(img_lvl, alpha=2200, sigma=5.48)
368 |
369 | # Morphological GAC. Initialization of the level-set.
370 | mgac = morphsnakes.MorphGAC(gI, smoothing=1, threshold=0.31, balloon=1)
371 | mgac.levelset = circle_levelset(img_lvl.shape, (cy, cx), (max_t*0.3))
372 |
373 | # Visual evolution.
374 | ppl.figure()
375 | ij = morphsnakes.evolve_visual(mgac, num_iters=50, background=img_lvl)
376 |
377 | x_list = []
378 | y_list = []
379 |
380 | for i in range(w-1):
381 | for j in range(h-1):
382 | if ij[j][i] == 0:
383 | iris_bw[j][i] = (255,0,0)
384 | else:
385 | x_list.append(i)
386 | y_list.append(j)
387 | iris_bw[j][i] = (0,0,255)
388 |
389 | p_down = max(y_list)
390 | p_up = min(y_list)
391 | p_right = max(x_list)
392 | p_left = min(x_list)
393 |
394 | test_pupil()
395 |
396 | if (p_left - lvl_left) > 1.3*(lvl_right - p_right):
397 | print 'Left WRONG'
398 | lvl_left = lvl_left + int((p_left - lvl_left)-(lvl_right - p_right))
399 | elif (lvl_right - p_right) > 1.3*(p_left - lvl_left):
400 | print 'Right WRONG'
401 | lvl_right = lvl_right - int((lvl_right - p_right)-(p_left - lvl_left))
402 |
403 | if (p_right - p_left) > (p_down - p_up):
404 | ellipse_size = (p_right - p_left)
405 | else:
406 | ellipse_size = (p_down - p_up)
407 |
408 | ellipse_size = 2*ellipse_size
409 |
410 | # STage 3 - GrabCut
411 |
412 | mask = np.zeros(img.shape[:2],dtype = np.uint8) # mask initialized to PR_BG
413 | output = np.zeros(img.shape,np.uint8) # output image to be shown
414 |
415 | # input and output windows
416 | cv2.namedWindow('output')
417 | cv2.namedWindow('input')
418 | cv2.moveWindow('input',img.shape[1]+10,90)
419 |
420 | rect_over = True
421 | cv2.rectangle(img,(lvl_left,lvl_down),(lvl_right,lvl_up),BLUE,2)
422 | rect = (min(lvl_left,lvl_right),min(lvl_up,lvl_down),abs(lvl_left-lvl_right),abs(lvl_up-lvl_down))
423 | rect_or_mask = 0
424 | bgdmodel = np.zeros((1,65),np.float64)
425 | fgdmodel = np.zeros((1,65),np.float64)
426 | cv2.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv2.GC_INIT_WITH_RECT)
427 | rect_or_mask = 1
428 |
429 | diff = p_up - lvl_up
430 |
431 | m = p_left - 2
432 | n = p_up - 2
433 | while n > (p_up - 1.8*(diff/5)):
434 | cv2.circle(img,(m,n),thickness,value['color'],-1)
435 | cv2.circle(mask,(m,n),thickness,value['val'],-1)
436 | m -= 1
437 | n -= 1
438 |
439 | m = p_right + 2
440 | n = p_up + 2
441 | while n > (p_up - 1.8*(diff/5)):
442 | cv2.circle(img,(m,n),thickness,value['color'],-1)
443 | cv2.circle(mask,(m,n),thickness,value['val'],-1)
444 | m += 1
445 | n -= 1
446 |
447 |
448 | diff = lvl_down - p_down
449 | m = p_left - 2
450 | n = p_down + 2
451 | while n < (p_down + 1.8*(diff/5)):
452 | cv2.circle(img,(m,n),thickness,value['color'],-1)
453 | cv2.circle(mask,(m,n),thickness,value['val'],-1)
454 | m -= 1
455 | n += 1
456 |
457 | m = p_right + 2
458 | n = p_down + 2
459 | while n < (p_down + 1.8*(diff/5)):
460 | cv2.circle(img,(m,n),thickness,value['color'],-1)
461 | cv2.circle(mask,(m,n),thickness,value['val'],-1)
462 | m += 1
463 | n += 1
464 |
465 | diff = (p_left - lvl_left)/10
466 | m = p_left - diff
467 | while m > (lvl_left + diff):
468 | cv2.circle(img,(m,cy),thickness,value['color'],-1)
469 | cv2.circle(mask,(m,cy),thickness,value['val'],-1)
470 | m -= 1
471 |
472 | diff = (lvl_right - p_right)/10
473 | m = p_right + diff
474 | while m < (lvl_right - diff):
475 | cv2.circle(img,(m,cy),thickness,value['color'],-1)
476 | cv2.circle(mask,(m,cy),thickness,value['val'],-1)
477 | m += 1
478 |
479 |
480 | diff = p_right - p_left
481 | m = p_left + (diff/5)
482 | value = DRAW_BG
483 | while m < (p_left + 4*(diff/5)):
484 | cv2.circle(img,(m,cy),thickness,value['color'],-1)
485 | cv2.circle(mask,(m,cy),thickness,value['val'],-1)
486 | m += 1
487 |
488 | tempi = 0
489 |
490 | while tempi < 10:
491 | bgdmodel = np.zeros((1,65),np.float64)
492 | fgdmodel = np.zeros((1,65),np.float64)
493 | cv2.grabCut(img2,mask,rect,bgdmodel,fgdmodel,1,cv2.GC_INIT_WITH_MASK)
494 | tempi += 1
495 |
496 | mask2 = np.where((mask==1) + (mask==3),255,0).astype('uint8')
497 | output = cv2.bitwise_and(img2,img2,mask=mask2)
498 |
499 | strng = os.path.join(segF, os.path.basename(filename).split('.')[0] + '_seg.png')
500 | cv2.imwrite(strng,output)
501 |
502 | source_image1 = cv.LoadImage(filename, cv.CV_LOAD_IMAGE_GRAYSCALE)
503 | source_image = cv.LoadImage(strng, cv.CV_LOAD_IMAGE_GRAYSCALE)
504 |
505 | cv.NamedWindow("Result", 1)
506 |
507 | # Stage 4 - Ellipse fitting
508 |
509 | fe = FitEllipse(source_image, (min_val+20))
510 |
511 | tab1 = cv2.imread(strng)
512 | iter = 1
513 | flag_t = 0
514 |
515 | if (p_up - lvl_up) < (0.75*(lvl_down - p_down)):
516 | flag_t = 1
517 | elif (lvl_down - p_down) < (0.75*(p_up - lvl_up)):
518 | flag_t = 2
519 |
520 | if flag_t == 1:
521 | bnd = p_up - 10
522 | for i in range(w-1):
523 | for j in range(h-1):
524 | if j <= bnd and tab1[j][i][0] == 100:
525 | tab1[j][i] = (0,0,0)
526 | #if j <= bnd and tab2[j][i][0] == 255:
527 | #tab2[j][i] = (0,0,0)
528 | elif flag_t == 2:
529 | bnd = p_down + 10
530 | for i in range(w-1):
531 | for j in range(h-1):
532 | if j >= bnd and tab1[j][i][0] == 100:
533 | tab1[j][i] = (0,0,0)
534 |
535 | cv2.imwrite(strng,tab1)
536 |
537 | source_image = cv.LoadImage(strng, cv.CV_LOAD_IMAGE_GRAYSCALE)
538 | source_image1 = cv.LoadImage(filename, cv.CV_LOAD_IMAGE_GRAYSCALE)
539 | fe = FitEllipse(source_image, (min_val+20))
540 |
541 | iter = 2
542 | source_image = cv.LoadImage(strng, cv.CV_LOAD_IMAGE_GRAYSCALE)
543 | source_image1 = cv.LoadImage(filename, cv.CV_LOAD_IMAGE_GRAYSCALE)
544 | fe = FitEllipse(source_image, (min_val+20))
545 |
546 | # Saving results
547 |
548 | strng1 = os.path.join(segF, os.path.basename(filename).split('.')[0] + '_contour.png')
549 | cv.SaveImage(strng1,source_image1)
550 | cimg1 = cv2.imread(strng1)
551 | bar = np.zeros((img.shape[0],5,3),np.uint8)
552 | res = np.hstack((img2,bar,eyeball_bw,bar,iris_bw,bar,img,bar,output,bar,cimg1))
553 | output_file = os.path.join(segF, os.path.basename(filename).split('.')[0] + '_grabcut_output.png')
554 | cv2.imwrite(output_file,res)
555 |
556 | print 'Done segmenting!!!'
557 | cv2.destroyAllWindows()
--------------------------------------------------------------------------------