-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compare-images): add compare_images.py, compare_images_async.py …
…to dispatch
- Loading branch information
Showing
9 changed files
with
355 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
48 changes: 48 additions & 0 deletions
48
...ges/compare-images/python/itkwasm-compare-images/itkwasm_compare_images/compare_images.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import os | ||
from typing import Dict, Tuple, Optional, List, Any | ||
|
||
from itkwasm import ( | ||
environment_dispatch, | ||
Image, | ||
) | ||
|
||
def compare_images( | ||
test_image: Image, | ||
baseline_images: List[Image] = [], | ||
difference_threshold: float = 0, | ||
radius_tolerance: int = 0, | ||
number_of_pixels_tolerance: int = 0, | ||
ignore_boundary_pixels: bool = False, | ||
) -> Tuple[Any, Image, Image]: | ||
"""Compare images with a tolerance for regression testing. | ||
:param test_image: The input test image | ||
:type test_image: Image | ||
:param baseline_images: Baseline images compare against | ||
:type baseline_images: Image | ||
:param difference_threshold: Intensity difference for pixels to be considered different. | ||
:type difference_threshold: float | ||
:param radius_tolerance: Radius of the neighborhood around a pixel to search for similar intensity values. | ||
:type radius_tolerance: int | ||
:param number_of_pixels_tolerance: Number of pixels that can be different before the test fails. | ||
:type number_of_pixels_tolerance: int | ||
:param ignore_boundary_pixels: Ignore boundary pixels. Useful when resampling may have introduced difference pixel values along the image edge. | ||
:type ignore_boundary_pixels: bool | ||
:return: Metrics for the baseline with the fewest number of pixels outside the tolerances. | ||
:rtype: Any | ||
:return: Absolute difference image | ||
:rtype: Image | ||
:return: Unsigned char, 2D difference image for rendering | ||
:rtype: Image | ||
""" | ||
func = environment_dispatch("itkwasm_compare_images", "compare_images") | ||
output = func(test_image, baseline_images=baseline_images, difference_threshold=difference_threshold, radius_tolerance=radius_tolerance, number_of_pixels_tolerance=number_of_pixels_tolerance, ignore_boundary_pixels=ignore_boundary_pixels) | ||
return output |
48 changes: 48 additions & 0 deletions
48
...mpare-images/python/itkwasm-compare-images/itkwasm_compare_images/compare_images_async.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import os | ||
from typing import Dict, Tuple, Optional, List, Any | ||
|
||
from itkwasm import ( | ||
environment_dispatch, | ||
Image, | ||
) | ||
|
||
async def compare_images_async( | ||
test_image: Image, | ||
baseline_images: List[Image] = [], | ||
difference_threshold: float = 0, | ||
radius_tolerance: int = 0, | ||
number_of_pixels_tolerance: int = 0, | ||
ignore_boundary_pixels: bool = False, | ||
) -> Tuple[Any, Image, Image]: | ||
"""Compare images with a tolerance for regression testing. | ||
:param test_image: The input test image | ||
:type test_image: Image | ||
:param baseline_images: Baseline images compare against | ||
:type baseline_images: Image | ||
:param difference_threshold: Intensity difference for pixels to be considered different. | ||
:type difference_threshold: float | ||
:param radius_tolerance: Radius of the neighborhood around a pixel to search for similar intensity values. | ||
:type radius_tolerance: int | ||
:param number_of_pixels_tolerance: Number of pixels that can be different before the test fails. | ||
:type number_of_pixels_tolerance: int | ||
:param ignore_boundary_pixels: Ignore boundary pixels. Useful when resampling may have introduced difference pixel values along the image edge. | ||
:type ignore_boundary_pixels: bool | ||
:return: Metrics for the baseline with the fewest number of pixels outside the tolerances. | ||
:rtype: Any | ||
:return: Absolute difference image | ||
:rtype: Image | ||
:return: Unsigned char, 2D difference image for rendering | ||
:rtype: Image | ||
""" | ||
func = environment_dispatch("itkwasm_compare_images", "compare_images_async") | ||
output = await func(test_image, baseline_images=baseline_images, difference_threshold=difference_threshold, radius_tolerance=radius_tolerance, number_of_pixels_tolerance=number_of_pixels_tolerance, ignore_boundary_pixels=ignore_boundary_pixels) | ||
return output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
31 changes: 31 additions & 0 deletions
31
packages/compare-images/python/itkwasm-compare-images/test/fixtures.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import pytest | ||
import sys | ||
import pickle | ||
|
||
if sys.version_info < (3,10): | ||
pytest.skip("Skipping pyodide tests on older Python", allow_module_level=True) | ||
|
||
from itkwasm_compare_images import __version__ as test_package_version | ||
|
||
@pytest.fixture | ||
def package_wheel(): | ||
return f"itkwasm_compare_images-{test_package_version}-py3-none-any.whl" | ||
|
||
@pytest.fixture | ||
def input_data(): | ||
from pathlib import Path | ||
input_base_path = Path('..', '..', 'test', 'data') | ||
test_files = [ | ||
Path('input') / 'cake_easy.iwi.cbor', | ||
Path('input') / 'cake_hard.iwi.cbor', | ||
Path('input') / 'cake_easy.png', | ||
Path('input') / 'cake_hard.png', | ||
Path('input') / 'apple.jpg', | ||
Path('input') / 'orange.jpg', | ||
] | ||
data = {} | ||
for f in test_files: | ||
path = str(input_base_path / f) + '.pickle' | ||
with open(path, 'rb') as fp: | ||
data[str(f.name)] = pickle.load(fp) | ||
return data |
30 changes: 30 additions & 0 deletions
30
packages/compare-images/python/itkwasm-compare-images/test/test_compare_double_images.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from pathlib import Path | ||
import itk | ||
from itkwasm import Image | ||
|
||
def test_compare_double_images(): | ||
from itkwasm_compare_images_wasi import compare_double_images | ||
|
||
test_image_file = 'cake_easy.iwi.cbor' | ||
test_image_path = Path('..', '..', 'test', 'data', 'input', test_image_file) | ||
test_image = itk.imread(test_image_path) | ||
test_dict = itk.dict_from_image(test_image) | ||
test_image = Image(**test_dict) | ||
|
||
baseline_image_file = 'cake_hard.iwi.cbor' | ||
baseline_image_path = Path('..', '..', 'test', 'data', 'input', baseline_image_file) | ||
baseline_image = itk.imread(baseline_image_path) | ||
baseline_dict = itk.dict_from_image(baseline_image) | ||
baseline_image = Image(**baseline_dict) | ||
|
||
metrics, difference_image, difference_image_rendering = compare_double_images(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' |
84 changes: 84 additions & 0 deletions
84
packages/compare-images/python/itkwasm-compare-images/test/test_compare_images.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
from pathlib import Path | ||
import itk | ||
from itkwasm import Image | ||
|
||
def test_compare_double_images(): | ||
from itkwasm_compare_images import compare_images | ||
|
||
test_image_file = 'cake_easy.iwi.cbor' | ||
test_image_path = Path('..', '..', 'test', 'data', 'input', test_image_file) | ||
test_image = itk.imread(test_image_path) | ||
test_dict = itk.dict_from_image(test_image) | ||
test_image = Image(**test_dict) | ||
|
||
baseline_image_file = 'cake_hard.iwi.cbor' | ||
baseline_image_path = Path('..', '..', 'test', 'data', 'input', baseline_image_file) | ||
baseline_image = itk.imread(baseline_image_path) | ||
baseline_dict = itk.dict_from_image(baseline_image) | ||
baseline_image = Image(**baseline_dict) | ||
|
||
metrics, difference_image, difference_image_rendering = compare_images(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' | ||
|
||
def test_compare_uint8_images(): | ||
from itkwasm_compare_images import compare_images | ||
|
||
test_image_file = 'cake_easy.png' | ||
test_image_path = Path('..', '..', 'test', 'data', 'input', test_image_file) | ||
test_image = itk.imread(test_image_path) | ||
test_dict = itk.dict_from_image(test_image) | ||
test_image = Image(**test_dict) | ||
|
||
baseline_image_file = 'cake_hard.png' | ||
baseline_image_path = Path('..', '..', 'test', 'data', 'input', baseline_image_file) | ||
baseline_image = itk.imread(baseline_image_path) | ||
baseline_dict = itk.dict_from_image(baseline_image) | ||
baseline_image = Image(**baseline_dict) | ||
|
||
metrics, difference_image, difference_image_rendering = compare_images(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' | ||
|
||
def test_compare_rgb_images(): | ||
from itkwasm_compare_images import compare_images | ||
|
||
test_image_file = 'apple.jpg' | ||
test_image_path = Path('..', '..', 'test', 'data', 'input', test_image_file) | ||
test_image = itk.imread(test_image_path) | ||
test_dict = itk.dict_from_image(test_image) | ||
test_image = Image(**test_dict) | ||
|
||
baseline_image_file = 'orange.jpg' | ||
baseline_image_path = Path('..', '..', 'test', 'data', 'input', baseline_image_file) | ||
baseline_image = itk.imread(baseline_image_path) | ||
baseline_dict = itk.dict_from_image(baseline_image) | ||
baseline_image = Image(**baseline_dict) | ||
|
||
metrics, difference_image, difference_image_rendering = compare_images(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 26477 | ||
assert metrics['minimumDifference'] == 0.002273026683894841 | ||
assert metrics['maximumDifference'] == 312.2511648746159 | ||
assert metrics['totalDifference'] == 3121656.100202402 | ||
assert metrics['meanDifference'] == 117.90067228924735 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' |
99 changes: 99 additions & 0 deletions
99
packages/compare-images/python/itkwasm-compare-images/test/test_compare_images_async.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import sys | ||
|
||
if sys.version_info < (3,10): | ||
pytest.skip("Skipping pyodide tests on older Python", allow_module_level=True) | ||
|
||
from pytest_pyodide import run_in_pyodide | ||
from .fixtures import package_wheel, input_data | ||
|
||
@run_in_pyodide(packages=['micropip', 'numpy']) | ||
async def test_compare_double_images_async(selenium, package_wheel, input_data): | ||
import micropip | ||
await micropip.install(package_wheel, 'numpy', 'itkwasm') | ||
|
||
from itkwasm_compare_images import compare_double_images_async | ||
import numpy as np | ||
from itkwasm import Image | ||
from itkwasm.pyodide import to_js as itkwasm_to_js | ||
|
||
test_image_file = 'cake_easy.iwi.cbor' | ||
test_image = Image(**input_data[test_image_file]) | ||
|
||
baseline_image_file = 'cake_hard.iwi.cbor' | ||
baseline_image = Image(**input_data[baseline_image_file]) | ||
|
||
metrics, difference_image, difference_image_rendering = await compare_double_images_async(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' | ||
|
||
@run_in_pyodide(packages=['micropip', 'numpy']) | ||
async def test_compare_images_async(selenium, package_wheel, input_data): | ||
import micropip | ||
await micropip.install(package_wheel, 'numpy', 'itkwasm') | ||
|
||
from itkwasm_compare_images import compare_images_async | ||
import numpy as np | ||
from itkwasm import Image | ||
from itkwasm.pyodide import to_js as itkwasm_to_js | ||
|
||
test_image_file = 'cake_easy.iwi.cbor' | ||
test_image = Image(**input_data[test_image_file]) | ||
|
||
baseline_image_file = 'cake_hard.iwi.cbor' | ||
baseline_image = Image(**input_data[baseline_image_file]) | ||
|
||
metrics, difference_image, difference_image_rendering = await compare_images_async(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' | ||
|
||
test_image_file = 'cake_easy.png' | ||
test_image = Image(**input_data[test_image_file]) | ||
|
||
baseline_image_file = 'cake_hard.png' | ||
baseline_image = Image(**input_data[baseline_image_file]) | ||
|
||
metrics, difference_image, difference_image_rendering = await compare_images_async(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 9915 | ||
assert metrics['minimumDifference'] == 1.0 | ||
assert metrics['maximumDifference'] == 107.0 | ||
assert metrics['totalDifference'] == 337334.0 | ||
assert metrics['meanDifference'] == 34.02259203227433 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' | ||
|
||
test_image_file = 'apple.jpg' | ||
test_image = Image(**input_data[test_image_file]) | ||
|
||
baseline_image_file = 'orange.jpg' | ||
baseline_image = Image(**input_data[baseline_image_file]) | ||
|
||
metrics, difference_image, difference_image_rendering = await compare_images_async(test_image, baseline_images=[baseline_image]) | ||
|
||
assert metrics['almostEqual'] == False | ||
assert metrics['numberOfPixelsWithDifferences'] == 26477 | ||
assert metrics['minimumDifference'] == 0.002273026683894841 | ||
assert metrics['maximumDifference'] == 312.2511648746159 | ||
assert metrics['totalDifference'] == 3121656.100202402 | ||
assert metrics['meanDifference'] == 117.90067228924735 | ||
|
||
assert difference_image.imageType.componentType == 'float64' | ||
assert difference_image_rendering.imageType.componentType == 'uint8' |