import io import os import re import time import numpy as np import logging import urllib.request from flask import Blueprint, request, jsonify from PIL import Image from app.models.sql_factory import search_by_phash, add_phash from app.utils.im_utils import compute_phash_int from app.utils.file_utils import sha256_stream sanitize_re = re.compile('[\W]+') valid_exts = ['.gif', '.jpg', '.jpeg', '.png'] MATCH_THRESHOLD = 1 MATCH_LIMIT = 1 SIMILAR_THRESHOLD = 20 SIMILAR_LIMIT = 10 api = Blueprint('api', __name__) @api.route('/') def index(): """ API status test endpoint """ return jsonify({ 'status': 'ok' }) def get_params(default_threshold=MATCH_THRESHOLD, default_limit=MATCH_LIMIT): try: threshold = int(request.form.get('threshold') or default_threshold) limit = int(request.form.get('limit') or default_limit) except: return None, 'param_error' # Process uploaded file if 'q' in request.files: file = request.files['q'] fn = file.filename # demo client currently uploads a jpeg called 'blob' if fn.endswith('blob'): logging.debug('received a blob, assuming JPEG') fn = 'filename.jpg' basename, ext = os.path.splitext(fn) if ext.lower() not in valid_exts: return None, 'not_an_image' ext = ext[1:].lower() raw = None im = Image.open(file.stream).convert('RGB') url = None # Fetch remote URL else: url = request.form.get('url') if not url: return None, 'no_image' basename, ext = os.path.splitext(url) if ext.lower() not in valid_exts: return None, 'not_an_image' ext = ext[1:].lower() remote_request = urllib.request.Request(url) remote_response = urllib.request.urlopen(remote_request) raw = remote_response.read() im = Image.open(io.BytesIO(raw)).convert('RGB') return (threshold, limit, url, ext, raw, im,), None @api.route('/v1/match', methods=['POST']) def match(): """ Search by uploading an image """ params, error = get_params() if error: return jsonify({ 'success': False, 'match': False, 'added': False, 'error': error, }) threshold, limit, url, ext, raw, im = params start = time.time() phash = compute_phash_int(im) results = search_by_phash(phash=phash, threshold=threshold, limit=limit) if len(results) == 0: if url: # hash = sha256_stream(file) hash = sha256_stream(io.BytesIO(raw)) add_phash(sha256=hash, phash=phash, ext=ext, url=url) match = False else: match = True logging.debug('query took {0:.2g} s.'.format(time.time() - start)) return jsonify({ 'success': True, 'match': match, 'added': not match, 'results': results, 'timing': time.time() - start, }) @api.route('/v1/similar', methods=['POST']) def similar(): """ Search by uploading an image """ params, error = get_params(default_threshold=SIMILAR_THRESHOLD, default_limit=SIMILAR_LIMIT) if error: return jsonify({ 'success': False, 'match': False, 'error': error, }) threshold, limit, url, ext, raw, im = params start = time.time() phash = compute_phash_int(im) ext = ext[1:].lower() results = search_by_phash(phash=phash, threshold=threshold, limit=limit) if len(results) == 0: match = False else: match = True logging.debug('query took {0:.2g} s.'.format(time.time() - start)) return jsonify({ 'success': True, 'match': match, 'results': results, 'timing': time.time() - start, })