Spaces:
Sleeping
Sleeping
Upload 6 files
Browse files- .gitattributes +1 -0
- Dockerfile +32 -0
- app.py +97 -0
- demo.mp4 +3 -0
- requirements.txt +9 -0
- templates/index.html +99 -0
- yolov8l.pt +3 -0
.gitattributes
CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
demo.mp4 filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
|
4 |
+
|
5 |
+
|
6 |
+
|
7 |
+
# Base image with Python 3.9
|
8 |
+
FROM python:3.9
|
9 |
+
|
10 |
+
# For fixing ImportError: libGL.so.1: cannot open shared object file: No such file or directory
|
11 |
+
RUN apt-get update
|
12 |
+
RUN apt install -y libgl1-mesa-glx
|
13 |
+
|
14 |
+
|
15 |
+
# Create a non-root user
|
16 |
+
RUN useradd -m -u 1000 user
|
17 |
+
|
18 |
+
# Set the working directory
|
19 |
+
WORKDIR /app
|
20 |
+
|
21 |
+
# Copy requirements.txt and install Python dependencies
|
22 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
23 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
24 |
+
|
25 |
+
# Copy the rest of the application code
|
26 |
+
COPY --chown=user . /app
|
27 |
+
|
28 |
+
# Switch to the non-root user
|
29 |
+
USER user
|
30 |
+
|
31 |
+
# Command to run the FastAPI application
|
32 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
app.py
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import cv2
|
3 |
+
from fastapi import FastAPI, Request, UploadFile, File
|
4 |
+
from fastapi.responses import StreamingResponse, HTMLResponse
|
5 |
+
from fastapi.templating import Jinja2Templates
|
6 |
+
from typing import Generator
|
7 |
+
from ultralytics import YOLO
|
8 |
+
import numpy as np
|
9 |
+
|
10 |
+
app = FastAPI()
|
11 |
+
templates = Jinja2Templates(directory="templates")
|
12 |
+
|
13 |
+
# Load the YOLOv8 model
|
14 |
+
model = YOLO("yolov8l.pt")
|
15 |
+
|
16 |
+
video_path = None
|
17 |
+
cap = None
|
18 |
+
bird_count = 0
|
19 |
+
tracker_initialized = False
|
20 |
+
trackers = None
|
21 |
+
|
22 |
+
@app.post("/upload_video/")
|
23 |
+
async def upload_video(file: UploadFile = File(...)):
|
24 |
+
global cap, tracker_initialized, trackers
|
25 |
+
|
26 |
+
# Save uploaded video file
|
27 |
+
file_location = f"uploads/{file.filename}"
|
28 |
+
with open(file_location, "wb") as f:
|
29 |
+
f.write(file.file.read())
|
30 |
+
|
31 |
+
# Open the uploaded video file
|
32 |
+
cap = cv2.VideoCapture(file_location)
|
33 |
+
|
34 |
+
# Reset tracker and counter
|
35 |
+
tracker_initialized = False
|
36 |
+
trackers = None
|
37 |
+
|
38 |
+
return {"info": f"file '{file.filename}' saved at '{file_location}'"}
|
39 |
+
|
40 |
+
def process_video() -> Generator[bytes, None, None]:
|
41 |
+
global bird_count, tracker_initialized, trackers, cap
|
42 |
+
while cap.isOpened():
|
43 |
+
success, frame = cap.read()
|
44 |
+
|
45 |
+
if success:
|
46 |
+
frame_height, frame_width = frame.shape[:2]
|
47 |
+
if not tracker_initialized:
|
48 |
+
results = model(frame)
|
49 |
+
detections = results[0].boxes.data.cpu().numpy()
|
50 |
+
bird_results = [detection for detection in detections if int(detection[5]) == 14]
|
51 |
+
|
52 |
+
try:
|
53 |
+
trackers = cv2.legacy.MultiTracker_create()
|
54 |
+
except AttributeError:
|
55 |
+
trackers = cv2.MultiTracker_create()
|
56 |
+
|
57 |
+
for res in bird_results:
|
58 |
+
x1, y1, x2, y2, confidence, class_id = res
|
59 |
+
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
|
60 |
+
if 0 <= x1 < frame_width and 0 <= y1 < frame_height and x2 <= frame_width and y2 <= frame_height:
|
61 |
+
bbox = (x1, y1, x2 - x1, y2 - y1)
|
62 |
+
tracker = cv2.legacy.TrackerCSRT_create() if hasattr(cv2, 'legacy') else cv2.TrackerCSRT_create()
|
63 |
+
trackers.add(tracker, frame, bbox)
|
64 |
+
|
65 |
+
bird_count = len(bird_results)
|
66 |
+
tracker_initialized = True
|
67 |
+
else:
|
68 |
+
success, boxes = trackers.update(frame)
|
69 |
+
|
70 |
+
if success:
|
71 |
+
bird_count = len(boxes)
|
72 |
+
for box in boxes:
|
73 |
+
x, y, w, h = [int(v) for v in box]
|
74 |
+
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
|
75 |
+
cv2.putText(frame, 'bird', (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
|
76 |
+
else:
|
77 |
+
tracker_initialized = False
|
78 |
+
|
79 |
+
ret, buffer = cv2.imencode('.jpg', frame)
|
80 |
+
frame = buffer.tobytes()
|
81 |
+
yield (b'--frame\r\n'
|
82 |
+
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
|
83 |
+
else:
|
84 |
+
break
|
85 |
+
cap.release()
|
86 |
+
|
87 |
+
@app.get("/", response_class=HTMLResponse)
|
88 |
+
async def index(request: Request):
|
89 |
+
return templates.TemplateResponse("index.html", {"request": request, "bird_count": bird_count})
|
90 |
+
|
91 |
+
@app.get("/video_feed")
|
92 |
+
async def video_feed():
|
93 |
+
return StreamingResponse(process_video(), media_type='multipart/x-mixed-replace; boundary=frame')
|
94 |
+
|
95 |
+
if __name__ == "__main__":
|
96 |
+
import uvicorn
|
97 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
demo.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1f13ec6b61585746d6db66f057bb5aab8106a1f37b581adab03b7a14d4c98a0d
|
3 |
+
size 1399765
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
uvicorn
|
2 |
+
jinja2
|
3 |
+
opencv-python-headless
|
4 |
+
ultralytics
|
5 |
+
fastapi
|
6 |
+
uvicorn
|
7 |
+
opencv-python-headless
|
8 |
+
ultralytics
|
9 |
+
jinja2
|
templates/index.html
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<title>Birds Count</title>
|
6 |
+
<style>
|
7 |
+
body, html {
|
8 |
+
margin: 0;
|
9 |
+
padding: 0;
|
10 |
+
width: 100%;
|
11 |
+
height: 100%;
|
12 |
+
display: flex;
|
13 |
+
flex-direction: column;
|
14 |
+
justify-content: space-between;
|
15 |
+
}
|
16 |
+
.header, .footer {
|
17 |
+
background-color: #216d7c;
|
18 |
+
width: 100%;
|
19 |
+
display: flex;
|
20 |
+
align-items: center;
|
21 |
+
justify-content: center;
|
22 |
+
padding: 20px;
|
23 |
+
box-sizing: border-box;
|
24 |
+
}
|
25 |
+
.header-content {
|
26 |
+
display: flex;
|
27 |
+
align-items: center;
|
28 |
+
}
|
29 |
+
.content {
|
30 |
+
flex: 1;
|
31 |
+
display: flex;
|
32 |
+
flex-direction: column;
|
33 |
+
align-items: center;
|
34 |
+
justify-content: center;
|
35 |
+
}
|
36 |
+
img {
|
37 |
+
max-width: 100%;
|
38 |
+
height: auto;
|
39 |
+
}
|
40 |
+
.logo {
|
41 |
+
height: 40px;
|
42 |
+
margin-right: 10px;
|
43 |
+
}
|
44 |
+
.header-text {
|
45 |
+
font-size: 24px;
|
46 |
+
font-weight: bold;
|
47 |
+
color: white;
|
48 |
+
}
|
49 |
+
.footer-content {
|
50 |
+
display: flex;
|
51 |
+
flex-direction: column;
|
52 |
+
align-items: center;
|
53 |
+
}
|
54 |
+
.footer-text {
|
55 |
+
color: white;
|
56 |
+
margin-bottom: 10px;
|
57 |
+
}
|
58 |
+
.social-links img {
|
59 |
+
height: 24px;
|
60 |
+
margin: 0 5px;
|
61 |
+
}
|
62 |
+
</style>
|
63 |
+
</head>
|
64 |
+
<body>
|
65 |
+
<div class="header">
|
66 |
+
<div class="header-content">
|
67 |
+
<!-- <img src="pyresearch.png" alt="Logo" class="logo"> -->
|
68 |
+
<div class="header-text"> Bird Count Dashboard</div>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
|
72 |
+
<div class="content">
|
73 |
+
<h2>Current Bird Count: {{ bird_count }}</h2>
|
74 |
+
<img src="{{ url_for('video_feed') }}" width="720" height="480">
|
75 |
+
</div>
|
76 |
+
|
77 |
+
<div class="footer">
|
78 |
+
<div class="footer-content">
|
79 |
+
<div class="footer-text"> 2024 | All rights reserved</div>
|
80 |
+
<div class="social-links">
|
81 |
+
<a href="https://www.facebook.com/Pyresearch" target="_blank">
|
82 |
+
<img src="" alt="Facebook">
|
83 |
+
</a>
|
84 |
+
<a href="https://www.youtube.com/Pyresearch" target="_blank">
|
85 |
+
<img src="" alt="YouTube">
|
86 |
+
</a>
|
87 |
+
<a href="https://www.instagram.com/pyresearch/" target="_blank">
|
88 |
+
<img src="" alt="Instagram">
|
89 |
+
</a>
|
90 |
+
<a href="https://www.tiktok.com/@pyresearch2" target="_blank">
|
91 |
+
<img src="" alt="TikTok">
|
92 |
+
</a>
|
93 |
+
</div>
|
94 |
+
</div>
|
95 |
+
</div>
|
96 |
+
</body>
|
97 |
+
</html>
|
98 |
+
|
99 |
+
|
yolov8l.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:18218ea4798da042d9862e6029ca9531adbd40ace19b6c9a75e2e28f1adf30cc
|
3 |
+
size 87769683
|