Spaces:
Build error
Build error
Upload 2 files
Browse files- app.py +127 -0
- labels.txt +81 -0
app.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from fastapi.responses import StreamingResponse, JSONResponse
|
| 3 |
+
from io import BytesIO
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import cv2
|
| 6 |
+
import supervision as sv
|
| 7 |
+
from inference import get_model
|
| 8 |
+
|
| 9 |
+
app = FastAPI()
|
| 10 |
+
|
| 11 |
+
# Load the pre-trained model once when the server starts
|
| 12 |
+
model = get_model(model_id="yolov11n-640")
|
| 13 |
+
|
| 14 |
+
def gen_frames():
|
| 15 |
+
# Open the default camera (index 0)
|
| 16 |
+
cap = cv2.VideoCapture(0)
|
| 17 |
+
if not cap.isOpened():
|
| 18 |
+
raise HTTPException(status_code=500, detail="Could not open video device")
|
| 19 |
+
|
| 20 |
+
while True:
|
| 21 |
+
success, frame = cap.read()
|
| 22 |
+
if not success:
|
| 23 |
+
break
|
| 24 |
+
|
| 25 |
+
# Convert the frame from BGR (OpenCV default) to RGB
|
| 26 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 27 |
+
# Convert the numpy array to a PIL Image
|
| 28 |
+
pil_img = Image.fromarray(frame_rgb)
|
| 29 |
+
|
| 30 |
+
# Run inference on the current frame (model.infer returns a list, so we take the first element)
|
| 31 |
+
try:
|
| 32 |
+
results = model.infer(pil_img)[0]
|
| 33 |
+
except Exception as e:
|
| 34 |
+
# If inference fails, skip this frame
|
| 35 |
+
print(f"Inference error: {e}")
|
| 36 |
+
continue
|
| 37 |
+
|
| 38 |
+
# Convert inference results to a Supervision detections object
|
| 39 |
+
detections = sv.Detections.from_inference(results)
|
| 40 |
+
|
| 41 |
+
# Create annotators for bounding boxes and labels
|
| 42 |
+
bounding_box_annotator = sv.BoxAnnotator()
|
| 43 |
+
label_annotator = sv.LabelAnnotator()
|
| 44 |
+
|
| 45 |
+
# Annotate the frame with bounding boxes and labels
|
| 46 |
+
annotated_image = bounding_box_annotator.annotate(scene=pil_img, detections=detections)
|
| 47 |
+
annotated_image = label_annotator.annotate(scene=annotated_image, detections=detections)
|
| 48 |
+
|
| 49 |
+
# Save the annotated image to an in-memory buffer in JPEG format
|
| 50 |
+
buf = BytesIO()
|
| 51 |
+
annotated_image.save(buf, format="JPEG")
|
| 52 |
+
buf.seek(0)
|
| 53 |
+
frame_bytes = buf.read()
|
| 54 |
+
|
| 55 |
+
# Yield the frame in MJPEG format
|
| 56 |
+
yield (b'--frame\r\n'
|
| 57 |
+
b'Content-Type: image/jpeg\r\n\r\n' + frame_bytes + b'\r\n')
|
| 58 |
+
|
| 59 |
+
# Release the camera when the loop ends
|
| 60 |
+
cap.release()
|
| 61 |
+
|
| 62 |
+
@app.get("/live_feed")
|
| 63 |
+
async def live_feed():
|
| 64 |
+
"""
|
| 65 |
+
Streams a live feed from the camera with inference annotations.
|
| 66 |
+
Access via: http://localhost:8000/live_feed
|
| 67 |
+
"""
|
| 68 |
+
return StreamingResponse(
|
| 69 |
+
gen_frames(),
|
| 70 |
+
media_type="multipart/x-mixed-replace; boundary=frame"
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
@app.get("/detect_classes")
|
| 74 |
+
async def detect_classes():
|
| 75 |
+
"""
|
| 76 |
+
Detects and returns the labels of the detected classes in the current camera frame as JSON.
|
| 77 |
+
Updates every 30 frames.
|
| 78 |
+
Access via: http://localhost:8000/detect_classes
|
| 79 |
+
"""
|
| 80 |
+
# Open the default camera (index 0)
|
| 81 |
+
cap = cv2.VideoCapture(0)
|
| 82 |
+
if not cap.isOpened():
|
| 83 |
+
raise HTTPException(status_code=500, detail="Could not open video device")
|
| 84 |
+
|
| 85 |
+
frame_count = 0
|
| 86 |
+
all_class_ids = []
|
| 87 |
+
|
| 88 |
+
while frame_count < 3:
|
| 89 |
+
success, frame = cap.read()
|
| 90 |
+
if not success:
|
| 91 |
+
cap.release()
|
| 92 |
+
raise HTTPException(status_code=500, detail="Failed to read frame from camera")
|
| 93 |
+
|
| 94 |
+
# Convert the frame from BGR (OpenCV default) to RGB
|
| 95 |
+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 96 |
+
# Convert the numpy array to a PIL Image
|
| 97 |
+
pil_img = Image.fromarray(frame_rgb)
|
| 98 |
+
|
| 99 |
+
# Run inference on the current frame (model.infer returns a list, so we take the first element)
|
| 100 |
+
try:
|
| 101 |
+
results = model.infer(pil_img)[0]
|
| 102 |
+
except Exception as e:
|
| 103 |
+
print(f"Inference error: {e}")
|
| 104 |
+
continue
|
| 105 |
+
|
| 106 |
+
# Convert inference results to a Supervision detections object
|
| 107 |
+
detections = sv.Detections.from_inference(results)
|
| 108 |
+
|
| 109 |
+
# Collect class IDs from the current frame
|
| 110 |
+
if detections.class_id is not None:
|
| 111 |
+
all_class_ids.extend(detections.class_id.tolist())
|
| 112 |
+
|
| 113 |
+
frame_count += 1
|
| 114 |
+
|
| 115 |
+
# Release the camera
|
| 116 |
+
cap.release()
|
| 117 |
+
|
| 118 |
+
# Extract unique detected class labels from all collected class IDs
|
| 119 |
+
unique_class_ids = set(all_class_ids)
|
| 120 |
+
class_labels = [model.class_names[class_id] for class_id in unique_class_ids]
|
| 121 |
+
|
| 122 |
+
# Return the detected class labels as JSON
|
| 123 |
+
return JSONResponse(content={"detected_classes": class_labels})
|
| 124 |
+
|
| 125 |
+
if __name__ == "__main__":
|
| 126 |
+
import uvicorn
|
| 127 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
labels.txt
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
person
|
| 2 |
+
bicycle
|
| 3 |
+
car
|
| 4 |
+
motorcycle
|
| 5 |
+
airplane
|
| 6 |
+
bus
|
| 7 |
+
train
|
| 8 |
+
truck
|
| 9 |
+
boat
|
| 10 |
+
traffic light
|
| 11 |
+
fire hydrant
|
| 12 |
+
stop sign
|
| 13 |
+
parking meter
|
| 14 |
+
bench
|
| 15 |
+
bird
|
| 16 |
+
cat
|
| 17 |
+
dog
|
| 18 |
+
horse
|
| 19 |
+
sheep
|
| 20 |
+
cow
|
| 21 |
+
elephant
|
| 22 |
+
bear
|
| 23 |
+
zebra
|
| 24 |
+
giraffe
|
| 25 |
+
backpack
|
| 26 |
+
umbrella
|
| 27 |
+
handbag
|
| 28 |
+
tie
|
| 29 |
+
suitcase
|
| 30 |
+
frisbee
|
| 31 |
+
skis
|
| 32 |
+
snowboard
|
| 33 |
+
sports ball
|
| 34 |
+
kite
|
| 35 |
+
baseball bat
|
| 36 |
+
baseball glove
|
| 37 |
+
skateboard
|
| 38 |
+
surfboard
|
| 39 |
+
tennis racket
|
| 40 |
+
bottle
|
| 41 |
+
wine glass
|
| 42 |
+
cup
|
| 43 |
+
fork
|
| 44 |
+
knife
|
| 45 |
+
spoon
|
| 46 |
+
bowl
|
| 47 |
+
banana
|
| 48 |
+
apple
|
| 49 |
+
sandwich
|
| 50 |
+
orange
|
| 51 |
+
broccoli
|
| 52 |
+
carrot
|
| 53 |
+
hot dog
|
| 54 |
+
pizza
|
| 55 |
+
donut
|
| 56 |
+
cake
|
| 57 |
+
chair
|
| 58 |
+
couch
|
| 59 |
+
potted plant
|
| 60 |
+
bed
|
| 61 |
+
dining table
|
| 62 |
+
toilet
|
| 63 |
+
tv
|
| 64 |
+
laptop
|
| 65 |
+
mouse
|
| 66 |
+
remote
|
| 67 |
+
keyboard
|
| 68 |
+
cell phone
|
| 69 |
+
microwave
|
| 70 |
+
oven
|
| 71 |
+
toaster
|
| 72 |
+
sink
|
| 73 |
+
refrigerator
|
| 74 |
+
book
|
| 75 |
+
clock
|
| 76 |
+
vase
|
| 77 |
+
scissors
|
| 78 |
+
teddy bear
|
| 79 |
+
hair drier
|
| 80 |
+
toothbrush
|
| 81 |
+
|