Spaces:
Sleeping
Sleeping
Upload 3 files
Browse filesfixed token issue
- README.md +9 -8
- app.py +60 -24
- requirements.txt +2 -2
README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: green
|
| 5 |
-
colorTo:
|
| 6 |
sdk: gradio
|
| 7 |
-
sdk_version: 6.19.0
|
| 8 |
-
python_version: '3.13'
|
| 9 |
app_file: app.py
|
|
|
|
| 10 |
pinned: false
|
| 11 |
-
license: agpl-3.0
|
| 12 |
-
short_description: Just a location to test my vision models on predators
|
| 13 |
---
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: SpotPredator Predator Detection
|
| 3 |
+
emoji: 🦅
|
| 4 |
colorFrom: green
|
| 5 |
+
colorTo: blue
|
| 6 |
sdk: gradio
|
|
|
|
|
|
|
| 7 |
app_file: app.py
|
| 8 |
+
python_version: "3.11"
|
| 9 |
pinned: false
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# SpotPredator — predator_v2_fp16
|
| 13 |
+
|
| 14 |
+
Browser demo for an off-grid farm predator detector (coyote / fox / raptor).
|
| 15 |
+
Runs the TFLite model directly via LiteRT — no ultralytics required.
|
| 16 |
+
Drag in a field image to see detections.
|
app.py
CHANGED
|
@@ -10,47 +10,72 @@ Create the Space:
|
|
| 10 |
Or from CLI after `pip install huggingface_hub`:
|
| 11 |
huggingface-cli repo create predator-demo --type space --space_sdk gradio
|
| 12 |
"""
|
|
|
|
| 13 |
import numpy as np
|
| 14 |
from PIL import Image, ImageDraw
|
| 15 |
import gradio as gr
|
| 16 |
from huggingface_hub import hf_hub_download, list_repo_files
|
| 17 |
|
| 18 |
MODEL_REPO = "JZVince/predator_v2_fp16"
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
# ---- fetch the .tflite from the model repo (auto-detect the filename) --------
|
| 22 |
-
tflite_name = next(f for f in list_repo_files(MODEL_REPO
|
| 23 |
-
|
|
|
|
| 24 |
|
|
|
|
| 25 |
try:
|
| 26 |
-
from
|
| 27 |
except ImportError:
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
# ---- inference core (same verified decode as detect_tflite.py) ---------------
|
|
|
|
|
|
|
| 34 |
def letterbox(img, new, color=(114, 114, 114)):
|
| 35 |
-
w, h = img.size
|
|
|
|
| 36 |
nw, nh = round(w*gain), round(h*gain)
|
| 37 |
canvas = Image.new("RGB", (new, new), color)
|
| 38 |
px, py = (new-nw)//2, (new-nh)//2
|
| 39 |
canvas.paste(img.resize((nw, nh), Image.BILINEAR), (px, py))
|
| 40 |
return canvas, gain, (px, py)
|
| 41 |
|
|
|
|
| 42 |
def nms(boxes, scores, iou_thr=0.45):
|
| 43 |
-
x1, y1, x2, y2 = boxes.T
|
| 44 |
-
|
|
|
|
|
|
|
| 45 |
while order.size:
|
| 46 |
-
i = order[0]
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
iou = inter/(areas[i]+areas[order[1:]]-inter+1e-9)
|
| 51 |
order = order[1:][iou <= iou_thr]
|
| 52 |
return keep
|
| 53 |
|
|
|
|
| 54 |
def detect(image, conf_thr=0.25, iou_thr=0.45):
|
| 55 |
if image is None:
|
| 56 |
return None, "Upload an image."
|
|
@@ -58,38 +83,49 @@ def detect(image, conf_thr=0.25, iou_thr=0.45):
|
|
| 58 |
lb, gain, pad = letterbox(img, IN_SIZE)
|
| 59 |
x = np.asarray(lb, np.float32)[None] / 255.0
|
| 60 |
if INP["dtype"] == np.uint8:
|
| 61 |
-
s, z = INP["quantization"]
|
| 62 |
-
|
|
|
|
|
|
|
| 63 |
y = interp.get_tensor(OUT["index"])
|
| 64 |
if OUT["dtype"] == np.uint8:
|
| 65 |
-
s, z = OUT["quantization"]
|
|
|
|
| 66 |
o = y[0]
|
| 67 |
if o.shape[0] == 4 + NC:
|
| 68 |
o = o.T
|
| 69 |
xywh = o[:, :4].astype(np.float32).copy()
|
| 70 |
-
cls = o[:, 4:4+NC]
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
colors = [(220, 70, 60), (240, 160, 40), (60, 130, 200)]
|
| 74 |
if len(xywh):
|
| 75 |
if xywh.max() <= 1.5:
|
| 76 |
xywh *= IN_SIZE
|
| 77 |
cx, cy, w, h = xywh.T
|
| 78 |
-
x1 = (cx-w/2-pad[0])/gain
|
| 79 |
-
|
|
|
|
|
|
|
| 80 |
xyxy = np.stack([x1, y1, x2, y2], 1)
|
| 81 |
xyxy[:, [0, 2]] = xyxy[:, [0, 2]].clip(0, img.width)
|
| 82 |
xyxy[:, [1, 3]] = xyxy[:, [1, 3]].clip(0, img.height)
|
| 83 |
for c in np.unique(cid):
|
| 84 |
idx = np.where(cid == c)[0]
|
| 85 |
for k in nms(xyxy[idx], conf[idx], iou_thr):
|
| 86 |
-
b = xyxy[idx][k]
|
|
|
|
| 87 |
draw.rectangle(list(b), outline=col, width=3)
|
| 88 |
draw.text((b[0]+3, max(0, b[1]-12)),
|
| 89 |
f"{CLASS_NAMES[int(c)]} {conf[idx][k]:.2f}", fill=col)
|
| 90 |
lines.append(f"{CLASS_NAMES[int(c)]}: {conf[idx][k]:.2f}")
|
| 91 |
return img, ("\n".join(lines) if lines else "No predators detected.")
|
| 92 |
|
|
|
|
| 93 |
demo = gr.Interface(
|
| 94 |
fn=detect,
|
| 95 |
inputs=[gr.Image(type="pil", label="Field image"),
|
|
|
|
| 10 |
Or from CLI after `pip install huggingface_hub`:
|
| 11 |
huggingface-cli repo create predator-demo --type space --space_sdk gradio
|
| 12 |
"""
|
| 13 |
+
import os
|
| 14 |
import numpy as np
|
| 15 |
from PIL import Image, ImageDraw
|
| 16 |
import gradio as gr
|
| 17 |
from huggingface_hub import hf_hub_download, list_repo_files
|
| 18 |
|
| 19 |
MODEL_REPO = "JZVince/predator_v2_fp16"
|
| 20 |
+
# confirm this matches training order
|
| 21 |
+
CLASS_NAMES = ["coyote", "fox", "raptor"]
|
| 22 |
+
|
| 23 |
+
# Optional: set HF_TOKEN as a Space secret for higher rate limits / faster pulls.
|
| 24 |
+
HF_TOKEN = os.environ.get("spcae")
|
| 25 |
|
| 26 |
# ---- fetch the .tflite from the model repo (auto-detect the filename) --------
|
| 27 |
+
tflite_name = next(f for f in list_repo_files(MODEL_REPO, token=HF_TOKEN)
|
| 28 |
+
if f.endswith(".tflite"))
|
| 29 |
+
MODEL_PATH = hf_hub_download(MODEL_REPO, tflite_name, token=HF_TOKEN)
|
| 30 |
|
| 31 |
+
# LiteRT is the successor to tflite-runtime; fall back through older runtimes.
|
| 32 |
try:
|
| 33 |
+
from ai_edge_litert.interpreter import Interpreter
|
| 34 |
except ImportError:
|
| 35 |
+
try:
|
| 36 |
+
from tflite_runtime.interpreter import Interpreter
|
| 37 |
+
except ImportError:
|
| 38 |
+
from tensorflow.lite import Interpreter
|
| 39 |
+
interp = Interpreter(model_path=MODEL_PATH)
|
| 40 |
+
interp.allocate_tensors()
|
| 41 |
+
INP = interp.get_input_details()[0]
|
| 42 |
+
OUT = interp.get_output_details()[0]
|
| 43 |
+
IN_SIZE = int(INP["shape"][1])
|
| 44 |
+
NC = len(CLASS_NAMES)
|
| 45 |
|
| 46 |
# ---- inference core (same verified decode as detect_tflite.py) ---------------
|
| 47 |
+
|
| 48 |
+
|
| 49 |
def letterbox(img, new, color=(114, 114, 114)):
|
| 50 |
+
w, h = img.size
|
| 51 |
+
gain = min(new/w, new/h)
|
| 52 |
nw, nh = round(w*gain), round(h*gain)
|
| 53 |
canvas = Image.new("RGB", (new, new), color)
|
| 54 |
px, py = (new-nw)//2, (new-nh)//2
|
| 55 |
canvas.paste(img.resize((nw, nh), Image.BILINEAR), (px, py))
|
| 56 |
return canvas, gain, (px, py)
|
| 57 |
|
| 58 |
+
|
| 59 |
def nms(boxes, scores, iou_thr=0.45):
|
| 60 |
+
x1, y1, x2, y2 = boxes.T
|
| 61 |
+
areas = (x2-x1)*(y2-y1)
|
| 62 |
+
order = scores.argsort()[::-1]
|
| 63 |
+
keep = []
|
| 64 |
while order.size:
|
| 65 |
+
i = order[0]
|
| 66 |
+
keep.append(i)
|
| 67 |
+
xx1 = np.maximum(x1[i], x1[order[1:]])
|
| 68 |
+
yy1 = np.maximum(y1[i], y1[order[1:]])
|
| 69 |
+
xx2 = np.minimum(x2[i], x2[order[1:]])
|
| 70 |
+
yy2 = np.minimum(y2[i], y2[order[1:]])
|
| 71 |
+
w = np.maximum(0, xx2-xx1)
|
| 72 |
+
h = np.maximum(0, yy2-yy1)
|
| 73 |
+
inter = w*h
|
| 74 |
iou = inter/(areas[i]+areas[order[1:]]-inter+1e-9)
|
| 75 |
order = order[1:][iou <= iou_thr]
|
| 76 |
return keep
|
| 77 |
|
| 78 |
+
|
| 79 |
def detect(image, conf_thr=0.25, iou_thr=0.45):
|
| 80 |
if image is None:
|
| 81 |
return None, "Upload an image."
|
|
|
|
| 83 |
lb, gain, pad = letterbox(img, IN_SIZE)
|
| 84 |
x = np.asarray(lb, np.float32)[None] / 255.0
|
| 85 |
if INP["dtype"] == np.uint8:
|
| 86 |
+
s, z = INP["quantization"]
|
| 87 |
+
x = (x/s + z).astype(np.uint8)
|
| 88 |
+
interp.set_tensor(INP["index"], x)
|
| 89 |
+
interp.invoke()
|
| 90 |
y = interp.get_tensor(OUT["index"])
|
| 91 |
if OUT["dtype"] == np.uint8:
|
| 92 |
+
s, z = OUT["quantization"]
|
| 93 |
+
y = (y.astype(np.float32)-z)*s
|
| 94 |
o = y[0]
|
| 95 |
if o.shape[0] == 4 + NC:
|
| 96 |
o = o.T
|
| 97 |
xywh = o[:, :4].astype(np.float32).copy()
|
| 98 |
+
cls = o[:, 4:4+NC]
|
| 99 |
+
cid = cls.argmax(1)
|
| 100 |
+
conf = cls.max(1)
|
| 101 |
+
m = conf >= conf_thr
|
| 102 |
+
xywh, conf, cid = xywh[m], conf[m], cid[m]
|
| 103 |
+
draw = ImageDraw.Draw(img)
|
| 104 |
+
lines = []
|
| 105 |
colors = [(220, 70, 60), (240, 160, 40), (60, 130, 200)]
|
| 106 |
if len(xywh):
|
| 107 |
if xywh.max() <= 1.5:
|
| 108 |
xywh *= IN_SIZE
|
| 109 |
cx, cy, w, h = xywh.T
|
| 110 |
+
x1 = (cx-w/2-pad[0])/gain
|
| 111 |
+
y1 = (cy-h/2-pad[1])/gain
|
| 112 |
+
x2 = (cx+w/2-pad[0])/gain
|
| 113 |
+
y2 = (cy+h/2-pad[1])/gain
|
| 114 |
xyxy = np.stack([x1, y1, x2, y2], 1)
|
| 115 |
xyxy[:, [0, 2]] = xyxy[:, [0, 2]].clip(0, img.width)
|
| 116 |
xyxy[:, [1, 3]] = xyxy[:, [1, 3]].clip(0, img.height)
|
| 117 |
for c in np.unique(cid):
|
| 118 |
idx = np.where(cid == c)[0]
|
| 119 |
for k in nms(xyxy[idx], conf[idx], iou_thr):
|
| 120 |
+
b = xyxy[idx][k]
|
| 121 |
+
col = colors[int(c) % 3]
|
| 122 |
draw.rectangle(list(b), outline=col, width=3)
|
| 123 |
draw.text((b[0]+3, max(0, b[1]-12)),
|
| 124 |
f"{CLASS_NAMES[int(c)]} {conf[idx][k]:.2f}", fill=col)
|
| 125 |
lines.append(f"{CLASS_NAMES[int(c)]}: {conf[idx][k]:.2f}")
|
| 126 |
return img, ("\n".join(lines) if lines else "No predators detected.")
|
| 127 |
|
| 128 |
+
|
| 129 |
demo = gr.Interface(
|
| 130 |
fn=detect,
|
| 131 |
inputs=[gr.Image(type="pil", label="Field image"),
|
requirements.txt
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
gradio
|
| 2 |
numpy
|
| 3 |
pillow
|
| 4 |
-
|
| 5 |
-
huggingface_hub
|
|
|
|
| 1 |
gradio
|
| 2 |
numpy
|
| 3 |
pillow
|
| 4 |
+
ai-edge-litert
|
| 5 |
+
huggingface_hub
|