Spaces:
Sleeping
Sleeping
| """Flask web app for crack detection and measurement.""" | |
| from __future__ import annotations | |
| import base64 | |
| import io | |
| import os | |
| import cv2 | |
| import numpy as np | |
| from flask import Flask, jsonify, render_template, request, send_file | |
| from PIL import Image | |
| import aruco_scale | |
| import crack_pipeline | |
| app = Flask(__name__) | |
| app.config["MAX_CONTENT_LENGTH"] = 25 * 1024 * 1024 # 25 MB upload cap | |
| def _read_image(file_storage) -> np.ndarray: | |
| """Decode an uploaded file into an RGB numpy array.""" | |
| image = Image.open(io.BytesIO(file_storage.read())).convert("RGB") | |
| return np.array(image) | |
| def _encode_png(image_rgb: np.ndarray) -> str: | |
| """Encode an RGB array as a base64 PNG data URI.""" | |
| bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR) | |
| ok, buf = cv2.imencode(".png", bgr) | |
| if not ok: | |
| raise RuntimeError("PNG encoding failed") | |
| return "data:image/png;base64," + base64.b64encode(buf).decode("ascii") | |
| def index(): | |
| return render_template("index.html") | |
| def health(): | |
| return jsonify(status="ok") | |
| def marker(): | |
| """Serve the printable ArUco marker PDF (generated by make_marker.py).""" | |
| size = int(aruco_scale.DEFAULT_MARKER_LENGTH_MM) | |
| pdf = os.path.join(os.path.dirname(__file__), f"aruco_marker_{size}mm_A4.pdf") | |
| if not os.path.exists(pdf): | |
| return jsonify(error="Marker PDF missing - run: python make_marker.py"), 404 | |
| return send_file(pdf, mimetype="application/pdf") | |
| def analyze(): | |
| """Analyse an upload and return only the computer-vision result. | |
| Scale always comes from the ArUco marker (no manual options); the model | |
| threshold is fixed. The response carries the CV overlay and the CV | |
| measurement (intensity-based, refined within the ML-detected region). | |
| """ | |
| if "image" not in request.files or request.files["image"].filename == "": | |
| return jsonify(error="No image uploaded"), 400 | |
| try: | |
| image_rgb = _read_image(request.files["image"]) | |
| except Exception: | |
| return jsonify(error="Could not read the uploaded image"), 400 | |
| result = crack_pipeline.analyze( | |
| image_rgb, use_aruco=True, | |
| marker_length_mm=aruco_scale.DEFAULT_MARKER_LENGTH_MM, | |
| ) | |
| overlay = result["overlay"] | |
| return jsonify( | |
| overlay=_encode_png(overlay), | |
| measurements=result["measurements"], | |
| aruco=result["aruco"], | |
| image_size={"width": overlay.shape[1], "height": overlay.shape[0]}, | |
| ) | |
| if __name__ == "__main__": | |
| # Port 5000 is taken by macOS AirPlay Receiver, so default to 5001. | |
| port = int(os.environ.get("PORT", 5001)) | |
| print("Loading crack segmentation model...") | |
| crack_pipeline.get_model() | |
| print(f"Model ready. Open http://127.0.0.1:{port}") | |
| app.run(host="127.0.0.1", port=port, debug=False) | |