bar_code / app.py
mohamedtsou's picture
Update app.py
d049900 verified
from flask import Flask, request, jsonify
from flask_cors import CORS
import cv2
import numpy as np
import base64
import io
from pyzbar.pyzbar import decode
from PIL import Image
import os
import logging
import requests
# ================== INITIALISATION ==================
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "*"}})
HF_SPACE = os.environ.get("SPACE_ID") is not None
PORT = 7860 if HF_SPACE else int(os.environ.get("PORT", 5000))
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("barcode-api")
# ================== UTILS ==================
def safe_b64decode(data: str) -> bytes:
"""Base64 decode safe (Flutter/Web compatible)"""
return base64.b64decode(data + "=" * (-len(data) % 4))
def decode_barcode(image_bytes: bytes) -> dict:
"""Decode barcode using PIL then OpenCV"""
try:
# --- PIL first ---
try:
pil_image = Image.open(io.BytesIO(image_bytes))
barcodes = decode(pil_image)
if barcodes:
b = barcodes[0]
return {
"success": True,
"barcode": b.data.decode("utf-8"),
"type": b.type,
"method": "pil"
}
except Exception as e:
logger.debug(f"PIL failed: {e}")
# --- OpenCV fallback ---
try:
nparr = np.frombuffer(image_bytes, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_GRAYSCALE)
if img is not None:
barcodes = decode(img)
if barcodes:
b = barcodes[0]
return {
"success": True,
"barcode": b.data.decode("utf-8"),
"type": b.type,
"method": "opencv"
}
# Contrast enhancement
clahe = cv2.createCLAHE(2.0, (8, 8))
enhanced = clahe.apply(img)
barcodes = decode(enhanced)
if barcodes:
b = barcodes[0]
return {
"success": True,
"barcode": b.data.decode("utf-8"),
"type": b.type,
"method": "enhanced"
}
except Exception as e:
logger.debug(f"OpenCV failed: {e}")
return {"success": False, "error": "No barcode detected"}
except Exception as e:
logger.error(e)
return {"success": False, "error": str(e)}
# ================== FLUTTER API ==================
@app.route("/api/decode-barcode", methods=["POST"])
def api_decode_barcode():
if not request.is_json:
return jsonify({"success": False, "error": "JSON required"}), 400
data = request.get_json()
image_data = data.get("image")
if not image_data:
return jsonify({"success": False, "error": "Image missing"}), 400
if "," in image_data:
image_data = image_data.split(",")[1]
if len(image_data) > 3 * 1024 * 1024:
return jsonify({"success": False, "error": "Image too large"}), 400
image_bytes = safe_b64decode(image_data)
result = decode_barcode(image_bytes)
return jsonify(result)
@app.route("/api/product-info/<barcode>", methods=["GET"])
def api_product_info(barcode):
local_db = {
"3017620422003": {
"name": "Nutella",
"brand": "Ferrero",
"category": "Food",
"price": 4.99,
"description": "Hazelnut spread"
},
"5901234123457": {
"name": "Milk UHT",
"brand": "Candia",
"category": "Food",
"price": 1.20,
"description": "UHT milk"
}
}
if barcode in local_db:
return jsonify({"success": True, "product": local_db[barcode], "source": "local"})
# OpenFoodFacts fallback
try:
r = requests.get(
f"https://world.openfoodfacts.org/api/v0/product/{barcode}.json",
timeout=4
)
if r.status_code == 200:
data = r.json()
if data.get("status") == 1:
p = data["product"]
return jsonify({
"success": True,
"source": "openfoodfacts",
"product": {
"name": p.get("product_name"),
"brand": p.get("brands"),
"category": p.get("categories"),
"description": p.get("generic_name"),
"image": p.get("image_url")
}
})
except Exception as e:
logger.debug(e)
return jsonify({"success": False, "error": "Product not found"}), 404
# ================== WEB SCAN ==================
@app.route("/api/scan", methods=["POST"])
def api_scan():
if not request.is_json:
return jsonify({"success": False, "error": "JSON required"}), 400
image_data = request.json.get("image")
if not image_data:
return jsonify({"success": False, "error": "Image missing"}), 400
if "," in image_data:
image_data = image_data.split(",")[1]
image_bytes = safe_b64decode(image_data)
return jsonify(decode_barcode(image_bytes))
# ================== HEALTH ==================
@app.route("/api/health")
def health():
return jsonify({
"status": "ok",
"service": "Barcode Scanner API",
"platform": "HuggingFace" if HF_SPACE else "Local",
"endpoints": [
"/api/decode-barcode",
"/api/product-info/<barcode>",
"/api/scan",
"/api/health"
]
})
@app.route("/")
def home():
return jsonify({
"message": "Barcode Scanner API",
"use": "/api/decode-barcode (POST)"
})
# ================== MAIN ==================
if __name__ == "__main__":
app.run(host="0.0.0.0", port=PORT, debug=False)