Update api.py
Browse files
api.py
CHANGED
|
@@ -3,9 +3,11 @@ import json
|
|
| 3 |
import logging
|
| 4 |
import os
|
| 5 |
from typing import Optional
|
|
|
|
| 6 |
|
| 7 |
import cv2
|
| 8 |
import numpy as np
|
|
|
|
| 9 |
import uvicorn
|
| 10 |
from fastapi import FastAPI, File, Form, UploadFile, HTTPException
|
| 11 |
from fastapi.middleware.cors import CORSMiddleware
|
|
@@ -16,6 +18,9 @@ from ultralytics import YOLO
|
|
| 16 |
logging.basicConfig(level=logging.INFO)
|
| 17 |
logger = logging.getLogger(__name__)
|
| 18 |
|
|
|
|
|
|
|
|
|
|
| 19 |
app = FastAPI(title="Arise AI API", version="1.0.0")
|
| 20 |
|
| 21 |
# CORS
|
|
@@ -62,9 +67,24 @@ async def analyze_endpoint(
|
|
| 62 |
pil_image = pil_image.convert("RGB")
|
| 63 |
img_np = np.array(pil_image)
|
| 64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
# Run Inference
|
| 66 |
logger.info("Running YOLO inference...")
|
| 67 |
-
results = model(img_np, conf=0.
|
| 68 |
|
| 69 |
detections = []
|
| 70 |
primary_issue = "Unknown"
|
|
@@ -90,7 +110,7 @@ async def analyze_endpoint(
|
|
| 90 |
|
| 91 |
# Fallback: Check Description if YOLO fails
|
| 92 |
if primary_issue == "Unknown" and description:
|
| 93 |
-
logger.info(f"YOLO
|
| 94 |
desc_lower = description.lower()
|
| 95 |
keywords = {
|
| 96 |
"pothole": "Pothole",
|
|
@@ -119,8 +139,9 @@ async def analyze_endpoint(
|
|
| 119 |
|
| 120 |
|
| 121 |
# Process Image for Overlay (if needed)
|
| 122 |
-
#
|
| 123 |
-
|
|
|
|
| 124 |
is_success, buffer = cv2.imencode(".jpg", cv2.cvtColor(annotated_frame, cv2.COLOR_RGB2BGR))
|
| 125 |
processed_image_base64 = None
|
| 126 |
if is_success:
|
|
@@ -160,6 +181,20 @@ async def analyze_endpoint(
|
|
| 160 |
"processed_image": processed_image_base64,
|
| 161 |
"resolution_estimation": {
|
| 162 |
"estimated_hours": 24 if severity == "High" else 48
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
}
|
| 164 |
}
|
| 165 |
|
|
|
|
| 3 |
import logging
|
| 4 |
import os
|
| 5 |
from typing import Optional
|
| 6 |
+
from collections import deque
|
| 7 |
|
| 8 |
import cv2
|
| 9 |
import numpy as np
|
| 10 |
+
import imagehash
|
| 11 |
import uvicorn
|
| 12 |
from fastapi import FastAPI, File, Form, UploadFile, HTTPException
|
| 13 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 18 |
logging.basicConfig(level=logging.INFO)
|
| 19 |
logger = logging.getLogger(__name__)
|
| 20 |
|
| 21 |
+
# In-memory store for recent image hashes (Simple Deduplication)
|
| 22 |
+
RECENT_HASHES = deque(maxlen=100)
|
| 23 |
+
|
| 24 |
app = FastAPI(title="Arise AI API", version="1.0.0")
|
| 25 |
|
| 26 |
# CORS
|
|
|
|
| 67 |
pil_image = pil_image.convert("RGB")
|
| 68 |
img_np = np.array(pil_image)
|
| 69 |
|
| 70 |
+
# --- Enhanced Features ---
|
| 71 |
+
|
| 72 |
+
# 1. Spam Detection (Blur Analysis)
|
| 73 |
+
# Calculate variance of Laplacian: Low variance = Blurry = Potential Spam/Low Quality
|
| 74 |
+
gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
|
| 75 |
+
blur_score = cv2.Laplacian(gray, cv2.CV_64F).var()
|
| 76 |
+
is_spam = blur_score < 100.0 # Threshold can be tuned
|
| 77 |
+
|
| 78 |
+
# 2. Deduplication (Perceptual Hashing)
|
| 79 |
+
phash = str(imagehash.phash(pil_image))
|
| 80 |
+
is_duplicate = phash in RECENT_HASHES
|
| 81 |
+
RECENT_HASHES.append(phash)
|
| 82 |
+
|
| 83 |
+
logger.info(f"Image Analysis - Spam(Blur): {is_spam} ({blur_score:.2f}), Duplicate: {is_duplicate}, Hash: {phash}")
|
| 84 |
+
|
| 85 |
# Run Inference
|
| 86 |
logger.info("Running YOLO inference...")
|
| 87 |
+
results = model(img_np, conf=0.1) # Lower confidence to 0.1 to catch more objects
|
| 88 |
|
| 89 |
detections = []
|
| 90 |
primary_issue = "Unknown"
|
|
|
|
| 110 |
|
| 111 |
# Fallback: Check Description if YOLO fails
|
| 112 |
if primary_issue == "Unknown" and description:
|
| 113 |
+
logger.info(f"YOLO found no objects, checking description: {description}")
|
| 114 |
desc_lower = description.lower()
|
| 115 |
keywords = {
|
| 116 |
"pothole": "Pothole",
|
|
|
|
| 139 |
|
| 140 |
|
| 141 |
# Process Image for Overlay (if needed)
|
| 142 |
+
# return the base64 of the plotted image with bounding boxes and labels
|
| 143 |
+
# Customizing plot: line_width=2 (thinner), font_size=1.0 (smaller) for cleaner mobile view
|
| 144 |
+
annotated_frame = result.plot(line_width=2, font_size=1.0)
|
| 145 |
is_success, buffer = cv2.imencode(".jpg", cv2.cvtColor(annotated_frame, cv2.COLOR_RGB2BGR))
|
| 146 |
processed_image_base64 = None
|
| 147 |
if is_success:
|
|
|
|
| 181 |
"processed_image": processed_image_base64,
|
| 182 |
"resolution_estimation": {
|
| 183 |
"estimated_hours": 24 if severity == "High" else 48
|
| 184 |
+
},
|
| 185 |
+
"spam_analysis": {
|
| 186 |
+
"is_spam": is_spam,
|
| 187 |
+
"spam_score": round(blur_score, 2),
|
| 188 |
+
"reason": "Image is too blurry" if is_spam else None
|
| 189 |
+
},
|
| 190 |
+
"deduplication": {
|
| 191 |
+
"is_duplicate": is_duplicate,
|
| 192 |
+
"image_hash": phash,
|
| 193 |
+
"method": "phash"
|
| 194 |
+
},
|
| 195 |
+
"steganography": {
|
| 196 |
+
"has_hidden_data": False,
|
| 197 |
+
"method": None
|
| 198 |
}
|
| 199 |
}
|
| 200 |
|