Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,32 @@ jobs:
run: |
docker run -d -it --rm nvcr.io/nvidia/pytorch:23.07-py3 echo "I have to be startet to authenticate against nvcr.io"
- name: build
if: github.ref != 'refs/heads/release'
run: |
cd trainer
./docker.sh b test_latest
- name: buildnocaches
if: github.ref == 'refs/heads/release'
run: |
cd trainer
./docker.sh bnc test_latest
- name: run
run: |
docker run -d -it --rm -v /home/zauberzeug/data:/data -h n6 -e LOOP_HOST=$LOOP_HOST -e LOOP_USERNAME=$LOOP_USERNAME -e LOOP_PASSWORD=$LOOP_PASSWORD -e YOLOV5_MODE=DETECTION --name yolov5_trainer_node_github_action --runtime=nvidia -e NVIDIA_VISIBLE_DEVICES=all --gpus all --ipc host zauberzeug/yolov5-trainer:latest
- name: tests
run: |
docker exec yolov5_trainer_node_github_action pytest -vv
- name: publish
if: github.ref == 'refs/heads/release'
run: |
cd trainer
./docker.sh p test_latest
- name: cleanup
if: always() # also execute when pytest fails
run: |
docker stop yolov5_trainer_node_github_action | echo "Nothing to do. No container running."

pytest_detector_cpu:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: build
run: |
cd detector_cpu
./docker.sh b test_latest
- name: run tests
run: |
docker run --rm zauberzeug/yolov5-detector:latest-cpu pytest -vv

slack:
needs:
- pytest
Expand Down
Empty file added detector_cpu/__init__.py
Empty file.
5 changes: 3 additions & 2 deletions detector_cpu/cloud_cpu.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ RUN pip3 install IPython
ARG NODE_LIB_VERSION
RUN pip3 install "learning_loop_node==${NODE_LIB_VERSION}"

ADD ./trainer/app_code/yolov5 /yolov5_node/detector_cpu/app_code/yolov5
RUN pip install --no-cache -r /yolov5_node/detector_cpu/app_code/yolov5/requirements.txt

ADD ./detector_cpu /yolov5_node/detector_cpu/
ADD ./trainer/app_code /yolov5_node/detector_cpu/app_code
RUN rm -f /yolov5_node/detector_cpu/.env
RUN ln -sf /yolov5_node/detector_cpu /app

RUN pip install --no-cache -r /yolov5_node/detector_cpu/app_code/yolov5/requirements.txt


WORKDIR /app
Expand Down
12 changes: 10 additions & 2 deletions detector_cpu/docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ build_args=" --build-arg NODE_LIB_VERSION=$NODE_LIB_VERSION"


dockerfile="cloud_cpu.dockerfile"
image="zauberzeug/yolov5-detector:$SEMANTIC_VERSION-nlv$NODE_LIB_VERSION-cloud-cpu"
if [ "$2" = "test_latest" ]; then
image="zauberzeug/yolov5-detector:latest-cpu"
else
image="zauberzeug/yolov5-detector:$SEMANTIC_VERSION-nlv$NODE_LIB_VERSION-cloud-cpu"
fi


# ========================== RUN CONFIGURATION =========================================
Expand All @@ -61,7 +65,11 @@ fi
# ========================== COMMAND EXECUTION =========================================

cmd=$1
cmd_args=${@:2}
if [ "$2" = "test_latest" ]; then
cmd_args=${@:3}
else
cmd_args=${@:2}
fi
set -x
case $cmd in
b | build)
Expand Down
Empty file added detector_cpu/tests/__init__.py
Empty file.
46 changes: 46 additions & 0 deletions detector_cpu/tests/test_yolov5_cpu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import numpy as np

from ..yolov5_detector import Yolov5Detector

# pylint: disable=protected-access


def test_postprocess_empty():
boxes, scores, class_id = Yolov5Detector()._post_process(np.empty(shape=(0, 8)), 100, 100, 0.2, 0.45)
assert len(boxes) == 0
assert len(scores) == 0
assert len(class_id) == 0


def test_postprocess_conf_thresh_filtered_conf():
data = np.array([[0, 0, 10, 10, 0.1, 0.8, 0.8]])
boxes, scores, class_id = Yolov5Detector()._post_process(data, 100, 100, 0.2, 0.45)
assert len(boxes) == 0
assert len(scores) == 0
assert len(class_id) == 0


def test_postprocess_conf_thresh_filtered_iou():
data = np.array(
[[0.5, 0, 0.1, 0.1, 0.95],
[0.5, 0, 0.1, 0.11, 0.9]]
)
detector = Yolov5Detector()
detector.input_size = 100
boxes, scores, class_id = detector._post_process(data, 100, 100, 0.2, 0.45)
assert len(boxes) == 1
assert len(scores) == 1
assert len(class_id) == 1


def test_postprocess_conf_thresh_not_filtered():
data = np.array(
[[0.5, 0, 0.1, 0.1, 0.95],
[0, 0.5, 0.1, 0.1, 0.9]]
)
detector = Yolov5Detector()
detector.input_size = 100
boxes, scores, class_id = detector._post_process(data, 100, 100, 0.2, 0.45)
assert len(boxes) == 2
assert len(scores) == 2
assert len(class_id) == 2
19 changes: 10 additions & 9 deletions detector_cpu/yolov5_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@ def init(self) -> None:
raise RuntimeError("model_info.resolution must be an integer > 0")

pt_file = f'{self.model_info.model_root_path}/model.pt'
yolov5_path = os.path.join(
os.path.dirname(__file__), 'app_code', 'yolov5')
self.yolov5 = torch.hub.load(
yolov5_path, 'custom', pt_file, source='local')
yolov5_path = os.path.join(os.path.dirname(__file__), 'app_code', 'yolov5')
self.yolov5 = torch.hub.load(yolov5_path, 'custom', pt_file, source='local')

if self.yolov5 is None:
raise RuntimeError('Failed to load YOLOv5 model')
Expand Down Expand Up @@ -222,12 +220,15 @@ def _post_process(self, pred, origin_h, origin_w, conf_thres, nms_thres):
# Do nms
boxes = self._non_max_suppression(
pred, origin_h, origin_w, conf_thres, nms_thres)
result_boxes = boxes[:, :4] if len(boxes) else np.array([])
result_scores = boxes[:, 4] if len(boxes) else np.array([])
if num_classes > 1 and len(boxes.shape) >= 2:
if len(boxes) == 0:
return np.empty((0, 4)), np.empty((0,)), np.empty((0,), dtype=int)

result_boxes = boxes[:, :4]
result_scores = boxes[:, 4]
if num_classes > 1:
result_classid = np.argmax(boxes[:, 5:], axis=1)
else:
# Either we have no classes or all boxes have been removed in _non_max_suppression
# Without classes there is no classid to return
result_classid = np.zeros(boxes.shape[0], dtype=int)
return result_boxes, result_scores, result_classid

Expand All @@ -251,7 +252,7 @@ def _non_max_suppression(self, pred, origin_h, origin_w, conf_thres, nms_thres):
if len(boxes) == 0:
return np.array([])
num_classes = boxes.shape[1] - 5
# Trasform bbox from [center_x, center_y, w, h] to [x1, y1, x2, y2]
# Transform bbox from [center_x, center_y, w, h] to [x1, y1, x2, y2]
boxes[:, :4] = self.xywh2xyxy(origin_h, origin_w, boxes[:, :4])
# Object confidence
confs = boxes[:, 4]
Expand Down