Fix handling of border pixels and update tests

This commit is contained in:
blink1073
2014-07-12 09:40:30 -05:00
parent 5cafbd95e6
commit a924e55b52
2 changed files with 33 additions and 68 deletions
+29 -13
View File
@@ -55,9 +55,10 @@ def equalize_adapthist(image, ntiles_x=8, ntiles_y=8, clip_limit=0.01,
- The CLAHE algorithm is run on the V (Value) channel
- The image is converted back to RGB space and returned
* For RGBA images, the original alpha channel is removed.
* The CLAHE algorithm relies on image blocks of equal size. This results
in extra border pixels that are not handled. Extra blocks are created
around the border to handle these pixels.
* The CLAHE algorithm relies on image blocks of equal size. This may
result in extra border pixels that would not be handled. In that case,
we pad the image with a repeat of the border pixels, apply the
algorithm, and then trim the image to original size.
References
----------
@@ -120,19 +121,36 @@ def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128):
if clip_limit == 1.0:
return image # is OK, immediately returns original image.
map_array = np.zeros((ntiles_y, ntiles_x, nbins), dtype=int)
y_res = image.shape[0] - image.shape[0] % ntiles_y
x_res = image.shape[1] - image.shape[1] % ntiles_x
# make the tile size divisible by 2
while y_res % (2 * ntiles_y):
y_res -= 1
while x_res % (2 * ntiles_x):
x_res -= 1
image = image[: y_res, : x_res]
x_size = image.shape[1] // ntiles_x # Actual size of contextual regions
y_size = image.shape[0] // ntiles_y
orig_shape = image.shape
x_size = x_res // ntiles_x # Actual size of contextual regions
y_size = y_res // ntiles_y
if y_res != image.shape[0]:
ntiles_y += 1
if x_res != image.shape[1]:
ntiles_x += 1
if y_res != image.shape[1] or x_res != image.shape[0]:
hgt = y_size * ntiles_y - image.shape[0]
wid = x_size * ntiles_x - image.shape[1]
image = np.vstack((image, image[-hgt:, :]))
image = np.hstack((image, image[:, -wid:]))
y_res, x_res = image.shape
bin_size = 1 + NR_OF_GREY / nbins
aLUT = np.arange(NR_OF_GREY)
aLUT //= bin_size
img_blocks = view_as_blocks(image, (y_size, x_size))
map_array = np.zeros((ntiles_y, ntiles_x, nbins), dtype=int)
n_pixels = x_size * y_size
if clip_limit > 0.0: # Calculate actual cliplimit
@@ -142,11 +160,6 @@ def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128):
else:
clip_limit = NR_OF_GREY # Large value, do not clip (AHE)
bin_size = 1 + NR_OF_GREY / nbins
aLUT = np.arange(NR_OF_GREY)
aLUT //= bin_size
img_blocks = view_as_blocks(image, (y_size, x_size))
# Calculate greylevel mappings for each contextual region
for y in range(ntiles_y):
for x in range(ntiles_x):
@@ -203,6 +216,9 @@ def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128):
ystart += ystep
if image.shape != orig_shape:
image = image[:orig_shape[0], :orig_shape[1]]
return image
+4 -55
View File
@@ -16,6 +16,8 @@ import matplotlib.pyplot as plt
# Test histogram equalization
# ===========================
np.random.seed(0)
# squeeze image intensities to lower image contrast
test_img = skimage.img_as_float(data.camera())
test_img = exposure.rescale_intensity(test_img / 5. + 100)
@@ -170,8 +172,8 @@ def test_adapthist_grayscale():
nbins=128)
assert_almost_equal = np.testing.assert_almost_equal
assert img.shape == adapted.shape
assert_almost_equal(peak_snr(img, adapted), 105.669, 3)
assert_almost_equal(norm_brightness_err(img, adapted), 0.02470, 3)
assert_almost_equal(peak_snr(img, adapted), 104.3168, 3)
assert_almost_equal(norm_brightness_err(img, adapted), 0.0265, 3)
return data, adapted
@@ -212,59 +214,6 @@ def test_adapthist_alpha():
assert_almost_equal(norm_brightness_err(full_scale, adapted), 0.0544, 3)
def test_adapthist_modes_scalar():
'''Test adaptist `mode` parameter values for ndim==2 image.
'''
img = skimage.img_as_float(data.moon())
full_scale = skimage.exposure.rescale_intensity(skimage.img_as_uint(img))
ignore = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=9)
zero = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=9,
mode='zero')
crop = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=9,
mode='crop')
assert ignore.shape == zero.shape
assert ignore.shape != crop.shape
assert_array_equal(zero[crop.shape[0]:, :], 0)
assert_array_equal(zero[:, crop.shape[1]:], 0)
assert_almost_equal = np.testing.assert_almost_equal
full_cropped = full_scale[:crop.shape[0], :crop.shape[1]]
zero_cropped = zero[:crop.shape[0], :crop.shape[1]]
ignore_cropped = ignore[:crop.shape[0], :crop.shape[1]]
for crop_img in [ignore_cropped, zero_cropped, crop]:
assert_almost_equal(peak_snr(full_cropped, crop_img), 120.456, 3)
assert_almost_equal(norm_brightness_err(full_cropped, crop_img),
0.4398, 3)
def test_adapthist_modes_rgb():
'''Test adaptist `mode` parameter values for rgb image.
'''
img = skimage.img_as_float(data.lena())
full_scale = skimage.exposure.rescale_intensity(skimage.img_as_uint(img))
ignore = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=10)
zero = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=10,
mode='zero')
crop = exposure.equalize_adapthist(img.copy(), ntiles_x=9, ntiles_y=10,
mode='crop')
assert ignore.shape == zero.shape
assert ignore.shape != crop.shape
assert_array_equal(zero[crop.shape[0]:, :, :], 0)
assert_array_equal(zero[:, crop.shape[1]:, :], 0)
assert_almost_equal = np.testing.assert_almost_equal
full_cropped = full_scale[:crop.shape[0], :crop.shape[1], :]
zero_cropped = zero[:crop.shape[0], :crop.shape[1], :]
ignore_cropped = ignore[:crop.shape[0], :crop.shape[1], :]
for crop_img in [ignore_cropped, zero_cropped, crop]:
assert np.floor(peak_snr(full_cropped, crop_img)) == 106
assert_almost_equal(norm_brightness_err(full_cropped, crop_img),
0.0517, 3)
def peak_snr(img1, img2):
'''Peak signal to noise ratio of two images