Skip to content
This repository has been archived by the owner on Aug 27, 2024. It is now read-only.

Commit

Permalink
test: add image conversion properties for improved implementation (#112)
Browse files Browse the repository at this point in the history
* test: add image conversion properties for improved implementation

* pr-fix: install hypothesis in CI build

* pr-sug: remove unnecessary imports
  • Loading branch information
stijnmoreels authored Feb 3, 2022
1 parent 7b99c6c commit f522635
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 24 deletions.
24 changes: 15 additions & 9 deletions arcus/ml/images/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,30 @@ def to_blackwhite(image_list: np.array, threshold = 128, keep_3d_shape = False)
Args:
image_list (np.array): A numpy array that contains all selected images represented as np.array
threshold (int): The threshold (between 0 and 255) that decides a pixel will be 0 or 255 (W/B)
keep_3d_shape (bool): The flag indicating whether to keep the images in shape (H,W,1) or not when encoutering 2D arrays
Returns:
np.array: A numpy array that contains all black & white images represented as np.array
'''
if threshold < 0 or threshold > 255:
raise ValueError("Requires a threshold for black/white conversion between 0-255 (inconclusive)")

def to_gray_scale(img):
return np.dot(img, [0.3, 0.59, 0.11])
def make_black_or_white(img):
return 1.0 * (img > threshold)

images = []
for img in image_list:
# Check if the image is scaled down by 255 or not
if(np.count_nonzero(img > 1)==0):
is_scaled_down_by_255 = np.count_nonzero(img > 1) == 0
if is_scaled_down_by_255:
img *= 255
if(len(img.shape) == 3):
if(img.shape[2] == 3):
# Convert to grey scale
img = np.dot(img, [0.3, 0.59, 0.11])
if(len(img.shape) == 3 and img.shape[2] == 3):
img = to_gray_scale(img)
if (len(img.shape) == 2 and keep_3d_shape):
img = np.expand_dims(img, -1)
# make all pixels < threshold black
img = 1.0 * (img > threshold)
img = make_black_or_white(img)
images.append(img)

return np.array(images)

def prepare(image: np.array, image_size = None,
Expand Down
1 change: 1 addition & 0 deletions build/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ stages:
- script: |
pip install pytest
pip install pytest-cov
pip install hypothesis
pip install lazydocs
pytest tests --doctest-modules --junitxml=junit/test-results.xml --cov=. --cov-report=xml --cov-report=html
displayName: 'Unit tests (pytest)'
Expand Down
69 changes: 54 additions & 15 deletions tests/test_images_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,66 @@
import arcus.ml.images.io as ami
import arcus.ml.images.conversion as conv
import logging
import numpy as np
import numpy as np
from hypothesis import given, strategies as st

image_path = 'tests/resources/images/lungs'

def is_black_or_white(cell):
return cell == 0 or cell == 1
def all_black_and_white(arr):
result = []
for cell_z in arr:
for cell_y in cell_z:
if isinstance(cell_y, np.ndarray):
for cell_x in cell_y:
result.append(is_black_or_white(cell_x))
else:
result.append(is_black_or_white(cell_y))
return all(result)

@st.composite
def images(draw, dimension = 3):
def create_array_rec(strategy, lengths, index = 0):
if index == len(lengths):
return strategy
length = lengths[index]
arr = st.lists(strategy, min_size=length, max_size=length).map(lambda x : np.array(x))
return create_array_rec(arr, lengths, index + 1)

num = st.integers(min_value=0, max_value=255)
amount = st.integers(min_value=1, max_value=10)
return draw(st.lists(amount, min_size=dimension, max_size=dimension).flatmap(lambda lengths : create_array_rec(num, lengths)))

@given(images())
def test_black_white_property(image_list):
image_converted = conv.to_blackwhite(image_list)
assert all_black_and_white(image_converted)

@given(images(dimension=2))
def test_black_white_2D_property(image_list):
image_converted = conv.to_blackwhite(image_list)
assert all_black_and_white(image_converted)

def test_black_white_empty_stays_empty():
image_converted = conv.to_blackwhite(np.array([]))
image_converted = []

@given(images(), st.integers().filter(lambda x : x < 0 or x > 255))
def test_black_white_outofbounds_threshold(image_list, threshold):
with pytest.raises(ValueError, match=r".* threshold .*"):
conv.to_blackwhite(image_list, threshold)

def test_black_white_conversion():
image_list = ami.load_images(image_path)
ls = conv.to_blackwhite(image_list)
pixel_value = ls[0][0][0]
assert pixel_value == 0 or pixel_value == 1
assert len(ls[0].shape)==2
image_converted = conv.to_blackwhite(image_list)
assert all_black_and_white(image_converted)

def test_black_white_conversion_double_arrays():
image_list = ami.load_images(image_path)
test_list = list()
for img in image_list:
test_list.append(img / 255)
ls = conv.to_blackwhite(np.array(test_list))
pixel_value = ls[0][0][0]
assert pixel_value == 0 or pixel_value == 1
test_list = image_list / 255
image_converted = conv.to_blackwhite(np.array(test_list))
assert all_black_and_white(image_converted)

def test_crop_image():
image = ami.load_images(image_path, max_images=1, image_size=50)[0]
Expand All @@ -32,10 +73,8 @@ def test_crop_image():

def test_black_white_conversion_3dshape():
image_list = ami.load_images(image_path)
ls = conv.to_blackwhite(image_list, keep_3d_shape=True)
pixel_value = ls[0][0][0]
assert pixel_value == 0 or pixel_value == 1
assert len(ls[0].shape)==3
image_converted = conv.to_blackwhite(image_list, keep_3d_shape=True)
assert all_black_and_white(image_converted)

def test_black_white_conversion_2x3dshape():
lung = ami.load_images(image_path, convert_to_grey=True, image_size = 30, keep_3d_shape=True)[0]
Expand Down

0 comments on commit f522635

Please sign in to comment.