Buck_Tracker / api /detection.py
codewithRiz's picture
edit tag
b177e35
# from fastapi import APIRouter, UploadFile, File, Form, HTTPException
# from pathlib import Path
# import cv2
# import numpy as np
# from .config import UPLOAD_DIR
# from .utils import (
# validate_form,
# process_image,
# save_image,
# load_json,
# save_json,
# validate_user_and_camera,extract_metadata
# )
# router = APIRouter()
# @router.post("/predict")
# async def predict(
# user_id: str = Form(...),
# camera_name: str = Form(...),
# images: list[UploadFile] = File(...)
# ):
# images = validate_form(user_id, camera_name, images)
# validate_user_and_camera(user_id, camera_name)
# base = Path(UPLOAD_DIR) / user_id / camera_name
# base.mkdir(parents=True, exist_ok=True)
# json_path = base / f"{camera_name}_detections.json"
# data = load_json(json_path)
# new_results = []
# for file in images:
# raw = await file.read()
# metadata = extract_metadata(raw)
# nparr = np.frombuffer(raw, np.uint8)
# img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
# if img is None:
# raise HTTPException(400, f"Invalid image: {file.filename}")
# detections = process_image(img)
# url = save_image(user_id, camera_name, file.filename, raw)
# record = {
# "filename": file.filename,
# "image_url": url,
# "detections": detections,
# "metadata": metadata
# }
# data.append(record)
# new_results.append(record)
# save_json(json_path, data)
# return {
# "message": "Images processed successfully",
# "camera": camera_name,
# "results": new_results
# }
from fastapi import APIRouter, UploadFile, File, Form, HTTPException
from pydantic import BaseModel
from pathlib import Path
from typing import Optional, List
import cv2
import numpy as np
import logging
from .config import UPLOAD_DIR
from .utils import (
validate_form,
process_image,
save_image,
load_json,
save_json,
validate_user_and_camera,
extract_metadata
)
router = APIRouter()
logger = logging.getLogger(__name__)
# ─── existing endpoint (unchanged) ───────────────────────────────────────────
@router.post("/predict")
async def predict(
user_id: str = Form(...),
camera_name: str = Form(...),
images: list[UploadFile] = File(...)
):
images = validate_form(user_id, camera_name, images)
validate_user_and_camera(user_id, camera_name)
base = Path(UPLOAD_DIR) / user_id / camera_name
base.mkdir(parents=True, exist_ok=True)
json_path = base / f"{camera_name}_detections.json"
data = load_json(json_path)
new_results = []
for file in images:
raw = await file.read()
metadata = extract_metadata(raw)
nparr = np.frombuffer(raw, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if img is None:
raise HTTPException(400, f"Invalid image: {file.filename}")
detections = process_image(img)
url = save_image(user_id, camera_name, file.filename, raw)
record = {
"filename": file.filename,
"image_url": url,
"detections": detections,
"metadata": metadata
}
data.append(record)
new_results.append(record)
save_json(json_path, data)
return {
"message": "Images processed successfully",
"camera": camera_name,
"results": new_results
}
# ─── NEW: request model ───────────────────────────────────────────────────────
class UpdateDetectionRequest(BaseModel):
user_id: str
camera_name: str
image_url: str # used to locate the record
detection_index: int = 0 # which detection inside the image to edit
new_label: str # e.g. "Buck", "Doe", "Unknown"
new_bbox: Optional[List[float]] = None # [x1,y1,x2,y2] in natural px, or null
# ─── NEW: endpoint ────────────────────────────────────────────────────────────
@router.post("/update_detection")
async def update_detection(req: UpdateDetectionRequest):
"""
Edit the label and/or bounding box of one detection on an already-processed image.
Writes the change back into user_data/<user_id>/<camera_name>/<camera_name>_detections.json
"""
# ── 1. First check if user directory exists ─────────────────────────
user_path = Path(UPLOAD_DIR) / req.user_id
if not user_path.exists() or not user_path.is_dir():
raise HTTPException(
status_code=404,
detail="user not found"
)
# ── 2. Check if camera directory exists ────────────────────────────
camera_path = user_path / req.camera_name
if not camera_path.exists() or not camera_path.is_dir():
raise HTTPException(
status_code=404,
detail="camera not found"
)
# ── 3. Check if detections JSON file exists ─────────────────────────
json_path = camera_path / f"{req.camera_name}_detections.json"
if not json_path.exists():
raise HTTPException(
status_code=404,
detail="camera not found" # Camera ke hisaab se JSON file nahi hai
)
# ── 4. Load the JSON data ───────────────────────────────────────────
data = load_json(json_path)
# ── 5. Find the record by matching the filename ─────────────────────
target_filename = req.image_url.split("/")[-1].split("?")[0]
record = None
for item in data:
stored = item.get("image_url", item.get("filename", ""))
stored_filename = stored.split("/")[-1].split("?")[0]
if stored_filename == target_filename:
record = item
break
if record is None:
raise HTTPException(
status_code=404,
detail="image not found"
)
# ── 6. Apply the edit ───────────────────────────────────────────────
dets = record.get("detections", [])
if not dets:
# Image had zero detections before β€” create the first one manually
record["detections"] = [{
"label": req.new_label,
"confidence": 1.0,
"bbox": req.new_bbox or [],
"manually_edited": True
}]
elif req.detection_index < len(dets):
# Normal case: update the detection at the requested index
dets[req.detection_index]["label"] = req.new_label
dets[req.detection_index]["manually_edited"] = True
if req.new_bbox is not None:
dets[req.detection_index]["bbox"] = req.new_bbox
else:
raise HTTPException(
status_code=400,
detail=f"detection_index {req.detection_index} is out of range "
f"(image has {len(dets)} detection(s))"
)
# ── 7. Save back ────────────────────────────────────────────────────
save_json(json_path, data)
logger.info(
"Detection updated | user=%s camera=%s file=%s idx=%d label=%s bbox=%s",
req.user_id, req.camera_name, target_filename,
req.detection_index, req.new_label, req.new_bbox
)
return {
"success": True,
"message": "Detection updated successfully",
"updated": {
"filename": target_filename,
"detection_index": req.detection_index,
"new_label": req.new_label,
"new_bbox": req.new_bbox,
}
}