From f8ebb5977477d18047f0807e00cdde676e911ece Mon Sep 17 00:00:00 2001 From: "William F. Broderick" Date: Mon, 25 Nov 2024 11:33:02 -0500 Subject: [PATCH 1/3] updates steerpyr behavior to match plenoptic --- src/pyrtools/pyramids/SteerablePyramidFreq.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/pyrtools/pyramids/SteerablePyramidFreq.py b/src/pyrtools/pyramids/SteerablePyramidFreq.py index c05797a..c5cc63e 100644 --- a/src/pyrtools/pyramids/SteerablePyramidFreq.py +++ b/src/pyrtools/pyramids/SteerablePyramidFreq.py @@ -20,6 +20,9 @@ class SteerablePyramidFreq(SteerablePyramidBase): The squared radial functions tile the Fourier plane with a raised-cosine falloff. Angular functions are cos(theta- k*pi/order+1)^(order). + Note that reconstruction will not be exact if the image has an odd shape (due to + boundary-handling issues) or if the pyramid is complex with order=0. + Notes ----- Transform described in [1]_, filter kernel design described in [2]_. @@ -66,6 +69,7 @@ class SteerablePyramidFreq(SteerablePyramidBase): Oct 1995. .. [2] A Karasaridis and E P Simoncelli, "A Filter Design Technique for Steerable Pyramid Image Transforms", ICASSP, Atlanta, GA, May 1996. + """ def __init__(self, image, height='auto', order=3, twidth=1, is_complex=False): # in the Fourier domain, there's only one choice for how do edge-handling: circular. to @@ -78,6 +82,12 @@ def __init__(self, image, height='auto', order=3, twidth=1, is_complex=False): self.filters = {} self.order = int(order) + if (image.shape[0] % 2 != 0) or (image.shape[1] % 2 != 0): + warnings.warn("Reconstruction will not be perfect with odd-sized images") + + if self.order == 0 and self.is_complex: + warnings.warn("Reconstruction will not be perfect for a complex pyramid with order=0") + # we can't use the base class's _set_num_scales method because the max height is calculated # slightly differently max_ht = np.floor(np.log2(min(self.image.shape))) - 2 @@ -89,13 +99,12 @@ def __init__(self, image, height='auto', order=3, twidth=1, is_complex=False): self.num_scales = int(height) if self.order > 15 or self.order < 0: - raise Exception("order must be an integer in the range [0,15]. Truncating.") + raise Exception("order must be an integer in the range [0,15].") self.num_orientations = int(order + 1) if twidth <= 0: - warnings.warn("twidth must be positive. Setting to 1.") - twidth = 1 + raise ValueError("twidth must be positive.") twidth = int(twidth) dims = np.array(self.image.shape) @@ -220,8 +229,7 @@ def recon_pyr(self, levels='all', bands='all', twidth=1): """ if twidth <= 0: - warnings.warn("twidth must be positive. Setting to 1.") - twidth = 1 + raise ValueError("twidth must be positive.") recon_keys = self._recon_keys(levels, bands) From 1ac1c4c982844fdae74970b19861ac85cd188334 Mon Sep 17 00:00:00 2001 From: "William F. Broderick" Date: Mon, 25 Nov 2024 11:52:01 -0500 Subject: [PATCH 2/3] replace Exception with ValueError --- src/pyrtools/pyramids/SteerablePyramidFreq.py | 4 ++-- src/pyrtools/pyramids/pyramid.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pyrtools/pyramids/SteerablePyramidFreq.py b/src/pyrtools/pyramids/SteerablePyramidFreq.py index c5cc63e..7bcf8dc 100644 --- a/src/pyrtools/pyramids/SteerablePyramidFreq.py +++ b/src/pyrtools/pyramids/SteerablePyramidFreq.py @@ -94,12 +94,12 @@ def __init__(self, image, height='auto', order=3, twidth=1, is_complex=False): if height == 'auto' or height is None: self.num_scales = int(max_ht) elif height > max_ht: - raise Exception("Cannot build pyramid higher than %d levels." % (max_ht)) + raise ValueError("Cannot build pyramid higher than %d levels." % (max_ht)) else: self.num_scales = int(height) if self.order > 15 or self.order < 0: - raise Exception("order must be an integer in the range [0,15].") + raise ValueError("order must be an integer in the range [0,15].") self.num_orientations = int(order + 1) diff --git a/src/pyrtools/pyramids/pyramid.py b/src/pyrtools/pyramids/pyramid.py index 9670582..5f216e6 100644 --- a/src/pyrtools/pyramids/pyramid.py +++ b/src/pyrtools/pyramids/pyramid.py @@ -96,7 +96,7 @@ def _set_num_scales(self, filter_name, height, extra_height=0): if height == 'auto': self.num_scales = max_ht elif height > max_ht: - raise Exception("Cannot build pyramid higher than %d levels." % (max_ht)) + raise ValueError("Cannot build pyramid higher than %d levels." % (max_ht)) else: self.num_scales = int(height) From 08d2856c53a588bb6ee4bef2891db7c6496fbf44 Mon Sep 17 00:00:00 2001 From: "William F. Broderick" Date: Mon, 2 Dec 2024 11:26:09 -0500 Subject: [PATCH 3/3] adds height check --- src/pyrtools/pyramids/SteerablePyramidFreq.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pyrtools/pyramids/SteerablePyramidFreq.py b/src/pyrtools/pyramids/SteerablePyramidFreq.py index 7bcf8dc..0fffd58 100644 --- a/src/pyrtools/pyramids/SteerablePyramidFreq.py +++ b/src/pyrtools/pyramids/SteerablePyramidFreq.py @@ -33,7 +33,7 @@ class SteerablePyramidFreq(SteerablePyramidBase): 2d image upon which to construct to the pyramid. height : 'auto' or `int`. The height of the pyramid. If 'auto', will automatically determine based on the size of - `image`. + `image`. If an int, must be non-negative. When height=0, only returns the residuals. order : `int`. The Gaussian derivative order used for the steerable filters. Default value is 3. Note that to achieve steerability the minimum number of orientation is `order` + 1, @@ -95,6 +95,8 @@ def __init__(self, image, height='auto', order=3, twidth=1, is_complex=False): self.num_scales = int(max_ht) elif height > max_ht: raise ValueError("Cannot build pyramid higher than %d levels." % (max_ht)) + elif height < 0: + raise ValueError("Height must be a non-negative int.") else: self.num_scales = int(height)