Himel09's picture
Update CV-Project/app.py
4fd820d verified
Raw
History Blame Contribute Delete
7.23 kB
from flask import Flask, render_template, request, jsonify, Response
from ultralytics import YOLO
import cv2
import numpy as np
import base64
import os
from PIL import Image
import io
import threading
# ── TOGGLE: set USE_VLM = True to enable VLM analysis ────────────────────────
USE_VLM = True
# ─────────────────────────────────────────────────────────────────────────────
if USE_VLM:
from vlm_analyzer import MicroplasticRiskAnalyzer
app = Flask(__name__)
# ── YOLO MODEL PATH ───────────────────────────────────────────────────────────
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
MODEL_PATH = os.path.join(BASE_DIR, "best.pt")
# ─────────────────────────────────────────────────────────────────────────────
CONFIDENCE = 0.25
IOU_THRESH = 0.45
IMAGE_SIZE = 640
# Load YOLO once at startup
print("Loading YOLO model...")
model = YOLO(MODEL_PATH)
CLASS_NAMES = model.names
print(f"YOLO loaded. Classes: {CLASS_NAMES}")
# Load VLM if enabled
vlm = None
if USE_VLM:
print("Loading VLM (Qwen2-VL-2B-Instruct)...")
vlm = MicroplasticRiskAnalyzer()
print("VLM loaded and ready.")
# Webcam state
webcam_active = False
webcam_cap = None
webcam_lock = threading.Lock()
# ── HELPERS ───────────────────────────────────────────────────────────────────
def run_detection(image_array):
"""Run YOLO detection. Returns annotated image + detections list."""
results = model.predict(
source = image_array,
conf = CONFIDENCE,
iou = IOU_THRESH,
imgsz = IMAGE_SIZE,
verbose = False,
)[0]
annotated = results.plot(line_width=2)
detections = []
if results.boxes is not None and len(results.boxes):
for box in results.boxes:
detections.append({
"class": CLASS_NAMES[int(box.cls[0])],
"confidence": round(float(box.conf[0]) * 100, 1),
"bbox": [round(v) for v in box.xyxy[0].tolist()]
})
return annotated, detections
def numpy_to_base64(img_array):
"""Convert BGR numpy image β†’ base64 JPEG string."""
img_rgb = cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(img_rgb)
buffer = io.BytesIO()
pil_img.save(buffer, format="JPEG", quality=90)
return base64.b64encode(buffer.getvalue()).decode("utf-8")
def run_vlm_analysis(image_array, detections):
"""Run VLM scene analysis if enabled. Returns dict or None."""
if not USE_VLM or vlm is None:
return None
try:
return vlm.analyze_scene(image_array, detections)
except Exception as e:
print(f"[VLM ERROR] {e}")
return {
"risk_level": "Error",
"explanation": str(e),
"recommendations": "VLM analysis failed. Check logs.",
"raw": "",
}
# ── ROUTES ────────────────────────────────────────────────────────────────────
@app.route("/")
def index():
return render_template(
"index.html",
class_names = list(CLASS_NAMES.values()),
vlm_enabled = USE_VLM,
)
@app.route("/detect/image", methods=["POST"])
def detect_image():
"""YOLO detection (+ optional VLM) on uploaded image."""
if "file" not in request.files:
return jsonify({"error": "No file uploaded"}), 400
file = request.files["file"]
data = np.frombuffer(file.read(), np.uint8)
image = cv2.imdecode(data, cv2.IMREAD_COLOR)
if image is None:
return jsonify({"error": "Could not read image"}), 400
annotated, detections = run_detection(image)
vlm_result = run_vlm_analysis(image, detections)
response = {
"image": numpy_to_base64(annotated),
"detections": detections,
"count": len(detections),
"vlm": vlm_result, # None when VLM disabled
}
return jsonify(response)
@app.route("/detect/video_frame", methods=["POST"])
def detect_video_frame():
"""YOLO detection (+ optional VLM) on a single video frame sent as base64."""
data = request.get_json()
if not data or "frame" not in data:
return jsonify({"error": "No frame data"}), 400
# Strip data-URL prefix if present
raw_b64 = data["frame"].split(",")[-1]
frame_data = base64.b64decode(raw_b64)
nparr = np.frombuffer(frame_data, np.uint8)
image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if image is None:
return jsonify({"error": "Could not decode frame"}), 400
annotated, detections = run_detection(image)
vlm_result = run_vlm_analysis(image, detections)
return jsonify({
"image": numpy_to_base64(annotated),
"detections": detections,
"count": len(detections),
"vlm": vlm_result,
})
@app.route("/classes")
def get_classes():
return jsonify({
"classes": list(CLASS_NAMES.values()),
"total": len(CLASS_NAMES),
"vlm_enabled": USE_VLM,
})
# ── WEBCAM STREAM (server-side, optional) ─────────────────────────────────────
@app.route("/webcam/start", methods=["POST"])
def webcam_start():
global webcam_active, webcam_cap
with webcam_lock:
if webcam_active:
return jsonify({"status": "already_running"})
webcam_cap = cv2.VideoCapture(0)
if not webcam_cap.isOpened():
webcam_cap = None
return jsonify({"error": "Cannot open webcam"}), 500
webcam_active = True
return jsonify({"status": "started"})
@app.route("/webcam/stop", methods=["POST"])
def webcam_stop():
global webcam_active, webcam_cap
with webcam_lock:
webcam_active = False
if webcam_cap is not None:
webcam_cap.release()
webcam_cap = None
return jsonify({"status": "stopped"})
# ── ENTRY POINT ───────────────────────────────────────────────────────────────
if __name__ == "__main__":
port = int(os.environ.get("PORT", 7860))
app.run(debug=False, host="0.0.0.0", port=port)