123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- from flask import Flask, request, jsonify, render_template, send_from_directory
- import os
- from image_search import ImageSearchEngine
- import magic
- from urllib.request import urlretrieve
- import time
- app = Flask(__name__)
- UPLOAD_FOLDER = 'static/images'
- ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'bmp'}
- TOP_K = 5
- MIN_SCORE = 0.0
- MAX_SCORE = 100.0
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
- search_engine = ImageSearchEngine()
- def allowed_file(filename):
- """检查文件是否允许上传"""
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
- def is_valid_image(file_path):
- """检查文件是否为有效的图片文件"""
- try:
- mime = magic.Magic(mime=True)
- file_type = mime.from_file(file_path)
- return file_type.startswith('image/')
- except Exception:
- return False
- @app.route('/')
- def index():
- """渲染主页"""
- return render_template('index.html')
- @app.route('/upload', methods=['POST'])
- def upload_file():
- """处理图片上传请求"""
- try:
-
- data = request.get_json()
- if not data:
- return jsonify({'error': '请求必须包含JSON数据'}), 400
-
- product_id = data.get('product_id')
- image_url = data.get('url')
-
- if not product_id:
- return jsonify({'error': '缺少product_id参数'}), 400
- if not image_url:
- return jsonify({'error': '缺少url参数'}), 400
- if not isinstance(image_url, str) or not (image_url.startswith('http://') or image_url.startswith('https://')):
- return jsonify({'error': '无效的图片URL'}), 400
-
- time_base = str(time.time() * 1000)
- image_path = os.path.join(UPLOAD_FOLDER, time_base + os.path.basename(image_url))
- urlretrieve(image_url, image_path)
- if allowed_file(image_path) and is_valid_image(image_path):
-
- if search_engine.add_image_from_url(image_path, product_id):
-
- os.remove(image_path)
- return jsonify({'message': '上传成功', 'product_id': product_id})
- else:
- return jsonify({'error': '处理图片失败'}), 500
- else:
- return jsonify({'error': '图片错误'}), 500
-
- except Exception as e:
- return jsonify({'error': str(e)}), 500
- @app.route('/search', methods=['POST'])
- def search():
- """处理图片搜索请求"""
- try:
-
- data = request.get_json()
- if not data:
- return jsonify({'error': '请求必须包含JSON数据'}), 400
-
- image_url = data.get('url')
- if not image_url:
- return jsonify({'error': '缺少url参数'}), 400
- if not isinstance(image_url, str) or not (image_url.startswith('http://') or image_url.startswith('https://')):
- return jsonify({'error': '无效的图片URL'}), 400
-
-
- limit = data.get('limit', TOP_K)
- min_score = data.get('min_score', MIN_SCORE)
- max_score = data.get('max_score', MAX_SCORE)
-
-
- try:
- limit = int(limit)
- min_score = float(min_score)
- max_score = float(max_score)
-
- if limit <= 0:
- return jsonify({'error': 'limit必须大于0'}), 400
- if min_score < 0 or min_score > 100:
- return jsonify({'error': 'min_score必须在0到100之间'}), 400
- if max_score < 0 or max_score > 100:
- return jsonify({'error': 'max_score必须在0到100之间'}), 400
- if min_score > max_score:
- return jsonify({'error': 'min_score不能大于max_score'}), 400
- except ValueError:
- return jsonify({'error': '参数类型错误'}), 400
-
- start_download_time = time.time()
- time_base = str(time.time() * 1000)
- image_path = os.path.join(UPLOAD_FOLDER, time_base + os.path.basename(image_url))
- urlretrieve(image_url, image_path)
- end_download_time = time.time()
- print(f"下载耗时: { end_download_time - start_download_time } s",)
- if allowed_file(image_path) and is_valid_image(image_path):
-
- start_search_time = time.time()
- results = search_engine.search(image_path, top_k=limit)
- os.remove(image_path)
-
- formatted_results = []
- for product_id, score in results:
- similarity = round(score * 100, 2)
- if min_score <= similarity <= max_score:
- formatted_results.append({
- 'product_id': product_id,
- 'similarity': similarity
- })
- end_search_time = time.time()
- print(f"搜索耗时: { end_search_time - start_search_time } s",)
- return jsonify({'results': formatted_results})
- else:
- return jsonify({'error': '图片错误'})
- except Exception as e:
- return jsonify({'error': str(e)}), 500
- @app.route('/remove/<product_id>', methods=['POST'])
- def remove_image(product_id):
- """移除指定商品ID的图片特征"""
- try:
-
- if search_engine.remove_by_product_id(product_id):
- return jsonify({'message': '删除成功', 'product_id': product_id})
- else:
- return jsonify({'error': '商品ID不存在或删除失败'}), 404
-
- except Exception as e:
- return jsonify({'error': str(e)}), 500
- @app.route('/clear/<password>', methods=['POST'])
- def clear_index(password):
- """清空索引"""
- if password == "infish@2025":
- try:
- search_engine.clear()
- except Exception as e:
- return jsonify({'error': str(e)}), 500
- if __name__ == '__main__':
- app.run(host='0.0.0.0', port=5000, debug=False)
|