-
Notifications
You must be signed in to change notification settings - Fork 2
/
features_extractor.py
169 lines (139 loc) · 5.34 KB
/
features_extractor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import glob
import numpy as np
from PIL import Image
from keras import Model
from keras.layers import GlobalAveragePooling2D, GlobalMaxPooling2D
from keras.applications.vgg19 import VGG19
from keras.applications.resnet50 import ResNet50
from keras.applications.inception_v3 import InceptionV3
def get_filenames(glob_pattern, recursive=True):
"""Extracts list of filenames (full paths) based on specific glob path pattern.
Parameters
----------
glob_pattern : str
Glob pattern for glob to extract filenames, eg. "directory/**/*.jpg"
recursive : bool, optional
Recursively search through subdirectories, by default True
Returns
-------
list
List of file paths
"""
all_files = glob.glob(glob_pattern, recursive=recursive)
print('Found %s files using pattern: %s' % (len(all_files), glob_pattern))
return all_files
def expand2square(pil_img, background_color):
"""Function to pad an image to square using specific bg clr.
Parameters
----------
pil_img : PIL.Image
Pillow Image object that should be processed
background_color : int
Integer value representing bg color
Returns
-------
PIL.Image
Square-padded image object
"""
width, height = pil_img.size
if width == height:
return pil_img
elif width > height:
result = Image.new(pil_img.mode, (width, width), background_color)
result.paste(pil_img, (0, (width - height) // 2))
return result
else:
result = Image.new(pil_img.mode, (height, height), background_color)
result.paste(pil_img, ((height - width) // 2, 0))
return result
def get_images(filenames, target_size=(200,200), color='RGB', bg_clr=0):
"""Reads image files from provided file paths list, applies square-padding,
resizes all images into target size and returns them as a single numpy array
Parameters
----------
filenames : list
List of image file paths
target_size : tuple, optional
Target size for all the images to be resized to, by default (200,200)
color : str, optional
Color mode strategy for PIL when loading images, by default 'RGB'
bg_clr : int, optional
Integer representing background color used for square-padding, by default 0
Returns
-------
numpy.array
Numpy array with resized images
"""
imgs_list = []
for filename in filenames:
img = Image.open(filename).convert(color)
im_square = expand2square(img, bg_clr)
im_res = im_square.resize(target_size)
imgs_list.append(np.array(im_res))
return np.asarray(imgs_list)
def create_feat_extractor(base_model, pooling_method='avg'):
"""Creates a features extractor based on the provided base network.
Parameters
----------
base_model : keras.Model
Base network for feature extraction
pooling_method : str, optional
Pooling method that will be used as the last layer, by default 'avg'
Returns
-------
keras.Model
Ready to use feature extractor
"""
assert pooling_method in ['avg', 'max']
x = base_model.output
if pooling_method=='avg':
x = GlobalAveragePooling2D()(x)
elif pooling_method=='max':
x = GlobalMaxPooling2D()(x)
model = Model(input=base_model.input, output=[x])
return model
def extract_features(imgs_np, pretrained_model="resnet50", pooling_method='avg'):
"""Takes in an array of fixed size images and returns features/embeddings
returned by one of the selected pretrained networks.
Parameters
----------
imgs_np : numpy.array
Numpy array of images
pretrained_model : str, optional
Name of the pretrained model to be used, by default "resnet50"
['resnet50', 'inception_v3', 'vgg19']
pooling_method : str, optional
Defines the last pooling layer that should be applied, by default 'avg'
['avg', 'max']
Returns
-------
numpy.array
Array of embeddings vectors. Each row represents embeddings for single input image
"""
print('Input images shape: ', imgs_np.shape)
pretrained_model = pretrained_model.lower()
assert pretrained_model in ['resnet50', 'inception_v3', 'vgg19']
assert pooling_method in ['avg', 'max']
model_args={
'weights': 'imagenet',
'include_top': False,
'input_shape': imgs_np[0].shape
}
if pretrained_model=="resnet50":
base = ResNet50(**model_args)
from keras.applications.resnet50 import preprocess_input
elif pretrained_model=="inception_v3":
base = InceptionV3(**model_args)
from keras.applications.inception_v3 import preprocess_input
elif pretrained_model=="vgg19":
base = VGG19(**model_args)
from keras.applications.vgg19 import preprocess_input
feat_extractor = create_feat_extractor(base, pooling_method=pooling_method)
imgs_np = preprocess_input(imgs_np)
embeddings_np = feat_extractor.predict(imgs_np)
print('Features shape: ', embeddings_np.shape)
return embeddings_np
# if __name__ == "__main__":
# filenames = get_filenames("101_ObjectCategories//**//*.*")
# imgs_np = get_images(filenames, target_size=(200,200), color='RGB', bg_clr=0)
# embeddings = extract_features(imgs_np, pretrained_model="resnet50")