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
140 lines
5.5 KiB
Python
140 lines
5.5 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
|
|
|
|
import pytest
|
|
import torch
|
|
|
|
from kornia.core.exceptions import BaseError
|
|
from kornia.models.structures import Prompts, SegmentationResults
|
|
|
|
|
|
class TestSegmentationResults:
|
|
def _make_results(self, B=2, C=3, H=16, W=16, threshold=0.0):
|
|
logits = torch.randn(B, C, H, W)
|
|
scores = torch.rand(B, C)
|
|
return SegmentationResults(logits=logits, scores=scores, mask_threshold=threshold)
|
|
|
|
def test_binary_masks_uses_logits_when_no_original(self):
|
|
r = self._make_results()
|
|
masks = r.binary_masks
|
|
assert masks.shape == r.logits.shape
|
|
assert masks.dtype == torch.bool
|
|
assert torch.equal(masks, r.logits > r.mask_threshold)
|
|
|
|
def test_binary_masks_uses_original_res_logits_when_set(self):
|
|
r = self._make_results()
|
|
# Simulate having called original_res_logits()
|
|
fake_hires = torch.randn(2, 3, 32, 32) + 10.0 # all positive -> all True
|
|
r._original_res_logits = fake_hires
|
|
masks = r.binary_masks
|
|
assert masks.shape == (2, 3, 32, 32)
|
|
assert masks.all()
|
|
|
|
def test_original_res_logits_without_encoder_resize(self):
|
|
r = self._make_results(B=1, C=1, H=8, W=8)
|
|
# No encoder resize (image_size_encoder=None), just crop and resize
|
|
result = r.original_res_logits(input_size=(8, 8), original_size=(32, 32), image_size_encoder=None)
|
|
assert result.shape == (1, 1, 32, 32)
|
|
assert r._original_res_logits is not None
|
|
|
|
def test_original_res_logits_with_encoder_resize(self):
|
|
r = self._make_results(B=1, C=1, H=8, W=8)
|
|
# With encoder resize: first resize to (16, 16), then crop, then resize to (32, 32)
|
|
result = r.original_res_logits(input_size=(16, 16), original_size=(32, 32), image_size_encoder=(16, 16))
|
|
assert result.shape == (1, 1, 32, 32)
|
|
|
|
def test_original_res_logits_crops_padding(self):
|
|
# Logits have extra spatial dimension due to padding
|
|
r = self._make_results(B=1, C=1, H=10, W=10)
|
|
# Crop to 8x8, then resize to 4x4
|
|
result = r.original_res_logits(input_size=(8, 8), original_size=(4, 4), image_size_encoder=None)
|
|
assert result.shape == (1, 1, 4, 4)
|
|
|
|
def test_squeeze_without_original_res_logits(self):
|
|
r = self._make_results(B=1, C=3, H=8, W=8)
|
|
squeezed = r.squeeze(dim=0)
|
|
assert squeezed.logits.shape == (3, 8, 8)
|
|
assert squeezed.scores.shape == (3,)
|
|
|
|
def test_squeeze_with_original_res_logits(self):
|
|
r = self._make_results(B=1, C=3, H=8, W=8)
|
|
r._original_res_logits = torch.randn(1, 3, 32, 32)
|
|
squeezed = r.squeeze(dim=0)
|
|
assert squeezed.logits.shape == (3, 8, 8)
|
|
assert isinstance(squeezed._original_res_logits, torch.Tensor)
|
|
assert squeezed._original_res_logits.shape == (3, 32, 32)
|
|
|
|
def test_binary_masks_threshold(self):
|
|
logits = torch.tensor([[[[0.5, -0.5], [0.3, 0.1]]]])
|
|
scores = torch.ones(1, 1)
|
|
r = SegmentationResults(logits=logits, scores=scores, mask_threshold=0.2)
|
|
masks = r.binary_masks
|
|
# 0.5 > 0.2 -> True, -0.5 > 0.2 -> False, 0.3 > 0.2 -> True, 0.1 > 0.2 -> False
|
|
expected = torch.tensor([[[[True, False], [True, False]]]])
|
|
assert torch.equal(masks, expected)
|
|
|
|
|
|
class TestPrompts:
|
|
def test_no_prompts(self):
|
|
p = Prompts()
|
|
assert p.points is None
|
|
assert p.boxes is None
|
|
assert p.masks is None
|
|
assert p.keypoints is None
|
|
assert p.keypoints_labels is None
|
|
|
|
def test_keypoints_from_tuple(self):
|
|
coords = torch.rand(2, 5, 2)
|
|
labels = torch.randint(0, 2, (2, 5)).float()
|
|
p = Prompts(points=(coords, labels))
|
|
assert torch.equal(p.keypoints, coords)
|
|
assert torch.equal(p.keypoints_labels, labels)
|
|
|
|
def test_keypoints_none_when_points_none(self):
|
|
p = Prompts(points=None)
|
|
assert p.keypoints is None
|
|
assert p.keypoints_labels is None
|
|
|
|
def test_boxes_only(self):
|
|
boxes = torch.rand(2, 4)
|
|
p = Prompts(boxes=boxes)
|
|
assert torch.equal(p.boxes, boxes)
|
|
assert p.keypoints is None
|
|
|
|
def test_keypoints_and_boxes_matching_batch(self):
|
|
coords = torch.rand(3, 5, 2)
|
|
labels = torch.rand(3, 5)
|
|
boxes = torch.rand(3, 4)
|
|
# Should not raise: batch sizes match
|
|
p = Prompts(points=(coords, labels), boxes=boxes)
|
|
assert p.keypoints.shape[0] == 3
|
|
assert p.boxes.shape[0] == 3
|
|
|
|
def test_keypoints_and_boxes_mismatched_batch_raises(self):
|
|
coords = torch.rand(2, 5, 2)
|
|
labels = torch.rand(2, 5)
|
|
boxes = torch.rand(3, 4) # different batch size
|
|
with pytest.raises(BaseError, match="same batch size"):
|
|
Prompts(points=(coords, labels), boxes=boxes)
|
|
|
|
def test_masks_only(self):
|
|
masks = torch.rand(2, 1, 32, 32)
|
|
p = Prompts(masks=masks)
|
|
assert torch.equal(p.masks, masks)
|