Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Resolved characteristic wavelength overestimation via re-scaling #64

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions examples/example_1/config
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ method = response_distance
[response_distance]
shapelet_order = default
num_clusters = 20
ux = [50, 80]
uy = [150, 180]
ux = [109, 158]
uy = [283, 322]
4 changes: 2 additions & 2 deletions examples/example_1/example_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
image_name = "lamSIM1.png"
shapelet_order = 'default' # can also be integer value to set upper bound
num_clusters = 20 # default is 20, can be any other positive integer
ux = [50, 80]
uy = [150, 180]
ux = [109, 158]
uy = [283, 322]

## Section 3: code

Expand Down
2 changes: 1 addition & 1 deletion examples/example_3/example_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

# 3.1: image and output directory handling
image_path = os.path.join(Path(__file__).parents[0], 'images')
image = read_image(image_name = image_name, image_path = image_path)
image = read_image(image_name = image_name, image_path = image_path, do_rescale=False)
save_path = os.path.join(Path(__file__).parents[0], 'output')
if not os.path.exists(save_path):
os.mkdir(save_path)
Expand Down
31 changes: 17 additions & 14 deletions shapelets/self_assembly/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
'trim_image'
]

def read_image(image_name: str, image_path: str, verbose: bool = True) -> np.ndarray:
def read_image(image_name: str, image_path: str, do_rescale: bool = True, verbose: bool = True) -> np.ndarray:
r"""
Read an image using OpenCV, with some extra handling. By default, re-scales images as greyscale on [-1, 1].

Expand All @@ -42,6 +42,8 @@ def read_image(image_name: str, image_path: str, verbose: bool = True) -> np.nda
* The filename of the image (including extension)
* image_path: str
* The path holding the image
* do_rescale : bool, optional
* Automatically re-scale in accordance with requirements for wavelength algorithm. Default is True
* verbose: bool, optional
* True (default) to print image-related information

Expand All @@ -55,33 +57,34 @@ def read_image(image_name: str, image_path: str, verbose: bool = True) -> np.nda
Re-scaling of image to greyscale on [-1, 1] is intentional to align with the minimum and maximum of shapelet function values.

"""
# Ensure image_path provided exists
if not os.path.exists(image_path):
raise RuntimeError(f'Image path: {image_path} does not exist.')

# read image then ensure image does exist (by evaluating result of cv2.imread)
f = cv2.imread(os.path.join(image_path, image_name))
if not isinstance(f, np.ndarray):
raise RuntimeError(f"Could not read image: {image_name}. Ensure it is located in {image_path} and is of image format.")

# convert to grayscale
f = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)

# check if re-scaling is needed b/c k-means clustering cannot handle very large images
init_shape = f.shape
resized = False
while any(dim >= 1000 for dim in f.shape):
dim0, dim1 = round(f.shape[0]*0.8), round(f.shape[1]*0.8)
f = cv2.resize(f, dsize=(dim0, dim1))
resized = True
# NOTE: image may require rescaling based on two observed issues:
# 1. scipy.cluster.vq.kmeans yields abort: coredump if one dim of image large (~2000 pixels)
# 2. wavelength algorithm fails if image is too small... need to upsample

# rescale to [-1, 1] greyscale
if do_rescale:
while any(dim >= 2000 for dim in f.shape):
height, width = round(f.shape[0]*0.8), round(f.shape[1]*0.8)
f = cv2.resize(f, dsize=(width, height)) # dsize is opposite numpy convention

while any(dim <= 500 for dim in f.shape):
height, width = round(f.shape[0]*1.2), round(f.shape[1]*1.2)
f = cv2.resize(f, dsize=(width, height)) # dsize is opposite numpy convention

# rescale to [-1, 1] greyscale to match shapelet kernel min/max values
f = ( ((f-f.min()) / (f.max()-f.min())) * 2 ) - 1

if verbose:
print(f"Successfully loaded image: {image_name}")
if resized: print(f"New image dimensions are: {f.shape} as initial size {init_shape} too large")
else: print(f"Image dimensions are: {f.shape}")
print(f"Image loaded with dimensions: {f.shape}")
print(f"Image {image_name} normalized to greyscale on [-1, 1] to align with shapelet kernels")

return f
Expand Down
2 changes: 1 addition & 1 deletion shapelets/self_assembly/wavelength.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
]


def get_wavelength(image: np.ndarray, rng: list = [0, 70], verbose: bool = True) -> float:
def get_wavelength(image: np.ndarray, rng: list = [0, 100], verbose: bool = True) -> float:
r"""
Find characteristic wavelength of an image. Computed using method described in ref. [1].

Expand Down
12 changes: 6 additions & 6 deletions shapelets/tests/test_self_assembly_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_a_read_image(self) -> None:
self.assertTrue(isinstance(self.lamSIM, np.ndarray))
self.assertEqual(self.lamSIM.min(), -1)
self.assertEqual(self.lamSIM.max(), 1)
self.assertEqual(self.lamSIM.shape, (296, 296))
self.assertEqual(self.lamSIM.shape, (511, 511))

self.assertTrue(isinstance(self.hexSIM, np.ndarray))
self.assertEqual(self.hexSIM.min(), -1)
Expand All @@ -63,27 +63,27 @@ def test_scaling(self) -> None:

lamSIM_wvl = get_wavelength(image = self.lamSIM, verbose = False)
self.assertTrue(isinstance(lamSIM_wvl, numbers.Real))
self.assertAlmostEqual(lamSIM_wvl, 10.231, places = 3)
self.assertAlmostEqual(lamSIM_wvl, 17.60, places = 2)

lamSIM_beta_n0 = lambda_to_beta_n0(3, lamSIM_wvl)
self.assertTrue(isinstance(lamSIM_beta_n0, numbers.Real))
self.assertAlmostEqual(lamSIM_beta_n0, 3.41, places = 2)
self.assertAlmostEqual(lamSIM_beta_n0, 5.87, places = 2)

lamSIM_beta_n1 = lambda_to_beta_n1(3, lamSIM_wvl)
self.assertTrue(isinstance(lamSIM_beta_n1, numbers.Real))
self.assertAlmostEqual(lamSIM_beta_n1, 7.3, places = 5)
self.assertAlmostEqual(lamSIM_beta_n1, 12.4, places = 1)

hexSIM_wvl = get_wavelength(image = self.hexSIM, verbose = False)
self.assertTrue(isinstance(hexSIM_wvl, numbers.Real))
self.assertAlmostEqual(hexSIM_wvl, 16.882, places = 3)
self.assertAlmostEqual(hexSIM_wvl, 16.88, places = 2)

hexSIM_beta_n0 = lambda_to_beta_n0(6, hexSIM_wvl)
self.assertTrue(isinstance(hexSIM_beta_n0, numbers.Real))
self.assertAlmostEqual(hexSIM_beta_n0, 6.89, places = 2)

hexSIM_beta_n1 = lambda_to_beta_n1(6, hexSIM_wvl)
self.assertTrue(isinstance(hexSIM_beta_n1, numbers.Real))
self.assertAlmostEqual(hexSIM_beta_n1, 9.1, places = 5)
self.assertAlmostEqual(hexSIM_beta_n1, 9.1, places = 1)

if __name__ == "__main__":
unittest.main()
5 changes: 3 additions & 2 deletions shapelets/tests/test_self_assembly_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ def setUpClass(cls) -> None:
cls.dir = __file__.replace(os.path.basename(__file__), 'images/')

# Ensure core functions have appropriate outputs before proceeding.
# Note that read_image and get_wavelength are not tested here, so inline asserts
# are used to ensure correct output.
# NOTE: read_image & get_wavelength are not tested here, so inline asserts are used to ensure correct output.

cls.image = read_image(image_name="hexSIM1.png", image_path=cls.dir, verbose=False)

assert isinstance(cls.image, np.ndarray)

cls.omega, cls.phi = convresponse_n0(cls.image, shapelet_order='default', verbose=False)
Expand Down