Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
565dd15
adding base file github actions
JHZ5583233 Apr 22, 2025
b52bc3a
adding actions
JHZ5583233 Apr 22, 2025
992e7a5
trying to fix flake 8 action
JHZ5583233 Apr 22, 2025
02a7c2d
Create pylint.yml
JHZ5583233 Apr 22, 2025
91d33de
Delete .github/workflows/github-actions.yml
JHZ5583233 Apr 22, 2025
ee641f2
Create flake.yml
JHZ5583233 Apr 22, 2025
461ae52
Update flake.yml
JHZ5583233 Apr 22, 2025
32e2606
now using given flake 8 yaml file
JHZ5583233 Apr 23, 2025
7ec52b2
Revert "now using given flake 8 yaml file"
JHZ5583233 Apr 23, 2025
e4c3710
Delete .github/workflows/flake.yml
JHZ5583233 Apr 23, 2025
f85b103
added oop flake8 check
JHZ5583233 Apr 23, 2025
e696c36
removed template code from main
JHZ5583233 May 7, 2025
b907f41
removed template unittest
JHZ5583233 May 7, 2025
100e729
Merge branch 'ivopascal:main' into make-a-clean-start
JHZ5583233 May 7, 2025
62e6312
updated the workflows
JHZ5583233 May 7, 2025
1216c6f
got rid of innit.py for flake 8 compliance
JHZ5583233 May 7, 2025
eee85f4
changes back for file name
JHZ5583233 May 7, 2025
a4e46b1
Merge pull request #5 from JHZ5583233/make-a-clean-start
JHZ5583233 May 7, 2025
7d8dd1f
changed innit to temp to keep the folders committed
JHZ5583233 May 7, 2025
b7d7deb
Preprocessing_class created functions for tiling and padding, reconst…
juliastgermain May 16, 2025
480bdbb
Preprocessing_class test created with 11 tests looking at normalizati…
juliastgermain May 16, 2025
390b4ff
changed some docstrings and comments, deleted what was unneeded
juliastgermain May 17, 2025
d6c5a1a
delete in blank lines and shortened lines over 79 to adhere to flake8
juliastgermain May 19, 2025
25e82e7
added docstrings for each function the Preprocessing_class.py and typ…
juliastgermain May 20, 2025
7a832cb
deleted temps
juliastgermain May 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
43 changes: 43 additions & 0 deletions .github/workflows/style.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Style check

on:
push:
branches:
- main
- master

pull_request:
branches:
- main
- master

jobs:
flake8_py3:
permissions: write-all
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

- uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install flake8 and plugins
run: |
pip install flake8 flake8-docstrings flake8-annotations

- name: Configure Flake8
run: |
echo "[flake8]" > .flake8
echo "extend-ignore = E402" >> .flake8
echo "exclude = .github,autoop/tests" >> .flake8
# exclude A101, A102, D100 and everything that starts with D2 and D4
echo "ignore = ANN101,ANN102,D100,D2,D4,ANN002,ANN003" >> .flake8

- name: Run flake8
uses: suo/flake8-github-action@releases/v1
with:
checkName: "flake8_py3"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 0 additions & 8 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,8 +0,0 @@
# This is a sample Python script.

def hello_world():
return "Hello, World!"


if __name__ == '__main__':
hello_world()
File renamed without changes.
File renamed without changes.
151 changes: 151 additions & 0 deletions project_name/models/Preprocessing_class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import numpy as np
import matplotlib.pyplot as plt
from typing import Tuple, Union, List


class Preprocessing:
def __init__(self, tile_size: Tuple[int, int] = (256, 256)) -> None:
"""
Initialize the Preprocessing class with a tile size.
"""
self.tile_size = tile_size
self.last_padding_info: dict[int, dict] = {}

def is_8_bit(self, np_array: np.ndarray) -> bool:
"""
Check if a numpy array is of type uint8.
"""
return np_array.dtype == np.uint8

def normalize(self, np_array: np.ndarray) -> np.ndarray:
"""
Normalize an array to the [0, 1] range as float32.
"""
if self.is_8_bit(np_array):
return np_array.astype(np.float32) / 255.0
if np.issubdtype(np_array.dtype, np.floating):
return np_array.astype(np.float32)
return (np_array / np.iinfo(np_array.dtype).max).astype(np.float32)

def tile_with_padding(
self,
np_arrays: Union[np.ndarray, List[np.ndarray]],
pad_mode: str = 'constant'
) -> np.ndarray:
"""
Tile one or more images with padding to fit the specified tile size.
"""
if not isinstance(np_arrays, (list, tuple)):
np_arrays = [np_arrays]

all_tiles = []
for idx, np_array in enumerate(np_arrays):
if not isinstance(np_array, np.ndarray):
raise TypeError("Input must be numpy array")
if not self.is_8_bit(np_array):
raise ValueError("Input must be uint8 array")

original_shape = np_array.shape
normalized = self.normalize(np_array.copy())

tile_h, tile_w = self.tile_size
h, w = original_shape[:2]

pad_h = (tile_h - (h % tile_h)) % tile_h
pad_w = (tile_w - (w % tile_w)) % tile_w

self.last_padding_info[idx] = {
'original_shape': original_shape,
'pad_h': pad_h,
'pad_w': pad_w,
'is_grayscale': len(original_shape) == 2
}

if len(original_shape) == 3:
pad_width = ((0, pad_h), (0, pad_w), (0, 0))
else:
pad_width = ((0, pad_h), (0, pad_w))

padded = np.pad(normalized, pad_width, mode=pad_mode)
padded_h, padded_w = padded.shape[:2]

tiles = []
for i in range(0, padded_h, tile_h):
for j in range(0, padded_w, tile_w):
tile = padded[i:i + tile_h, j:j + tile_w]
if tile.shape[:2] != (tile_h, tile_w):
tile = np.pad(
tile,
((0, tile_h - tile.shape[0]),
(0, tile_w - tile.shape[1])),
mode=pad_mode
)
tiles.append(tile)
all_tiles.extend(tiles)

return np.array(all_tiles)

def reconstruct_image(
self,
tiles: np.ndarray,
original_idx: int = 0
) -> np.ndarray:
"""
Reconstruct the original image from tiles.
"""
info = self.last_padding_info.get(original_idx)
if not info:
raise ValueError("No padding info found for this index")

tile_h, tile_w = self.tile_size
h, w = info['original_shape'][:2]
pad_h = info['pad_h']
pad_w = info['pad_w']

rows = (h + pad_h) // tile_h
cols = (w + pad_w) // tile_w

if info['is_grayscale']:
recon_shape = (h + pad_h, w + pad_w)
else:
recon_shape = (h + pad_h, w + pad_w, info['original_shape'][2])

reconstructed = np.zeros(recon_shape, dtype=np.float32)

for i in range(rows):
for j in range(cols):
idx = i * cols + j
reconstructed[
i * tile_h:(i + 1) * tile_h,
j * tile_w:(j + 1) * tile_w
] = tiles[idx]

final_image = np.clip(reconstructed[:h, :w], 0, 1)
if info['is_grayscale']:
final_image = np.squeeze(final_image)

return (final_image * 255).astype(np.uint8)

def depth_to_rgb(
self,
depth_map: np.ndarray,
cmap: str = 'plasma'
) -> np.ndarray:
"""
Convert a 2D depth map to a 3-channel RGB image using a colormap.
"""
if not isinstance(depth_map, np.ndarray) or depth_map.ndim != 2:
raise ValueError("Input must be a 2D numpy array "
"(grayscale depth map)")

min_val = np.min(depth_map)
max_val = np.max(depth_map)
if max_val - min_val == 0:
norm_depth = np.zeros_like(depth_map, dtype=np.float32)
else:
norm_depth = (depth_map - min_val) / (max_val - min_val)

colormap = plt.get_cmap(cmap)
colored = colormap(norm_depth) # Returns RGBA
rgb = (colored[:, :, :3] * 255).astype(np.uint8)
return rgb
Empty file removed project_name/models/__init__.py
Empty file.
Empty file removed tests/__init__.py
Empty file.
Empty file removed tests/data/__init__.py
Empty file.
File renamed without changes.
Empty file removed tests/features/__init__.py
Empty file.
File renamed without changes.
Empty file removed tests/models/__init__.py
Empty file.
103 changes: 103 additions & 0 deletions tests/models/unittest_preprocessing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import unittest
import numpy as np
from project_name.models.Preprocessing_class import Preprocessing


class TestPreprocessing(unittest.TestCase):
def setUp(self):
self.tile_size = (256, 256)
self.preprocessor = Preprocessing(tile_size=self.tile_size)
self.rgb_image = np.random.randint(
0, 256, (510, 510, 3), dtype=np.uint8
)
self.gray_image = np.random.randint(
0, 256, (510, 510), dtype=np.uint8
)

def test_is_8_bit(self):
self.assertTrue(self.preprocessor.is_8_bit(self.rgb_image))
self.assertFalse(
self.preprocessor.is_8_bit(
self.rgb_image.astype(np.float32)
)
)

def test_normalize_uint8(self):
norm = self.preprocessor.normalize(self.rgb_image)
self.assertTrue(np.all(norm >= 0.0) and np.all(norm <= 1.0))
self.assertEqual(norm.dtype, np.float32)

def test_normalize_float(self):
float_array = self.rgb_image.astype(np.float32) / 255.0
norm = self.preprocessor.normalize(float_array)
self.assertTrue(np.allclose(float_array, norm))
self.assertEqual(norm.dtype, np.float32)

def test_tile_with_padding_rgb(self):
tiles = self.preprocessor.tile_with_padding(self.rgb_image)
expected_num_tiles = (
(510 + (256 - 510 % 256)) // 256
) ** 2 # 2x2 = 4
self.assertEqual(tiles.shape[0], expected_num_tiles)
self.assertEqual(tiles.shape[1:], (256, 256, 3))

def test_tile_with_padding_grayscale(self):
tiles = self.preprocessor.tile_with_padding(self.gray_image)
expected_num_tiles = (
(510 + (256 - 510 % 256)) // 256
) ** 2 # 2x2 = 4
self.assertEqual(tiles.shape[0], expected_num_tiles)
self.assertEqual(tiles.shape[1:], (256, 256))

def test_reconstruction_rgb(self):
tiles = self.preprocessor.tile_with_padding(self.rgb_image)
recon = self.preprocessor.reconstruct_image(tiles)
self.assertEqual(recon.shape, self.rgb_image.shape)
self.assertTrue(np.allclose(recon, self.rgb_image, atol=2))

def test_reconstruction_grayscale(self):
tiles = self.preprocessor.tile_with_padding(self.gray_image)
recon = self.preprocessor.reconstruct_image(tiles)
self.assertEqual(recon.shape, self.gray_image.shape)
self.assertTrue(np.allclose(recon, self.gray_image, atol=2))

def test_invalid_input_type(self):
with self.assertRaises(TypeError):
self.preprocessor.tile_with_padding("not an array")

def test_invalid_input_dtype(self):
with self.assertRaises(ValueError):
self.preprocessor.tile_with_padding(
np.ones((100, 100), dtype=np.float32)
)

def test_missing_padding_info(self):
with self.assertRaises(ValueError):
self.preprocessor.reconstruct_image(
np.zeros((4, 256, 256, 3))
)

def test_depth_to_rgb(self):
# Create a mock depth map with float32 values from 0 to 10
depth_map = np.random.uniform(
low=0.0, high=10.0, size=(480, 640)
).astype(np.float32)
rgb_depth = self.preprocessor.depth_to_rgb(
depth_map, cmap='plasma'
)

self.assertEqual(rgb_depth.shape, (480, 640, 3))
self.assertEqual(rgb_depth.dtype, np.uint8)
self.assertTrue(
np.all(rgb_depth >= 0) and np.all(rgb_depth <= 255)
)
self.assertGreater(np.std(rgb_depth), 0)

with self.assertRaises(ValueError):
self.preprocessor.depth_to_rgb(
np.ones((10, 10, 3)) # Not 2D
)


if __name__ == '__main__':
unittest.main()
11 changes: 0 additions & 11 deletions tests/test_main.py

This file was deleted.