vk
commited on
Commit
·
1a2f039
1
Parent(s):
f8c3c89
first commit
Browse files- app.py +67 -0
- models/pedestrian-detection-best55.onnx +3 -0
- requirements.txt +5 -0
- yolox_onnx.py +159 -0
app.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from yolox_onnx import YOLOX_ONNX
|
| 2 |
+
import albumentations as A
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import cv2
|
| 5 |
+
|
| 6 |
+
def show_example(path):
|
| 7 |
+
return cv2.cvtColor(cv2.imread(path), cv2.COLOR_BGR2RGB)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def get_response(input_img,add_snow,add_rain,add_fog,confidence_threshold,iou_threshold):
|
| 11 |
+
|
| 12 |
+
'''
|
| 13 |
+
detects all possible pedestrians in the image and recognizes it
|
| 14 |
+
Args:
|
| 15 |
+
input_img (numpy array): one image of type numpy array
|
| 16 |
+
add_snow (boolean) : apply random snow augmentation
|
| 17 |
+
add_rain (boolean) : apply random rain augmentation
|
| 18 |
+
add_fog (boolean) : apply random fog augmentation
|
| 19 |
+
confidence_threshold (float) : minimum confidence prob required for bounding box candidate
|
| 20 |
+
iou_threshold (float) : intersection threshold above which bounding box will be neglected
|
| 21 |
+
|
| 22 |
+
Returns:
|
| 23 |
+
return img(numpy array): image with bounding boxes of pedestrians
|
| 24 |
+
'''
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
if not hasattr(input_img,'shape'):
|
| 28 |
+
return "invalid input",input_img
|
| 29 |
+
|
| 30 |
+
pedestrian_detector.predict(input_img,confidence_threshold,iou_threshold)
|
| 31 |
+
out_img=pedestrian_detector.output_img
|
| 32 |
+
|
| 33 |
+
if add_snow:
|
| 34 |
+
out_img = weather_transform["add_snow"] (image=out_img)['image']
|
| 35 |
+
elif add_rain:
|
| 36 |
+
out_img = weather_transform["add_rain"](image=out_img)['image']
|
| 37 |
+
elif add_fog:
|
| 38 |
+
out_img = weather_transform["add_fog"](image=out_img)['image']
|
| 39 |
+
else:
|
| 40 |
+
pass
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
return out_img
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
if __name__ == "__main__":
|
| 47 |
+
weather_transform = {}
|
| 48 |
+
weather_transform["add_rain"] = A.RandomRain(brightness_coefficient=0.9, drop_width=1, blur_value=5, p=1)
|
| 49 |
+
weather_transform["add_snow"] = A.RandomSnow(brightness_coeff=2.5, snow_point_lower=0.3, snow_point_upper=0.5, p=1)
|
| 50 |
+
weather_transform["add_fog"] = A.RandomFog(fog_coef_lower=0.7, fog_coef_upper=0.8, alpha_coef=0.1, p=1)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
pedestrian_detector=YOLOX_ONNX('models/pedestrian-detection-best55.onnx')
|
| 54 |
+
iface = gr.Interface(
|
| 55 |
+
fn=get_response,
|
| 56 |
+
inputs=[gr.Image(type="numpy"), # Accepts image input
|
| 57 |
+
gr.Checkbox(label="add_snow"),
|
| 58 |
+
gr.Checkbox(label="add_rain"),
|
| 59 |
+
gr.Checkbox(label="add_fog"),
|
| 60 |
+
gr.Slider(0, 1,value=0.5, step=0.01, label="confidence_threshold"),
|
| 61 |
+
gr.Slider(0, 1,value=0.45, step=0.01, label="iou_threshold")],
|
| 62 |
+
examples=[[show_example('test1.jpg')],[show_example('test2.jpg')],[show_example('test3.jpg')]],
|
| 63 |
+
outputs=[gr.Image(type="numpy")],
|
| 64 |
+
title="Pedestrian Detection with All weather augmentation",
|
| 65 |
+
description="Upload images for pedestrian detection")
|
| 66 |
+
|
| 67 |
+
iface.launch(share=True)
|
models/pedestrian-detection-best55.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:dcb7921907ddf0b59f99f13223000011a41467dfafbdb6a1ab2a4c22432f72bd
|
| 3 |
+
size 3674588
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
opencv_python
|
| 2 |
+
albumentations==1.4.10
|
| 3 |
+
albucore==0.0.13
|
| 4 |
+
onnxruntime
|
| 5 |
+
gradio
|
yolox_onnx.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
from time import time
|
| 3 |
+
import numpy as np
|
| 4 |
+
import onnxruntime
|
| 5 |
+
from matplotlib import pyplot as plt
|
| 6 |
+
from collections import defaultdict
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class YOLOX_ONNX:
|
| 10 |
+
|
| 11 |
+
def __init__(self, model_path):
|
| 12 |
+
providers = ['CPUExecutionProvider']
|
| 13 |
+
self.model = onnxruntime.InferenceSession(model_path, providers=providers)
|
| 14 |
+
self.image_size = self.model.get_inputs()[0].shape[-2:]
|
| 15 |
+
# print(self.model.get_outputs()[0].name)
|
| 16 |
+
# print(self.image_size)
|
| 17 |
+
self.labels_map=['pedestrian']
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def __preprocess_image(self, img, swap=(2, 0, 1)):
|
| 23 |
+
|
| 24 |
+
padded_img = np.ones((self.image_size[0], self.image_size[1], 3), dtype=np.uint8) * 114
|
| 25 |
+
r = min(self.image_size[0] / img.shape[0], self.image_size[1] / img.shape[1])
|
| 26 |
+
resized_img = cv2.resize(img, (int(img.shape[1] * r), int(img.shape[0] * r)), interpolation=cv2.INTER_LINEAR).astype(np.uint8)
|
| 27 |
+
padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
|
| 28 |
+
padded_img = padded_img.transpose(swap)
|
| 29 |
+
padded_img = np.ascontiguousarray(padded_img, dtype=np.float32)
|
| 30 |
+
return padded_img, r
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@staticmethod
|
| 34 |
+
def __new_nms(boxes, scores, iou_thresh):
|
| 35 |
+
x1 = boxes[:, 0]
|
| 36 |
+
y1 = boxes[:, 1]
|
| 37 |
+
x2 = boxes[:, 2]
|
| 38 |
+
y2 = boxes[:, 3]
|
| 39 |
+
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
|
| 40 |
+
order = scores.argsort()[::-1]
|
| 41 |
+
keep = []
|
| 42 |
+
while order.size > 0:
|
| 43 |
+
i = order[0]
|
| 44 |
+
keep.append(i)
|
| 45 |
+
xx1 = np.maximum(x1[i], x1[order[1:]])
|
| 46 |
+
yy1 = np.maximum(y1[i], y1[order[1:]])
|
| 47 |
+
xx2 = np.minimum(x2[i], x2[order[1:]])
|
| 48 |
+
yy2 = np.minimum(y2[i], y2[order[1:]])
|
| 49 |
+
w = np.maximum(0.0, xx2 - xx1 + 1)
|
| 50 |
+
h = np.maximum(0.0, yy2 - yy1 + 1)
|
| 51 |
+
inter = w * h
|
| 52 |
+
ovr = inter / (areas[i] + areas[order[1:]] - inter)
|
| 53 |
+
inds = np.where(ovr <= iou_thresh)[0]
|
| 54 |
+
order = order[inds + 1]
|
| 55 |
+
|
| 56 |
+
return keep
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def __parse_output_data(self, outputs):
|
| 60 |
+
grids = []
|
| 61 |
+
expanded_strides = []
|
| 62 |
+
strides = [8, 16, 32]
|
| 63 |
+
hsizes = [self.image_size[0] // stride for stride in strides]
|
| 64 |
+
wsizes = [self.image_size[1] // stride for stride in strides]
|
| 65 |
+
for hsize, wsize, stride in zip(hsizes, wsizes, strides):
|
| 66 |
+
xv, yv = np.meshgrid(np.arange(wsize), np.arange(hsize))
|
| 67 |
+
grid = np.stack((xv, yv), 2).reshape(1, -1, 2)
|
| 68 |
+
grids.append(grid)
|
| 69 |
+
shape = grid.shape[:2]
|
| 70 |
+
expanded_strides.append(np.full((*shape, 1), stride))
|
| 71 |
+
grids = np.concatenate(grids, 1)
|
| 72 |
+
expanded_strides = np.concatenate(expanded_strides, 1)
|
| 73 |
+
outputs[..., :2] = (outputs[..., :2] + grids) * expanded_strides
|
| 74 |
+
outputs[..., 2:4] = np.exp(outputs[..., 2:4]) * expanded_strides
|
| 75 |
+
return outputs[0]
|
| 76 |
+
|
| 77 |
+
def __decode_prediction(self, prediction, img_size, resize_ratio, score_thresh, iou_thresh):
|
| 78 |
+
|
| 79 |
+
boxes = prediction[:, :4]
|
| 80 |
+
classes = prediction[:, 4:5] * prediction[:, 5:]
|
| 81 |
+
scores = np.amax(classes, axis=1)
|
| 82 |
+
classes = np.argmax(classes, axis=1)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
valid_score_mask = scores > score_thresh
|
| 86 |
+
if valid_score_mask.sum() == 0:
|
| 87 |
+
return np.array([]), np.array([]), np.array([])
|
| 88 |
+
valid_scores = scores[valid_score_mask]
|
| 89 |
+
valid_boxes = boxes[valid_score_mask]
|
| 90 |
+
valid_classes = classes[valid_score_mask]
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
valid_boxes_xyxy = np.ones_like(valid_boxes)
|
| 94 |
+
valid_boxes_xyxy[:, 0] = valid_boxes[:, 0] - valid_boxes[:, 2]/2.
|
| 95 |
+
valid_boxes_xyxy[:, 1] = valid_boxes[:, 1] - valid_boxes[:, 3]/2.
|
| 96 |
+
valid_boxes_xyxy[:, 2] = valid_boxes[:, 0] + valid_boxes[:, 2]/2.
|
| 97 |
+
valid_boxes_xyxy[:, 3] = valid_boxes[:, 1] + valid_boxes[:, 3]/2.
|
| 98 |
+
valid_boxes_xyxy /= resize_ratio
|
| 99 |
+
|
| 100 |
+
indices = self.__new_nms(valid_boxes_xyxy, valid_scores, iou_thresh)
|
| 101 |
+
valid_boxes_xyxy = valid_boxes_xyxy[indices, :]
|
| 102 |
+
valid_scores = valid_scores[indices]
|
| 103 |
+
valid_classes = valid_classes[indices].astype('int')
|
| 104 |
+
|
| 105 |
+
#valid_boxes_xyxy, valid_scores, valid_classes = self.__remove_duplicates(valid_boxes_xyxy, valid_scores, valid_classes)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
return valid_boxes_xyxy, valid_scores, valid_classes
|
| 109 |
+
|
| 110 |
+
def draw_boxes(self,img, boxes, scores=None, classes=None, labels=None):
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
for i in range(boxes.shape[0]):
|
| 114 |
+
cv2.rectangle(img,
|
| 115 |
+
(int(boxes[i,0]), int(boxes[i,1])),
|
| 116 |
+
(int(boxes[i,2]), int(boxes[i,3])),
|
| 117 |
+
(0, 128, 0),
|
| 118 |
+
int(0.005*img.shape[1]))
|
| 119 |
+
|
| 120 |
+
### not drawing classes since num_classes is 1(pedestrian) and text not greatly visible in gradio UI
|
| 121 |
+
# text_label = ''
|
| 122 |
+
# if labels is not None:
|
| 123 |
+
# if classes is not None:
|
| 124 |
+
# text_label = labels[classes[i]]
|
| 125 |
+
# if scores is not None:
|
| 126 |
+
# text_label+= ' ' + str("%.2f" % round(scores[i],2))
|
| 127 |
+
# elif scores is not None:
|
| 128 |
+
# text_label = str("%.2f" % round(scores[i],2))
|
| 129 |
+
|
| 130 |
+
#w, h = cv2.getTextSize(text_label, 0, fontScale=0.5, thickness=1)[0]
|
| 131 |
+
# cv2.putText(img,
|
| 132 |
+
# text_label,
|
| 133 |
+
# (int(boxes[i,0]) if int(boxes[i,0])+w<img.shape[1] else img.shape[1]-w, int(boxes[i,1])-2 if (int(boxes[i,1])-h>=3) else int(boxes[i,1])+h+2),
|
| 134 |
+
# 0,
|
| 135 |
+
# 0.5,
|
| 136 |
+
# (0,0,255),
|
| 137 |
+
# thickness= int(0.005*img.shape[1]),
|
| 138 |
+
# lineType=cv2.LINE_AA)
|
| 139 |
+
return img
|
| 140 |
+
|
| 141 |
+
def predict(self, image, score_thresh=0.5, iou_thresh=0.4):
|
| 142 |
+
h,w = image.shape[:2]
|
| 143 |
+
origin_img=np.copy(image)
|
| 144 |
+
model_input = np.copy(image)
|
| 145 |
+
model_input, resize_ratio = self.__preprocess_image(model_input)
|
| 146 |
+
#print(model_input.shape)
|
| 147 |
+
#print('input mean:', np.mean(model_input))
|
| 148 |
+
start_time=time()
|
| 149 |
+
prediction = self.model.run(None, {self.model.get_inputs()[0].name: model_input[None, :, :, :]})
|
| 150 |
+
#print(self.model.get_inputs()[0].name)
|
| 151 |
+
#print('output mean:',np.mean(prediction))
|
| 152 |
+
prediction = self.__parse_output_data(prediction[0])
|
| 153 |
+
d_boxes, d_scores, d_classes=self.__decode_prediction(prediction, (h,w), resize_ratio, score_thresh, iou_thresh)
|
| 154 |
+
self.output_img = self.draw_boxes(origin_img, d_boxes,None, d_classes, self.labels_map)
|
| 155 |
+
print('elapsed time:',time()-start_time)
|
| 156 |
+
|
| 157 |
+
return d_boxes, d_scores, d_classes
|
| 158 |
+
|
| 159 |
+
|