Skip to content

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
cpvannier committed Oct 26, 2023
2 parents 84fbd1a + a29d311 commit 8ddec6d
Show file tree
Hide file tree
Showing 10 changed files with 376 additions and 308 deletions.
15 changes: 9 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ All notable changes to Pixano will be documented in this file.

## [Unreleased]

## [0.2.0] - 2023-10-26

### Changed
- **Breaking:** Update models to the new **PixanoTypes** and **lancedb storage format** of Pixano 0.4.0


## [0.1.6] - 2023-07-10
Expand Down Expand Up @@ -34,10 +38,8 @@ All notable changes to Pixano will be documented in this file.

## [0.1.3] - 2023-07-07

### Fixed
- Fix uses of Image type for compatibility with Pixano 0.3.0
- Fix calls of InferenceModel for compatibility with Pixano 0.3.0

### Changed
- **Breaking:** Update models to the new **InferenceModel class** and **Image type** of Pixano 0.3.0


## [0.1.2] - 2023-06-12
Expand All @@ -62,7 +64,7 @@ All notable changes to Pixano will be documented in this file.
## [0.1.0] - 2023-06-02

### Changed
- Merge inference generation and embedding precomputing into a **single inference model class**
- **Breaking:** Merge inference generation and embedding precomputing into a **single InferenceModel class**
- Improve README file

### Fixed
Expand All @@ -79,7 +81,8 @@ All notable changes to Pixano will be documented in this file.



[Unreleased]: https://github.com/pixano/pixano-inference/compare/v0.1.5...develop
[Unreleased]: https://github.com/pixano/pixano-inference/compare/v0.2.0...develop
[0.2.0]: https://github.com/pixano/pixano-inference/compare/v0.1.6...v0.2.0
[0.1.6]: https://github.com/pixano/pixano-inference/compare/v0.1.5...v0.1.6
[0.1.5]: https://github.com/pixano/pixano-inference/compare/v0.1.4...v0.1.5
[0.1.4]: https://github.com/pixano/pixano-inference/compare/v0.1.3...v0.1.4
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ plugins:
options:
docstring_style: google
docstring_options:
returns_named_value: false
ignore_init_summary: yes
show_submodules: no
docstring_section_style: table
Expand Down
2 changes: 1 addition & 1 deletion pixano_inference/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
#
# http://www.cecill.info

__version__ = "0.1.6"
__version__ = "0.2.0"
95 changes: 50 additions & 45 deletions pixano_inference/pytorch_models/deeplabv3.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@
#
# http://www.cecill.info

from pathlib import Path

import numpy as np
import pyarrow as pa
import shortuuid
import torch
import torchvision.transforms as T
from PIL import Image
from pixano.core import arrow_types
from pixano.core import CompressedRLE, Image
from pixano.models import InferenceModel
from pixano.transforms import mask_to_rle, voc_names
from pixano.utils import voc_names


def unmold_mask(mask: torch.Tensor, threshold: float = 0.5):
Expand Down Expand Up @@ -55,8 +52,7 @@ class DeepLabV3(InferenceModel):
name (str): Model name
id (str): Model ID
device (str): Model GPU or CPU device
source (str): Model source
info (str): Additional model info
description (str): Model description
model (torch.nn.Module): PyTorch model
transforms (torch.nn.Module): PyTorch preprocessing transforms
"""
Expand All @@ -73,8 +69,7 @@ def __init__(self, id: str = "", device: str = "cuda") -> None:
name="DeepLabV3",
id=id,
device=device,
source="PyTorch Hub",
info="DeepLabV3, ResNet-50 Backbone",
description="From PyTorch Hub. DeepLabV3, ResNet-50 Backbone.",
)

# Model
Expand All @@ -92,49 +87,59 @@ def __init__(self, id: str = "", device: str = "cuda") -> None:
]
)

def inference_batch(
self, batch: pa.RecordBatch, view: str, uri_prefix: str, threshold: float = 0.0
) -> list[list[arrow_types.ObjectAnnotation]]:
def preannotate(
self,
batch: pa.RecordBatch,
views: list[str],
uri_prefix: str,
threshold: float = 0.0,
) -> list[dict]:
"""Inference pre-annotation for a batch
Args:
batch (pa.RecordBatch): Input batch
view (str): Dataset view
views (list[str]): Dataset views
uri_prefix (str): URI prefix for media files
threshold (float, optional): Confidence threshold. Defaults to 0.0.
Returns:
list[list[arrow_types.ObjectAnnotation]]: Model inferences as lists of ObjectAnnotation
list[dict]: Processed rows
"""

objects = []

# PyTorch Transforms don't support different-sized image batches, so iterate manually
for x in range(batch.num_rows):
# Preprocess image
im = batch[view][x].as_py(uri_prefix).as_pillow()
im_tensor = self.transforms(im).unsqueeze(0).to(self.device)

# Inference
with torch.no_grad():
output = self.model(im_tensor)["out"][0]

# Process model outputs
sem_mask = output.argmax(0)
labels = torch.unique(sem_mask)[1:]
masks = sem_mask == labels[:, None, None]

objects.append(
[
arrow_types.ObjectAnnotation(
id=shortuuid.uuid(),
view_id=view,
mask=mask_to_rle(unmold_mask(mask)),
mask_source=self.id,
category_id=int(label),
category_name=voc_names(label),
)
for label, mask in zip(labels, masks)
]
)
return objects
rows = []

for view in views:
# PyTorch Transforms don't support different-sized image batches, so iterate manually
for x in range(batch.num_rows):
# Preprocess image
im = Image.from_dict(batch[view][x].as_py())
im.uri_prefix = uri_prefix
im = im.as_pillow()
im_tensor = self.transforms(im).unsqueeze(0).to(self.device)

# Inference
with torch.no_grad():
output = self.model(im_tensor)["out"][0]

# Process model outputs
sem_mask = output.argmax(0)
labels = torch.unique(sem_mask)[1:]
masks = sem_mask == labels[:, None, None]

rows.extend(
[
{
"id": shortuuid.uuid(),
"item_id": batch["id"][x].as_py(),
"view_id": view,
"mask": CompressedRLE.from_mask(
unmold_mask(mask)
).to_dict(),
"category_id": int(label),
"category_name": voc_names(label),
}
for label, mask in zip(labels, masks)
]
)

return rows
100 changes: 54 additions & 46 deletions pixano_inference/pytorch_models/maskrcnnv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@
#
# http://www.cecill.info

from pathlib import Path

import numpy as np
import pyarrow as pa
import shortuuid
import torch
from PIL import Image
from pixano.core import arrow_types
from pixano.core import BBox, CompressedRLE, Image
from pixano.models import InferenceModel
from pixano.transforms import coco_names_91, mask_to_rle, normalize, xyxy_to_xywh
from pixano.utils import coco_names_91
from torchvision.models.detection import (
MaskRCNN_ResNet50_FPN_V2_Weights,
maskrcnn_resnet50_fpn_v2,
Expand Down Expand Up @@ -58,8 +55,7 @@ class MaskRCNNv2(InferenceModel):
name (str): Model name
id (str): Model ID
device (str): Model GPU or CPU device
source (str): Model source
info (str): Additional model info
description (str): Model description
model (torch.nn.Module): PyTorch model
transforms (torch.nn.Module): PyTorch preprocessing transforms
"""
Expand All @@ -76,8 +72,7 @@ def __init__(self, id: str = "", device: str = "cuda") -> None:
name="MaskRCNNv2",
id=id,
device=device,
source="PyTorch Hub",
info="MaskRCNN, ResNet-50-FPN v2 Backbone, COCO_V1 Weights",
description="From PyTorch Hub. MaskRCNN, ResNet-50-FPN v2 Backbone, COCO_V1 Weights.",
)

# Model
Expand All @@ -90,50 +85,63 @@ def __init__(self, id: str = "", device: str = "cuda") -> None:
# Transforms
self.transforms = MaskRCNN_ResNet50_FPN_V2_Weights.COCO_V1.transforms()

def inference_batch(
self, batch: pa.RecordBatch, view: str, uri_prefix: str, threshold: float = 0.0
) -> list[list[arrow_types.ObjectAnnotation]]:
def preannotate(
self,
batch: pa.RecordBatch,
views: list[str],
uri_prefix: str,
threshold: float = 0.0,
) -> list[dict]:
"""Inference pre-annotation for a batch
Args:
batch (pa.RecordBatch): Input batch
view (str): Dataset view
views (list[str]): Dataset views
uri_prefix (str): URI prefix for media files
threshold (float, optional): Confidence threshold. Defaults to 0.0.
Returns:
list[list[arrow_types.ObjectAnnotation]]: Model inferences as lists of ObjectAnnotation
list[dict]: Processed rows
"""

objects = []

# PyTorch Transforms don't support different-sized image batches, so iterate manually
for x in range(batch.num_rows):
# Preprocess image
im = batch[view][x].as_py(uri_prefix).as_pillow()
im_tensor = self.transforms(im).unsqueeze(0).to(self.device)

# Inference
with torch.no_grad():
output = self.model(im_tensor)[0]

# Process model outputs
w, h = im.size
objects.append(
[
arrow_types.ObjectAnnotation(
id=shortuuid.uuid(),
view_id=view,
bbox=normalize(xyxy_to_xywh(output["boxes"][i]), h, w),
bbox_confidence=float(output["scores"][i]),
bbox_source=self.id,
mask=mask_to_rle(unmold_mask(output["masks"][i])),
mask_source=self.id,
category_id=int(output["labels"][i]),
category_name=coco_names_91(output["labels"][i]),
)
for i in range(len(output["scores"]))
if output["scores"][i] > threshold
]
)
return objects
rows = []

for view in views:
# PyTorch Transforms don't support different-sized image batches, so iterate manually
for x in range(batch.num_rows):
# Preprocess image
im = Image.from_dict(batch[view][x].as_py())
im.uri_prefix = uri_prefix
im = im.as_pillow()
im_tensor = self.transforms(im).unsqueeze(0).to(self.device)

# Inference
with torch.no_grad():
output = self.model(im_tensor)[0]

# Process model outputs
w, h = im.size
rows.extend(
[
{
"id": shortuuid.uuid(),
"item_id": batch["id"][x].as_py(),
"view_id": view,
"bbox": BBox.from_xyxy(
list(output["boxes"][i]),
confidence=float(output["scores"][i]),
)
.normalize(h, w)
.to_dict(),
"mask": CompressedRLE.from_mask(
unmold_mask(output["masks"][i])
).to_dict(),
"category_id": int(output["labels"][i]),
"category_name": coco_names_91(output["labels"][i]),
}
for i in range(len(output["scores"]))
if output["scores"][i] > threshold
]
)

return rows
Loading

0 comments on commit 8ddec6d

Please sign in to comment.