개요
사람의 지문을 인공지능 모델로 인식해보는 실험입니다.
아래 URL을 참고하여 구현하였음을 명시합니다.
- 데이터셋 : https://www.kaggle.com/ruizgara/socofing
- 참고소스 : https://www.kaggle.com/prasanjeetkeshriii/biometrics-project
구현
import numpy as np
import matplotlib.pyplot as plt
import keras
from keras import layers
from keras.models import Model
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from imgaug import augmenters as iaa
import cv2
import os
import random
BASE_DIR = os.getcwd()
DATASET_DIR = os.path.join(BASE_DIR, 'datasets', 'SOCOFing')
ALTERED_PREFIX_DIR = os.path.join(DATASET_DIR, 'Altered', 'Altered-')
REAL_DIR = os.path.join(DATASET_DIR, 'Real')
def load_data(path, train=True, img_size=90):
print(">>> Loading directory: ", path)
gender_indexes = ['m', 'f']
lr_indexes = ['left', 'right']
finger_indexes = ['thumb', 'index', 'middle', 'ring', 'little']
images = []
labels = []
for img in os.listdir(path):
imgname, ext = os.path.splitext(img)
ID, etc = imgname.split('__')
ID = int(ID) - 1
etc = etc.lower()
if train:
gender, lr, finger, _, _ = etc.split('_')
else:
gender, lr, finger, _ = etc.split('_')
gender_index = gender_indexes.index(gender)
lr_index = lr_indexes.index(lr)
finger_index = finger_indexes.index(finger)
# ID + gender + lef, right + finger index
label = np.array([ID, gender_index, lr_index, finger_index])
img_array = cv2.imread(os.path.join(path, img), cv2.IMREAD_GRAYSCALE)
img_resize = cv2.resize(img_array, (img_size, img_size))
img_resize = img_resize.reshape(img_size, img_size, 1)
images.append(img_resize)
labels.append(label)
return images, labels
x_real, y_real = load_data(REAL_DIR, train=False)
x_easy, y_easy = load_data(ALTERED_PREFIX_DIR + 'Easy', train=True)
x_medium, y_medium = load_data(ALTERED_PREFIX_DIR + 'Medium', train=True)
x_hard, y_hard = load_data(ALTERED_PREFIX_DIR + 'Hard', train=True)
>>> Loading directory: D:\fingerprint_recognition\datasets\SOCOFing\Real
>>> Loading directory: D:\fingerprint_recognition\datasets\SOCOFing\Altered\Altered-Easy
>>> Loading directory: D:\fingerprint_recognition\datasets\SOCOFing\Altered\Altered-Medium
>>> Loading directory: D:\fingerprint_recognition\datasets\SOCOFing\Altered\Altered-Hard
def show_images(sample_data):
_, ax = plt.subplots(1, 4, figsize=(15, 10))
for i, data in enumerate(sample_data):
category = data[0]
img_array = data[1]
img_title = data[2]
ax[i].set_title(category + ': ' + str(img_title))
ax[i].imshow(img_array, cmap='gray')
sample_data = [('Real', x_real[0], y_real[0]), ('Easy', x_easy[0], y_easy[0]), ('Medium', x_medium[0], y_medium[0]), ('Hard', x_hard[0], y_hard[0])]
show_images(sample_data)
x_data = np.concatenate([x_easy, x_medium, x_hard], axis=0)
label_data = np.concatenate([y_easy, y_medium, y_hard], axis=0)
x_train, x_val, label_train, label_val = train_test_split(x_data, label_data, test_size=0.1)
print(x_data.shape, label_data.shape)
print(x_train.shape, label_train.shape)
print(x_val.shape, label_val.shape)
augs = [x_data[40000]] * 9
seq = iaa.Sequential([
# blur images with a sigma of 0 to 0.5
iaa.GaussianBlur(sigma=(0, 0.5)),
iaa.Affine(
# scale images to 90-110% of their size, individually per axis
scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},
# translate by -10 to +10 percent (per axis)
translate_percent={"x": (-0.1, 0.1), "y": (-0.1, 0.1)},
# rotate by -30 to +30 degrees
rotate=(-30, 30),
# use nearest neighbour or bilinear interpolation (fast)
order=[0, 1],
# if mode is constant, use a cval between 0 and 255
cval=255
)
], random_order=True)
augs = seq.augment_images(augs)
plt.figure(figsize=(16, 6))
plt.subplot(2, 5, 1)
plt.title('original')
plt.imshow(x_data[40000].squeeze(), cmap='gray')
for i, aug in enumerate(augs):
plt.subplot(2, 5, i+2)
plt.title('aug %02d' % int(i+1))
plt.imshow(aug.squeeze(), cmap='gray')
label_real_dict = {}
for i, y in enumerate(y_real):
key = y.astype(str)
key = ''.join(key).zfill(6)
label_real_dict[key] = i
class DataGenerator(keras.utils.Sequence):
def __init__(self, x, label, x_real, label_real_dict, batch_size=32, shuffle=True):
'Initialization'
self.x = x
self.label = label
self.x_real = x_real
self.label_real_dict = label_real_dict
self.batch_size = batch_size
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.x) / self.batch_size))
def __getitem__(self, index):
'Generate one batch of data'
# Generate indexes of the batch
x1_batch = self.x[index*self.batch_size:(index+1)*self.batch_size]
label_batch = self.label[index*self.batch_size:(index+1)*self.batch_size]
x2_batch = np.empty((self.batch_size, 90, 90, 1), dtype=np.float32)
y_batch = np.zeros((self.batch_size, 1), dtype=np.float32)
# augmentation
if self.shuffle:
seq = iaa.Sequential([
iaa.GaussianBlur(sigma=(0, 0.5)),
iaa.Affine(
scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},
translate_percent={"x": (-0.1, 0.1), "y": (-0.1, 0.1)},
rotate=(-30, 30),
order=[0, 1],
cval=255
)
], random_order=True)
x1_batch = seq.augment_images(x1_batch)
# pick matched images(label 1.0) and unmatched images(label 0.0) and put together in batch
# matched images must be all same, [subject_id(3), gender(1), left_right(1), finger(1)], e.g) 034010
for i, l in enumerate(label_batch):
match_key = l.astype(str)
match_key = ''.join(match_key).zfill(6)
if random.random() > 0.5:
# put matched image
x2_batch[i] = self.x_real[self.label_real_dict[match_key]]
y_batch[i] = 1.
else:
# put unmatched image
while True:
unmatch_key, unmatch_idx = random.choice(list(self.label_real_dict.items()))
if unmatch_key != match_key:
break
x2_batch[i] = self.x_real[unmatch_idx]
y_batch[i] = 0.
return [x1_batch.astype(np.float32) / 255., x2_batch.astype(np.float32) / 255.], y_batch
def on_epoch_end(self):
if self.shuffle == True:
self.x, self.label = shuffle(self.x, self.label)
train_gen = DataGenerator(x_train, label_train, x_real, label_real_dict, shuffle=True)
val_gen = DataGenerator(x_val, label_val, x_real, label_real_dict, shuffle=False)
x1 = layers.Input(shape=(90, 90, 1))
x2 = layers.Input(shape=(90, 90, 1))
# share weights both inputs
inputs = layers.Input(shape=(90, 90, 1))
feature = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(inputs)
feature = layers.MaxPooling2D(pool_size=2)(feature)
feature = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(feature)
feature = layers.MaxPooling2D(pool_size=2)(feature)
feature_model = Model(inputs=inputs, outputs=feature)
# 2 feature models that sharing weights
x1_net = feature_model(x1)
x2_net = feature_model(x2)
# subtract features
net = layers.Subtract()([x1_net, x2_net])
net = layers.Conv2D(32, kernel_size=3, padding='same', activation='relu')(net)
net = layers.MaxPooling2D(pool_size=2)(net)
net = layers.Flatten()(net)
net = layers.Dense(64, activation='relu')(net)
net = layers.Dense(1, activation='sigmoid')(net)
model = Model(inputs=[x1, x2], outputs=net)
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc'])
model.summary()
Model: "model_4"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_4 (InputLayer) (None, 90, 90, 1) 0
__________________________________________________________________________________________________
input_5 (InputLayer) (None, 90, 90, 1) 0
__________________________________________________________________________________________________
model_3 (Model) (None, 22, 22, 32) 9568 input_4[0][0]
input_5[0][0]
__________________________________________________________________________________________________
subtract_2 (Subtract) (None, 22, 22, 32) 0 model_3[1][0]
model_3[2][0]
__________________________________________________________________________________________________
conv2d_6 (Conv2D) (None, 22, 22, 32) 9248 subtract_2[0][0]
__________________________________________________________________________________________________
max_pooling2d_6 (MaxPooling2D) (None, 11, 11, 32) 0 conv2d_6[0][0]
__________________________________________________________________________________________________
flatten_2 (Flatten) (None, 3872) 0 max_pooling2d_6[0][0]
__________________________________________________________________________________________________
dense_3 (Dense) (None, 64) 247872 flatten_2[0][0]
__________________________________________________________________________________________________
dense_4 (Dense) (None, 1) 65 dense_3[0][0]
==================================================================================================
Total params: 266,753
Trainable params: 266,753
Non-trainable params: 0
__________________________________________________________________________________________________
history = model.fit_generator(train_gen, epochs=10, validation_data=val_gen)
Epoch 1/10
1385/1385 [==============================] - 58s 42ms/step - loss: 0.2183 - acc: 0.9088 - val_loss: 0.0762 - val_acc: 0.9902
Epoch 2/10
1385/1385 [==============================] - 33s 24ms/step - loss: 0.1220 - acc: 0.9536 - val_loss: 0.0253 - val_acc: 0.9959
Epoch 3/10
1385/1385 [==============================] - 31s 23ms/step - loss: 0.0886 - acc: 0.9676 - val_loss: 0.0087 - val_acc: 0.9949
Epoch 4/10
1385/1385 [==============================] - 31s 23ms/step - loss: 0.0753 - acc: 0.9732 - val_loss: 0.0106 - val_acc: 0.9990
Epoch 5/10
1385/1385 [==============================] - 32s 23ms/step - loss: 0.0635 - acc: 0.9774 - val_loss: 0.0072 - val_acc: 0.9984
Epoch 6/10
1385/1385 [==============================] - 31s 23ms/step - loss: 0.0539 - acc: 0.9818 - val_loss: 0.0062 - val_acc: 0.9963
Epoch 7/10
1385/1385 [==============================] - 34s 24ms/step - loss: 0.0505 - acc: 0.9823 - val_loss: 0.0014 - val_acc: 0.9969
Epoch 8/10
1385/1385 [==============================] - 32s 23ms/step - loss: 0.0479 - acc: 0.9827 - val_loss: 0.0151 - val_acc: 0.9984
Epoch 9/10
1385/1385 [==============================] - 31s 22ms/step - loss: 0.0427 - acc: 0.9852 - val_loss: 0.0374 - val_acc: 0.9978
Epoch 10/10
1385/1385 [==============================] - 31s 22ms/step - loss: 0.0425 - acc: 0.9865 - val_loss: 0.0053 - val_acc: 0.9986 loss: 0.0434 - acc: 0.9 - ETA:
# new user fingerprint input
random_idx = random.randint(0, len(x_val))
random_img = x_val[random_idx]
random_label = label_val[random_idx]
seq = iaa.Sequential([
iaa.GaussianBlur(sigma=(0, 0.5)),
iaa.Affine(
scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},
translate_percent={"x": (-0.1, 0.1), "y": (-0.1, 0.1)},
rotate=(-30, 30),
order=[0, 1],
cval=255
)
], random_order=True)
random_img = seq.augment_image(random_img).reshape((1, 90, 90, 1)).astype(np.float32) / 255.
# matched image
match_key = random_label.astype(str)
match_key = ''.join(match_key).zfill(6)
rx = x_real[label_real_dict[match_key]].reshape((1, 90, 90, 1)).astype(np.float32) / 255.
ry = y_real[label_real_dict[match_key]]
pred_rx = model.predict([random_img, rx])
# unmatched image
unmatch_key, unmatch_idx = random.choice(list(label_real_dict.items()))
ux = x_real[unmatch_idx].reshape((1, 90, 90, 1)).astype(np.float32) / 255.
uy = y_real[unmatch_idx]
pred_ux = model.predict([random_img, ux])
plt.figure(figsize=(8, 4))
plt.subplot(1, 3, 1)
plt.title('Input: %s' %random_label)
plt.imshow(random_img.squeeze(), cmap='gray')
plt.subplot(1, 3, 2)
plt.title('O: %.02f, %s' % (pred_rx, ry))
plt.imshow(rx.squeeze(), cmap='gray')
plt.subplot(1, 3, 3)
plt.title('X: %.02f, %s' % (pred_ux, uy))
plt.imshow(ux.squeeze(), cmap='gray')
'AI 인공지능 > Research' 카테고리의 다른 글
월리를 찾아라 (0) | 2022.03.01 |
---|---|
마스크 착용을 인식할 수 있을까? (0) | 2022.02.22 |
바코드, QR 코드 인식할 수 있을까? (0) | 2022.02.16 |
이미지 캡차를 인식할 수 있을까? (0) | 2022.02.14 |