Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
MateoLostanlen committed Jan 29, 2025
2 parents 35b772b + 15d5399 commit e48dc9a
Show file tree
Hide file tree
Showing 26 changed files with 810 additions and 1,054 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/doc-status.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.10
python-version: 3.9
architecture: x64
- name: check status
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/gh-page.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ jobs:
see-page-build-payload:
runs-on: ubuntu-latest
steps:
- name: Set up Python 3.10
- name: Set up Python 3.9
uses: actions/setup-python@v4
with:
python-version: 3.10
python-version: 3.9
architecture: x64
- name: check status
run: |
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.10
python-version: 3.9
architecture: x64
- name: Cache python modules
uses: actions/cache@v4
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.10
python-version: 3.9
architecture: x64
- name: Install package
run: |
Expand All @@ -64,7 +64,7 @@ jobs:
uses: conda-incubator/setup-miniconda@v4
with:
auto-update-conda: true
python-version: 3.10
python-version: 3.9
- name: Install dependencies
run: |
conda install -y conda-build conda-verify anaconda-client
Expand All @@ -90,7 +90,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Miniconda setup
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -31,7 +31,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -51,7 +51,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand All @@ -98,7 +98,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
- name: Set up Python
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -65,7 +65,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
python: ['3.10']
python: ['3.9']
steps:
- uses: actions/checkout@v4
with:
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,9 @@ pyroengine/version.py
conda-dist/

docker-compose.yml.bak
docker-compose.override.yml
docker-compose.override.yml


captured_images/
captured_positions/
*.jpg
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10.16-slim
FROM python:3.9.16-slim

# set environment variables
ENV PATH="/usr/local/bin:$PATH"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ prediction = engine.predict(im)

## Setup

Python 3.10 (or higher) and [pip](https://pip.pypa.io/en/stable/)/[conda](https://docs.conda.io/en/latest/miniconda.html) are required to install PyroEngine.
Python 3.9 (or higher) and [pip](https://pip.pypa.io/en/stable/)/[conda](https://docs.conda.io/en/latest/miniconda.html) are required to install PyroEngine.

### Developer installation

Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ classifiers = [
"Natural Language :: English",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Mathematics",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
Expand Down
2 changes: 1 addition & 1 deletion pyroengine/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down
6 changes: 4 additions & 2 deletions pyroengine/engine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down Expand Up @@ -81,6 +81,7 @@ def __init__(
model_path: Optional[str] = None,
conf_thresh: float = 0.15,
api_host: Optional[str] = None,
max_bbox_size: float = 0.4,
cam_creds: Optional[Dict[str, Dict[str, str]]] = None,
nb_consecutive_frames: int = 4,
frame_size: Optional[Tuple[int, int]] = None,
Expand All @@ -96,7 +97,8 @@ def __init__(
) -> None:
"""Init engine"""
# Engine Setup
self.model = Classifier(model_path=model_path, conf=0.05)

self.model = Classifier(model_path=model_path, conf=0.05, max_bbox_size=max_bbox_size)
self.conf_thresh = conf_thresh

# API Setup
Expand Down
5 changes: 3 additions & 2 deletions pyroengine/sensors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down Expand Up @@ -69,6 +69,7 @@ def _build_url(self, command: str) -> str:

def _handle_response(self, response, success_message: str):
"""Handles HTTP responses, logging success or errors based on response data."""
logging.info(f"{response.status_code}")
if response.status_code == 200:
response_data = response.json()
if response_data[0]["code"] == 0:
Expand Down Expand Up @@ -141,7 +142,7 @@ def move_in_seconds(self, s: float, operation: str = "Right", speed: int = 20, s
self.move_camera("Stop")
time.sleep(1)
im = self.capture()
if im is not None:
if im is not None and save_path is not None:
im.save(save_path)

def get_ptz_preset(self):
Expand Down
2 changes: 1 addition & 1 deletion pyroengine/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down
81 changes: 62 additions & 19 deletions pyroengine/vision.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2023-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down Expand Up @@ -34,14 +34,18 @@ def save_metadata(metadata_path: str, metadata: dict) -> None:


class Classifier:
"""Implements an image classification model using YOLO backend.
Examples:
>>> from pyroengine.vision import Classifier
>>> model = Classifier()
Args:
model_path: model path
"""

def __init__(
self,
model_folder: str = "data",
imgsz: int = 1024,
conf: float = 0.15,
iou: float = 0,
format: str = "ncnn",
model_path: Optional[str] = None,
self, model_folder="data", imgsz=1024, conf=0.15, iou=0, format="ncnn", model_path=None, max_bbox_size=0.4
) -> None:
if model_path is None:
if format == "ncnn":
Expand Down Expand Up @@ -93,6 +97,7 @@ def __init__(
self.imgsz = imgsz
self.conf = conf
self.iou = iou
self.max_bbox_size = max_bbox_size

def is_arm_architecture(self) -> bool:
return platform.machine().startswith("arm") or platform.machine().startswith("aarch")
Expand Down Expand Up @@ -120,41 +125,70 @@ def load_metadata(self, metadata_path: str) -> Optional[dict]:
return json.load(f)
return None

def prep_process(self, pil_img: Image.Image) -> Tuple[Union[np.ndarray, ncnn.Mat], Tuple[int, int]]:
np_img, pad = letterbox(np.array(pil_img), (self.imgsz, self.imgsz))
def prep_process(self, pil_img: Image.Image) -> Tuple[np.ndarray, Tuple[int, int]]:
"""Preprocess an image for inference
Args:
pil_img: A valid PIL image.
Returns:
A tuple containing:
- The resized and normalized image of shape (1, C, H, W).
- Padding information as a tuple of integers (pad_height, pad_width).
"""
np_img, pad = letterbox(np.array(pil_img), self.imgsz) # Applies letterbox resize with padding

if self.format == "ncnn":
np_img = ncnn.Mat.from_pixels(np_img, ncnn.Mat.PixelType.PIXEL_BGR, np_img.shape[1], np_img.shape[0])
mean = [0, 0, 0]
std = [1 / 255, 1 / 255, 1 / 255]
np_img.substract_mean_normalize(mean=mean, norm=std)
else:
np_img = np.expand_dims(np_img.astype("float32"), axis=0)
np_img = np.ascontiguousarray(np_img.transpose((0, 3, 1, 2)))
np_img /= 255.0
np_img = np.expand_dims(np_img.astype("float32"), axis=0) # Add batch dimension
np_img = np.ascontiguousarray(np_img.transpose((0, 3, 1, 2))) # Convert from BHWC to BCHW format
np_img /= 255.0 # Normalize to [0, 1]

return np_img, pad

def post_process(self, pred: np.ndarray, pad: Tuple[int, int]) -> np.ndarray:
pred = pred[:, pred[-1, :] > self.conf]
"""Post-process model predictions.
Args:
pred: Raw predictions from the model.
pad: Padding information as (left_pad, top_pad).
Returns:
Processed predictions as a numpy array.
"""
pred = pred[:, pred[-1, :] > self.conf] # Drop low-confidence predictions
pred = np.transpose(pred)
pred = xywh2xyxy(pred)
pred = pred[pred[:, 4].argsort()]
pred = pred[pred[:, 4].argsort()] # Sort by confidence
pred = nms(pred)
pred = pred[::-1]
pred = pred[::-1] # Reverse for highest confidence first

if len(pred) > 0:
left_pad, top_pad = pad
left_pad, top_pad = pad # Unpack the tuple
pred[:, :4:2] -= left_pad
pred[:, 1:4:2] -= top_pad
pred[:, :4:2] /= max(1, self.imgsz - 2 * left_pad)
pred[:, 1:4:2] /= max(1, self.imgsz - 2 * top_pad)
pred = np.clip(pred, 0, 1)
else:
pred = np.zeros((0, 5))
pred = np.zeros((0, 5)) # Return empty prediction array

return pred

def __call__(self, pil_img: Image.Image, occlusion_mask: Optional[np.ndarray] = None) -> np.ndarray:
"""Run the classifier on an input image.
Args:
pil_img: The input PIL image.
occlusion_mask: Optional occlusion mask to exclude certain areas.
Returns:
Processed predictions.
"""
np_img, pad = self.prep_process(pil_img)

if self.format == "ncnn":
Expand All @@ -167,7 +201,16 @@ def __call__(self, pil_img: Image.Image, occlusion_mask: Optional[np.ndarray] =
else:
pred = self.ort_session.run(["output0"], {"images": np_img})[0][0]

pred = self.post_process(pred, pad)
# Convert pad to a tuple if required
if isinstance(pad, list):
pad = tuple(pad)

pred = self.post_process(pred, pad) # Ensure pad is passed as a tuple

# drop big detections
pred = np.clip(pred, 0, 1)
pred = pred[(pred[:, 2] - pred[:, 0]) < self.max_bbox_size, :]
pred = np.reshape(pred, (-1, 5))

if occlusion_mask is not None:
hm, wm = occlusion_mask.shape
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (C) 2022-2024, Pyronear.
# Copyright (C) 2022-2025, Pyronear.

# This program is licensed under the Apache License 2.0.
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.
Expand Down
Loading

0 comments on commit e48dc9a

Please sign in to comment.