Spaces:
Running
Running
GitHub Actions
commited on
Commit
·
2368d87
1
Parent(s):
c5358ec
Auto-deploy from GitHub
Browse files- detect.py +149 -19
- models/detect.pt +3 -0
detect.py
CHANGED
|
@@ -1,50 +1,180 @@
|
|
| 1 |
from collections import Counter
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
from PIL import Image
|
| 4 |
from io import BytesIO
|
| 5 |
from ultralytics import YOLO
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
class DengueDetector:
|
| 8 |
-
def __init__(self, model_path="./models/
|
| 9 |
self.model = YOLO(model_path)
|
| 10 |
self.names = self.model.names
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
def calculate_intensity(self, objects):
|
| 13 |
-
weights = {"piscina": 9, "caixa_agua": 4
|
| 14 |
score = sum(weights.get(obj["class"], 0) for obj in objects)
|
| 15 |
return score
|
| 16 |
|
| 17 |
-
def detect_image(self, image_bytes):
|
| 18 |
img = Image.open(BytesIO(image_bytes)).convert("RGB")
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
height, width = img_np.shape[:2]
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
detections = []
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
detections.append({
|
| 36 |
-
"class":
|
| 37 |
-
"confidence": round(
|
| 38 |
"box": {
|
| 39 |
"x1": x1, "y1": y1, "x2": x2, "y2": y2,
|
| 40 |
-
"original_width":
|
| 41 |
}
|
| 42 |
})
|
| 43 |
|
|
|
|
| 44 |
intensity_score = self.calculate_intensity(detections)
|
| 45 |
|
| 46 |
return {
|
| 47 |
-
"total": len(
|
| 48 |
"contagem": counts,
|
| 49 |
"objetos": detections,
|
| 50 |
"intensity_score": intensity_score
|
|
|
|
| 1 |
from collections import Counter
|
| 2 |
+
import math
|
| 3 |
import numpy as np
|
| 4 |
from PIL import Image
|
| 5 |
from io import BytesIO
|
| 6 |
from ultralytics import YOLO
|
| 7 |
|
| 8 |
+
def _compute_iou(box_a, box_b):
|
| 9 |
+
ax1, ay1, ax2, ay2 = box_a
|
| 10 |
+
bx1, by1, bx2, by2 = box_b
|
| 11 |
+
inter_x1 = max(ax1, bx1)
|
| 12 |
+
inter_y1 = max(ay1, by1)
|
| 13 |
+
inter_x2 = min(ax2, bx2)
|
| 14 |
+
inter_y2 = min(ay2, by2)
|
| 15 |
+
inter_w = max(0.0, inter_x2 - inter_x1)
|
| 16 |
+
inter_h = max(0.0, inter_y2 - inter_y1)
|
| 17 |
+
inter_area = inter_w * inter_h
|
| 18 |
+
area_a = max(0.0, (ax2 - ax1)) * max(0.0, (ay2 - ay1))
|
| 19 |
+
area_b = max(0.0, (bx2 - bx1)) * max(0.0, (by2 - by1))
|
| 20 |
+
union = area_a + area_b - inter_area
|
| 21 |
+
return inter_area / union if union > 0 else 0.0
|
| 22 |
+
|
| 23 |
+
def _nms_xyxy(boxes, scores, iou_threshold=0.5):
|
| 24 |
+
if len(boxes) == 0:
|
| 25 |
+
return []
|
| 26 |
+
idxs = np.argsort(scores)[::-1]
|
| 27 |
+
keep = []
|
| 28 |
+
while len(idxs) > 0:
|
| 29 |
+
i = idxs[0]
|
| 30 |
+
keep.append(i)
|
| 31 |
+
if len(idxs) == 1:
|
| 32 |
+
break
|
| 33 |
+
rest = idxs[1:]
|
| 34 |
+
ious = np.array([_compute_iou(boxes[i], boxes[j]) for j in rest])
|
| 35 |
+
idxs = rest[ious <= iou_threshold]
|
| 36 |
+
return keep
|
| 37 |
+
|
| 38 |
class DengueDetector:
|
| 39 |
+
def __init__(self, model_path="./models/detect.pt"):
|
| 40 |
self.model = YOLO(model_path)
|
| 41 |
self.names = self.model.names
|
| 42 |
+
|
| 43 |
+
self.tile_size = 1024
|
| 44 |
+
self.default_overlap = 0.2
|
| 45 |
+
self.fast_max_side = 3072
|
| 46 |
+
self.batch_tiles = 8
|
| 47 |
+
try:
|
| 48 |
+
if hasattr(self.model, "fuse"):
|
| 49 |
+
self.model.fuse()
|
| 50 |
+
except Exception:
|
| 51 |
+
pass
|
| 52 |
+
print("Modelo carregado com as seguintes classes:", self.names)
|
| 53 |
|
| 54 |
def calculate_intensity(self, objects):
|
| 55 |
+
weights = {"piscina": 9, "caixa_agua": 4}
|
| 56 |
score = sum(weights.get(obj["class"], 0) for obj in objects)
|
| 57 |
return score
|
| 58 |
|
| 59 |
+
def detect_image(self, image_bytes, fast: bool = True):
|
| 60 |
img = Image.open(BytesIO(image_bytes)).convert("RGB")
|
| 61 |
+
orig_width, orig_height = img.size
|
| 62 |
+
|
| 63 |
+
scale = 1.0
|
| 64 |
+
tile_size = self.tile_size
|
| 65 |
+
overlap = self.default_overlap
|
| 66 |
+
|
| 67 |
+
if fast:
|
| 68 |
+
max_side = max(orig_width, orig_height)
|
| 69 |
+
if max_side > self.fast_max_side:
|
| 70 |
+
scale = self.fast_max_side / float(max_side)
|
| 71 |
+
new_w = max(1, int(round(orig_width * scale)))
|
| 72 |
+
new_h = max(1, int(round(orig_height * scale)))
|
| 73 |
+
img_resized = img.resize((new_w, new_h), resample=Image.BILINEAR)
|
| 74 |
+
else:
|
| 75 |
+
img_resized = img
|
| 76 |
+
else:
|
| 77 |
+
img_resized = img
|
| 78 |
+
|
| 79 |
+
img_np = np.array(img_resized)
|
| 80 |
height, width = img_np.shape[:2]
|
| 81 |
|
| 82 |
+
stride = max(1, int(tile_size * (1 - overlap)))
|
| 83 |
+
|
| 84 |
+
def compute_starts(total, size, stride):
|
| 85 |
+
starts = list(range(0, max(total - size, 0) + 1, stride))
|
| 86 |
+
if len(starts) == 0:
|
| 87 |
+
starts = [0]
|
| 88 |
+
last = max(total - size, 0)
|
| 89 |
+
if starts[-1] != last:
|
| 90 |
+
starts.append(last)
|
| 91 |
+
return starts
|
| 92 |
+
|
| 93 |
+
x_starts = compute_starts(width, tile_size, stride)
|
| 94 |
+
y_starts = compute_starts(height, tile_size, stride)
|
| 95 |
+
|
| 96 |
+
tiles = []
|
| 97 |
+
origins = []
|
| 98 |
+
for y0 in y_starts:
|
| 99 |
+
for x0 in x_starts:
|
| 100 |
+
x1 = x0
|
| 101 |
+
y1 = y0
|
| 102 |
+
x2 = min(x0 + tile_size, width)
|
| 103 |
+
y2 = min(y0 + tile_size, height)
|
| 104 |
+
tile = img_np[y1:y2, x1:x2, :]
|
| 105 |
+
if tile.size == 0:
|
| 106 |
+
continue
|
| 107 |
+
tiles.append(tile)
|
| 108 |
+
origins.append((x1, y1))
|
| 109 |
+
|
| 110 |
+
all_boxes = []
|
| 111 |
+
all_scores = []
|
| 112 |
+
all_classes = []
|
| 113 |
+
|
| 114 |
+
if len(tiles) > 0:
|
| 115 |
+
bs = max(1, int(self.batch_tiles))
|
| 116 |
+
for i in range(0, len(tiles), bs):
|
| 117 |
+
batch = tiles[i:i+bs]
|
| 118 |
+
batch_origins = origins[i:i+bs]
|
| 119 |
+
results = self.model(batch, verbose=False)
|
| 120 |
+
for res, (ox, oy) in zip(results, batch_origins):
|
| 121 |
+
boxes = res.boxes
|
| 122 |
+
if boxes is None or len(boxes) == 0:
|
| 123 |
+
continue
|
| 124 |
+
class_ids = boxes.cls.tolist()
|
| 125 |
+
confidences = boxes.conf.tolist()
|
| 126 |
+
xyxy = boxes.xyxy.cpu().numpy() if hasattr(boxes.xyxy, 'cpu') else np.array(boxes.xyxy)
|
| 127 |
+
|
| 128 |
+
for j in range(len(class_ids)):
|
| 129 |
+
bx1, by1, bx2, by2 = map(float, xyxy[j])
|
| 130 |
+
all_boxes.append((bx1 + ox, by1 + oy, bx2 + ox, by2 + oy))
|
| 131 |
+
all_scores.append(float(confidences[j]))
|
| 132 |
+
all_classes.append(int(class_ids[j]))
|
| 133 |
+
|
| 134 |
+
final_indices = []
|
| 135 |
+
final_boxes = []
|
| 136 |
+
final_scores = []
|
| 137 |
+
final_classes = []
|
| 138 |
+
all_boxes_np = np.array(all_boxes, dtype=float)
|
| 139 |
+
all_scores_np = np.array(all_scores, dtype=float)
|
| 140 |
+
all_classes_np = np.array(all_classes, dtype=int)
|
| 141 |
+
|
| 142 |
+
for cls in set(all_classes_np.tolist()) if len(all_classes_np) else []:
|
| 143 |
+
cls_mask = (all_classes_np == cls)
|
| 144 |
+
boxes_cls = all_boxes_np[cls_mask]
|
| 145 |
+
scores_cls = all_scores_np[cls_mask]
|
| 146 |
+
keep = _nms_xyxy(boxes_cls, scores_cls, iou_threshold=0.5)
|
| 147 |
+
for k in keep:
|
| 148 |
+
final_boxes.append(tuple(boxes_cls[k]))
|
| 149 |
+
final_scores.append(float(scores_cls[k]))
|
| 150 |
+
final_classes.append(int(cls))
|
| 151 |
|
| 152 |
detections = []
|
| 153 |
+
class_names = []
|
| 154 |
+
for b, s, c in zip(final_boxes, final_scores, final_classes):
|
| 155 |
+
x1, y1, x2, y2 = map(float, b)
|
| 156 |
+
if scale != 1.0:
|
| 157 |
+
inv = 1.0 / scale
|
| 158 |
+
x1 *= inv
|
| 159 |
+
y1 *= inv
|
| 160 |
+
x2 *= inv
|
| 161 |
+
y2 *= inv
|
| 162 |
+
cname = self.names[int(c)]
|
| 163 |
+
class_names.append(cname)
|
| 164 |
detections.append({
|
| 165 |
+
"class": cname,
|
| 166 |
+
"confidence": round(s, 4),
|
| 167 |
"box": {
|
| 168 |
"x1": x1, "y1": y1, "x2": x2, "y2": y2,
|
| 169 |
+
"original_width": orig_width, "original_height": orig_height
|
| 170 |
}
|
| 171 |
})
|
| 172 |
|
| 173 |
+
counts = Counter(class_names)
|
| 174 |
intensity_score = self.calculate_intensity(detections)
|
| 175 |
|
| 176 |
return {
|
| 177 |
+
"total": len(detections),
|
| 178 |
"contagem": counts,
|
| 179 |
"objetos": detections,
|
| 180 |
"intensity_score": intensity_score
|
models/detect.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:27a0e59e4f7573d93e1a4ead65ddbde30cc8f5b8db31fe089cfffda4baa2b43b
|
| 3 |
+
size 52117266
|