109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
import os
|
|
import sys
|
|
import numpy as np
|
|
from tensorflow.keras.applications import vgg19
|
|
from tensorflow.keras.preprocessing import image
|
|
from tensorflow.keras.applications.vgg19 import preprocess_input
|
|
import faiss
|
|
import cv2
|
|
|
|
|
|
def query_yes_no(question, default="yes"):
|
|
"""Ask a yes/no question via raw_input() and return their answer.
|
|
|
|
"question" is a string that is presented to the user.
|
|
"default" is the presumed answer if the user just hits <Enter>.
|
|
It must be "yes" (the default), "no" or None (meaning
|
|
an answer is required of the user).
|
|
|
|
The "answer" return value is True for "yes" or False for "no".
|
|
"""
|
|
valid = {"yes": True, "y": True, "ye": True,
|
|
"no": False, "n": False}
|
|
if default is None:
|
|
prompt = " [y/n] "
|
|
elif default == "yes":
|
|
prompt = " [Y/n] "
|
|
elif default == "no":
|
|
prompt = " [y/N] "
|
|
else:
|
|
raise ValueError("invalid default answer: '%s'" % default)
|
|
|
|
while True:
|
|
sys.stdout.write(question + prompt)
|
|
choice = input().lower()
|
|
if default is not None and choice == '':
|
|
return valid[default]
|
|
elif choice in valid:
|
|
return valid[choice]
|
|
else:
|
|
sys.stdout.write("Please respond with 'yes' or 'no' "
|
|
"(or 'y' or 'n').\n")
|
|
|
|
|
|
model = vgg19.VGG19(weights="imagenet", include_top=False, pooling="avg")
|
|
|
|
|
|
def extract_features(img_path, model):
|
|
img = image.load_img(img_path, target_size=(224, 224))
|
|
img = image.img_to_array(img)
|
|
img = np.expand_dims(img, axis=0)
|
|
img = preprocess_input(img)
|
|
feature = model.predict(img)
|
|
return feature.flatten()
|
|
|
|
|
|
image_dir = "images/"
|
|
image_paths = [
|
|
os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith(".jpg")
|
|
]
|
|
features = []
|
|
|
|
if os.path.exists("image_index.bin"):
|
|
if query_yes_no("Load the index?", default="yes"):
|
|
index = faiss.read_index("image_index.bin")
|
|
else:
|
|
for image_path in image_paths:
|
|
img_feature = extract_features(image_path, model)
|
|
features.append(img_feature)
|
|
|
|
features = np.array(features)
|
|
|
|
d = features.shape[1]
|
|
index = faiss.IndexFlatL2(d)
|
|
index.add(features)
|
|
|
|
if query_yes_no("Save the index?", default="yes"):
|
|
faiss.write_index(index, "image_index.bin")
|
|
else:
|
|
for image_path in image_paths:
|
|
img_feature = extract_features(image_path, model)
|
|
features.append(img_feature)
|
|
|
|
features = np.array(features)
|
|
|
|
d = features.shape[1]
|
|
index = faiss.IndexFlatL2(d)
|
|
index.add(features)
|
|
|
|
if query_yes_no("Save the index?", default="yes"):
|
|
faiss.write_index(index, "image_index.bin")
|
|
|
|
|
|
def find_similar_images(query_image_path, index, k=6):
|
|
query_feature = extract_features(query_image_path, model).reshape(1, -1)
|
|
distance, indices = index.search(query_feature, k)
|
|
return distance.flatten(), indices.flatten()
|
|
|
|
|
|
query_image_path = "query_image.jpg"
|
|
similar_image_distance, similar_image_indices = find_similar_images(query_image_path, index)
|
|
|
|
# display the results
|
|
for i, idx in enumerate(similar_image_indices):
|
|
similar_image_path = image_paths[idx]
|
|
img = cv2.imread(similar_image_path)
|
|
similarity = 1 - similar_image_distance[i] / np.max(similar_image_distance)
|
|
cv2.imshow(f"Similarity: {similarity * 100:.2f}% ({i+1}/{len(similar_image_indices)})", img)
|
|
cv2.waitKey(0)
|
|
cv2.destroyAllWindows()
|