Skip to content
53 changes: 53 additions & 0 deletions packages/scratch-core/src/conversion/data_formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from enum import Enum, auto

from pydantic import BaseModel

from container_models.scan_image import ScanImage


class MarkType(Enum):
# Impression marks
BREECH_FACE_IMPRESSION = auto()
CHAMBER_IMPRESSION = auto()
EJECTOR_IMPRESSION = auto()
EXTRACTOR_IMPRESSION = auto()
FIRING_PIN_IMPRESSION = auto()

# Striation marks
APERTURE_SHEAR_STRIATION = auto()
BULLET_GEA_STRIATION = auto()
BULLET_LEA_STRIATION = auto()
CHAMBER_STRIATION = auto()
EJECTOR_STRIATION = auto()
EJECTOR_PORT_STRIATION = auto()
EXTRACTOR_STRIATION = auto()
FIRING_PIN_DRAG_STRIATION = auto()

def is_impression(self) -> bool:
return "IMPRESSION" in self.name

def is_striation(self) -> bool:
return "STRIATION" in self.name

@property
def scale(self) -> float:
if self == MarkType.BREECH_FACE_IMPRESSION:
return 3.5e-6
return 1.5e-6


class CropType(Enum):
RECTANGLE = auto()
CIRCLE = auto()
ELLIPSE = auto()
POLYGON = auto()


class Mark(BaseModel):
"""
Representation of a mark (impression or striation)
"""

scan_image: ScanImage
mark_type: MarkType
crop_type: CropType
11 changes: 11 additions & 0 deletions packages/scratch-core/src/conversion/resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from numpy.typing import NDArray
from scipy import ndimage

from conversion.data_formats import Mark
from container_models.scan_image import ScanImage
from container_models.base import MaskArray

Expand Down Expand Up @@ -56,6 +57,16 @@ def resample_image_and_mask(
return image, mask


def resample_mark(mark: Mark) -> Mark:
"""Resample a MarkImage so that the scale matches the scale specific for the mark type."""
resampled_scan_image, _ = resample_image_and_mask(
mark.scan_image,
target_scale=mark.mark_type.scale,
only_downsample=False,
)
return mark.model_copy(update={"scan_image": resampled_scan_image})


def resample_mask(mask: MaskArray, resample_factors: tuple[float, float]) -> MaskArray:
"""Resample the provided mask array using the specified resampling factors."""
return _resample_array(mask, resample_factors, order=0, mode="nearest")
Expand Down
Empty file.
15 changes: 12 additions & 3 deletions packages/scratch-core/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

import numpy as np
import pytest
from loguru import logger
from PIL import Image
from loguru import logger

from container_models.base import MaskArray, ScanMap2DArray
from container_models.base import ScanMap2DArray, MaskArray
from container_models.scan_image import ScanImage
from conversion.data_formats import MarkType, CropType, Mark
from parsers import load_scan_image

from .helper_function import unwrap_result

TEST_ROOT = Path(__file__).parent
Expand Down Expand Up @@ -95,3 +95,12 @@ def mask_array(scan_image_replica) -> MaskArray:
data[:, 0] = 0 # First column
data[:, -1] = 0 # Last column
return data


@pytest.fixture(scope="session")
def mark(scan_image: ScanImage) -> Mark:
return Mark(
scan_image=scan_image,
mark_type=MarkType.BREECH_FACE_IMPRESSION,
crop_type=CropType.RECTANGLE,
)
19 changes: 19 additions & 0 deletions packages/scratch-core/tests/conversion/test_data_formats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from conversion.data_formats import MarkType


class TestMarkType:
def test_impression_marks_are_identified_as_impressions(self):
assert MarkType.BREECH_FACE_IMPRESSION.is_impression()
assert MarkType.FIRING_PIN_IMPRESSION.is_impression()
assert not MarkType.BULLET_GEA_STRIATION.is_impression()

def test_striation_marks_are_identified_as_striations(self):
assert MarkType.BULLET_GEA_STRIATION.is_striation()
assert MarkType.FIRING_PIN_DRAG_STRIATION.is_striation()
assert not MarkType.BREECH_FACE_IMPRESSION.is_striation()

def test_all_marks_are_either_impression_or_striation(self):
for mark in MarkType:
assert mark.is_impression() ^ mark.is_striation(), (
f"{mark} is neither or both"
)
11 changes: 11 additions & 0 deletions packages/scratch-core/tests/conversion/test_resample.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import numpy as np
import pytest

from conversion.data_formats import Mark
from conversion.resample import (
get_resampling_factors,
resample_image_and_mask,
clip_resample_factors,
resample_mark,
)
from container_models.scan_image import ScanImage
from container_models.base import MaskArray
Expand Down Expand Up @@ -126,3 +128,12 @@ def test_mask_stays_binary(
assert result_mask is not None
unique_values = np.unique(result_mask)
assert all(v in [0, 1] for v in unique_values)


class TestResampleMark:
def test_uses_mark_target_sampling(self, mark: Mark):
resampled = resample_mark(mark)

scale = mark.mark_type.scale
assert resampled.scan_image.scale_x == scale
assert resampled.scan_image.scale_y == scale