├── README.md ├── S2001R01.jpg ├── S2001R09.jpg ├── S2002R01.jpg ├── S2002R20.jpg ├── S2005R07.jpg ├── S2005R09.jpg └── iris_recognition.py /README.md: -------------------------------------------------------------------------------- 1 | # Python Script for an Iris Recognition System using OpenCV 2 | 3 | This Python application takes 2 infrared eye images as input and gives a matching score after comparing the 2 irises. If a matching score threshold is selected, it can decide if the images represent the same person. 4 | 5 | The algorithm firstly localizes the iris region in the 2 eye images, identifies and encodes the keypoints characterizing each of the irises and then uses the SIFT algorithm provided by OpenCV to compare the 2 sets of key points. SIFT (Scale-invariant feature transform) is an algorithm used in computer vision to detect and describe local features in images. 6 | 7 | For testing purposes I used an iris image database provided by CASIA (The Institute of Automation, Chinese Academy of Sciences) containing more than 16k eye images from more than 400 individuals. I generated approximately 50k test experiments and, if it is selected a matching score threshold so that the ‘false accept rate’ is 0 (no matches in comparisons between irises from 2 different persons), then the ‘false reject rate’ is approximately 25%, meaning the algorithm gives the right answer in 75% of cases when comparing iris images coming from the same person. 8 | -------------------------------------------------------------------------------- /S2001R01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2001R01.jpg -------------------------------------------------------------------------------- /S2001R09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2001R09.jpg -------------------------------------------------------------------------------- /S2002R01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2002R01.jpg -------------------------------------------------------------------------------- /S2002R20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2002R20.jpg -------------------------------------------------------------------------------- /S2005R07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2005R07.jpg -------------------------------------------------------------------------------- /S2005R09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andreibercu/iris-recognition/7138105b48f011ec8ba4d7d01daf1e217652b780/S2005R09.jpg -------------------------------------------------------------------------------- /iris_recognition.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import os 4 | import sys 5 | import math 6 | import random 7 | import cPickle as pickle 8 | import copy 9 | import gzip 10 | import inspect 11 | import itertools 12 | 13 | from matplotlib import pyplot as plt 14 | 15 | 16 | def compare_images(filepath1, filepath2): 17 | print "Analysing " + filepath1 18 | rois_1 = load_rois_from_image(filepath1) 19 | 20 | print "Analysing " + filepath2 21 | rois_2 = load_rois_from_image(filepath2) 22 | 23 | getall_matches(rois_1, rois_2, 0.8, 10, 0.15, show=True) 24 | 25 | def compare_binfiles(bin_path1, bin_path2): 26 | print "Analysing " + bin_path1 27 | rois_1 = load_rois_from_bin(bin_path1) 28 | 29 | print "Analysing " + bin_path2 30 | rois_2 = load_rois_from_bin(bin_path2) 31 | 32 | getall_matches(rois_1, rois_2, 0.88, 10, 0.07, show=True) 33 | 34 | def load_rois_from_image(filepath): 35 | img = load_image(filepath, show=True) 36 | 37 | print "Getting iris boundaries.." 38 | pupil_circle, ext_iris_circle = get_iris_boundaries(img, show=True) 39 | if not pupil_circle or not ext_iris_circle: 40 | print "Error finding iris boundaries!" 41 | return 42 | 43 | print "Equalizing histogram .." 44 | roi = get_equalized_iris(img, ext_iris_circle, pupil_circle, show=True) 45 | 46 | print "Getting roi iris images ..." 47 | rois = get_rois(roi, pupil_circle, ext_iris_circle, show=True) 48 | 49 | print "Searching for keypoints ... \n" 50 | sift = cv2.xfeatures2d.SIFT_create() 51 | load_keypoints(sift, rois, show=True) 52 | load_descriptors(sift, rois) 53 | 54 | return rois 55 | 56 | def load_image(filepath, show=False): 57 | img = cv2.imread(filepath, 0) 58 | if show: 59 | cv2.imshow(filepath, img) 60 | ch = cv2.waitKey(0) 61 | cv2.destroyAllWindows() 62 | return img 63 | 64 | def get_iris_boundaries(img, show=False): 65 | # Finding iris inner boundary 66 | pupil_circle = find_pupil(img) 67 | 68 | if not pupil_circle: 69 | print 'ERROR: Pupil circle not found!' 70 | return None, None 71 | 72 | # Finding iris outer boundary 73 | radius_range = int(math.ceil(pupil_circle[2]*1.5)) 74 | multiplier = 0.25 75 | center_range = int(math.ceil(pupil_circle[2]*multiplier)) 76 | ext_iris_circle = find_ext_iris( 77 | img, pupil_circle, center_range, radius_range) 78 | 79 | while(not ext_iris_circle and multiplier <= 0.7): 80 | multiplier += 0.05 81 | print 'Searching exterior iris circle with multiplier ' + \ 82 | str(multiplier) 83 | center_range = int(math.ceil(pupil_circle[2]*multiplier)) 84 | ext_iris_circle = find_ext_iris(img, pupil_circle, 85 | center_range, radius_range) 86 | if not ext_iris_circle: 87 | print 'ERROR: Exterior iris circle not found!' 88 | return None, None 89 | 90 | if show: 91 | cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) 92 | draw_circles(cimg, pupil_circle, ext_iris_circle, 93 | center_range, radius_range) 94 | cv2.imshow('iris boundaries', cimg) 95 | ch = cv2.waitKey(0) 96 | cv2.destroyAllWindows() 97 | 98 | return pupil_circle, ext_iris_circle 99 | 100 | def find_pupil(img): 101 | def get_edges(image): 102 | edges = cv2.Canny(image,20,100) 103 | kernel = np.ones((3,3),np.uint8) 104 | edges = cv2.dilate(edges, kernel, iterations=2) 105 | ksize = 2 * random.randrange(5,11) + 1 106 | edges = cv2.GaussianBlur(edges,(ksize,ksize),0) 107 | return edges 108 | 109 | param1 = 200 # 200 110 | param2 = 120 # 150 111 | pupil_circles = [] 112 | while(param2 > 35 and len(pupil_circles) < 100): 113 | for mdn,thrs in [(m,t) 114 | for m in [3,5,7] 115 | for t in [20,25,30,35,40,45,50,55,60]]: 116 | # Median Blur 117 | median = cv2.medianBlur(img, 2*mdn+1) 118 | 119 | # Threshold 120 | ret, thres = cv2.threshold( 121 | median, thrs, 255, 122 | cv2.THRESH_BINARY_INV) 123 | 124 | # Fill Contours 125 | con_img, contours, hierarchy = \ 126 | cv2.findContours(thres.copy(), 127 | cv2.RETR_EXTERNAL, 128 | cv2.CHAIN_APPROX_NONE) 129 | draw_con = cv2.drawContours(thres, contours, -1, (255), -1) 130 | 131 | # Canny Edges 132 | edges = get_edges(thres) 133 | 134 | # HoughCircles 135 | circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 1, 136 | np.array([]), param1, param2) 137 | if circles is not None: 138 | # convert the (x, y) coordinates and radius of the circles 139 | # to integers 140 | circles = np.round(circles[0, :]).astype("int") 141 | for c in circles: 142 | pupil_circles.append(c) 143 | 144 | param2 = param2 -1 145 | 146 | cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) 147 | 148 | return get_mean_circle(pupil_circles) 149 | 150 | def get_mean_circle(circles, draw=None): 151 | if not circles: 152 | return 153 | mean_0 = int(np.mean([c[0] for c in circles])) 154 | mean_1 = int(np.mean([c[1] for c in circles])) 155 | mean_2 = int(np.mean([c[2] for c in circles])) 156 | 157 | if draw is not None: 158 | draw = draw.copy() 159 | # draw the outer circle 160 | cv2.circle(draw,(mean_0,mean_1),mean_2,(0,255,0),1) 161 | # draw the center of the circle 162 | cv2.circle(draw,(mean_0,mean_1),2,(0,255,0),2) 163 | cv2.imshow('mean circle', draw) 164 | ch = cv2.waitKey(0) 165 | cv2.destroyAllWindows() 166 | 167 | return mean_0, mean_1, mean_2 168 | 169 | def find_ext_iris(img, pupil_circle, center_range, radius_range): 170 | def get_edges(image, thrs2): 171 | thrs1 = 0 # 0 172 | edges = cv2.Canny(image, thrs1, thrs2, apertureSize=5) 173 | kernel = np.ones((3,3),np.uint8) 174 | edges = cv2.dilate(edges, kernel, iterations=1) 175 | ksize = 2 * random.randrange(5,11) + 1 176 | edges = cv2.GaussianBlur(edges,(ksize,ksize),0) 177 | return edges 178 | 179 | def get_circles(hough_param, median_params, edge_params): 180 | crt_circles = [] 181 | for mdn,thrs2 in [(m,t) 182 | for m in median_params 183 | for t in edge_params]: 184 | # Median Blur 185 | median = cv2.medianBlur(img, 2*mdn+1) 186 | 187 | # Canny Edges 188 | edges = get_edges(median, thrs2) 189 | 190 | # HoughCircles 191 | circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 1, 192 | np.array([]), 200, hough_param) 193 | if circles is not None: 194 | # convert the (x, y) coordinates and radius of the 195 | # circles to integers 196 | circles = np.round(circles[0, :]).astype("int") 197 | for (c_col, c_row, r) in circles: 198 | if point_in_circle( 199 | int(pupil_circle[0]), int(pupil_circle[1]), 200 | center_range, c_col, c_row) and \ 201 | r > radius_range: 202 | crt_circles.append((c_col, c_row, r)) 203 | return crt_circles 204 | 205 | param2 = 120 # 150 206 | total_circles = [] 207 | while(param2 > 40 and len(total_circles) < 50): 208 | crt_circles = get_circles( 209 | param2, [8,10,12,14,16,18,20], [430,480,530]) 210 | if crt_circles: 211 | total_circles += crt_circles 212 | param2 = param2 -1 213 | 214 | if not total_circles: 215 | print "Running plan B on finding ext iris circle" 216 | param2 = 120 217 | while(param2 > 40 and len(total_circles) < 50): 218 | crt_circles = get_circles( 219 | param2, [3,5,7,21,23,25], [430,480,530]) 220 | if crt_circles: 221 | total_circles += crt_circles 222 | param2 = param2 -1 223 | 224 | if not total_circles: 225 | return 226 | 227 | cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR) 228 | filtered = filtered_circles(total_circles) 229 | 230 | return get_mean_circle(filtered) 231 | 232 | def point_in_circle(c_col, c_row, c_radius, p_col, p_row): 233 | return distance(c_col, c_row, p_col, p_row) <= c_radius 234 | 235 | def filtered_circles(circles, draw=None): 236 | # what if there are only 2 circles - which is alpha? 237 | def get_alpha_radius(circles0): 238 | alpha_circle = None 239 | dist_min = None 240 | circles1 = circles0[:] 241 | circles2 = circles0[:] 242 | for crt_c in circles1: 243 | dist = 0 244 | for c in circles2: 245 | dist += math.fabs(float(crt_c[2]) - float(c[2])) 246 | if not dist_min or dist < dist_min: 247 | dist_min = dist 248 | alpha_circle = crt_c 249 | return alpha_circle[2] 250 | 251 | if not circles: 252 | print 'Error: empty circles list in filtered_circles() !' 253 | return [] 254 | c_0_mean, c_0_dev = standard_dev([int(i[0]) for i in circles]) 255 | c_1_mean, c_1_dev = standard_dev([int(i[1]) for i in circles]) 256 | filtered = [] 257 | filtered_pos = [] 258 | not_filtered = [] 259 | ratio = 1.5 260 | for c in circles[:]: 261 | if c[0] < c_0_mean - ratio*c_0_dev or \ 262 | c[0] > c_0_mean + ratio*c_0_dev or \ 263 | c[1] < c_1_mean - ratio*c_1_dev or \ 264 | c[1] > c_1_mean + ratio*c_1_dev: 265 | not_filtered.append(c) 266 | else: 267 | filtered_pos.append(c) 268 | if len([float(c[2]) for c in filtered_pos]) < 3: 269 | filtered = filtered_pos 270 | else: 271 | alpha_radius = get_alpha_radius(filtered_pos) 272 | mean_radius, dev_radius = standard_dev( 273 | [float(c[2]) for c in filtered_pos]) 274 | max_radius = alpha_radius + dev_radius 275 | min_radius = alpha_radius - dev_radius 276 | for c in filtered_pos: 277 | if c[2] < min_radius or \ 278 | c[2] > max_radius: 279 | not_filtered.append(c) 280 | else: 281 | filtered.append(c) 282 | 283 | if draw is not None: 284 | draw = draw.copy() 285 | for circle in not_filtered: 286 | # draw the outer circle 287 | cv2.circle(draw,(circle[0],circle[1]),circle[2],(255,0,0),1) 288 | # draw the center of the circle 289 | cv2.circle(draw,(circle[0],circle[1]),2,(255,0,0),2) 290 | for circle in filtered: 291 | # draw the outer circle 292 | cv2.circle(draw,(circle[0],circle[1]),circle[2],(0,255,0),1) 293 | # draw the center of the circle 294 | cv2.circle(draw,(circle[0],circle[1]),2,(0,255,0),2) 295 | cv2.imshow('filtered_circles() total={0} filtered_pos={1} filtered={2}'.\ 296 | format(len(circles), len(filtered_pos), len(filtered)), 297 | draw) 298 | ch = cv2.waitKey(0) 299 | cv2.destroyAllWindows() 300 | return filtered 301 | 302 | def draw_circles(cimg, pupil_circle, ext_iris_circle, 303 | center_range=None, radius_range=None): 304 | # draw the outer pupil circle 305 | cv2.circle(cimg,(pupil_circle[0], pupil_circle[1]), pupil_circle[2], 306 | (0,0,255),1) 307 | # draw the center of the pupil circle 308 | cv2.circle(cimg,(pupil_circle[0],pupil_circle[1]),1,(0,0,255),1) 309 | if center_range: 310 | # draw ext iris center range limit 311 | cv2.circle(cimg,(pupil_circle[0], pupil_circle[1]), center_range, 312 | (0,255,255),1) 313 | if radius_range: 314 | # draw ext iris radius range limit 315 | cv2.circle(cimg,(pupil_circle[0], pupil_circle[1]), radius_range, 316 | (0,255,255),1) 317 | # draw the outer ext iris circle 318 | cv2.circle(cimg, (ext_iris_circle[0], ext_iris_circle[1]), 319 | ext_iris_circle[2],(0,255,0),1) 320 | # draw the center of the ext iris circle 321 | cv2.circle(cimg, (ext_iris_circle[0], ext_iris_circle[1]), 322 | 1,(0,255,0),1) 323 | 324 | def get_equalized_iris(img, ext_iris_circle, pupil_circle, show=False): 325 | def find_roi(): 326 | mask = img.copy() 327 | mask[:] = (0) 328 | 329 | cv2.circle(mask, 330 | (ext_iris_circle[0], ext_iris_circle[1]), 331 | ext_iris_circle[2], (255), -1) 332 | cv2.circle(mask, 333 | (pupil_circle[0],pupil_circle[1]), 334 | pupil_circle[2],(0), -1) 335 | 336 | roi = cv2.bitwise_and(img, mask) 337 | 338 | return roi 339 | 340 | roi = find_roi() 341 | 342 | # Mask the top side of the iris 343 | for p_col in range(roi.shape[1]): 344 | for p_row in range(roi.shape[0]): 345 | theta = angle_v(ext_iris_circle[0], ext_iris_circle[1], 346 | p_col, p_row) 347 | if theta > 50 and theta < 130: 348 | roi[p_row,p_col] = 0 349 | 350 | ret, roi = cv2.threshold(roi,50,255,cv2.THRESH_TOZERO) 351 | 352 | equ_roi = roi.copy() 353 | cv2.equalizeHist(roi, equ_roi) 354 | roi = cv2.addWeighted(roi, 0.0, equ_roi, 1.0, 0) 355 | 356 | if show: 357 | cv2.imshow('equalized histogram iris region', roi) 358 | ch = cv2.waitKey(0) 359 | cv2.destroyAllWindows() 360 | 361 | return roi 362 | 363 | def get_rois(img, pupil_circle, ext_circle, show=False): 364 | bg = img.copy() 365 | bg[:] = 0 366 | 367 | init_dict = {'img': bg.copy(), 368 | 'pupil_circle': pupil_circle, 369 | 'ext_circle': ext_circle, 370 | 'kp': None, 371 | 'img_kp_init': bg.copy(), 372 | 'img_kp_filtered': bg.copy(), 373 | 'des': None 374 | } 375 | 376 | rois = {'right-side': copy.deepcopy(init_dict), 377 | 'left-side': copy.deepcopy(init_dict), 378 | 'bottom': copy.deepcopy(init_dict), 379 | 'complete': copy.deepcopy(init_dict) 380 | } 381 | 382 | for p_col in range(img.shape[1]): 383 | for p_row in range(img.shape[0]): 384 | if not point_in_circle(pupil_circle[0], pupil_circle[1], 385 | pupil_circle[2], p_col, p_row) and \ 386 | point_in_circle(ext_circle[0], ext_circle[1], ext_circle[2], 387 | p_col, p_row): 388 | theta = angle_v(ext_circle[0], ext_circle[1], p_col, p_row) 389 | if theta >= -50 and theta <= 50: 390 | rois['right-side']['img'][p_row,p_col] = img[p_row,p_col] 391 | if theta >= 130 or theta <= -130: 392 | rois['left-side']['img'][p_row,p_col] = img[p_row,p_col] 393 | if theta >= -140 and theta <= -40: 394 | rois['bottom']['img'][p_row,p_col] = img[p_row,p_col] 395 | rois['complete']['img'][p_row,p_col] = img[p_row,p_col] 396 | 397 | rois['right-side']['ext_circle'] = \ 398 | (0, int(1.25*ext_circle[2]), int(ext_circle[2])) 399 | rois['left-side']['ext_circle'] = \ 400 | (int(1.25*ext_circle[2]), 401 | int(1.25*ext_circle[2]), 402 | int(ext_circle[2])) 403 | rois['bottom']['ext_circle'] = \ 404 | (int(1.25*ext_circle[2]), 0, int(ext_circle[2])) 405 | rois['complete']['ext_circle'] = \ 406 | (int(1.25*ext_circle[2]), 407 | int(1.25*ext_circle[2]), 408 | int(ext_circle[2])) 409 | 410 | for pos in ['right-side','left-side','bottom','complete']: 411 | tx = rois[pos]['ext_circle'][0] - ext_circle[0] 412 | ty = rois[pos]['ext_circle'][1] - ext_circle[1] 413 | rois[pos]['pupil_circle'] = (int(tx + pupil_circle[0]), 414 | int(ty + pupil_circle[1]), 415 | int(pupil_circle[2])) 416 | M = np.float32([[1,0,tx],[0,1,ty]]) 417 | rois[pos]['img'] = cv2.warpAffine( 418 | rois[pos]['img'], M, 419 | (img.shape[1], img.shape[0])) 420 | 421 | rois['right-side']['img'] = \ 422 | rois['right-side']['img'][0:2.5*ext_circle[2], 0:1.25*ext_circle[2]] 423 | rois['left-side']['img'] = \ 424 | rois['left-side']['img'][0:2.5*ext_circle[2], 0:1.25*ext_circle[2]] 425 | rois['bottom']['img'] = \ 426 | rois['bottom']['img'][0:1.25*ext_circle[2], 0:2.5*ext_circle[2]] 427 | rois['complete']['img'] = \ 428 | rois['complete']['img'][0:2.5*ext_circle[2], 0:2.5*ext_circle[2]] 429 | 430 | if show: 431 | plt.subplot(2,2,1),plt.imshow(rois['right-side']['img'], cmap='gray') 432 | plt.title('right-side'),plt.xticks([]),plt.yticks([]) 433 | plt.subplot(2,2,2),plt.imshow(rois['left-side']['img'], cmap='gray') 434 | plt.title('left-side'),plt.xticks([]),plt.yticks([]) 435 | plt.subplot(2,2,3),plt.imshow(rois['bottom']['img'], cmap='gray') 436 | plt.title('bottom'),plt.xticks([]),plt.yticks([]) 437 | plt.subplot(2,2,4),plt.imshow(rois['complete']['img'], cmap='gray') 438 | plt.title('complete'),plt.xticks([]),plt.yticks([]) 439 | plt.show() 440 | 441 | return rois 442 | 443 | def load_keypoints(sift, rois, show=False): 444 | bf = cv2.BFMatcher() 445 | for pos in ['right-side','left-side','bottom','complete']: 446 | rois[pos]['kp'] = sift.detect(rois[pos]['img'],None) 447 | 448 | # Create image with non-filtered keypoints 449 | rois[pos]['img_kp_init'] = cv2.drawKeypoints( 450 | rois[pos]['img'], rois[pos]['kp'], 451 | color=(0,255,0), flags=0, 452 | outImage=None) 453 | cv2.circle( 454 | rois[pos]['img_kp_init'], 455 | (rois[pos]['pupil_circle'][0], rois[pos]['pupil_circle'][1]), 456 | rois[pos]['pupil_circle'][2], (0,0,255), 1) 457 | cv2.circle( 458 | rois[pos]['img_kp_init'], 459 | (rois[pos]['ext_circle'][0], rois[pos]['ext_circle'][1]), 460 | rois[pos]['ext_circle'][2], (0,255,255), 1) 461 | 462 | # Filter detected keypoints 463 | inside = 0 464 | outside = 0 465 | wrong_angle = 0 466 | for kp in rois[pos]['kp'][:]: 467 | c_angle = angle_v(rois[pos]['ext_circle'][0], 468 | rois[pos]['ext_circle'][1], 469 | kp.pt[0], kp.pt[1]) 470 | if point_in_circle(rois[pos]['pupil_circle'][0], 471 | rois[pos]['pupil_circle'][1], 472 | rois[pos]['pupil_circle'][2]+3, 473 | kp.pt[0], kp.pt[1]): 474 | rois[pos]['kp'].remove(kp) 475 | inside +=1 476 | elif not point_in_circle(rois[pos]['ext_circle'][0], 477 | rois[pos]['ext_circle'][1], 478 | rois[pos]['ext_circle'][2]-5, 479 | kp.pt[0], kp.pt[1]): 480 | rois[pos]['kp'].remove(kp) 481 | outside +=1 482 | elif (pos == 'right-side' and (c_angle <= -45 or c_angle >= 45)) or \ 483 | (pos == 'left-side' and (c_angle <= 135 and c_angle >= -135)) or \ 484 | (pos == 'bottom' and (c_angle <= -135 or c_angle >= -45)): 485 | rois[pos]['kp'].remove(kp) 486 | wrong_angle +=1 487 | 488 | # Create images with filtered keypoints 489 | rois[pos]['img_kp_filtered'] = cv2.drawKeypoints( 490 | rois[pos]['img'],rois[pos]['kp'], 491 | color=(0,255,0), flags=0, 492 | outImage=None) 493 | cv2.circle( 494 | rois[pos]['img_kp_filtered'], 495 | (rois[pos]['pupil_circle'][0],rois[pos]['pupil_circle'][1]), 496 | rois[pos]['pupil_circle'][2], (0,0,255), 1) 497 | cv2.circle( 498 | rois[pos]['img_kp_filtered'], 499 | (rois[pos]['ext_circle'][0],rois[pos]['ext_circle'][1]), 500 | rois[pos]['ext_circle'][2], (0,255,255), 1) 501 | 502 | # Show keypoints images 503 | if show: 504 | i=0 505 | for pos in ['right-side','left-side','bottom']: 506 | plt.subplot(3, 2, 2*i+1), \ 507 | plt.imshow(rois[pos]['img_kp_init']) 508 | plt.xticks([]), plt.yticks([]) 509 | plt.subplot(3, 2, 2*i+2), \ 510 | plt.imshow(rois[pos]['img_kp_filtered']) 511 | plt.xticks([]), plt.yticks([]) 512 | i+=1 513 | plt.show() 514 | 515 | def load_descriptors(sift, rois): 516 | for pos in ['right-side','left-side','bottom','complete']: 517 | rois[pos]['kp'], rois[pos]['des'] = \ 518 | sift.compute( rois[pos]['img'], rois[pos]['kp'] ) 519 | 520 | def getall_matches(rois_1, rois_2, dratio, 521 | stdev_angle, stdev_dist, show=False): 522 | img_matches = [] 523 | numberof_matches = {'right-side': 0, 524 | 'left-side': 0, 525 | 'bottom': 0, 526 | 'complete': 0} 527 | 528 | for pos in ['right-side','left-side','bottom','complete']: 529 | if not rois_1[pos]['kp'] or not rois_2[pos]['kp']: 530 | print "KeyPoints not found in one of rois_x[pos]['kp'] !!!" 531 | print " -->", pos, len(rois_1[pos]['kp']), len(rois_2[pos]['kp']) 532 | else: 533 | matches = get_matches(rois_1[pos], rois_2[pos], 534 | dratio, stdev_angle, stdev_dist) 535 | numberof_matches[pos] = len(matches) 536 | 537 | if show: 538 | print "{0} matches: {1}".format(pos, str(len(matches))) 539 | crt_image = cv2.drawMatchesKnn( 540 | rois_1[pos]['img'],rois_1[pos]['kp'], 541 | rois_2[pos]['img'],rois_2[pos]['kp'], 542 | [matches], flags=2, outImg=None) 543 | 544 | img_matches.append(crt_image) 545 | cv2.imshow('matches', crt_image) 546 | ch = cv2.waitKey(0) 547 | cv2.destroyAllWindows() 548 | 549 | return numberof_matches 550 | 551 | def get_matches(roipos_1, roipos_2, 552 | dratio, stdev_angle, stdev_dist): 553 | if not roipos_1['kp'] or not roipos_2['kp']: 554 | print "KeyPoints not found in one of roipos_x['kp'] !!!" 555 | return [] 556 | 557 | bf = cv2.BFMatcher() 558 | matches = bf.knnMatch(roipos_1['des'], roipos_2['des'], k=2) 559 | kp1 = roipos_1['kp'] 560 | kp2 = roipos_2['kp'] 561 | 562 | diff_dist_1 = roipos_1['ext_circle'][2] - roipos_1['pupil_circle'][2] 563 | diff_dist_2 = roipos_2['ext_circle'][2] - roipos_2['pupil_circle'][2] 564 | 565 | diff_angles = [] 566 | diff_dists = [] 567 | filtered = [] 568 | for m,n in matches: 569 | if (m.distance/n.distance) > dratio: 570 | continue 571 | 572 | x1,y1 = kp1[m.queryIdx].pt 573 | x2,y2 = kp2[m.trainIdx].pt 574 | 575 | angle_1 = angle_v( 576 | x1,y1, 577 | roipos_1['pupil_circle'][0], 578 | roipos_1['pupil_circle'][1]) 579 | angle_2 = angle_v( 580 | x2,y2, 581 | roipos_2['pupil_circle'][0], 582 | roipos_2['pupil_circle'][1]) 583 | diff_angle = angle_1 - angle_2 584 | diff_angles.append(diff_angle) 585 | 586 | dist_1 = distance(x1,y1, 587 | roipos_1['pupil_circle'][0], 588 | roipos_1['pupil_circle'][1]) 589 | dist_1 = dist_1 - roipos_1['pupil_circle'][2] 590 | dist_1 = dist_1 / diff_dist_1 591 | 592 | dist_2 = distance(x2,y2, 593 | roipos_2['pupil_circle'][0], 594 | roipos_2['pupil_circle'][1]) 595 | dist_2 = dist_2 - roipos_2['pupil_circle'][2] 596 | dist_2 = dist_2 / diff_dist_2 597 | 598 | diff_dist = dist_1 - dist_2 599 | diff_dists.append(diff_dist) 600 | 601 | filtered.append(m) 602 | 603 | # Remove bad matches 604 | if True and filtered: 605 | median_diff_angle = median(diff_angles) 606 | median_diff_dist = median(diff_dists) 607 | #print "median dist:", median_diff_dist 608 | for m in filtered[:]: 609 | x1,y1 = kp1[m.queryIdx].pt 610 | x2,y2 = kp2[m.trainIdx].pt 611 | 612 | angle_1 = angle_v( 613 | x1,y1, 614 | roipos_1['pupil_circle'][0], 615 | roipos_1['pupil_circle'][1]) 616 | angle_2 = angle_v( 617 | x2,y2, 618 | roipos_2['pupil_circle'][0], 619 | roipos_2['pupil_circle'][1]) 620 | diff_angle = angle_1 - angle_2 621 | 622 | good_diff_angle = \ 623 | (diff_angle > median_diff_angle - stdev_angle and \ 624 | diff_angle < median_diff_angle + stdev_angle) 625 | 626 | dist_1 = distance(x1,y1, 627 | roipos_1['pupil_circle'][0], 628 | roipos_1['pupil_circle'][1]) 629 | dist_1 = dist_1 - roipos_1['pupil_circle'][2] 630 | dist_1 = dist_1 / diff_dist_1 631 | 632 | dist_2 = distance(x2,y2, 633 | roipos_2['pupil_circle'][0], 634 | roipos_2['pupil_circle'][1]) 635 | dist_2 = dist_2 - roipos_2['pupil_circle'][2] 636 | dist_2 = dist_2 / diff_dist_2 637 | 638 | diff_dist = dist_1 - dist_2 639 | good_dist = (diff_dist > median_diff_dist - stdev_dist and \ 640 | diff_dist < median_diff_dist + stdev_dist) 641 | 642 | if good_diff_angle and good_dist: 643 | continue 644 | 645 | filtered.remove(m) 646 | 647 | return filtered 648 | 649 | def angle_v(x1,y1,x2,y2): 650 | return math.degrees(math.atan2(-(y2-y1),(x2-x1))) 651 | 652 | def distance(x1,y1,x2,y2): 653 | dst = math.sqrt((x2-x1)**2 + (y2-y1)**2) 654 | return dst 655 | 656 | def mean(x): 657 | sum = 0.0 658 | for i in range(len(x)): 659 | sum += x[i] 660 | return sum/len(x) 661 | 662 | def median(x): 663 | return np.median(np.array(x)) 664 | 665 | def standard_dev(x): 666 | if not x: 667 | print 'Error: empty list parameter in standard_dev() !' 668 | print inspect.getouterframes( inspect.currentframe() )[1] 669 | print 670 | return None, None 671 | m = mean(x) 672 | sumsq = 0.0 673 | for i in range(len(x)): 674 | sumsq += (x[i] - m) ** 2 675 | return m, math.sqrt(sumsq/len(x)) 676 | 677 | def load_rois_from_bin(bin_path): 678 | with gzip.open(bin_path, 'rb') as bin_file: 679 | rois = pickle.load(bin_file) 680 | unpickle_rois(rois) 681 | return rois 682 | 683 | def unpickle_rois(rois): 684 | for pos in ['right-side','left-side','bottom','complete']: 685 | rois[pos]['kp'] = unpickle_keypoints(rois[pos]['kp']) 686 | 687 | def unpickle_keypoints(array): 688 | keypoints = [] 689 | for point in array: 690 | temp_kp = cv2.KeyPoint(x=point[0][0],y=point[0][1],_size=point[1], 691 | _angle=point[2], _response=point[3], 692 | _octave=point[4], _class_id=point[5]) 693 | keypoints.append(temp_kp) 694 | return keypoints 695 | 696 | def pickle_rois(rois): 697 | for pos in ['right-side','left-side','bottom','complete']: 698 | rois[pos]['kp'] = pickle_keypoints(rois[pos]['kp']) 699 | 700 | def pickle_keypoints(keypoints): 701 | unfolded = [] 702 | for point in keypoints: 703 | temp = (point.pt, point.size, point.angle, point.response, 704 | point.octave, point.class_id) 705 | unfolded.append(temp) 706 | 707 | return unfolded 708 | 709 | 710 | if __name__ == "__main__": 711 | 712 | # Specify 2 image paths 713 | filepath1 = r'./S2005R07.jpg' 714 | filepath2 = r'./S2005R09.jpg' 715 | 716 | if os.path.isfile(filepath1) and os.path.isfile(filepath2): 717 | compare_images(filepath1, filepath2) 718 | 719 | --------------------------------------------------------------------------------