Added image resizing related nodes and a single image prediction node

This commit is contained in:
HowToSD
2025-03-08 03:38:52 -08:00
parent a2fc03f41f
commit 5fee9ffb60
32 changed files with 1007 additions and 13 deletions
+49
View File
@@ -0,0 +1,49 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_crop import PtCrop
class TestPtCrop(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtCrop()
# Test cases: (Input tensor, Height, Width, Expected shape)
self.test_cases = [
(torch.rand(1, 3, 16, 16), 8, 8, (1, 3, 8, 8)), # Crop 4D tensor
(torch.rand(3, 16, 16), 8, 8, (3, 8, 8)), # Crop 3D tensor
(torch.rand(1, 3, 16, 16), 16, 16, (1, 3, 16, 16)), # No crop, same size
]
def test_crop(self):
"""Test tensor cropping to specific sizes."""
for tens, height, width, expected_shape in self.test_cases:
with self.subTest(height=height, width=width):
result, = self.node.f(tens, height, width)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
def test_invalid_tensor_rank(self):
"""Test that an invalid tensor rank raises an error."""
tens = torch.rand(16, 16) # 2D tensor (invalid)
with self.assertRaises(ValueError, msg="Only rank 3 or 4 tensors are supported for crop."):
self.node.f(tens, 8, 8)
def test_crop_exceeds_dimensions(self):
"""Test that cropping beyond tensor dimensions raises an error."""
tens = torch.rand(1, 3, 16, 16)
with self.assertRaises(ValueError, msg="Specified crop size exceeds tensor dimensions."):
self.node.f(tens, 32, 32)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,57 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_from_image_transpose import PtFromImageTranspose
class TestPtFromImageTranspose(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtFromImageTranspose()
# Define test cases: (input_tensor, expected_shape)
self.test_cases = [
# Case 1: 3D tensor (H, W, C) -> (C, H, W)
(torch.ones(64, 128, 3), (3, 64, 128)), # (64,128,3) -> (3,64,128)
# Case 2: 4D tensor (N, H, W, C) -> (N, C, H, W)
(torch.ones(10, 64, 128, 3), (10, 3, 64, 128)), # (10,64,128,3) -> (10,3,64,128)
# Case 3: Invalid input (2D tensor)
(torch.ones(64, 128), ValueError), # 2D tensors should raise an error
# Case 4: Invalid input (5D tensor)
(torch.ones(10, 64, 128, 3, 2), ValueError), # 5D tensors should raise an error
]
def test_transpose(self):
"""Test image transposition operation with valid and invalid cases."""
for input_tensor, expected in self.test_cases:
with self.subTest(input_shape=input_tensor.shape):
if isinstance(expected, tuple):
# Valid cases
result, = self.node.f(input_tensor)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result has correct shape
self.assertEqual(result.shape, torch.Size(expected), f"Expected shape {expected}, got {result.shape}")
# Ensure result has the same dtype
self.assertEqual(result.dtype, input_tensor.dtype, "Dtype mismatch after transpose operation")
else:
# Invalid cases should raise an error
with self.assertRaises(expected):
self.node.f(input_tensor)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,60 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_interpolate_by_scale_factor import PtInterpolateByScaleFactor
class TestPtInterpolateByScaleFactor(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtInterpolateByScaleFactor()
# Test cases 3d: (Input tensor, Scale factor, Mode, Expected shape)
self.test_cases_3d = [
(torch.rand(3, 16, 16), 2.0, "nearest", (3, 32, 32)),
(torch.rand(3, 16, 16), 2.0, "nearest-exact", (3, 32, 32)),
(torch.rand(3, 16, 16), 0.5, "bilinear", (3, 8, 8)),
(torch.rand(3, 16, 16), 1.5, "bicubic", (3, 24, 24)),
]
# Test cases: (Input tensor, Scale factor, Mode, Expected shape)
self.test_cases_4d = [
(torch.rand(1, 3, 16, 16), 2.0, "nearest", (1, 3, 32, 32)), # Upscale 4D tensor
(torch.rand(1, 3, 16, 16), 2.0, "nearest-exact", (1, 3, 32, 32)), # Upscale 4D tensor
(torch.rand(1, 3, 16, 16), 0.5, "bilinear", (1, 3, 8, 8)), # Downscale 4D tensor
(torch.rand(1, 3, 16, 16), 1.5, "bicubic", (1, 3, 24, 24)), # Bicubic mode with 4D tensor
]
def test_interpolation_3d(self):
"""Test tensor interpolation with different scale factors and modes."""
for tens, scale_factor, mode, expected_shape in self.test_cases_3d:
with self.subTest(scale_factor=scale_factor, mode=mode):
result, = self.node.f(tens, scale_factor, mode)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
def test_interpolation_4d(self):
"""Test tensor interpolation with different scale factors and modes."""
for tens, scale_factor, mode, expected_shape in self.test_cases_4d:
with self.subTest(scale_factor=scale_factor, mode=mode):
result, = self.node.f(tens, scale_factor, mode)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,61 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_interpolate_to_size import PtInterpolateToSize
class TestPtInterpolateToSize(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtInterpolateToSize()
# Test cases 3d: (Input tensor, Height, Width, Mode, Expected shape)
self.test_cases_3d = [
(torch.rand(3, 16, 16), 32, 32, "nearest", (3, 32, 32)), # Upscale 3D tensor
(torch.rand(3, 16, 16), 32, 32, "nearest-exact", (3, 32, 32)), # Upscale 3D tensor
(torch.rand(3, 16, 16), 8, 8, "bilinear", (3, 8, 8)), # Downscale 3D tensor
(torch.rand(3, 16, 16), 24, 24, "bicubic", (3, 24, 24)), # Bicubic mode with 3D tensor
]
# Test cases 4d: (Input tensor, Height, Width, Mode, Expected shape)
self.test_cases_4d = [
(torch.rand(1, 3, 16, 16), 32, 32, "nearest", (1, 3, 32, 32)), # Upscale 4D tensor
(torch.rand(1, 3, 16, 16), 32, 32, "nearest-exact", (1, 3, 32, 32)), # Upscale 4D tensor
(torch.rand(1, 3, 16, 16), 8, 8, "bilinear", (1, 3, 8, 8)), # Downscale 4D tensor
(torch.rand(1, 3, 16, 16), 24, 24, "bicubic", (1, 3, 24, 24)), # Bicubic mode with 4D tensor
]
def test_interpolation_3d(self):
"""Test tensor interpolation with different target sizes and modes."""
for tens, height, width, mode, expected_shape in self.test_cases_3d:
with self.subTest(height=height, width=width, mode=mode):
result, = self.node.f(tens, height, width, mode)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
def test_interpolation_4d(self):
"""Test tensor interpolation with different target sizes and modes."""
for tens, height, width, mode, expected_shape in self.test_cases_4d:
with self.subTest(height=height, width=width, mode=mode):
result, = self.node.f(tens, height, width, mode)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
if __name__ == "__main__":
unittest.main()
+50
View File
@@ -0,0 +1,50 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_pad import PtPad
class TestPtPad(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtPad()
# Test cases: (Input tensor, Height, Width, Expected shape)
self.test_cases = [
(torch.rand(1, 3, 16, 16), 32, 32, (1, 3, 32, 32)), # Pad 4D tensor
(torch.rand(3, 16, 16), 32, 32, (3, 32, 32)), # Pad 3D tensor
(torch.rand(1, 3, 16, 16), 16, 16, (1, 3, 16, 16)), # No padding, same size
]
def test_padding(self):
"""Test tensor padding to specific sizes."""
for tens, height, width, expected_shape in self.test_cases:
with self.subTest(height=height, width=width):
result, = self.node.f(tens, height, width)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result shape matches expected shape
self.assertEqual(result.shape, expected_shape, f"Expected shape {expected_shape}, got {result.shape}")
def test_invalid_tensor_rank(self):
"""Test that an invalid tensor rank raises an error."""
tens = torch.rand(16, 16) # 2D tensor (invalid)
with self.assertRaises(ValueError, msg="Only rank 3 or 4 tensors are supported for padding."):
self.node.f(tens, 32, 32)
def test_padding_smaller_than_tensor(self):
"""Test that padding to a smaller size raises an error."""
tens = torch.rand(1, 3, 16, 16)
with self.assertRaises(ValueError, msg="Specified dimensions must be greater than or equal to tensor dimensions."):
self.node.f(tens, 8, 8)
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,68 @@
import os
import sys
import unittest
import torch
import torch.nn as nn
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.ptn_conv_model import PtnConvModel
from pytorch_wrapper.ptv_image_folder_dataset import PtvImageFolderDataset
from pytorch_wrapper.ptv_transforms_resize import PtvTransformsResize
from pytorch_wrapper.pt_data_loader import PtDataLoader
from pytorch_wrapper.pt_predict_classification_model import PtPredictClassificationModel
from pytorch_wrapper.pt_load_model import PtLoadModel
class TestPtPrediction(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
# Transform
compose_node = PtvTransformsResize()
transform = compose_node.f(256, 256)[0]
# Dataset
self.node = PtvImageFolderDataset()
self.dataset = self.node.f("dog_and_cat/val",
transform=transform)[0]
# Model
self.model_node = PtnConvModel()
self.model = self.model_node.f(
input_dim="(3, 256, 256)",
penultimate_dim=0,
output_dim=2,
channel_list="[32,64,128,256,512]", # To 128, 64, 32, 16, 8
kernel_size_list="[3,3,3,3,3]",
padding_list="[1,1,1,1,1]",
downsample_list="[True,True,True,True,True]"
)[0]
# Load weights
epochs = 20
self.load_model = PtLoadModel()
self.loaded_model = self.load_model.f(self.model,f"dog_cat_{epochs}_epochs_conv.pt")
# Instantiates evaluation node
self.predict = PtPredictClassificationModel()
def test_prediction(self):
"""
Tests prediction.
Note that this test requires a trained model file on the file system.
Unit test will fail if you run this for the first time.
Run the unit test for training to generate the weight, and re-run this unit test.
"""
inputs, y = next(iter(self.dataset))
class_id_to_name_map = {0:"cat", 1:"dog"}
class_name, class_id, prob = \
self.predict.f(self.model, inputs, class_id_to_name_map, use_gpu=True)
self.assertTrue(prob > 0.8)
self.assertEqual(class_id, 0)
self.assertEqual(class_name, "cat")
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,54 @@
import os
import sys
import unittest
import torch
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", ".."))
MODULE_ROOT = os.path.join(PROJECT_ROOT, "modules")
sys.path.append(MODULE_ROOT)
from pytorch_wrapper.pt_to_image_transpose import PtToImageTranspose
class TestPtToImageTranspose(unittest.TestCase):
def setUp(self):
"""Set up test instance."""
self.node = PtToImageTranspose()
# Define test cases: (input_tensor, expected_shape)
self.test_cases = [
# Case 1: 3D tensor (C, H, W) -> (H, W, C)
(torch.ones(3, 64, 128), (64, 128, 3)), # (3,64,128) -> (64,128,3)
# Case 2: 4D tensor (N, C, H, W) -> (N, H, W, C)
(torch.ones(10, 3, 64, 128), (10, 64, 128, 3)), # (10,3,64,128) -> (10,64,128,3)
# Case 3: Invalid input (2D tensor)
(torch.ones(64, 128), ValueError), # 2D tensors should raise an error
# Case 4: Invalid input (5D tensor)
(torch.ones(10, 3, 64, 128, 2), ValueError), # 5D tensors should raise an error
]
def test_transpose(self):
"""Test tensor to image transposition with valid and invalid cases."""
for input_tensor, expected in self.test_cases:
with self.subTest(input_shape=input_tensor.shape):
if isinstance(expected, tuple):
# Valid cases
result, = self.node.f(input_tensor)
# Ensure return value is a tensor
self.assertTrue(isinstance(result, torch.Tensor), "Output is not a tensor")
# Ensure result has correct shape
self.assertEqual(result.shape, torch.Size(expected), f"Expected shape {expected}, got {result.shape}")
# Ensure result has the same dtype
self.assertEqual(result.dtype, input_tensor.dtype, "Dtype mismatch after transpose operation")
else:
# Invalid cases should raise an error
with self.assertRaises(expected):
self.node.f(input_tensor)
if __name__ == "__main__":
unittest.main()