项目文件夹

文件
wehub-resource-sync 3a2c66702c
Tests on CPU (scheduled) / check-skip (push) Has been cancelled
Tests on CPU (scheduled) / pre-tests (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float32) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-ubuntu (float64) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.11, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.12, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-windows (3.13, float64, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.11, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.5.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.12, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / tests-cpu-mac (3.13, float32, 2.9.1) (push) Has been cancelled
Tests on CPU (scheduled) / coverage (push) Has been cancelled
Tests on CPU (scheduled) / typing (push) Has been cancelled
Tests on CPU (scheduled) / tutorials (push) Has been cancelled
Tests on CPU (scheduled) / docs (push) Has been cancelled
Lint / TOML Format (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:49:27 +08:00

236 行
9.3 KiB
Python

# LICENSE HEADER MANAGED BY add-license-header
#
# Copyright 2018 Kornia Team
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
from __future__ import annotations
from typing import ClassVar, Optional, Union
import torch
import kornia
from kornia.core.external import PILImage as Image
from kornia.models.base import ModelBase
__all__ = ["SemanticSegmentation"]
class SemanticSegmentation(ModelBase):
"""Semantic Segmentation is a module that wraps a semantic segmentation model.
This module uses SegmentationModel library for semantic segmentation.
"""
ONNX_DEFAULT_INPUTSHAPE: ClassVar[list[int]] = [-1, 3, -1, -1]
ONNX_DEFAULT_OUTPUTSHAPE: ClassVar[list[int]] = [-1, -1, -1, -1]
@torch.inference_mode()
def forward(self, images: Union[torch.Tensor, list[torch.Tensor]]) -> Union[torch.Tensor, list[torch.Tensor]]:
"""Forward pass of the semantic segmentation model.
Args:
images: If list of RGB images. Each image is a torch.Tensor with shape :math:`(3, H, W)`.
If torch.Tensor, a torch.Tensor with shape :math:`(B, 3, H, W)`.
Returns:
output tensor.
"""
outputs: Union[torch.Tensor, list[torch.Tensor]]
if isinstance(
images,
(
list,
tuple,
),
):
outputs = []
for image in images:
image = self.pre_processor(image[None])
output = self.model(image)
output = self.post_processor(output)
outputs.append(output[0])
else:
images = self.pre_processor(images)
outputs = self.model(images)
outputs = self.post_processor(outputs)
return outputs
def get_colormap(self, num_classes: int, colormap: str = "random", manual_seed: int = 2147) -> torch.Tensor:
"""Get a color map of size num_classes.
Args:
num_classes: The number of colors in the color map.
colormap: The colormap to use, can be "random" or a custom color map.
manual_seed: The manual seed to use for the colormap.
Returns:
A tensor of shape (num_classes, 3) representing the color map.
"""
if colormap == "random":
# Generate a color for each class
g_cpu = torch.Generator()
g_cpu.manual_seed(manual_seed)
colors = torch.rand(num_classes, 3, generator=g_cpu)
else:
raise ValueError(f"Unsupported colormap: {colormap}")
return colors
def visualize_output(self, semantic_mask: torch.Tensor, colors: torch.Tensor) -> torch.Tensor:
"""Visualize the output of the segmentation model.
Args:
semantic_mask: The output of the segmentation model. Shape should be (C, H, W) or (B, C, H, W).
colors: The color map to use for visualizing the output of the segmentation model.
Shape should be (num_classes, 3).
Returns:
A tensor of shape (3, H, W) or (B, 3, H, W) representing the visualized output of the segmentation model.
Raises:
ValueError: If the shape of the semantic mask is not of shape (C, H, W) or (B, C, H, W).
ValueError: If the shape of the colors is not of shape (num_classes, 3).
ValueError: If only muliclass segmentation is supported. Please ensure a softmax is used, or submit a PR.
"""
if semantic_mask.dim() == 3:
channel_dim = 0
elif semantic_mask.dim() == 4:
channel_dim = 1
else:
raise ValueError(f"Semantic mask must be of shape (C, H, W) or (B, C, H, W), got {semantic_mask.shape}.")
if torch.allclose(
semantic_mask.sum(dim=channel_dim), torch.tensor(1, dtype=semantic_mask.dtype, device=semantic_mask.device)
):
# Softmax is used, thus, muliclass segmentation
semantic_mask = semantic_mask.argmax(dim=channel_dim, keepdim=True)
# Create a colormap for each pixel based on the class with the highest probability
output = colors[semantic_mask.squeeze(channel_dim)]
if semantic_mask.dim() == 3:
output = output.permute(2, 0, 1)
elif semantic_mask.dim() == 4:
output = output.permute(0, 3, 1, 2)
else:
raise ValueError(
f"Semantic mask must be of shape (C, H, W) or (B, C, H, W), got {semantic_mask.shape}."
)
else:
raise ValueError(
"Only muliclass segmentation is supported. Please ensure a softmax is used, or submit a PR."
)
return output
def visualize(
self,
images: Union[torch.Tensor, list[torch.Tensor]],
semantic_masks: Optional[Union[torch.Tensor, list[torch.Tensor]]] = None,
output_type: str = "torch",
colormap: str = "random",
manual_seed: int = 2147,
) -> Union[torch.Tensor, list[torch.Tensor], list[Image.Image]]: # type: ignore
"""Visualize the segmentation masks.
Args:
images: If list of RGB images. Each image is a torch.Tensor with shape :math:`(3, H, W)`.
If torch.Tensor, a torch.Tensor with shape :math:`(B, 3, H, W)`.
semantic_masks: If list of segmentation masks. Each mask is a torch.Tensor with shape :math:`(C, H, W)`.
If torch.Tensor, a torch.Tensor with shape :math:`(B, C, H, W)`.
output_type: The type of output, can be "torch" or "PIL".
colormap: The colormap to use, can be "random" or a custom color map.
manual_seed: The manual seed to use for the colormap.
"""
if semantic_masks is None:
semantic_masks = self.forward(images)
outputs: Union[torch.Tensor, list[torch.Tensor]]
if isinstance(
semantic_masks,
(
list,
tuple,
),
):
outputs = []
for semantic_mask in semantic_masks:
if semantic_mask.ndim != 3:
raise ValueError(f"Semantic mask must be of shape (C, H, W), got {semantic_mask.shape}.")
# Generate a color for each class
colors = self.get_colormap(semantic_mask.size(0), colormap, manual_seed=manual_seed)
outputs.append(self.visualize_output(semantic_mask, colors))
else:
# Generate a color for each class
colors = self.get_colormap(semantic_masks.size(1), colormap, manual_seed=manual_seed)
outputs = self.visualize_output(semantic_masks, colors)
return self._tensor_to_type(outputs, output_type, is_batch=True if isinstance(outputs, torch.Tensor) else False)
def save(
self,
images: Union[torch.Tensor, list[torch.Tensor]],
semantic_masks: Optional[Union[torch.Tensor, list[torch.Tensor]]] = None,
directory: Optional[str] = None,
output_type: str = "torch",
colormap: str = "random",
manual_seed: int = 2147,
) -> None:
"""Save the segmentation results.
Args:
images: If list of RGB images. Each image is a torch.Tensor with shape :math:`(3, H, W)`.
If torch.Tensor, a torch.Tensor with shape :math:`(B, 3, H, W)`.
semantic_masks: If list of segmentation masks. Each mask is a torch.Tensor with shape :math:`(C, H, W)`.
If torch.Tensor, a torch.Tensor with shape :math:`(B, C, H, W)`.
directory: The directory to save the results.
output_type: The type of output, can be "torch" or "PIL".
colormap: The colormap to use, can be "random" or a custom color map.
manual_seed: The manual seed to use for the colormap.
"""
colored_masks = self.visualize(images, semantic_masks, output_type, colormap=colormap, manual_seed=manual_seed)
overlaid: Union[torch.Tensor, list[torch.Tensor]]
if isinstance(images, torch.Tensor) and isinstance(colored_masks, torch.Tensor):
overlaid = kornia.enhance.add_weighted(images, 0.5, colored_masks, 0.5, 1.0)
elif isinstance(
images,
(
list,
tuple,
),
) and isinstance(
colored_masks,
(
list,
tuple,
),
):
overlaid = []
for i in range(len(images)):
overlaid.append(kornia.enhance.add_weighted(images[i][None], 0.5, colored_masks[i][None], 0.5, 1.0)[0])
else:
raise ValueError(f"`images` should be a torch.Tensor or a list of Tensors. Got {type(images)}")
self._save_outputs(images, directory, suffix="_src")
self._save_outputs(colored_masks, directory, suffix="_mask")
self._save_outputs(overlaid, directory, suffix="_overlay")