Image Recognition code of Extension Project

This commit is contained in:
Paolo Bolettieri 2023-03-14 12:09:08 +01:00
commit e76128b095
651 changed files with 109836 additions and 0 deletions

View File

@ -0,0 +1,33 @@
from pathlib import Path
import tqdm
import LFUtilities
import BEBLIDExtractor as lf
import argparse
import os
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='BEBLID bulk extraction')
parser.add_argument('src', type=str, help='text file containing a list of img paths')
parser.add_argument('dest', type=str, help='BEBLID dest file')
args = parser.parse_args()
src = args.src
dest = args.dest
with open(src, 'r') as src_file:
dataset = []
print('Extracting lf...')
for line in src_file:
try:
kp, des = lf.extract(line.strip())
dataset.append((kp, des))
except:
print("cannot process '%s'" % line)
pass
LFUtilities.save(dataset, dest)
print('lf extracted.')

19
BEBLIDExtractor.py Executable file
View File

@ -0,0 +1,19 @@
import cv2
from pathlib import Path
import tqdm
import pickle
import os
import LFUtilities
import BEBLIDParameters as params
detector = cv2.ORB_create(params.KEYPOINTS)
descriptor = cv2.xfeatures2d.BEBLID_create(0.75)
def extract(img_path):
img = LFUtilities.resize(params.IMG_SIZE, cv2.imread(img_path))
kp = detector.detect(img, None)
kp, des = descriptor.compute(img, kp)
return (kp, des)

5
BEBLIDParameters.py Executable file
View File

@ -0,0 +1,5 @@
NN_MATCH_RATIO = 0.8
MIN_GOOD_MATCHES = 20
MIN_INLIERS = 16
KEYPOINTS = 1000
IMG_SIZE = 1000

68
BEBLIDRescorer.py Executable file
View File

@ -0,0 +1,68 @@
import cv2
import numpy as np
import LFUtilities
import BEBLIDParameters
import WebAppSettings as settings
class BEBLIDRescorer:
def __init__(self):
self.lf = LFUtilities.load(settings.DATASET_BEBLID)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
#self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
self.bf = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_BRUTEFORCE_HAMMING)
def rescore_by_id(self, query_id, resultset):
query_idx = self.ids.index(query_id)
return self.rescore_by_img(self.lf[query_idx], resultset)
def rescore_by_img(self, query, resultset):
max_inliers = -1
res = []
for data_id, _ in resultset:
data_idx = self.ids.index(data_id)
try:
data_el = self.lf[data_idx]
nn_matches = self.bf.knnMatch(query[1], data_el[1], 2)
good = [m for m, n in nn_matches if m.distance < BEBLIDParameters.NN_MATCH_RATIO * n.distance]
if len(good) > BEBLIDParameters.MIN_GOOD_MATCHES:
src_pts = np.float32([query[0][m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([data_el[0][m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 1.0)
matches_mask = mask.ravel().tolist()
# print(len(good))
inliers = np.count_nonzero(matches_mask)
# print(inliers)
if (inliers >= BEBLIDParameters.MIN_INLIERS and inliers > max_inliers):
max_inliers = inliers
res.append((data_id, round(inliers/len(good), 3)))
except:
print('rescore error evaluating ' + data_id)
pass
if res:
res.sort(key=lambda result: result[1], reverse=True)
return res
def add(self, lf):
self.lf.append(lf)
def remove(self, idx):
self.descs = np.delete(self.descs, idx, axis=0)
def save(self, is_backup=False):
lf_save_file = settings.DATASET_LF
ids_file = settings.DATASET_IDS_LF
if lf_save_file != "None":
if is_backup:
lf_save_file += '.bak'
ids_file += '.bak'
LFUtilities.save(lf_save_file, self.lf)
np.savetxt(ids_file, self.ids, fmt='%s')

69
BEBLIDSearcher.py Executable file
View File

@ -0,0 +1,69 @@
import cv2
import numpy as np
import LFUtilities
import BEBLIDParameters
import WebAppSettings as settings
class BEBLIDSearcher:
def __init__(self):
self.lf = LFUtilities.load(settings.DATASET_BEBLID)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
#self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
self.bf = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_BRUTEFORCE_HAMMING)
def get_id(self, idx):
return self.ids[idx]
def search_by_id(self, query_id):
query_idx = self.ids.index(query_id)
return self.search_by_img(self.lf[query_idx])
def search_by_img(self, query):
max_inliers = -1
res = []
for img_id, features in zip(self.ids, self.lf):
try:
nn_matches = self.bf.knnMatch(query[1], features[1], 2)
good = [m for m, n in nn_matches if m.distance < BEBLIDParameters.NN_MATCH_RATIO * n.distance]
if len(good) > BEBLIDParameters.MIN_GOOD_MATCHES:
src_pts = np.float32([query[0][m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([features[0][m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 1.0)
matches_mask = mask.ravel().tolist()
# print(len(good))
inliers = np.count_nonzero(matches_mask)
# print(inliers)
if (inliers >= BEBLIDParameters.MIN_INLIERS and inliers > max_inliers):
max_inliers = inliers
class_name = img_id.split('/')[0]
res.append((class_name, round(inliers/len(good), 3)))
except:
print('search error evaluating ' + img_id)
pass
if res:
res.sort(key=lambda result: result[1], reverse=True)
return res
def add(self, lf):
self.lf.append(lf)
def remove(self, idx):
self.descs = np.delete(self.descs, idx, axis=0)
def save(self, is_backup=False):
lf_save_file = settings.DATASET_LF
ids_file = settings.DATASET_IDS_LF
if lf_save_file != "None":
if is_backup:
lf_save_file += '.bak'
ids_file += '.bak'
LFUtilities.save(lf_save_file, self.lf)
np.savetxt(ids_file, self.ids, fmt='%s')

91
BeniCulturaliOut.py Executable file
View File

@ -0,0 +1,91 @@
import h5py
import numpy as np
rmac_out_file = '/media/Data/data/beni_culturali/out/rmac_out.txt'
orb_out_file = '/media/Data/data/beni_culturali/out/orb_out.txt'
ground_truth_file = '/media/Data/data/beni_culturali/groundtruth.txt'
ground_truth = {}
rmac_out = {}
orb_out = {}
query_folder = '/media/Data/data/beni_culturali/ImmaginiComparazioni/'
img_folder = '/media/Data/data/beni_culturali/thumbs/'
with open(ground_truth_file, 'r') as f:
for line in f:
values = line.strip().split(',')
ground_truth[values[0]] = values[1:]
with open(orb_out_file, 'r') as f:
for line in f:
values = line.strip().split(',')
orb_out[values[0]] = values[1]
with open(rmac_out_file, 'r') as f:
for line in f:
values = line.strip().split(',')
rmac_out[values[0]] = values[1:]
counter = 0
found = 0
html = '<html><body>'
html += '<h1 align="center">Image Analysis Report</h1>'
html += '<h2>Numero totale query: 64 (una query è ripetuta due volte)</h2>'
html += '<h2>Immagini Recuperate (evidenziate dal bordo verde):</h2>'
html += '<ul><li><h3>Al primo risultato: <span style="color:red;">78%</span> (50 su 64)</h3></li>'
html += '<li><h3>Entro il terzo risultato: <span style="color:red;">83%</span> (53 su 64)</h3></li>'
html += '<li><h3>Entro il 60esimo risultato: <span style="color:red;">84%</span> (54 su 64)</h3></li></ul>'
html += '<h2>Elenco delle query e delle immagini recuperate:</h2>'
html += '<h3>[query, <span style="color:green;">immagine ritrovata</span>, posizione (ranking)]</h3><hr>'
counter = 1
for key, value in ground_truth.items():
orb_res = orb_out[key]
rmac_res = rmac_out[key]
#print(key)
#print(value)
# print(orb_res + '\n')
html += '<div><span style="color:red;">' + str(counter) + '. </span><b>' + key + '</b>'
tmp = ''
found_id = ''
found = False
k = 0
if orb_res in value:
found = True
found_id = orb_res
ground_truth[key] = 'already_found'
tmp += '<img style="border-width: 5px; margin-left:10px; padding:2px; border-color: #00AA00; border-style: solid;" width="128" title="' + orb_res + '" src="' + img_folder + orb_res + '">'
else:
for k in range(60):
if rmac_res[k] in value:
tmp += '<img style="border-width: 5px; margin-left:10px; padding:2px; border-color: #00AA00; border-style: solid;" width="128" title="' + rmac_res[k] + '" src="' + img_folder + rmac_res[k] + '">'
found = True
found_id = rmac_res[k]
ground_truth[key] = 'already_found'
break
if k < 2:
tmp += '<img width="128" title="' + rmac_res[k] + '" src="' + img_folder + rmac_res[k] + '">'
else:
tmp += '<span>.</span>'
if found:
html += ', <span style="color:green;">' + found_id + '</span>' + ', (ranking: <b>' + str(k + 1) + '</b>)'
else:
html += ', <span style="color:red;"> NONE</span>'
html += '</br></br><img width="128" style="padding:5px;" src="' + query_folder + key + '">'
if found:
html += tmp
else:
html += '<img style="padding:32px; margin-left:10px;" height="48" src="/media/Data/data/beni_culturali/out/notfound.png">'
html += '</br></div><hr>'
counter += 1
html += '</body></html>'
print(html)

77
ExtensionBot.py Executable file
View File

@ -0,0 +1,77 @@
from telegram import ReplyKeyboardMarkup, KeyboardButton
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, ContextTypes
import requests
import json
import telegram
import re
endpoint = "http://bilioso.isti.cnr.it:8190/bcir/searchByURL"
endpoint_rescorer = "http://bilioso.isti.cnr.it:8190/bcir/setRescorer"
def start(update, context):
chat_id = update.effective_chat.id
# custom_keyboard = [['top-left', 'top-right'], ['bottom-left', 'bottom-right']]
# reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test", reply_markup = reply_markup)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test")
photo_id = update.message.photo[-1].file_id
if (photo_id is None):
print(update.message)
query = context.bot.getFile(photo_id)
print(query.file_path)
# r = requests.get(endpoint, params=params)
# json_res = json.loads(r.text)
response = "Sorry, I can't recognize it :-("
# if json_res is not None:
# title = re.sub("\\d", "", json_res['title'])
# response = "*" + json_res['author'] + "*, _" + title + "_\n\n"
# response += json_res['artworkPage']
# update.message.reply_text(response)
text = endpoint + '?tohtml=true&url=' + query.file_path
print(text)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
params = {'url': query.file_path, 'securityID': 'FA6mMmeWZ8JjWKz5'}
print(query.file_path)
r = requests.get(endpoint, params=params)
text = json.loads(r.text)
print(text)
#context.bot.send_message(chat_id=chat_id, text='GEM', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
'''
def commands(update, context):
text = update.message.text
if text.lower() == 'commands':
chat_id = update.effective_chat.id
markup = ReplyKeyboardMarkup(keyboard=[['GEM', KeyboardButton(text='BEBLID')],["GEM_TH", "BEB_TH"]])
context.bot.sendMessage(chat_id=chat_id, text="available commands", reply_markup=markup, parse_mode=telegram.ParseMode.HTML)
if text == "BEBLID":
params = {'rescorer': 'true'}
elif text == "GEM":
params = {'rescorer': 'false'}
r = requests.get(endpoint_rescorer, params=params)
res = r.text
print(res)
update.message.reply_text(res)
'''
def call_service(query):
print(query)
files = {'img': query}
r = requests.post(endpoint, files=files)
return r.text
updater = Updater('2140606085:AAHEBR3a_O9b8E8tbJFaWopDUvT4ptAUkP4')
updater.dispatcher.add_handler(MessageHandler(Filters.photo, start))
#updater.dispatcher.add_handler(MessageHandler(Filters.text, commands))
updater.start_polling()
updater.idle()

105
ExtensionBotBase64.py Normal file
View File

@ -0,0 +1,105 @@
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import requests
import json
import telegram
import re
import uuid
import urllib
import cv2
import io
import numpy as np
import json
import LFUtilities
import base64
endpoint = "http://bilioso.isti.cnr.it:8190/bcir/searchByImgB64"
log_folder = "/media/Data/data/extension/img_recognition/bot"
def url_to_file(url):
dest_file = uuid.uuid4().hex + ".png"
dest_path = log_folder + "/" + dest_file
req = urllib.request.Request(
url,
data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
resp = urllib.request.urlopen(req)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
decoded = cv2.imdecode(image, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
#im = Image.fromarray(image)
#im.save(dest_path)
return dest_path
def start(update, context):
chat_id = update.effective_chat.id
# custom_keyboard = [['top-left', 'top-right'], ['bottom-left', 'bottom-right']]
# reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test", reply_markup = reply_markup)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test")
photo_id = update.message.photo[-1].file_id
query = context.bot.getFile(photo_id)
params = {'url': query.file_path}
img_file = url_to_file(query.file_path)
with open(img_file, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read())
# r = requests.get(endpoint, params=params)
# json_res = json.loads(r.text)
response = "Sorry, I can't recognize it :-("
# if json_res is not None:
# title = re.sub("\\d", "", json_res['title'])
# response = "*" + json_res['author'] + "*, _" + title + "_\n\n"
# response += json_res['artworkPage']
# update.message.reply_text(response)
text = endpoint + '?tohtml=true&url=' + query.file_path
print(text)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
data = {'image': encoded_string, 'securityID': 'FA6mMmeWZ8JjWKz5', 'rescorer': 'false', 'lf_impl': 'beblid'}
r = requests.post(endpoint, data=data)
text = json.loads(r.text)
print(text)
#context.bot.send_message(chat_id=chat_id, text='GEM', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
data = {'image': encoded_string, 'securityID': 'FA6mMmeWZ8JjWKz5', 'rescorer': 'true', 'lf_impl': 'beblid'}
r = requests.post(endpoint, data=data)
text = json.loads(r.text)
print(text)
# context.bot.send_message(chat_id=chat_id, text='GEM', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
def hello(bot, update):
update.message.reply_text(
'Hello {}'.format(update.message.from_user.first_name))
def call_service(query):
files = {'img': query}
r = requests.post(endpoint, files=files)
return r.text
updater = Updater('2140606085:AAHEBR3a_O9b8E8tbJFaWopDUvT4ptAUkP4')
updater.dispatcher.add_handler(MessageHandler(Filters.photo, start))
updater.dispatcher.add_handler(CommandHandler('hello', hello))
updater.start_polling()
updater.idle()

87
ExtensionBotTest.py Executable file
View File

@ -0,0 +1,87 @@
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
import requests
import json
import telegram
import re
endpoint = "http://bilioso.isti.cnr.it:8290/bcirtest/searchByURL"
def start(update, context):
chat_id = update.effective_chat.id
# custom_keyboard = [['top-left', 'top-right'], ['bottom-left', 'bottom-right']]
# reply_markup = telegram.ReplyKeyboardMarkup(custom_keyboard)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test", reply_markup = reply_markup)
# context.bot.send_message(chat_id=chat_id, text = "Custom Keyboard Test")
photo_id = update.message.photo[-1].file_id
query = context.bot.getFile(photo_id)
params = {'url': query.file_path}
print(query.file_path)
# r = requests.get(endpoint, params=params)
# json_res = json.loads(r.text)
response = "Sorry, I can't recognize it :-("
# if json_res is not None:
# title = re.sub("\\d", "", json_res['title'])
# response = "*" + json_res['author'] + "*, _" + title + "_\n\n"
# response += json_res['artworkPage']
# update.message.reply_text(response)
text = endpoint + '?tohtml=true&url=' + query.file_path
print(text)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
#-GEM-#
params = {'url': query.file_path}
r = requests.get(endpoint, params=params)
text = json.loads(r.text)
print(text)
context.bot.send_message(chat_id=chat_id, text='GEM', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
#-ORB-#
params = {'url': query.file_path, 'rescorer': 'true', 'lf_impl': 'orb'}
r = requests.get(endpoint, params=params)
text = json.loads(r.text)
print(text)
context.bot.send_message(chat_id=chat_id, text='ORB', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
#-BEBLID-#
params = {'url': query.file_path, 'rescorer': 'true', 'lf_impl': 'beblid'}
r = requests.get(endpoint, params=params)
text = json.loads(r.text)
print(text)
context.bot.send_message(chat_id=chat_id, text='BEBLID', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
# -LATCH-#
params = {'url': query.file_path, 'rescorer': 'true', 'lf_impl': 'latch'}
r = requests.get(endpoint, params=params)
text = json.loads(r.text)
print(text)
context.bot.send_message(chat_id=chat_id, text='LATCH', parse_mode=telegram.ParseMode.HTML)
context.bot.send_message(chat_id=chat_id, text=text, parse_mode=telegram.ParseMode.HTML)
def hello(bot, update):
update.message.reply_text(
'Hello {}'.format(update.message.from_user.first_name))
def call_service(query):
files = {'img': query}
r = requests.post(endpoint, files=files)
return r.text
updater = Updater('2140606085:AAHEBR3a_O9b8E8tbJFaWopDUvT4ptAUkP4')
updater.dispatcher.add_handler(MessageHandler(Filters.photo, start))
updater.dispatcher.add_handler(CommandHandler('hello', hello))
updater.start_polling()
updater.idle()

10
GEMExtractor.py Executable file
View File

@ -0,0 +1,10 @@
import numpy as np
import WebAppSettings as settings
import requests
def extract(img_path):
files = {'image': ('img', open(img_path, 'rb'))}
data = {'resize': 'true'}
r = requests.post(settings.feature_extractor, data=data, files=files)
return np.array(r.json())

61
GEMSearcher.py Executable file
View File

@ -0,0 +1,61 @@
import h5py
import numpy as np
import WebAppSettings as settings
class GEMSearcher:
def __init__(self):
#self.dataset = h5py.File(settings.dataset_file, 'r')['rmac'][...]
#np.save('/media/Data/data/beni_culturali/deploy/dataset', self.dataset)
self.descs = np.load(settings.DATASET_GEM)
#self.desc1 = np.load(settings.DATASET1)
#self.desc2 = np.load(settings.DATASET2)
#self.descs = (self.desc1 + self.desc2) / 2
#self.descs /= np.linalg.norm(self.descs, axis=1, keepdims=True)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
def get_id(self, idx):
return self.ids[idx]
def add(self, desc, id):
self.ids.append(id)
self.descs = np.vstack((self.descs, desc))
self.save()
def remove(self, id):
idx = self.ids.index(id)
del self.ids[idx]
self.descs = np.delete(self.descs, idx, axis=0)
def search_by_id(self, query_id, k=10):
query_idx = self.ids.index(query_id)
return self.search_by_img(self.descs[query_idx], k)
def search_by_img(self, query, k=10):
# print('----------query features-------')
#print(query)
dot_product = np.dot(self.descs, query[0])
idx = dot_product.argsort()[::-1][:k]
res = []
for i in idx:
res.append((self.ids[i], round(float(dot_product[i]), 3)))
return res
def save(self, is_backup=False):
descs_file = settings.DATASET
ids_file = settings.DATASET_IDS
if is_backup:
descs_file += '.bak'
ids_file += '.bak'
np.save(descs_file, self.descs)
np.savetxt(ids_file, self.ids, fmt='%s')

16
Heic2Png.py Normal file
View File

@ -0,0 +1,16 @@
from PIL import Image
import pyheif
def conv(image_path):
new_name = image_path.replace('heic', 'png')
heif_file = pyheif.read(image_path)
data = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
heif_file.mode,
heif_file.stride,
)
data.save(new_name, "PNG")
return new_name

View File

@ -0,0 +1,33 @@
from pathlib import Path
import tqdm
import LFUtilities
import LATCHExtractor as lf
import argparse
import os
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='LATCH bulk extraction')
parser.add_argument('src', type=str, help='text file containing a list of img paths')
parser.add_argument('dest', type=str, help='LATCH dest file')
args = parser.parse_args()
src = args.src
dest = args.dest
with open(src, 'r') as src_file:
dataset = []
print('Extracting lf...')
for line in src_file:
try:
kp, des = lf.extract(line.strip())
dataset.append((kp, des))
except:
print("cannot process '%s'" % line)
pass
LFUtilities.save(dataset, dest)
print('lf extracted.')

16
LATCHExtractor.py Executable file
View File

@ -0,0 +1,16 @@
import cv2
import LFUtilities
import LATCHParameters as params
detector = cv2.ORB_create(params.KEYPOINTS)
#descriptor = cv2.xfeatures2d.LATCH_create(1, True, 15)
descriptor = cv2.xfeatures2d.LATCH_create()
def extract(img_path):
img = LFUtilities.resize(params.IMG_SIZE, cv2.imread(img_path))
kp = detector.detect(img, None)
kp, des = descriptor.compute(img, kp)
return (kp, des)

5
LATCHParameters.py Executable file
View File

@ -0,0 +1,5 @@
THRESHOLD = 70
MIN_GOOD_MATCHES = 12
MIN_INLIERS = 8
KEYPOINTS = 5000
IMG_SIZE = 500

67
LATCHRescorer.py Executable file
View File

@ -0,0 +1,67 @@
import cv2
import numpy as np
import LFUtilities
import LATCHParameters
import WebAppSettings as settings
class LATCHRescorer:
def __init__(self):
self.lf = LFUtilities.load(settings.DATASET_LATCH)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
#self.orb = cv2.LATCH_create()
self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
def rescore_by_id(self, query_id, resultset):
query_idx = self.ids.index(query_id)
return self.rescore_by_img(self.lf[query_idx], resultset)
def rescore_by_img(self, query, resultset):
max_inliers = -1
res = []
for data_id, _ in resultset:
data_idx = self.ids.index(data_id)
try:
data_el = self.lf[data_idx]
matches = self.bf.match(query[1], data_el[1])
good = [m for m in matches if m.distance <= LATCHParameters.THRESHOLD]
if len(good) > LATCHParameters.MIN_GOOD_MATCHES:
src_pts = np.float32([query[0][m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([data_el[0][m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 1.0)
matches_mask = mask.ravel().tolist()
# print(len(good))
inliers = np.count_nonzero(matches_mask)
# print(inliers)
if (inliers >= LATCHParameters.MIN_INLIERS and inliers > max_inliers):
max_inliers = inliers
res.append((data_id, inliers))
except:
print('rescore error evaluating ' + data_id)
pass
if res:
res.sort(key=lambda result: result[1], reverse=True)
return res
def add(self, lf):
self.lf.append(lf)
def remove(self, idx):
self.descs = np.delete(self.descs, idx, axis=0)
def save(self, is_backup=False):
lf_save_file = settings.DATASET_LF
ids_file = settings.DATASET_IDS_LF
if lf_save_file != "None":
if is_backup:
lf_save_file += '.bak'
ids_file += '.bak'
LFUtilities.save(lf_save_file, self.lf)
np.savetxt(ids_file, self.ids, fmt='%s')

38
LFBulkExtraction.py Executable file
View File

@ -0,0 +1,38 @@
from pathlib import Path
import tqdm
import LFUtilities
import ORBExtractor as lf
import argparse
import os
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='LF bulk extraction')
parser.add_argument('src', type=str, help='img src folder path')
parser.add_argument('dest', type=str, help='lf dest folder')
args = parser.parse_args()
src = args.src
dest = args.dest
paths = Path(src).rglob('*.*')
paths_list = list(paths)
dataset = []
print('Extracting lf...')
for path in tqdm.tqdm(paths_list):
try:
kp, des = lf.extract(os.path.join(path.parent, path.name))
dataset.append((kp, des))
except:
print("cannot process '%s'" % path)
pass
LFUtilities.save(dataset, os.path.join(dest, 'dataset_lf.dat'))
with open(os.path.join(dest, 'dataset_lf.ids'), 'w') as f:
for path in paths_list:
id, _ = os.path.splitext(path.name)
f.write("%s\n" % id)
print('lf extracted.')

54
LFSearcher.py Executable file
View File

@ -0,0 +1,54 @@
import cv2
import numpy as np
import pickle as pickle
import LFUtilities
import WebAppSettings as settings
from BEBLIDSearcher import BEBLIDSearcher
import GEMExtractor as fe
import ORBExtractor as lf
import BEBLIDExtractor as beblid_lf
import LATCHExtractor as latch_lf
class Searcher:
def __init__(self):
# self.dataset = h5py.File(settings.dataset_file, 'r')['rmac'][...]
# np.save('/media/Data/data/beni_culturali/deploy/dataset', self.dataset)
self.search_engine = BEBLIDSearcher()
def get_id(self, idx):
return self.search_engine.get_id(idx)
def add(self, img_file, id):
self.save(True)
desc = fe.extract(img_file)
orb = lf.extract(img_file)
self.search_engine.add(desc, id)
self.save()
print('added ' + id)
def remove(self, id):
self.save(True)
self.search_engine.remove(id)
self.save()
print('removed ' + id)
def search_by_id(self, query_id, k=10):
kq = k
res = self.search_engine.search_by_id(query_id)
return res
def search_by_img(self, query_img, k=10):
kq = k
query_desc = beblid_lf.extract(query_img)
res = self.search_engine.search_by_img(query_desc)
return res
def save(self, is_backup=False):
self.search_engine.save(is_backup)
#self.rescorer.save(is_backup)

59
LFUtilities.py Executable file
View File

@ -0,0 +1,59 @@
import cv2
import numpy as np
import pickle as pickle
import os
def resize(max_side, img):
if img.shape[1] > img.shape[0]:
r = max_side / img.shape[1]
dim = (max_side, int(img.shape[0] * r))
else:
r = max_side / img.shape[0]
dim = (int(img.shape[1] * r), max_side)
# perform the actual resizing of the image and show it
resized = cv2.resize(img, dim, interpolation=cv2.INTER_AREA)
return resized
def pickle_keypoints(keypoints, descriptors):
i = 0
temp_array = []
for point in keypoints:
temp = (point.pt, point.size, point.angle, point.response, point.octave,
point.class_id, descriptors[i])
i += 1
temp_array.append(temp)
return temp_array
def unpickle_keypoints(array):
keypoints = []
descriptors = []
for point in array:
temp_feature = cv2.KeyPoint(x=point[0][0],y=point[0][1], size=point[1], angle=point[2], response=point[3], octave=point[4], class_id=point[5])
temp_descriptor = point[6]
keypoints.append(temp_feature)
descriptors.append(temp_descriptor)
return keypoints, np.array(descriptors)
def load(lf_path):
print('loading LF dataset ' + lf_path)
ser_dataset = pickle.load(open(lf_path, "rb"))
lf_dataset = []
for item in ser_dataset:
kp, desc = unpickle_keypoints(item)
lf_dataset.append((kp, desc))
return lf_dataset
def save(lf_data, lf_path):
data = []
for lf in lf_data:
data.append(pickle_keypoints(lf[0], lf[1]))
pickle.dump(data, open(lf_path, 'wb'))

View File

@ -0,0 +1,33 @@
from pathlib import Path
import tqdm
import LFUtilities
import ORBExtractor as lf
import argparse
import os
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='ORB bulk extraction')
parser.add_argument('src', type=str, help='text file containing a list of img paths')
parser.add_argument('dest', type=str, help='ORB dest file')
args = parser.parse_args()
src = args.src
dest = args.dest
with open(src, 'r') as src_file:
dataset = []
print('Extracting lf...')
for line in src_file:
try:
kp, des = lf.extract(line.strip())
dataset.append((kp, des))
except:
print("cannot process '%s'" % line)
pass
LFUtilities.save(dataset, dest)
print('lf extracted.')

17
ORBExtractor.py Executable file
View File

@ -0,0 +1,17 @@
import cv2
from pathlib import Path
import tqdm
import pickle
import os
import LFUtilities
import ORBParameters as params
orb = cv2.ORB.create(params.KEYPOINTS)
def extract(img_path):
img = LFUtilities.resize(params.IMG_SIZE, cv2.imread(img_path))
kp, des = orb.detectAndCompute(img, mask=None)
return (kp, des)

5
ORBParameters.py Executable file
View File

@ -0,0 +1,5 @@
THRESHOLD = 35
MIN_GOOD_MATCHES = 12
MIN_INLIERS = 8
KEYPOINTS = 5000
IMG_SIZE = 500

67
ORBRescorer.py Executable file
View File

@ -0,0 +1,67 @@
import cv2
import numpy as np
import LFUtilities
import ORBParameters
import WebAppSettings as settings
class ORBRescorer:
def __init__(self):
self.lf = LFUtilities.load(settings.DATASET_LF)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
#self.orb = cv2.ORB_create()
self.bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
def rescore_by_id(self, query_id, resultset):
query_idx = self.ids.index(query_id)
return self.rescore_by_img(self.lf[query_idx], resultset)
def rescore_by_img(self, query, resultset):
max_inliers = -1
res = []
for data_id, _ in resultset:
data_idx = self.ids.index(data_id)
try:
data_el = self.lf[data_idx]
matches = self.bf.match(query[1], data_el[1])
good = [m for m in matches if m.distance <= ORBParameters.THRESHOLD]
if len(good) > ORBParameters.MIN_GOOD_MATCHES:
src_pts = np.float32([query[0][m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
dst_pts = np.float32([data_el[0][m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 1.0)
matches_mask = mask.ravel().tolist()
# print(len(good))
inliers = np.count_nonzero(matches_mask)
# print(inliers)
if (inliers >= ORBParameters.MIN_INLIERS and inliers > max_inliers):
max_inliers = inliers
res.append((data_id, inliers))
except:
print('rescore error evaluating ' + data_id)
pass
if res:
res.sort(key=lambda result: result[1], reverse=True)
return res
def add(self, lf):
self.lf.append(lf)
def remove(self, idx):
self.descs = np.delete(self.descs, idx, axis=0)
def save(self, is_backup=False):
lf_save_file = settings.DATASET_LF
ids_file = settings.DATASET_IDS_LF
if lf_save_file != "None":
if is_backup:
lf_save_file += '.bak'
ids_file += '.bak'
LFUtilities.save(lf_save_file, self.lf)
np.savetxt(ids_file, self.ids, fmt='%s')

97
Searcher.py Executable file
View File

@ -0,0 +1,97 @@
import json
import cv2
import numpy as np
import pickle as pickle
import LFUtilities
import WebAppSettings as settings
#from ORBRescorer import ORBRescorer
from BEBLIDRescorer import BEBLIDRescorer
#from LATCHRescorer import LATCHRescorer
from GEMSearcher import GEMSearcher
import GEMExtractor as fe
import ORBExtractor as orb_lf
import BEBLIDExtractor as beblid_lf
import LATCHExtractor as latch_lf
class Searcher:
GEM_THRESHOLD = 0.3
def __init__(self):
# self.dataset = h5py.File(settings.dataset_file, 'r')['rmac'][...]
# np.save('/media/Data/data/beni_culturali/deploy/dataset', self.dataset)
self.ids = np.loadtxt(settings.DATASET_IDS, dtype=str).tolist()
self.search_engine = GEMSearcher()
# self.orb_rescorer = ORBRescorer()
self.beblid_rescorer = BEBLIDRescorer()
#self.latch_rescorer = LATCHRescorer()
def get_id(self, idx):
return self.search_engine.get_id(idx)
def add(self, img_file, id):
# self.save(True)
#desc = fe.extract(img_file)
#orb = lf.extract(img_file)
#self.search_engine.add(desc, id)
#self.rescorer.add(orb)
#self.save()
print('added ' + id)
def remove(self, id):
#self.save(True)
#self.search_engine.remove(id)
#self.rescorer.remove(idx)
#self.save()
print('removed ' + id)
def search_by_id(self, query_id, k=10, rescorer=False, lf_impl='BEBLID'):
kq = k
if rescorer:
kq = settings.k_reorder
res = self.search_engine.search_by_id(query_id, kq)
if rescorer:
if lf_impl == 'BEBLID':
res_lf = self.beblid_rescorer.rescore_by_id(query_id, res)
#res = res_lf if res_lf else res[:k]
res = res_lf
return res
def search_by_img(self, query_img, k=10, rescorer=False, lf_impl='BEBLID'):
if rescorer:
print(lf_impl)
kq = k
if rescorer:
kq = settings.k_reorder
query_desc = fe.extract(query_img)
res = self.search_engine.search_by_img(query_desc, kq)
print(res[0])
if rescorer:
if lf_impl == 'BEBLID':
query_lf = beblid_lf.extract(query_img)
res_lf = self.beblid_rescorer.rescore_by_img(query_lf, res)
if res_lf:
label = res_lf[0][0].split('/')[0]
res = [label, res_lf[0][1]]
else:
res = None
else:
if res[0][1] > self.GEM_THRESHOLD:
label = res[0][0].split('/')[0]
res = [label, res[0][1]]
else:
res = None
return res
def save(self, is_backup=False):
self.search_engine.save(is_backup)
#self.rescorer.save(is_backup)

21
TestClient.py Executable file
View File

@ -0,0 +1,21 @@
import requests
import base64
BASE_URL = 'http://bilioso.isti.cnr.it:8190/bcir/'
image_path = '/home/paolo/Dropbox/extension_logs/352998a9ee4f490b9a56f84d05983168.png_90.jpg'
#searchByImg
files = {'image': ('query', open(image_path, 'rb'))}
post_data = {'securityID': 'A6mMmeWZ8JjWKz5', 'orientation': "1"}
r = requests.post(BASE_URL + 'searchByImg', files=files, data=post_data)
print(r.json())
with open(image_path, "rb") as image_file:
base64_img = base64.b64encode(image_file.read())
base64_data = {'image': base64_img, 'securityID': 'FA6mMmeWZ8JjWKz5', 'orientation': "8"}
r = requests.post(BASE_URL + 'searchByImgB64', data=base64_data)
print(r.json())

56
TestFeatures.py Executable file
View File

@ -0,0 +1,56 @@
import json
from Searcher import Searcher
import WebAppSettings as settings
def search_by_img(img_file, lf_impl, rescorer=False):
if lf_impl is not "GEM":
rescorer = True
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
return results
if __name__ == '__main__':
#testset= "/media/Data/data/extension/test_set/listfiles.txt"
testset= "/media/Data/data/extension/merged_train_test/test/listfiles.txt"
settings.load_setting('conf_train_test.json')
global searcher
searcher = Searcher()
# Using readlines()
file1 = open(testset, 'r')
Lines = file1.readlines()
count = 0
# Strips the newline character
#features = ["GEM", "ORB", "LATCH", "BEBLID"]
features = ["GEM", "BEBLID"]
counters = {"GEM":0, "ORB":0, "LATCH":0, "BEBLID":0}
count_orb = 0
count_latch = 0
count_beblid = 0
for line in Lines:
classname_q = line.replace("/media/Data/data/extension/merged_train_test/test/", "").split("/")[0]
print("Query {}: {}".format(count, line.strip()))
for f in features:
results = search_by_img(line.strip(), f)
if results is None or results[0][1] < 0.3:
results = ["unknown", 1.0]
classname = results[0][0].split("/")[0]
if (classname_q == classname):
counters[f] = counters[f] + 1
print(results[0][0] + '", "conf":' + str(results[0][1]))
count += 1
for f in features:
print(f'{f}: {counters}/{count}')

72
TestFeaturesVairo.py Executable file
View File

@ -0,0 +1,72 @@
import json
from Searcher import Searcher
import WebAppSettings as settings
def search_by_img(img_file, lf_impl, rescorer=False):
if lf_impl is not "GEM":
rescorer = True
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
return results
if __name__ == '__main__':
testset= "/media/Data/data/extension/test_set/listfiles.txt"
settings.load_setting('conf_test.json')
global searcher
searcher = Searcher()
# Using readlines()
file1 = open(testset, 'r')
Lines = file1.readlines()
count = 0
# Strips the newline character
features = ["GEM", "ORB", "LATCH", "BEBLID"]
counters = {"GEM":0, "ORB":0, "LATCH":0, "BEBLID":0}
# features = ["GEM", "BEBLID"]
# counters = {"GEM":0, "BEBLID":0}
out_csv = ''
for line in Lines:
classname_q = line.replace("/media/Data/data/extension/test_set/", "").split("/")[0]
filename_q = line.replace("/media/Data/data/extension/test_set/", "").split("/")[1]
print("Query {}: {}".format(count, line.strip()))
out_line = ''
out_line = out_line + f'{classname_q},{filename_q};'
for f in features:
results = search_by_img(line.strip(), f)
if results is None:
results = ["unknown", 1.0]
classname = results[0][0].split("/")[0]
filename = results[0][0].split("/")[1]
if (classname_q == classname):
counters[f] = counters[f] + 1
print(results[0][0] + '", "conf":' + str(results[0][1]))
out_line = out_line + f'{classname},{filename},{results[0][1]};'
print(out_line)
out_csv = out_line + '\n'
count += 1
print(f'{f}: {counters}/{count}')
print(f'GEM accuracy: {counters["GEM"]}/{count}')
print(f'BEBLID accuracy: {counters["BEBLID"]}/{count}')
#open text file
out_file = open("out_csv.txt", "w")
#write string to file
out_file.write(out_csv)
#close file
out_file.close()

47
ThumbnailCreator.py Executable file
View File

@ -0,0 +1,47 @@
import glob, os
from PIL import Image
import argparse
from pathlib import Path
import tqdm
w, h = 200,120
PATTERN = 'keyframes'
REPLACE = 'thumbs'
def resize_img(src, dest):
paths = Path(src).rglob('*.*')
paths_list = list(paths)
for path in tqdm.tqdm(paths_list):
outfile = str(path).replace(PATTERN, REPLACE)
folders = os.path.dirname(outfile)
#print(outfile)
if not os.path.isdir(folders):
#print(folders)
os.makedirs(folders)
try:
im = Image.open(path)
if im.mode in ("RGBA", "P"):
im = im.convert("RGB")
im.thumbnail((w, h), Image.ANTIALIAS)
im.save(outfile, "JPEG")
except IOError:
print("cannot create thumbnail for '%s'" % path)
pass
if __name__ == '__main__':
# parser = argparse.ArgumentParser(description='Image resizing')
# parser.add_argument('src', type=str, help='images source folder path')
# parser.add_argument('dest', type=str, help='images dest folder path')
#args = parser.parse_args()
#resize_img(args.src, args.dest)
resize_img('/media/Data/data/rai/img/keyframes', '/media/Data/data/rai/img/keyframes')
#resize_img('/media/Data/data/test/gem/img/originals/ImmaginiComparazioni', '/media/Data/data/test/gem/img/ImmaginiComparazioni_resized')

276
WebApp.py Executable file
View File

@ -0,0 +1,276 @@
from re import split
from flask import Flask, request, redirect, url_for, flash, render_template, send_from_directory, abort, Response, session
from random import randint
import cv2
import io
import numpy as np
import json
import urllib
import LFUtilities
from Searcher import Searcher
from GEMSearcher import GEMSearcher
import WebAppSettings as settings
import uuid
import requests
import os, os.path
from PIL import Image
import tornado.wsgi
import tornado.httpserver
import argparse
import base64
from Heic2Png import conv
from datetime import datetime
app = Flask(__name__)
app.secret_key = "27eduCBA09"
security_id = 'FA6mMmeWZ8JjWKz5'
rescorer = False
lf_impl = 'BEBLID'
HEIC_MAGIC_NUMBER = "AAAAGGZ0eXA="
@app.route('/bcir/')
def api_root():
print('index_with_randoms.html')
random_ids = []
for i in range(0, 15):
random_ids.append(searcher.get_id(randint(0, 600)))
return render_template('index_with_randoms.html', random_ids=random_ids)
def url_to_file(url):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
req = urllib.request.Request(
url,
data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
resp = urllib.request.urlopen(req)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
decoded = cv2.imdecode(image, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
#im = Image.fromarray(image)
#im.save(dest_path)
return dest_path
def post_to_file(image):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
in_memory_file = io.BytesIO()
image.save(in_memory_file)
data = np.fromstring(in_memory_file.getvalue(), dtype=np.uint8)
decoded = cv2.imdecode(data, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
return dest_path
def set_rescorer(rescore_param):
global rescorer
if rescore_param is not None:
if rescore_param == 'true':
rescorer = True
elif rescore_param == 'false':
rescorer = False
def base64_to_file(image_base64):
ext = ".jpg"
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
ext = ".heic"
dest_file = uuid.uuid4().hex + ext
dest_path = settings.logs + "/" + dest_file
with open(dest_path, "wb") as image_file:
byte_content = base64.b64decode(image_base64)
image_file.write(byte_content)
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
dest_path = conv(dest_path)
return dest_path
def get_res(results, query_url=None, img_base64=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = '{"classname":"Unknown"}'
else:
results = '{"classname":"' + results[0] + '", "conf":' + str(results[1]) + '}'
print(results)
json_res = json.dumps(results)
print(json_res)
if img_base64 != None:
html_txt = str(datetime.now())
html_txt += '</br>'
html_txt += results
html_txt += '</br>'
html_txt += '<img height="256" src="data:image/png;base64,' + img_base64 + '">'
html_txt += '</br></br></br>'
html_txt += '\n'
with open("/home/paolo/Dropbox/extension_logs/report.html", "a+") as myfile:
myfile.write(html_txt)
return json_res
def get_resDict(results, query_url=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = {"classname":"Unknown"}
else:
results = {"classname":"' + results[0][0] + '", "conf":' + str(results[0][1]) + '}
print(results)
json_res = json.dumps(results)
print(json_res)
return json_res
@app.route('/bcir/searchById')
def search_by_id():
id = request.args.get('id')
set_rescorer(request.args.get("rescorer"))
results = searcher.search_by_id(id, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = id + ".jpg"
return get_res(results, query_url)
@app.route('/bcir/searchByImg', methods=['POST'])
def search_by_img():
if 'image' not in request.files:
return Response(json.dumps("Error, unable to get the image"), status=401, mimetype='application/json')
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
file = request.files['image']
img_file = post_to_file(file)
set_rescorer(request.form.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url)
@app.route('/bcir/searchByImgB64', methods=['POST'])
def search_by_img_base64():
print("query by base64 received")
#print(request)
#print(request.form)
sec_id = request.form.get("securityID")
#to fix a problem with photo orientation
try:
orientation = request.form.get("orientation")
orientation = int(orientation)
except:
orientation = 1
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
image = request.form.get('image')
if image:
img_file = base64_to_file(image)
else:
flash('No img sent')
return redirect(request.url)
set_rescorer(request.form.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url, img_base64=image)
@app.route('/bcir/searchByURL')
def search_by_url():
sec_id = request.args.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
url = request.args.get('url')
img_file = url_to_file(url)
set_rescorer(request.args.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = url
return get_res(results, query_url)
@app.route('/bcir/setRescorer')
def set_bot_rescorer():
set_rescorer(request.args.get("rescorer"))
return "Rescorer set to " + str(rescorer)
@app.route('/bcir/<path:filename>')
def download_file(filename):
print(filename)
values = filename.split('/')
folder = values[0]
name = values[1]
print(folder)
print(name)
return send_from_directory(settings.working_folder + '/' + folder, name, as_attachment=False)
"""
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = BeniCulturaliSearcher()
#app.run(host='0.0.0.0', port=8090, ssl_context='adhoc')
app.run(host='0.0.0.0', port=settings.port)
# app.run(host='0.0.0.0', port=settings.port)
"""
def start_tornado(app, port=8190):
http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
http_server.listen(port)
app.logger.info("Tornado server starting on port {}".format(port))
tornado.ioloop.IOLoop.instance().start()
def start_from_terminal(app):
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = Searcher()
#if args.debug:
# app.run(debug=True, host='0.0.0.0', port=settings.port)
# else:
#start_tornado(app, settings.port)
app.run(debug=True, host='0.0.0.0', port=settings.port)
if __name__ == '__main__':
start_from_terminal(app)

283
WebAppFaultTolerant.py Normal file
View File

@ -0,0 +1,283 @@
from re import split
from flask import Flask, request, redirect, url_for, flash, render_template, send_from_directory, abort, Response, session
from random import randint
import cv2
import io
import numpy as np
import json
import urllib
import LFUtilities
from Searcher import Searcher
from GEMSearcher import GEMSearcher
import WebAppSettings as settings
import uuid
import requests
import os, os.path
from PIL import Image
import tornado.wsgi
import tornado.httpserver
import argparse
import base64
from Heic2Png import conv
from datetime import datetime
app = Flask(__name__)
app.secret_key = "27eduCBA09"
security_id = 'FA6mMmeWZ8JjWKz5'
rescorer = False
lf_impl = 'BEBLID'
HEIC_MAGIC_NUMBER = "AAAAGGZ0eXA="
@app.route('/bcir/')
def api_root():
print('index_with_randoms.html')
random_ids = []
for i in range(0, 15):
random_ids.append(searcher.get_id(randint(0, 600)))
return render_template('index_with_randoms.html', random_ids=random_ids)
def url_to_file(url):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
req = urllib.request.Request(
url,
data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
resp = urllib.request.urlopen(req)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
decoded = cv2.imdecode(image, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
#im = Image.fromarray(image)
#im.save(dest_path)
return dest_path
def post_to_file(image):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
in_memory_file = io.BytesIO()
image.save(in_memory_file)
data = np.fromstring(in_memory_file.getvalue(), dtype=np.uint8)
decoded = cv2.imdecode(data, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
return dest_path
def set_rescorer(rescore_param):
global rescorer
if rescore_param is not None:
if rescore_param == 'true':
rescorer = True
elif rescore_param == 'false':
rescorer = False
def base64_to_file(image_base64):
ext = ".jpg"
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
ext = ".heic"
dest_file = uuid.uuid4().hex + ext
dest_path = settings.logs + "/" + dest_file
with open(dest_path, "wb") as image_file:
byte_content = base64.b64decode(image_base64)
image_file.write(byte_content)
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
dest_path = conv(dest_path)
return dest_path
def get_res(results, query_url=None, img_base64=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = '{"classname":"Unknown"}'
else:
results = '{"classname":"' + results[0] + '", "conf":' + str(results[1]) + '}'
print(results)
json_res = json.dumps(results)
print(json_res)
if img_base64 != None:
html_txt = str(datetime.now())
html_txt += '</br>'
html_txt += results
html_txt += '</br>'
html_txt += '<img height="256" src="data:image/png;base64,' + img_base64 + '">'
html_txt += '</br></br></br>'
html_txt += '\n'
with open("/home/paolo/Dropbox/extension_logs/report.html", "a+") as myfile:
myfile.write(html_txt)
return json_res
def get_resDict(results, query_url=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = {"classname":"Unknown"}
else:
results = {"classname":"' + results[0][0] + '", "conf":' + str(results[0][1]) + '}
print(results)
json_res = json.dumps(results)
print(json_res)
return json_res
def rotate_img(img_path, degree):
im1 = Image.open(img_path)
im2 = im1.rotate(degree)
dest = img_path + str(degree) + ".jpg"
im2.save(dest)
return dest
@app.route('/bcir/searchById')
def search_by_id():
id = request.args.get('id')
set_rescorer(request.args.get("rescorer"))
results = searcher.search_by_id(id, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = id + ".jpg"
return get_res(results, query_url)
@app.route('/bcir/searchByImg', methods=['POST'])
def search_by_img():
if 'image' not in request.files:
return Response(json.dumps("Error, unable to get the image"), status=401, mimetype='application/json')
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
file = request.files['image']
img_file = post_to_file(file)
set_rescorer(request.form.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url)
@app.route('/bcir/searchByImgB64', methods=['POST'])
def search_by_img_base64():
print("query by base64 received")
#print(request)
#print(request.form)
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
image = request.form.get('image')
if image:
img_file = base64_to_file(image)
else:
flash('No img sent')
return redirect(request.url)
set_rescorer(request.form.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
if results is None:
print('No results. trying ' + str(90) + ' degrees')
degree_90 = rotate_img(img_file, 90)
results = searcher.search_by_img(degree_90, settings.k, rescorer, lf_impl)
if results is None:
print('No results. trying ' + str(270) + ' degrees')
degree_270 = rotate_img(img_file, 270)
results = searcher.search_by_img(degree_270, settings.k, rescorer, lf_impl)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url, img_base64=image)
@app.route('/bcir/searchByURL')
def search_by_url():
sec_id = request.args.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
url = request.args.get('url')
img_file = url_to_file(url)
set_rescorer(request.args.get("rescorer"))
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = url
return get_res(results, query_url)
@app.route('/bcir/setRescorer')
def set_bot_rescorer():
set_rescorer(request.args.get("rescorer"))
return "Rescorer set to " + str(rescorer)
@app.route('/bcir/<path:filename>')
def download_file(filename):
print(filename)
values = filename.split('/')
folder = values[0]
name = values[1]
print(folder)
print(name)
return send_from_directory(settings.working_folder + '/' + folder, name, as_attachment=False)
"""
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = BeniCulturaliSearcher()
#app.run(host='0.0.0.0', port=8090, ssl_context='adhoc')
app.run(host='0.0.0.0', port=settings.port)
# app.run(host='0.0.0.0', port=settings.port)
"""
def start_tornado(app, port=8190):
http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
http_server.listen(port)
app.logger.info("Tornado server starting on port {}".format(port))
tornado.ioloop.IOLoop.instance().start()
def start_from_terminal(app):
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = Searcher()
#if args.debug:
# app.run(debug=True, host='0.0.0.0', port=settings.port)
# else:
#start_tornado(app, settings.port)
app.run(debug=False, host='0.0.0.0', port=settings.port)
if __name__ == '__main__':
start_from_terminal(app)

378
WebAppFaultTolerant2.py Normal file
View File

@ -0,0 +1,378 @@
from re import split
from flask import Flask, request, redirect, url_for, flash, render_template, send_from_directory, abort, Response, session
from random import randint
import cv2
import io
import numpy as np
import json
import urllib
import LFUtilities
from Searcher import Searcher
from GEMSearcher import GEMSearcher
import WebAppSettings as settings
import uuid
import requests
import os, os.path
from PIL import Image
import tornado.wsgi
import tornado.httpserver
import argparse
import base64
from Heic2Png import conv
from datetime import datetime
app = Flask(__name__)
app.secret_key = "27eduCBA09"
security_id = 'FA6mMmeWZ8JjWKz5'
rescorer = False
lf_impl = 'BEBLID'
HEIC_MAGIC_NUMBER = "AAAAGGZ0eXA="
@app.route('/bcir/')
def api_root():
print('index_with_randoms.html')
random_ids = []
for i in range(0, 15):
random_ids.append(searcher.get_id(randint(0, 600)))
return render_template('index_with_randoms.html', random_ids=random_ids)
def url_to_file(url):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
req = urllib.request.Request(
url,
data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
resp = urllib.request.urlopen(req)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
decoded = cv2.imdecode(image, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
#im = Image.fromarray(image)
#im.save(dest_path)
return dest_path
def post_to_file(image):
dest_file = uuid.uuid4().hex + ".png"
session["query"] = settings.log_folder + '/' + dest_file
dest_path = settings.logs + "/" + dest_file
in_memory_file = io.BytesIO()
image.save(in_memory_file)
data = np.fromstring(in_memory_file.getvalue(), dtype=np.uint8)
decoded = cv2.imdecode(data, cv2.IMREAD_COLOR)
resized = LFUtilities.resize(500, decoded)
cv2.imwrite(dest_path, resized)
return dest_path
def set_rescorer(rescore_param):
global rescorer
if rescore_param is not None:
if rescore_param == 'true':
rescorer = True
elif rescore_param == 'false':
rescorer = False
def base64_to_file(image_base64):
ext = ".jpg"
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
ext = ".heic"
dest_file = uuid.uuid4().hex + ext
dest_path = settings.logs + "/" + dest_file
with open(dest_path, "wb") as image_file:
byte_content = base64.b64decode(image_base64)
image_file.write(byte_content)
if (image_base64.startswith(HEIC_MAGIC_NUMBER)):
dest_path = conv(dest_path)
return dest_path
def get_res(results, query_url=None, img_base64=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = '{"classname":"Unknown"}'
else:
results = '{"classname":"' + results[0] + '", "conf":' + str(results[1]) + '}'
print(results)
json_res = json.dumps(results)
print(json_res)
if img_base64 != None:
html_txt = str(datetime.now())
html_txt += '</br>'
html_txt += results
html_txt += '</br>'
html_txt += '<img height="256" src="data:image/png;base64,' + img_base64 + '">'
html_txt += '</br></br></br>'
html_txt += '\n'
with open("/home/paolo/Dropbox/extension_logs/report.html", "a+") as myfile:
myfile.write(html_txt)
return json_res
def get_resDict(results, query_url=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = {"classname":"Unknown"}
else:
results = {"classname":"' + results[0][0] + '", "conf":' + str(results[0][1]) + '}
print(results)
json_res = json.dumps(results)
print(json_res)
return json_res
def rotate_img(img_path, degree):
im1 = Image.open(img_path)
im2 = im1.rotate(degree, expand=True)
dest = img_path + "_" + str(degree) + ".jpg"
im2.save(dest)
return dest
@app.route('/bcir/searchById')
def search_by_id():
id = request.args.get('id')
set_rescorer(request.args.get("rescorer"))
results = searcher.search_by_id(id, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = id + ".jpg"
return get_res(results, query_url)
@app.route('/bcir/searchByImg', methods=['POST'])
def search_by_img():
if 'image' not in request.files:
return Response(json.dumps("Error, unable to get the image"), status=401, mimetype='application/json')
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
file = request.files['image']
img_file = post_to_file(file)
set_rescorer(request.form.get("rescorer"))
results_0 = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
degree_90 = rotate_img(img_file, 90)
results_90 = searcher.search_by_img(degree_90, settings.k, rescorer, lf_impl)
degree_270 = rotate_img(img_file, 270)
results_270 = searcher.search_by_img(degree_270, settings.k, rescorer, lf_impl)
max_res = results_0
if results_90 is not None:
if max_res is None:
max_res = results_90
elif results_90[1] > max_res[1]:
max_res = results_90
if results_270 is not None:
if max_res is None:
max_res = results_270
elif results_270[1] > max_res[1]:
max_res = results_270
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(max_res, query_url)
@app.route('/bcir/searchByImgB64', methods=['POST'])
def search_by_img_base64():
print("query by base64 received")
#print(request)
#print(request.form)
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
#to fix a problem with photo orientation
try:
orientation = request.form.get("orientation")
orientation = int(orientation)
except:
orientation = 1
image = request.form.get('image')
if image:
img_file = base64_to_file(image)
else:
flash('No img sent')
return redirect(request.url)
oriented_img = img_file
if orientation == 3:
oriented_img = rotate_img(img_file, 180)
elif orientation == 6:
oriented_img = rotate_img(img_file, 270)
elif orientation == 8:
oriented_img = rotate_img(img_file, 90)
results = searcher.search_by_img(oriented_img, settings.k, rescorer, lf_impl)
set_rescorer(request.form.get("rescorer"))
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url, img_base64=image)
'''
results_0 = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
degree_90 = rotate_img(img_file, 90)
results_90 = searcher.search_by_img(degree_90, settings.k, rescorer, lf_impl)
degree_270 = rotate_img(img_file, 270)
results_270 = searcher.search_by_img(degree_270, settings.k, rescorer, lf_impl)
max_res = results_0
if results_90 is not None:
if max_res is None:
max_res = results_90
elif results_90[1] > max_res[1]:
max_res = results_90
if results_270 is not None:
if max_res is None:
max_res = results_270
elif results_270[1] > max_res[1]:
max_res = results_270
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(max_res, query_url, img_base64=image)
'''
@app.route('/bcir/searchByURL')
def search_by_url():
sec_id = request.args.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
url = request.args.get('url')
img_file = url_to_file(url)
set_rescorer(request.args.get("rescorer"))
#to fix a problem with photo orientation
try:
orientation = request.args.get("orientation")
orientation = int(orientation)
except:
orientation = 1
oriented_img = img_file
if orientation == 3:
oriented_img = rotate_img(img_file, 180)
elif orientation == 6:
oriented_img = rotate_img(img_file, 90)
elif orientation == 8:
oriented_img = rotate_img(img_file, 270)
results = searcher.search_by_img(oriented_img, settings.k, rescorer, lf_impl)
set_rescorer(request.form.get("rescorer"))
query_url = None
if request.args.get("tohtml") is not None:
query_url = url
return get_res(results, query_url)
results_0 = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
degree_90 = rotate_img(img_file, 90)
results_90 = searcher.search_by_img(degree_90, settings.k, rescorer, lf_impl)
degree_270 = rotate_img(img_file, 270)
results_270 = searcher.search_by_img(degree_270, settings.k, rescorer, lf_impl)
max_res = results_0
if results_90 is not None:
if max_res is None:
max_res = results_90
elif results_90[1] > max_res[1]:
max_res = results_90
if results_270 is not None:
if max_res is None:
max_res = results_270
elif results_270[1] > max_res[1]:
max_res = results_270
query_url = None
if request.args.get("tohtml") is not None:
query_url = url
return get_res(max_res, query_url)
@app.route('/bcir/setRescorer')
def set_bot_rescorer():
set_rescorer(request.args.get("rescorer"))
return "Rescorer set to " + str(rescorer)
@app.route('/bcir/<path:filename>')
def download_file(filename):
print(filename)
values = filename.split('/')
folder = values[0]
name = values[1]
print(folder)
print(name)
return send_from_directory(settings.working_folder + '/' + folder, name, as_attachment=False)
"""
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = BeniCulturaliSearcher()
#app.run(host='0.0.0.0', port=8090, ssl_context='adhoc')
app.run(host='0.0.0.0', port=settings.port)
# app.run(host='0.0.0.0', port=settings.port)
"""
def start_tornado(app, port=8190):
http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
http_server.listen(port)
app.logger.info("Tornado server starting on port {}".format(port))
tornado.ioloop.IOLoop.instance().start()
def start_from_terminal(app):
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = Searcher()
#if args.debug:
# app.run(debug=True, host='0.0.0.0', port=settings.port)
# else:
#start_tornado(app, settings.port)
app.run(debug=False, host='0.0.0.0', port=settings.port)
if __name__ == '__main__':
start_from_terminal(app)

41
WebAppSettings.py Executable file
View File

@ -0,0 +1,41 @@
import json
import os
def load_setting(conf_file):
global port, feature_extractor, k, k_reorder, img_folder, log_folder, logs, working_folder, data_folder, DATASET_GEM, DATASET1, DATASET2, DATASET_LF, DATASET_IDS, DATASET_BEBLID, DATASET_LATCH
with open(conf_file) as settings_file:
settings = json.load(settings_file)
port = settings['port']
feature_extractor = settings['fe_service']
k = settings['k']
k_reorder = settings['k_reorder']
working_folder = settings['working_folder']
data_folder = os.path.join(working_folder, settings['data_folder'])
if not os.path.isdir(data_folder):
os.mkdir(data_folder)
DATASET_GEM = os.path.join(data_folder, 'gem.npy')
#DATASET1 = os.path.join(data_folder, 'dataset_resized.npy')
#DATASET2 = os.path.join(data_folder, 'dataset_bw.npy')
#DATASET_LF = os.path.join(data_folder, 'dataset_lf.dat')
#DATASET_LF = os.path.join(data_folder, 'orb_5k.dat')
DATASET_IDS = os.path.join(data_folder, 'dataset.ids')
#DATASET_BEBLID = os.path.join(data_folder, 'dataset_beblid.dat')
DATASET_BEBLID = os.path.join(data_folder, 'beblid.dat')
#DATASET_LATCH = os.path.join(data_folder, 'dataset_latch.dat')
#DATASET_LATCH = os.path.join(data_folder, 'latch_5k.dat')
img_folder = settings['img_folder']
log_folder = settings['log_folder']
#temporary for mobile app debugging
log_folder = settings['log_folder']
log_folder = '/home/paolo/Dropbox/extension_logs'
logs = os.path.join(working_folder, log_folder)
if not os.path.isdir(logs):
os.mkdir(logs)

290
WebAppTest.py Executable file
View File

@ -0,0 +1,290 @@
from re import split
from flask import Flask, request, redirect, url_for, flash, render_template, send_from_directory, abort, Response
from random import randint
import cv2
import io
import numpy as np
import json
import urllib
from Searcher import Searcher
from GEMSearcher import GEMSearcher
import WebAppSettings as settings
import uuid
import requests
import os, os.path
from PIL import Image
import tornado.wsgi
import tornado.httpserver
import argparse
import base64
app = Flask(__name__)
security_id = 'FA6mMmeWZ8JjWKz5'
@app.route('/bcirtest/')
def api_root():
print('index_with_randoms.html')
random_ids = []
for i in range(0, 15):
random_ids.append(searcher.get_id(randint(0, 600)))
return render_template('index_with_randoms.html', random_ids=random_ids)
def url_to_file(url):
dest_file = uuid.uuid4().hex + ".png"
dest_path = settings.logs + "/" + dest_file
req = urllib.request.Request(
url,
data=None,
headers={
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
}
)
resp = urllib.request.urlopen(req)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
decoded = cv2.imdecode(image, cv2.IMREAD_COLOR)
cv2.imwrite(dest_path, decoded)
#im = Image.fromarray(image)
#im.save(dest_path)
return dest_path
def post_to_file(image):
dest_file = uuid.uuid4().hex + ".png"
dest_path = settings.logs + "/" + dest_file
image.save(dest_path)
return dest_path
def base64_to_file(image_base64):
dest_file = uuid.uuid4().hex + ".png"
dest_path = settings.logs + "/" + dest_file
with open(dest_path, "wb") as image_file:
byte_content = base64.b64decode(image_base64)
image_file.write(byte_content)
return dest_path
def get_res(results, query_url=None):
if query_url is not None:
return render_template('search.html', results=results, query_url=query_url)
elif results is None:
results = '{"classname":"Unknown"}'
else:
results = '{"classname":"' + results[0][0] + '", "conf":' + str(results[0][1]) + '}'
print(results)
json_res = json.dumps(results)
print(json_res)
return json_res
@app.route('/bcirtest/searchById')
def search_by_id():
id = request.args.get('id')
rescorer = False
lf_impl = 'ORB'
if request.args.get("rescorer") == 'true':
rescorer = True
if request.args.get("lf_impl") == 'beblid':
lf_impl = 'BEBLID'
elif request.args.get("lf_impl") == 'latch':
lf_impl = 'LATCH'
#results = searcher.search_by_id(id, settings.k, rescorer, lf_impl)
results = searcher.search_by_id(id, settings.k)
query_url = None
if request.args.get("tohtml") is not None:
query_url = id + ".jpg"
return get_res(results, query_url)
@app.route('/bcirtest/searchByImg', methods=['POST'])
def search_by_img():
if 'image' not in request.files:
return Response(json.dumps("Error, unable to get the image"), status=401, mimetype='application/json')
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
file = request.files['image']
img_file = post_to_file(file)
rescorer = False
lf_impl = 'ORB500'
if request.form.get("rescorer") == 'true':
rescorer = True
if request.form.get("lf_impl") == 'beblid':
lf_impl = 'BEBLID'
elif request.form.get("lf_impl") == 'latch':
lf_impl = 'LATCH'
elif request.form.get("lf_impl") == 'orb':
lf_impl = 'ORB'
#dest_file = uuid.uuid4().hex + ".jpg"
#dest_path = settings.logs + "/" + dest_file
#file.save(dest_path)
#files = {'image': (dest_file, open(dest_path, 'rb'))}
#r = requests.post(settings.rmac_service, files=files)
#results = search_engine.search_by_img(np.array(r.json()), settings.k)
#results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url)
@app.route('/bcirtest/searchByImgB64', methods=['POST'])
def search_by_img_base64():
sec_id = request.form.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
image = request.form.get('image')
if image:
img_file = base64_to_file(image)
else:
flash('No img sent')
return redirect(request.url)
rescorer = False
lf_impl = 'ORB500'
if request.form.get("rescorer") == 'true':
rescorer = True
if request.form.get("lf_impl") == 'beblid':
lf_impl = 'BEBLID'
elif request.form.get("lf_impl") == 'latch':
lf_impl = 'LATCH'
elif request.form.get("lf_impl") == 'orb':
lf_impl = 'ORB'
#dest_file = uuid.uuid4().hex + ".jpg"
#dest_path = settings.logs + "/" + dest_file
#file.save(dest_path)
#files = {'image': (dest_file, open(dest_path, 'rb'))}
#r = requests.post(settings.rmac_service, files=files)
#results = search_engine.search_by_img(np.array(r.json()), settings.k)
#results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
results = searcher.search_by_img(img_file, settings.k)
query_url = None
if request.form.get("tohtml") is not None:
query_url = ""
return get_res(results, query_url)
@app.route('/bcirtest/searchByURL')
def search_by_url():
sec_id = request.args.get("securityID")
#if sec_id != security_id:
# return Response(json.dumps("Error 401, not authorized"), status=401, mimetype='application/json')
url = request.args.get('url')
rescorer = False
if request.args.get("rescorer") == 'true':
rescorer = True
img_file = url_to_file(url)
# query = cv2.imdecode(image, cv2.IMREAD_COLOR)
# dest_file = uuid.uuid4().hex + ".jpg"
# dest_path = settings.logs + "/" + dest_file
# cv2.imwrite(dest_path, query)
# files = {'image': open(dest_path, 'rb')}
# r = requests.post(settings.rmac_service, files=files)
# results = search_engine.search_by_img(np.array(r.json()), settings.k)
rescorer = False
lf_impl = 'ORB500'
if request.args.get("rescorer") == 'true':
rescorer = True
if request.args.get("lf_impl") == 'beblid':
lf_impl = 'BEBLID'
elif request.args.get("lf_impl") == 'latch':
lf_impl = 'LATCH'
elif request.args.get("lf_impl") == 'orb':
lf_impl = 'ORB'
#results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
results = searcher.search_by_img(img_file, settings.k, rescorer, lf_impl)
query_url = None
if request.args.get("tohtml") is not None:
query_url = url
return get_res(results, query_url)
@app.route('/bcirtest/addImg', methods=['POST'])
def add_img():
if 'image' not in request.files:
flash('No file part')
return redirect(request.url)
try:
file = request.files['image']
id = request.files['image'].filename
id, _ = os.path.splitext(id)
img_file = post_to_file(file)
searcher.add(img_file, id)
json_res = json.dumps("done")
return json_res
except:
abort(500)
@app.route('/bcirtest/rmImg')
def remove_img():
try:
id = request.args.get('id')
searcher.remove(id)
json_res = json.dumps("done")
return json_res
except:
abort(500)
@app.route('/bcirtest/<path:filename>')
def download_file(filename):
print(filename)
values = filename.split('/')
print(values)
print(settings.img_folder)
return send_from_directory(settings.img_folder, filename, as_attachment=False)
"""
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = BeniCulturaliSearcher()
#app.run(host='0.0.0.0', port=8090, ssl_context='adhoc')
app.run(host='0.0.0.0', port=settings.port)
# app.run(host='0.0.0.0', port=settings.port)
"""
def start_tornado(app, port=8190):
http_server = tornado.httpserver.HTTPServer(tornado.wsgi.WSGIContainer(app))
http_server.listen(port)
app.logger.info("Tornado server starting on port {}".format(port))
tornado.ioloop.IOLoop.instance().start()
def start_from_terminal(app):
parser = argparse.ArgumentParser(description='Reading configuration file')
parser.add_argument('conf', type=str, help='Configuration file path')
args = parser.parse_args()
settings.load_setting(args.conf)
global searcher
searcher = Searcher()
#if args.debug:
# app.run(debug=True, host='0.0.0.0', port=settings.port)
# else:
#start_tornado(app, settings.port)
app.run(debug=True, host='0.0.0.0', port=8290)
if __name__ == '__main__':
start_from_terminal(app)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

11
conf.json Executable file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8030/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 15,
"k_reorder": 15,
"working_folder": "/workspace/data",
"img_folder": "/workspace/data/data/img/dataset",
"log_folder": "logs",
"data_folder": "data"
}

11
conf_local.json Executable file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8190/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 15,
"k_reorder": 15,
"working_folder": "/media/Data/data/extension/img_recognition",
"img_folder": "/media/Data/data/extension/datasetMerged",
"log_folder": "logs",
"data_folder": "data"
}

11
conf_local_orig.json Normal file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8190/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 15,
"k_reorder": 15,
"working_folder": "/media/Data/data/extension/img_recognition",
"img_folder": "/media/Data/data/extension/img_recognition/data/img/dataset",
"log_folder": "logs",
"data_folder": "data"
}

11
conf_release1.json Normal file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8190/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 500,
"k_reorder": 500,
"working_folder": "/media/Data/data/extension/datasetEnriched",
"img_folder": "/media/Data/data/extension/datasetEnriched/img",
"log_folder": "logs",
"data_folder": "data"
}

11
conf_test.json Executable file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8190/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 15,
"k_reorder": 15,
"working_folder": "/media/Data/data/extension/datasetMerged",
"img_folder": "/media/Data/data/extension/datasetMerged",
"log_folder": "logs",
"data_folder": "data"
}

11
conf_train_test.json Normal file
View File

@ -0,0 +1,11 @@
{
"port": 8190,
"img_host": "http://bilioso.isti.cnr.it:8190/bcimages",
"fe_service": "http://localhost:5000/extract",
"k": 15,
"k_reorder": 15,
"working_folder": "/media/Data/data/extension/merged_train_test",
"img_folder": "/media/Data/data/extension/merged_train_test/train",
"log_folder": "logs",
"data_folder": "data"
}

BIN
static/img/ACC 130111[1].jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
static/img/Search.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/img/aimh_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
static/img/deepImgSearch.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
static/img/favicon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
static/img/refresh.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

105
static/js/ui.js Executable file
View File

@ -0,0 +1,105 @@
function changeImage(imageURL, objectID) {
//alert(imageURL + " - " + objectID);
if (document.getElementById('objId').value==objectID || objectID == "" || imageURL == "null" || objectID == "null") {
document.getElementById('advSearch').style.display = 'none';
//document.getElementById('comboInfo').style.display = 'none';
document.getElementById('queryImage').src='queryImage';
document.getElementById('objId').value='';
document.getElementById('objId').name="disabled";
}
else {
document.getElementById('advSearch').style.display = '';
//document.getElementById('comboInfo').style.display = '';
document.getElementById('queryImage').src=imageURL;
document.getElementById('objId').value=objectID;
document.getElementById('objId').name="id";
//document.getElementById('imageQueryCheckbox').checked='checked';
document.getElementById('objId').name="id";
document.getElementById('queryImage').style.display = '';
if (document.getElementById('imageToUpload') != null) {
document.getElementById('imageToUpload').value="";
}
}
// if (imageURL != "null" && imageURL != "" && imageURL == objectID) {
// document.getElementById('objId').name="url";
// }
}
function imageQuery() {
//alert(imageURL + " - " + objectID);
if (!document.getElementById('imageQueryCheckbox').checked) {
document.getElementById('objId').name="disabled";
document.getElementById('queryImage').style.display = 'none';
}
else {
document.getElementById('objId').name="id";
document.getElementById('queryImage').style.display = '';
}
}
function changeQueryBySampleMod(mode) {
if (mode == "url") {
document.getElementById("uploadText").style.display = 'none';
document.getElementById("uploadLink").style.display = '';
document.getElementById("urlText").style.display = '';
document.getElementById("urlLink").style.display = 'none';
document.getElementById("imageToUpload").style.display = 'none';
document.getElementById("urlToUpload").style.display = '';
document.getElementById("imageToUpload").value = '';
// document.getElementById("imageToUpload").type="text";
// document.getElementById("imageToUpload").name="url";
// document.getElementById("imageToUpload").size="49";
document.getElementById("searchbar").action="./searchByURL"
document.getElementById("searchbar").enctype="";
document.getElementById("searchbar").method="GET";
} else {
document.getElementById("uploadText").style.display = '';
document.getElementById("uploadLink").style.display = 'none';
document.getElementById("urlText").style.display = 'none';
document.getElementById("urlLink").style.display = '';
document.getElementById("urlToUpload").style.display = 'none';
document.getElementById("imageToUpload").style.display = '';
document.getElementById("urlToUpload").value = '';
// document.getElementById("imageToUpload").type="file";
// document.getElementById("imageToUpload").name="imgFile";
// document.getElementById("imageToUpload").size="38";
document.getElementById("searchbar").action="./searchByImg"
document.getElementById("searchbar").enctype="multipart/form-data";
document.getElementById("searchbar").method="POST";
}
}
function trim(str) {
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
}
function getWindowHeight() {
var windowHeight = 0;
if (typeof(window.innerHeight) == 'number') {
windowHeight = window.innerHeight;
}
else {
if (document.documentElement && document.documentElement.clientHeight) {
windowHeight = document.documentElement.clientHeight;
}
else {
if (document.body && document.body.clientHeight) {
windowHeight = document.body.clientHeight;
}
}
}
return windowHeight;
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}

207
static/styles/polaroidStyle.css Executable file
View File

@ -0,0 +1,207 @@
a.polaroid {
display: block;
text-decoration: none;
color: #333;
padding: 10px 10px 20px 10px;
width: 150px;
border: 1px solid #BFBFBF;
background-color: white;
z-index: 2;
font-size: 0.7em;
-webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
-moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
-webkit-transition: all 0.5s ease-in;
}
a.polaroid:hover,
a.polaroid:focus,
a.polaroid:active {
z-index: 999;
border-color: #6A6A6A;
-webkit-box-shadow: 15px 15px 20px rgba(0,0, 0, 0.4);
-moz-box-shadow: 15px 15px 20px rgba(0,0, 0, 0.4);
box-shadow: 15px 15px 20px rgba(0,0, 0, 0.4);
-webkit-transform: rotate(0deg) scale(1.05);
-moz-transform: rotate(0deg) scale(1.05);
transform: rotate(0deg) scale(1.05);
}
.polaroid img {
margin: 0 0 15px;
width: 150px;
height: 150px;
}
a img {
border: none;
display: block;
}
.photo-album {
position: relative;
width: 80%;
margin: 0 auto;
max-width: 70em;
height: 450px;
margin-top: 5em;
min-width: 800px;
max-width: 900px;
}
.photo-album .polaroid {
position: absolute;
}
.photo-album h1 {
position: absolute;
z-index: 5;
top: 150px;
text-align: center;
width: 100%;
line-height: 1.9;
}
.photo-album h1 span {
background-color: white;
font-family: "Helvetica Neue",Arial,Helvetica,sans-serif;
padding: 0.4em 0.8em 0.3em 0.8em;
-webkit-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
-moz-box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
box-shadow: 2px 2px 4px rgba(0,0, 0, 0.3);
border: 1px solid #6A6A6A;
}
.photo-album .small {
width: 75px;
padding: 6px 6px 12px 6px;
font-size: 0.6em;
}
.photo-album .small img {
width: 75px;
height: 75px;
}
.photo-album .medium {
width: 200px;
padding: 13px 13px 26px 13px;
font-size: 0.8em;
}
.photo-album .medium img {
width: 200px;
height: 200px;
}
.photo-album .large {
width: 300px;
padding: 20px 20px 30px 20px;
font-size: 1em;
}
.photo-album .large img {
width: 300px;
height: 300px;
}
.photo-album .img1 {
bottom: 10px;
right: 365px;
-webkit-transform: rotate(10deg);
-moz-transform: rotate(10deg);
transform: rotate(10deg);
}
.photo-album .img2 {
top: 50px;
right: 20px;
-webkit-transform: rotate(-4deg);
-moz-transform: rotate(-4deg);
transform: rotate(-4deg);
}
.photo-album .img3 {
left: 400px;
top: 0;
-webkit-transform: rotate(-5deg);
-moz-transform: rotate(-5deg);
transform: rotate(-5deg);
}
.photo-album .img4 {
top: 10px;
left: 495px;
-webkit-transform: rotate(-20deg);
-moz-transform: rotate(-20deg);
transform: rotate(-20deg);
}
.photo-album .img5 {
bottom: 0;
right: 0;
-webkit-transform: rotate(1deg);
-moz-transform: rotate(1deg);
transform: rotate(1deg);
}
.photo-album .img6 {
bottom: 10px;
right: 156px;
-webkit-transform: rotate(6deg);
-moz-transform: rotate(6deg);
transform: rotate(6deg);
}
.photo-album .img7 {
bottom:0;
left:400px;
-webkit-transform: rotate(-10deg);
-moz-transform: rotate(-10deg);
transform: rotate(-10deg);
}
.photo-album .img8 {
bottom: -20px;
left: 700px;
-webkit-transform: rotate(-8deg);
-moz-transform: rotate(-8deg);
transform: rotate(-8deg);
}
.photo-album .img9 {
bottom: 0;
left: 0;
-webkit-transform: rotate(-8deg);
-moz-transform: rotate(-8deg);
transform: rotate(-8deg);
}
.photo-album .img10 {
top: 0;
left: 20px;
-webkit-transform: rotate(8deg);
-moz-transform: rotate(8deg);
transform: rotate(8deg);
}
.photo-album .img11 {
top: 0;
right: 0;
-webkit-transform: rotate(-8deg);
-moz-transform: rotate(-8deg);
transform: rotate(-8deg);
}
.photo-album .img12 {
top: 0;
left: 680px;
-webkit-transform: rotate(18deg);
-moz-transform: rotate(18deg);
transform: rotate(18deg);
}
.photo-album .img13 {
bottom: -20px;
right: 630px;
-webkit-transform: rotate(4deg);
-moz-transform: rotate(4deg);
transform: rotate(4deg);
}
.photo-album .img14 {
top: 90px;
left: 430px;
-webkit-transform: rotate(15deg);
-moz-transform: rotate(15deg);
transform: rotate(15deg);
}
.photo-album .img15 {
left:176px;
top:20px;
-webkit-transform: rotate(-8deg);
-moz-transform: rotate(-8deg);
transform: rotate(-8deg);
}
a:hover,
a:focus {
z-index: 5;
}

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Deep Image Search</title>
<link rel="icon" href="{{ url_for('static',filename='img/favicon.ico') }}" />
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/polaroidStyle.css') }}">
<script src= "{{ url_for('static',filename='js/ui.js') }}"></script>
</head>
<body>
<div align="center" id="header"><br>
<div style="margin-bottom: 10px; vertical-align: middle;"><img class="header"
src="{{ url_for('static',filename='img/aimh_logo.png') }}" style="max-width: 100%; height: auto; width: 400px;" alt=""></div>
<table>
<tr>
<td colspan="3">
<div id="searchForm" style="">
<span id="urlText" style="display: none;"><b>Paste image URL</b></span>
<a href="#" id="urlLink" onclick="changeQueryBySampleMod('url')" >Paste image URL</a>
<span id="uploadText"><b>Upload an image</b></span>
<a href="#" id="uploadLink" onclick="changeQueryBySampleMod('upload')" style="display: none;">Upload an image</a>
<form id="searchbar" method="POST" enctype="multipart/form-data" name="form1" action="./searchByImg">
<table cellspacing="1" cellpadding="1" border="0">
<tr>
<td valign="top">
<input type="hidden" value="" name="" id="objId">
<input type="hidden" value="true" name="tohtml">
<input style="display: none;" id="urlToUpload" name="url" type="text" size="49" onclick="" onchange="document.getElementById('queryImage').value=''">
<input id="imageToUpload" name="image" type="file" size="38" onclick="" onchange="document.getElementById('queryImage').value=''">
</td>
<td valign="top">
<input type="image" src="{{ url_for('static',filename='img/Search.png') }}" value="Search" title="Search" onclick="if (trim(document.getElementById('urlToUpload').value)=='' && trim(document.getElementById('imageToUpload').value)=='')return false;">
</td>
</tr>
</table>
</form></div>
</td>
</tr>
</table>
{%
set css_classes = ["", "large polaroid img1","polaroid img2","small polaroid img3","medium polaroid img4","polaroid img5","polaroid img6","polaroid img7","small polaroid img8","medium polaroid img9","polaroid img10","small polaroid img11","small polaroid img12","small polaroid img13","small polaroid img14","polaroid img15"]
%}
<!-- <div align="center" class="photo-album">
{% for i in range(0, 15) %}
{% set id = random_ids[i] %}
{% set objectUrlSmall = id%}
{% set title = id.split("/")[0] %}
<a href="searchById?tohtml=true&rescorer=false&bw=false&id={%print(id)%}" title="Click To Search" class="{%print(css_classes[i%15+1])%}"><img src="{%print(objectUrlSmall)%}" border="0">
</a>
{% endfor %}
</div>
<div align="center" style="padding-top: 40px;">
<a href="./" title="more random images"><img src="{{ url_for('static',filename='img/refresh.png') }}" width=45></a>
</div>-->
</body>
</html>

88
templates/search.html Executable file
View File

@ -0,0 +1,88 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Deep Image Search</title>
<link rel="icon" href="{{ url_for('static',filename='img/favicon.ico') }}" />
<script src= "{{ url_for('static',filename='js/ui.js') }}"></script>
</head>
<body>
<div>
<table>
<tr>
<td><a href="." title="Home"><img
src="{{ url_for('static',filename='img/aimh_logo.png') }}" border="0" align="bottom"
width="160"></a></td>
<td><img id="queryImage" src="{{session['query']}}" alt=""
width="80" align="middle"></td>
</table>
<table>
<tr>
<td colspan="3">
<div id="searchForm">
<span id="urlText" style="display: none;"><b>Paste image URL</b></span>
<a href="#" id="urlLink" onclick="changeQueryBySampleMod('url')" >Paste image URL</a>
<span id="uploadText"><b>Upload an image</b></span>
<a href="#" id="uploadLink" onclick="changeQueryBySampleMod('upload')" style="display: none;">Upload an image</a>
<form id="searchbar" method="POST" enctype="multipart/form-data" name="form1" action="./searchByImg">
<table cellspacing="1" cellpadding="1" border="0">
<tr>
<td valign="top">
<input type="hidden" value="" name="" id="objId">
<input type="hidden" value="true" name="tohtml">
<input style="display: none;" id="urlToUpload" name="url" type="text" size="49" onclick="" onchange="document.getElementById('queryImage').value=''">
<input id="imageToUpload" name="image" type="file" size="38" onclick="" onchange="document.getElementById('queryImage').value=''">
</td>
<td valign="top">
<input type="image" src="{{ url_for('static',filename='img/Search.png') }}" value="Search" title="Search" onclick="if (trim(document.getElementById('urlToUpload').value)=='' && trim(document.getElementById('imageToUpload').value)=='')return false;">
</td>
</tr>
</table>
</form>
</div>
</td>
</tr>
</table>
</div>
<div align="center">
<table width="800">
{% if results != None%}
{% set columns = 0 %}
{% for idx in range(1) %}
{% if idx% 6 == 0 %}
<tr>
{% endif %}
{% set id = results[0] %}
{% set objectUrlSmall = 'img4class/' + id + '.jpg'%}
{% set title = id.split("/")[0] %}
{% set score = results[1] %}
<td>
<div>
<h2>{%print(score)%}, {%print(title)%}</h2>
<img title={{score}} style="width: 160px;" src="{%print(objectUrlSmall)%}" border="0">
<!--<br> <span style="font-size: x-small;"><h1>{%print(title)%}</h1></span><br>-->
</div>
</td>
{% set columns = columns + 1 %}
{% endfor %}
{% else %}
<h1>Unknown class</h1>
{% endif %}
</table>
</div>
</body>
</html>

241
venv/bin/Activate.ps1 Executable file
View File

@ -0,0 +1,241 @@
<#
.Synopsis
Activate a Python virtual environment for the current PowerShell session.
.Description
Pushes the python executable for a virtual environment to the front of the
$Env:PATH environment variable and sets the prompt to signify that you are
in a Python virtual environment. Makes use of the command line switches as
well as the `pyvenv.cfg` file values present in the virtual environment.
.Parameter VenvDir
Path to the directory that contains the virtual environment to activate. The
default value for this is the parent of the directory that the Activate.ps1
script is located within.
.Parameter Prompt
The prompt prefix to display when this virtual environment is activated. By
default, this prompt is the name of the virtual environment folder (VenvDir)
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
.Example
Activate.ps1
Activates the Python virtual environment that contains the Activate.ps1 script.
.Example
Activate.ps1 -Verbose
Activates the Python virtual environment that contains the Activate.ps1 script,
and shows extra information about the activation as it executes.
.Example
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
Activates the Python virtual environment located in the specified location.
.Example
Activate.ps1 -Prompt "MyPython"
Activates the Python virtual environment that contains the Activate.ps1 script,
and prefixes the current prompt with the specified string (surrounded in
parentheses) while the virtual environment is active.
.Notes
On Windows, it may be required to enable this Activate.ps1 script by setting the
execution policy for the user. You can do this by issuing the following PowerShell
command:
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
For more information on Execution Policies:
https://go.microsoft.com/fwlink/?LinkID=135170
#>
Param(
[Parameter(Mandatory = $false)]
[String]
$VenvDir,
[Parameter(Mandatory = $false)]
[String]
$Prompt
)
<# Function declarations --------------------------------------------------- #>
<#
.Synopsis
Remove all shell session elements added by the Activate script, including the
addition of the virtual environment's Python executable from the beginning of
the PATH variable.
.Parameter NonDestructive
If present, do not remove this function from the global namespace for the
session.
#>
function global:deactivate ([switch]$NonDestructive) {
# Revert to original values
# The prior prompt:
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
}
# The prior PYTHONHOME:
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
}
# The prior PATH:
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
}
# Just remove the VIRTUAL_ENV altogether:
if (Test-Path -Path Env:VIRTUAL_ENV) {
Remove-Item -Path env:VIRTUAL_ENV
}
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
}
# Leave deactivate function in the global namespace if requested:
if (-not $NonDestructive) {
Remove-Item -Path function:deactivate
}
}
<#
.Description
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
given folder, and returns them in a map.
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
two strings separated by `=` (with any amount of whitespace surrounding the =)
then it is considered a `key = value` line. The left hand string is the key,
the right hand is the value.
If the value starts with a `'` or a `"` then the first and last character is
stripped from the value before being captured.
.Parameter ConfigDir
Path to the directory that contains the `pyvenv.cfg` file.
#>
function Get-PyVenvConfig(
[String]
$ConfigDir
) {
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
# An empty map will be returned if no config file is found.
$pyvenvConfig = @{ }
if ($pyvenvConfigPath) {
Write-Verbose "File exists, parse `key = value` lines"
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
$pyvenvConfigContent | ForEach-Object {
$keyval = $PSItem -split "\s*=\s*", 2
if ($keyval[0] -and $keyval[1]) {
$val = $keyval[1]
# Remove extraneous quotations around a string value.
if ("'""".Contains($val.Substring(0, 1))) {
$val = $val.Substring(1, $val.Length - 2)
}
$pyvenvConfig[$keyval[0]] = $val
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
}
}
}
return $pyvenvConfig
}
<# Begin Activate script --------------------------------------------------- #>
# Determine the containing directory of this script
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$VenvExecDir = Get-Item -Path $VenvExecPath
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
# Set values required in priority: CmdLine, ConfigFile, Default
# First, get the location of the virtual environment, it might not be
# VenvExecDir if specified on the command line.
if ($VenvDir) {
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
}
else {
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
Write-Verbose "VenvDir=$VenvDir"
}
# Next, read the `pyvenv.cfg` file to determine any required value such
# as `prompt`.
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
# Next, set the prompt from the command line, or the config file, or
# just use the name of the virtual environment folder.
if ($Prompt) {
Write-Verbose "Prompt specified as argument, using '$Prompt'"
}
else {
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
$Prompt = $pyvenvCfg['prompt'];
}
else {
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)"
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
$Prompt = Split-Path -Path $venvDir -Leaf
}
}
Write-Verbose "Prompt = '$Prompt'"
Write-Verbose "VenvDir='$VenvDir'"
# Deactivate any currently active virtual environment, but leave the
# deactivate function in place.
deactivate -nondestructive
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
Write-Verbose "Setting prompt to '$Prompt'"
# Set the prompt to include the env name
# Make sure _OLD_VIRTUAL_PROMPT is global
function global:_OLD_VIRTUAL_PROMPT { "" }
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
function global:prompt {
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
_OLD_VIRTUAL_PROMPT
}
}
# Clear PYTHONHOME
if (Test-Path -Path Env:PYTHONHOME) {
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
Remove-Item -Path Env:PYTHONHOME
}
# Add the venv to the PATH
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"

76
venv/bin/activate Executable file
View File

@ -0,0 +1,76 @@
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r
fi
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
VIRTUAL_ENV="/media/Data/data/extension/img_recognition/code/venv"
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
if [ "x(venv) " != x ] ; then
PS1="(venv) ${PS1:-}"
else
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
# special case for Aspen magic directories
# see https://aspen.io/
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
else
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
fi
fi
export PS1
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
hash -r
fi

37
venv/bin/activate.csh Executable file
View File

@ -0,0 +1,37 @@
# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate'
# Unset irrelevant variables.
deactivate nondestructive
setenv VIRTUAL_ENV "/media/Data/data/extension/img_recognition/code/venv"
set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
set _OLD_VIRTUAL_PROMPT="$prompt"
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
if ("venv" != "") then
set env_name = "venv"
else
if (`basename "VIRTUAL_ENV"` == "__") then
# special case for Aspen magic directories
# see https://aspen.io/
set env_name = `basename \`dirname "$VIRTUAL_ENV"\``
else
set env_name = `basename "$VIRTUAL_ENV"`
endif
endif
set prompt = "[$env_name] $prompt"
unset env_name
endif
alias pydoc python -m pydoc
rehash

75
venv/bin/activate.fish Executable file
View File

@ -0,0 +1,75 @@
# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org)
# you cannot run it directly
function deactivate -d "Exit virtualenv and return to normal shell environment"
# reset old environment variables
if test -n "$_OLD_VIRTUAL_PATH"
set -gx PATH $_OLD_VIRTUAL_PATH
set -e _OLD_VIRTUAL_PATH
end
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
functions -e fish_prompt
set -e _OLD_FISH_PROMPT_OVERRIDE
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
end
set -e VIRTUAL_ENV
if test "$argv[1]" != "nondestructive"
# Self destruct!
functions -e deactivate
end
end
# unset irrelevant variables
deactivate nondestructive
set -gx VIRTUAL_ENV "/media/Data/data/extension/img_recognition/code/venv"
set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
# unset PYTHONHOME if set
if set -q PYTHONHOME
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
set -e PYTHONHOME
end
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# fish uses a function instead of an env var to generate the prompt.
# save the current fish_prompt function as the function _old_fish_prompt
functions -c fish_prompt _old_fish_prompt
# with the original prompt function renamed, we can override with our own.
function fish_prompt
# Save the return status of the last command
set -l old_status $status
# Prompt override?
if test -n "(venv) "
printf "%s%s" "(venv) " (set_color normal)
else
# ...Otherwise, prepend env
set -l _checkbase (basename "$VIRTUAL_ENV")
if test $_checkbase = "__"
# special case for Aspen magic directories
# see https://aspen.io/
printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal)
else
printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal)
end
end
# Restore the return status of the previous command.
echo "exit $old_status" | .
_old_fish_prompt
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
end

12
venv/bin/easy_install Executable file
View File

@ -0,0 +1,12 @@
#!/media/Data/data/extension/img_recognition/code/venv/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==40.8.0','console_scripts','easy_install'
__requires__ = 'setuptools==40.8.0'
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('setuptools==40.8.0', 'console_scripts', 'easy_install')()
)

12
venv/bin/easy_install-3.8 Executable file
View File

@ -0,0 +1,12 @@
#!/media/Data/data/extension/img_recognition/code/venv/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'setuptools==40.8.0','console_scripts','easy_install-3.8'
__requires__ = 'setuptools==40.8.0'
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('setuptools==40.8.0', 'console_scripts', 'easy_install-3.8')()
)

12
venv/bin/pip Executable file
View File

@ -0,0 +1,12 @@
#!/media/Data/data/extension/img_recognition/code/venv/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==19.0.3','console_scripts','pip'
__requires__ = 'pip==19.0.3'
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('pip==19.0.3', 'console_scripts', 'pip')()
)

12
venv/bin/pip3 Executable file
View File

@ -0,0 +1,12 @@
#!/media/Data/data/extension/img_recognition/code/venv/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==19.0.3','console_scripts','pip3'
__requires__ = 'pip==19.0.3'
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('pip==19.0.3', 'console_scripts', 'pip3')()
)

12
venv/bin/pip3.8 Executable file
View File

@ -0,0 +1,12 @@
#!/media/Data/data/extension/img_recognition/code/venv/bin/python
# EASY-INSTALL-ENTRY-SCRIPT: 'pip==19.0.3','console_scripts','pip3.8'
__requires__ = 'pip==19.0.3'
import re
import sys
from pkg_resources import load_entry_point
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(
load_entry_point('pip==19.0.3', 'console_scripts', 'pip3.8')()
)

1
venv/bin/python Symbolic link
View File

@ -0,0 +1 @@
python3.8

1
venv/bin/python3 Symbolic link
View File

@ -0,0 +1 @@
python3.8

1
venv/bin/python3.8 Symbolic link
View File

@ -0,0 +1 @@
/usr/bin/python3.8

View File

@ -0,0 +1,2 @@
./setuptools-40.8.0-py3.8.egg
./pip-19.0.3-py3.8.egg

View File

@ -0,0 +1,73 @@
Metadata-Version: 1.2
Name: pip
Version: 19.0.3
Summary: The PyPA recommended tool for installing Python packages.
Home-page: https://pip.pypa.io/
Author: The pip developers
Author-email: pypa-dev@groups.google.com
License: MIT
Description: pip - The Python Package Installer
==================================
.. image:: https://img.shields.io/pypi/v/pip.svg
:target: https://pypi.org/project/pip/
.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
:target: https://pip.pypa.io/en/latest
pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
Please take a look at our documentation for how to install and use pip:
* `Installation`_
* `Usage`_
* `Release notes`_
If you find bugs, need help, or want to talk to the developers please use our mailing lists or chat rooms:
* `Issue tracking`_
* `Discourse channel`_
* `User IRC`_
If you want to get involved head over to GitHub to get the source code and feel free to jump on the developer mailing lists and chat rooms:
* `GitHub page`_
* `Dev mailing list`_
* `Dev IRC`_
Code of Conduct
---------------
Everyone interacting in the pip project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PyPA Code of Conduct`_.
.. _package installer: https://packaging.python.org/en/latest/current/
.. _Python Package Index: https://pypi.org
.. _Installation: https://pip.pypa.io/en/stable/installing.html
.. _Usage: https://pip.pypa.io/en/stable/
.. _Release notes: https://pip.pypa.io/en/stable/news.html
.. _GitHub page: https://github.com/pypa/pip
.. _Issue tracking: https://github.com/pypa/pip/issues
.. _Discourse channel: https://discuss.python.org/c/packaging
.. _Dev mailing list: https://groups.google.com/forum/#!forum/pypa-dev
.. _User IRC: https://webchat.freenode.net/?channels=%23pypa
.. _Dev IRC: https://webchat.freenode.net/?channels=%23pypa-dev
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
Keywords: distutils easy_install egg setuptools wheel virtualenv
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Software Development :: Build Tools
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*

View File

@ -0,0 +1,391 @@
AUTHORS.txt
LICENSE.txt
MANIFEST.in
NEWS.rst
README.rst
pyproject.toml
setup.cfg
setup.py
docs/pip_sphinxext.py
docs/html/conf.py
docs/html/cookbook.rst
docs/html/index.rst
docs/html/installing.rst
docs/html/logic.rst
docs/html/news.rst
docs/html/quickstart.rst
docs/html/usage.rst
docs/html/user_guide.rst
docs/html/development/configuration.rst
docs/html/development/contributing.rst
docs/html/development/getting-started.rst
docs/html/development/index.rst
docs/html/development/release-process.rst
docs/html/development/vendoring-policy.rst
docs/html/reference/index.rst
docs/html/reference/pip.rst
docs/html/reference/pip_check.rst
docs/html/reference/pip_config.rst
docs/html/reference/pip_download.rst
docs/html/reference/pip_freeze.rst
docs/html/reference/pip_hash.rst
docs/html/reference/pip_install.rst
docs/html/reference/pip_list.rst
docs/html/reference/pip_search.rst
docs/html/reference/pip_show.rst
docs/html/reference/pip_uninstall.rst
docs/html/reference/pip_wheel.rst
docs/man/index.rst
docs/man/commands/check.rst
docs/man/commands/config.rst
docs/man/commands/download.rst
docs/man/commands/freeze.rst
docs/man/commands/hash.rst
docs/man/commands/help.rst
docs/man/commands/install.rst
docs/man/commands/list.rst
docs/man/commands/search.rst
docs/man/commands/show.rst
docs/man/commands/uninstall.rst
docs/man/commands/wheel.rst
src/pip/__init__.py
src/pip/__main__.py
src/pip.egg-info/PKG-INFO
src/pip.egg-info/SOURCES.txt
src/pip.egg-info/dependency_links.txt
src/pip.egg-info/entry_points.txt
src/pip.egg-info/not-zip-safe
src/pip.egg-info/top_level.txt
src/pip/_internal/__init__.py
src/pip/_internal/build_env.py
src/pip/_internal/cache.py
src/pip/_internal/configuration.py
src/pip/_internal/download.py
src/pip/_internal/exceptions.py
src/pip/_internal/index.py
src/pip/_internal/locations.py
src/pip/_internal/pep425tags.py
src/pip/_internal/pyproject.py
src/pip/_internal/resolve.py
src/pip/_internal/wheel.py
src/pip/_internal/cli/__init__.py
src/pip/_internal/cli/autocompletion.py
src/pip/_internal/cli/base_command.py
src/pip/_internal/cli/cmdoptions.py
src/pip/_internal/cli/main_parser.py
src/pip/_internal/cli/parser.py
src/pip/_internal/cli/status_codes.py
src/pip/_internal/commands/__init__.py
src/pip/_internal/commands/check.py
src/pip/_internal/commands/completion.py
src/pip/_internal/commands/configuration.py
src/pip/_internal/commands/download.py
src/pip/_internal/commands/freeze.py
src/pip/_internal/commands/hash.py
src/pip/_internal/commands/help.py
src/pip/_internal/commands/install.py
src/pip/_internal/commands/list.py
src/pip/_internal/commands/search.py
src/pip/_internal/commands/show.py
src/pip/_internal/commands/uninstall.py
src/pip/_internal/commands/wheel.py
src/pip/_internal/models/__init__.py
src/pip/_internal/models/candidate.py
src/pip/_internal/models/format_control.py
src/pip/_internal/models/index.py
src/pip/_internal/models/link.py
src/pip/_internal/operations/__init__.py
src/pip/_internal/operations/check.py
src/pip/_internal/operations/freeze.py
src/pip/_internal/operations/prepare.py
src/pip/_internal/req/__init__.py
src/pip/_internal/req/constructors.py
src/pip/_internal/req/req_file.py
src/pip/_internal/req/req_install.py
src/pip/_internal/req/req_set.py
src/pip/_internal/req/req_tracker.py
src/pip/_internal/req/req_uninstall.py
src/pip/_internal/utils/__init__.py
src/pip/_internal/utils/appdirs.py
src/pip/_internal/utils/compat.py
src/pip/_internal/utils/deprecation.py
src/pip/_internal/utils/encoding.py
src/pip/_internal/utils/filesystem.py
src/pip/_internal/utils/glibc.py
src/pip/_internal/utils/hashes.py
src/pip/_internal/utils/logging.py
src/pip/_internal/utils/misc.py
src/pip/_internal/utils/models.py
src/pip/_internal/utils/outdated.py
src/pip/_internal/utils/packaging.py
src/pip/_internal/utils/setuptools_build.py
src/pip/_internal/utils/temp_dir.py
src/pip/_internal/utils/typing.py
src/pip/_internal/utils/ui.py
src/pip/_internal/vcs/__init__.py
src/pip/_internal/vcs/bazaar.py
src/pip/_internal/vcs/git.py
src/pip/_internal/vcs/mercurial.py
src/pip/_internal/vcs/subversion.py
src/pip/_vendor/README.rst
src/pip/_vendor/__init__.py
src/pip/_vendor/appdirs.LICENSE.txt
src/pip/_vendor/appdirs.py
src/pip/_vendor/distro.LICENSE
src/pip/_vendor/distro.py
src/pip/_vendor/ipaddress.LICENSE
src/pip/_vendor/ipaddress.py
src/pip/_vendor/pyparsing.LICENSE
src/pip/_vendor/pyparsing.py
src/pip/_vendor/retrying.LICENSE
src/pip/_vendor/retrying.py
src/pip/_vendor/six.LICENSE
src/pip/_vendor/six.py
src/pip/_vendor/vendor.txt
src/pip/_vendor/cachecontrol/LICENSE.txt
src/pip/_vendor/cachecontrol/__init__.py
src/pip/_vendor/cachecontrol/_cmd.py
src/pip/_vendor/cachecontrol/adapter.py
src/pip/_vendor/cachecontrol/cache.py
src/pip/_vendor/cachecontrol/compat.py
src/pip/_vendor/cachecontrol/controller.py
src/pip/_vendor/cachecontrol/filewrapper.py
src/pip/_vendor/cachecontrol/heuristics.py
src/pip/_vendor/cachecontrol/serialize.py
src/pip/_vendor/cachecontrol/wrapper.py
src/pip/_vendor/cachecontrol/caches/__init__.py
src/pip/_vendor/cachecontrol/caches/file_cache.py
src/pip/_vendor/cachecontrol/caches/redis_cache.py
src/pip/_vendor/certifi/LICENSE
src/pip/_vendor/certifi/__init__.py
src/pip/_vendor/certifi/__main__.py
src/pip/_vendor/certifi/cacert.pem
src/pip/_vendor/certifi/core.py
src/pip/_vendor/chardet/LICENSE
src/pip/_vendor/chardet/__init__.py
src/pip/_vendor/chardet/big5freq.py
src/pip/_vendor/chardet/big5prober.py
src/pip/_vendor/chardet/chardistribution.py
src/pip/_vendor/chardet/charsetgroupprober.py
src/pip/_vendor/chardet/charsetprober.py
src/pip/_vendor/chardet/codingstatemachine.py
src/pip/_vendor/chardet/compat.py
src/pip/_vendor/chardet/cp949prober.py
src/pip/_vendor/chardet/enums.py
src/pip/_vendor/chardet/escprober.py
src/pip/_vendor/chardet/escsm.py
src/pip/_vendor/chardet/eucjpprober.py
src/pip/_vendor/chardet/euckrfreq.py
src/pip/_vendor/chardet/euckrprober.py
src/pip/_vendor/chardet/euctwfreq.py
src/pip/_vendor/chardet/euctwprober.py
src/pip/_vendor/chardet/gb2312freq.py
src/pip/_vendor/chardet/gb2312prober.py
src/pip/_vendor/chardet/hebrewprober.py
src/pip/_vendor/chardet/jisfreq.py
src/pip/_vendor/chardet/jpcntx.py
src/pip/_vendor/chardet/langbulgarianmodel.py
src/pip/_vendor/chardet/langcyrillicmodel.py
src/pip/_vendor/chardet/langgreekmodel.py
src/pip/_vendor/chardet/langhebrewmodel.py
src/pip/_vendor/chardet/langhungarianmodel.py
src/pip/_vendor/chardet/langthaimodel.py
src/pip/_vendor/chardet/langturkishmodel.py
src/pip/_vendor/chardet/latin1prober.py
src/pip/_vendor/chardet/mbcharsetprober.py
src/pip/_vendor/chardet/mbcsgroupprober.py
src/pip/_vendor/chardet/mbcssm.py
src/pip/_vendor/chardet/sbcharsetprober.py
src/pip/_vendor/chardet/sbcsgroupprober.py
src/pip/_vendor/chardet/sjisprober.py
src/pip/_vendor/chardet/universaldetector.py
src/pip/_vendor/chardet/utf8prober.py
src/pip/_vendor/chardet/version.py
src/pip/_vendor/chardet/cli/__init__.py
src/pip/_vendor/chardet/cli/chardetect.py
src/pip/_vendor/colorama/LICENSE.txt
src/pip/_vendor/colorama/__init__.py
src/pip/_vendor/colorama/ansi.py
src/pip/_vendor/colorama/ansitowin32.py
src/pip/_vendor/colorama/initialise.py
src/pip/_vendor/colorama/win32.py
src/pip/_vendor/colorama/winterm.py
src/pip/_vendor/distlib/LICENSE.txt
src/pip/_vendor/distlib/__init__.py
src/pip/_vendor/distlib/compat.py
src/pip/_vendor/distlib/database.py
src/pip/_vendor/distlib/index.py
src/pip/_vendor/distlib/locators.py
src/pip/_vendor/distlib/manifest.py
src/pip/_vendor/distlib/markers.py
src/pip/_vendor/distlib/metadata.py
src/pip/_vendor/distlib/resources.py
src/pip/_vendor/distlib/scripts.py
src/pip/_vendor/distlib/t32.exe
src/pip/_vendor/distlib/t64.exe
src/pip/_vendor/distlib/util.py
src/pip/_vendor/distlib/version.py
src/pip/_vendor/distlib/w32.exe
src/pip/_vendor/distlib/w64.exe
src/pip/_vendor/distlib/wheel.py
src/pip/_vendor/distlib/_backport/__init__.py
src/pip/_vendor/distlib/_backport/misc.py
src/pip/_vendor/distlib/_backport/shutil.py
src/pip/_vendor/distlib/_backport/sysconfig.cfg
src/pip/_vendor/distlib/_backport/sysconfig.py
src/pip/_vendor/distlib/_backport/tarfile.py
src/pip/_vendor/html5lib/LICENSE
src/pip/_vendor/html5lib/__init__.py
src/pip/_vendor/html5lib/_ihatexml.py
src/pip/_vendor/html5lib/_inputstream.py
src/pip/_vendor/html5lib/_tokenizer.py
src/pip/_vendor/html5lib/_utils.py
src/pip/_vendor/html5lib/constants.py
src/pip/_vendor/html5lib/html5parser.py
src/pip/_vendor/html5lib/serializer.py
src/pip/_vendor/html5lib/_trie/__init__.py
src/pip/_vendor/html5lib/_trie/_base.py
src/pip/_vendor/html5lib/_trie/datrie.py
src/pip/_vendor/html5lib/_trie/py.py
src/pip/_vendor/html5lib/filters/__init__.py
src/pip/_vendor/html5lib/filters/alphabeticalattributes.py
src/pip/_vendor/html5lib/filters/base.py
src/pip/_vendor/html5lib/filters/inject_meta_charset.py
src/pip/_vendor/html5lib/filters/lint.py
src/pip/_vendor/html5lib/filters/optionaltags.py
src/pip/_vendor/html5lib/filters/sanitizer.py
src/pip/_vendor/html5lib/filters/whitespace.py
src/pip/_vendor/html5lib/treeadapters/__init__.py
src/pip/_vendor/html5lib/treeadapters/genshi.py
src/pip/_vendor/html5lib/treeadapters/sax.py
src/pip/_vendor/html5lib/treebuilders/__init__.py
src/pip/_vendor/html5lib/treebuilders/base.py
src/pip/_vendor/html5lib/treebuilders/dom.py
src/pip/_vendor/html5lib/treebuilders/etree.py
src/pip/_vendor/html5lib/treebuilders/etree_lxml.py
src/pip/_vendor/html5lib/treewalkers/__init__.py
src/pip/_vendor/html5lib/treewalkers/base.py
src/pip/_vendor/html5lib/treewalkers/dom.py
src/pip/_vendor/html5lib/treewalkers/etree.py
src/pip/_vendor/html5lib/treewalkers/etree_lxml.py
src/pip/_vendor/html5lib/treewalkers/genshi.py
src/pip/_vendor/idna/LICENSE.rst
src/pip/_vendor/idna/__init__.py
src/pip/_vendor/idna/codec.py
src/pip/_vendor/idna/compat.py
src/pip/_vendor/idna/core.py
src/pip/_vendor/idna/idnadata.py
src/pip/_vendor/idna/intranges.py
src/pip/_vendor/idna/package_data.py
src/pip/_vendor/idna/uts46data.py
src/pip/_vendor/lockfile/LICENSE
src/pip/_vendor/lockfile/__init__.py
src/pip/_vendor/lockfile/linklockfile.py
src/pip/_vendor/lockfile/mkdirlockfile.py
src/pip/_vendor/lockfile/pidlockfile.py
src/pip/_vendor/lockfile/sqlitelockfile.py
src/pip/_vendor/lockfile/symlinklockfile.py
src/pip/_vendor/msgpack/COPYING
src/pip/_vendor/msgpack/__init__.py
src/pip/_vendor/msgpack/_version.py
src/pip/_vendor/msgpack/exceptions.py
src/pip/_vendor/msgpack/fallback.py
src/pip/_vendor/packaging/LICENSE
src/pip/_vendor/packaging/LICENSE.APACHE
src/pip/_vendor/packaging/LICENSE.BSD
src/pip/_vendor/packaging/__about__.py
src/pip/_vendor/packaging/__init__.py
src/pip/_vendor/packaging/_compat.py
src/pip/_vendor/packaging/_structures.py
src/pip/_vendor/packaging/markers.py
src/pip/_vendor/packaging/requirements.py
src/pip/_vendor/packaging/specifiers.py
src/pip/_vendor/packaging/utils.py
src/pip/_vendor/packaging/version.py
src/pip/_vendor/pep517/LICENSE
src/pip/_vendor/pep517/__init__.py
src/pip/_vendor/pep517/_in_process.py
src/pip/_vendor/pep517/build.py
src/pip/_vendor/pep517/check.py
src/pip/_vendor/pep517/colorlog.py
src/pip/_vendor/pep517/compat.py
src/pip/_vendor/pep517/envbuild.py
src/pip/_vendor/pep517/wrappers.py
src/pip/_vendor/pkg_resources/LICENSE
src/pip/_vendor/pkg_resources/__init__.py
src/pip/_vendor/pkg_resources/py31compat.py
src/pip/_vendor/progress/LICENSE
src/pip/_vendor/progress/__init__.py
src/pip/_vendor/progress/bar.py
src/pip/_vendor/progress/counter.py
src/pip/_vendor/progress/helpers.py
src/pip/_vendor/progress/spinner.py
src/pip/_vendor/pytoml/LICENSE
src/pip/_vendor/pytoml/__init__.py
src/pip/_vendor/pytoml/core.py
src/pip/_vendor/pytoml/parser.py
src/pip/_vendor/pytoml/test.py
src/pip/_vendor/pytoml/utils.py
src/pip/_vendor/pytoml/writer.py
src/pip/_vendor/requests/LICENSE
src/pip/_vendor/requests/__init__.py
src/pip/_vendor/requests/__version__.py
src/pip/_vendor/requests/_internal_utils.py
src/pip/_vendor/requests/adapters.py
src/pip/_vendor/requests/api.py
src/pip/_vendor/requests/auth.py
src/pip/_vendor/requests/certs.py
src/pip/_vendor/requests/compat.py
src/pip/_vendor/requests/cookies.py
src/pip/_vendor/requests/exceptions.py
src/pip/_vendor/requests/help.py
src/pip/_vendor/requests/hooks.py
src/pip/_vendor/requests/models.py
src/pip/_vendor/requests/packages.py
src/pip/_vendor/requests/sessions.py
src/pip/_vendor/requests/status_codes.py
src/pip/_vendor/requests/structures.py
src/pip/_vendor/requests/utils.py
src/pip/_vendor/urllib3/LICENSE.txt
src/pip/_vendor/urllib3/__init__.py
src/pip/_vendor/urllib3/_collections.py
src/pip/_vendor/urllib3/connection.py
src/pip/_vendor/urllib3/connectionpool.py
src/pip/_vendor/urllib3/exceptions.py
src/pip/_vendor/urllib3/fields.py
src/pip/_vendor/urllib3/filepost.py
src/pip/_vendor/urllib3/poolmanager.py
src/pip/_vendor/urllib3/request.py
src/pip/_vendor/urllib3/response.py
src/pip/_vendor/urllib3/contrib/__init__.py
src/pip/_vendor/urllib3/contrib/_appengine_environ.py
src/pip/_vendor/urllib3/contrib/appengine.py
src/pip/_vendor/urllib3/contrib/ntlmpool.py
src/pip/_vendor/urllib3/contrib/pyopenssl.py
src/pip/_vendor/urllib3/contrib/securetransport.py
src/pip/_vendor/urllib3/contrib/socks.py
src/pip/_vendor/urllib3/contrib/_securetransport/__init__.py
src/pip/_vendor/urllib3/contrib/_securetransport/bindings.py
src/pip/_vendor/urllib3/contrib/_securetransport/low_level.py
src/pip/_vendor/urllib3/packages/__init__.py
src/pip/_vendor/urllib3/packages/six.py
src/pip/_vendor/urllib3/packages/backports/__init__.py
src/pip/_vendor/urllib3/packages/backports/makefile.py
src/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py
src/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py
src/pip/_vendor/urllib3/util/__init__.py
src/pip/_vendor/urllib3/util/connection.py
src/pip/_vendor/urllib3/util/queue.py
src/pip/_vendor/urllib3/util/request.py
src/pip/_vendor/urllib3/util/response.py
src/pip/_vendor/urllib3/util/retry.py
src/pip/_vendor/urllib3/util/ssl_.py
src/pip/_vendor/urllib3/util/timeout.py
src/pip/_vendor/urllib3/util/url.py
src/pip/_vendor/urllib3/util/wait.py
src/pip/_vendor/webencodings/LICENSE
src/pip/_vendor/webencodings/__init__.py
src/pip/_vendor/webencodings/labels.py
src/pip/_vendor/webencodings/mklabels.py
src/pip/_vendor/webencodings/tests.py
src/pip/_vendor/webencodings/x_user_defined.py

View File

@ -0,0 +1,5 @@
[console_scripts]
pip = pip._internal:main
pip3 = pip._internal:main
pip3.8 = pip._internal:main

Some files were not shown because too many files have changed in this diff Show More