├── 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() --------------------------------------------------------------------------------