mirror of
https://github.com/wassname/scikit-image.git
synced 2026-08-09 12:30:07 +08:00
Add adapt_rgb decorator and helpers.
This applies `adapt_rgb` to `equalize_adapthist` and removes the special-casing of RGB images in that function. Note that tests of `adapt_rgb` fail because some type conversion and intensity scaling were added to pass tests for `equalize_adapthist`.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import functools
|
||||
|
||||
import numpy as np
|
||||
|
||||
from skimage import img_as_float
|
||||
from skimage import color
|
||||
from skimage.exposure import rescale_intensity
|
||||
from skimage.util.dtype import convert
|
||||
|
||||
|
||||
__all__ = ['adapt_rgb', 'hsv_value', 'each_channel']
|
||||
|
||||
|
||||
def is_rgb_like(image):
|
||||
"""Return True if the image *looks* like it's RGB.
|
||||
|
||||
This function should not be public because it is only intended to be used
|
||||
for functions that don't accept volumes as input, since checking an image's
|
||||
shape is fragile.
|
||||
"""
|
||||
return (image.ndim == 3) and (image.shape[2] in (3, 4))
|
||||
|
||||
|
||||
def adapt_rgb(apply_to_rgb):
|
||||
"""Return decorator that adapts to RGB images to a gray-scale filter.
|
||||
|
||||
This function is only intended to be used for functions that don't accept
|
||||
volumes as input, since checking an image's shape is fragile.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
apply_to_rgb : function
|
||||
Function that returns a filtered image from an image-filter and RGB
|
||||
image. This will only be called if the image is RGB-like.
|
||||
"""
|
||||
def decorator(image_filter):
|
||||
# @functools.wraps
|
||||
def image_filter_adapted(image, *args, **kwargs):
|
||||
if is_rgb_like(image):
|
||||
return apply_to_rgb(image_filter, image, *args, **kwargs)
|
||||
else:
|
||||
return image_filter(image, *args, **kwargs)
|
||||
return image_filter_adapted
|
||||
return decorator
|
||||
|
||||
|
||||
def hsv_value(image_filter, image, *args, **kwargs):
|
||||
"""Return color image by applying `image_filter` on HSV-value of `image`.
|
||||
|
||||
Note that this function is intended for use with `adapt_rgb`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_filter : function
|
||||
Function that filters a gray-scale image.
|
||||
image : array
|
||||
Input image. Note that RGBA images are treated as RGB.
|
||||
"""
|
||||
# XXX: Are these 2 lines really necessary?
|
||||
image = img_as_float(image[:, :, :3])
|
||||
image = rescale_intensity(image)
|
||||
# Slice the first three channels so that we remove any alpha channels.
|
||||
hsv = color.rgb2hsv(image)
|
||||
value = hsv[:, :, 2].copy()
|
||||
value = image_filter(value, *args, **kwargs)
|
||||
hsv[:, :, 2] = convert(value, hsv.dtype)
|
||||
return color.hsv2rgb(hsv)
|
||||
|
||||
|
||||
def each_channel(image_filter, image, *args, **kwargs):
|
||||
"""Return color image by applying `image_filter` on channels of `image`.
|
||||
|
||||
Note that this function is intended for use with `adapt_rgb`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image_filter : function
|
||||
Function that filters a gray-scale image.
|
||||
image : array
|
||||
Input image.
|
||||
"""
|
||||
c_new = [image_filter(c, *args, **kwargs) for c in image.T]
|
||||
return np.array(c_new).T
|
||||
@@ -0,0 +1,83 @@
|
||||
from functools import partial
|
||||
|
||||
import numpy as np
|
||||
|
||||
from skimage import img_as_float, img_as_uint
|
||||
from skimage import color, data, filter
|
||||
from skimage.color.adapt_rgb import adapt_rgb, each_channel, hsv_value
|
||||
|
||||
|
||||
# Down-sample image for quicker testing.
|
||||
COLOR_IMAGE = data.lena()[::5, ::5]
|
||||
GRAY_IMAGE = data.camera()[::5, ::5]
|
||||
|
||||
SIGMA = 3
|
||||
smooth = partial(filter.gaussian_filter, sigma=SIGMA)
|
||||
assert_allclose = partial(np.testing.assert_allclose, atol=1e-8)
|
||||
|
||||
|
||||
@adapt_rgb(each_channel)
|
||||
def edges_each(image):
|
||||
return filter.sobel(image)
|
||||
|
||||
|
||||
@adapt_rgb(each_channel)
|
||||
def smooth_each(image, sigma):
|
||||
return filter.gaussian_filter(image, sigma)
|
||||
|
||||
|
||||
@adapt_rgb(hsv_value)
|
||||
def edges_hsv(image):
|
||||
return filter.sobel(image)
|
||||
|
||||
|
||||
@adapt_rgb(hsv_value)
|
||||
def smooth_hsv(image, sigma):
|
||||
return filter.gaussian_filter(image, sigma)
|
||||
|
||||
|
||||
@adapt_rgb(hsv_value)
|
||||
def edges_hsv_uint(image):
|
||||
return img_as_uint(filter.sobel(image))
|
||||
|
||||
|
||||
def test_gray_scale_image():
|
||||
# We don't need to test both `hsv_value` and `each_channel` since
|
||||
# `adapt_rgb` is handling gray-scale inputs.
|
||||
assert_allclose(edges_each(GRAY_IMAGE), filter.sobel(GRAY_IMAGE))
|
||||
|
||||
|
||||
def test_each_channel():
|
||||
filtered = edges_each(COLOR_IMAGE)
|
||||
for i, channel in enumerate(np.rollaxis(filtered, axis=-1)):
|
||||
expected = img_as_float(filter.sobel(COLOR_IMAGE[:, :, i]))
|
||||
assert_allclose(channel, expected)
|
||||
|
||||
|
||||
def test_each_channel_with_filter_argument():
|
||||
filtered = smooth_each(COLOR_IMAGE, SIGMA)
|
||||
for i, channel in enumerate(np.rollaxis(filtered, axis=-1)):
|
||||
assert_allclose(channel, smooth(COLOR_IMAGE[:, :, i]))
|
||||
|
||||
|
||||
def test_hsv_value():
|
||||
filtered = edges_hsv(COLOR_IMAGE)
|
||||
value = color.rgb2hsv(COLOR_IMAGE)[:, :, 2]
|
||||
assert_allclose(color.rgb2hsv(filtered)[:, :, 2], filter.sobel(value))
|
||||
|
||||
|
||||
def test_hsv_value_with_filter_argument():
|
||||
filtered = smooth_hsv(COLOR_IMAGE, SIGMA)
|
||||
value = color.rgb2hsv(COLOR_IMAGE)[:, :, 2]
|
||||
assert_allclose(color.rgb2hsv(filtered)[:, :, 2], smooth(value))
|
||||
|
||||
|
||||
def test_hsv_value_with_non_float_output():
|
||||
# Since `rgb2hsv` returns a float image and the result of the filtered
|
||||
# result is inserted into the HSV image, we want to make sure there isn't
|
||||
# a dtype mismatch.
|
||||
filtered = edges_hsv_uint(COLOR_IMAGE)
|
||||
filtered_value = color.rgb2hsv(filtered)[:, :, 2]
|
||||
value = color.rgb2hsv(COLOR_IMAGE)[:, :, 2]
|
||||
# Reduce tolerance because dtype conversion.
|
||||
assert_allclose(filtered_value, filter.sobel(value), rtol=1e-5, atol=1e-5)
|
||||
@@ -16,6 +16,7 @@ comes with no guarantee.
|
||||
import numpy as np
|
||||
import skimage
|
||||
from skimage import color
|
||||
from skimage.color.adapt_rgb import adapt_rgb, hsv_value
|
||||
from skimage.exposure import rescale_intensity
|
||||
from skimage.util import view_as_blocks
|
||||
|
||||
@@ -25,6 +26,7 @@ MAX_REG_Y = 16 # max. # contextual regions in y-direction */
|
||||
NR_OF_GREY = 2**14 # number of grayscale levels to use in CLAHE algorithm
|
||||
|
||||
|
||||
@adapt_rgb(hsv_value)
|
||||
def equalize_adapthist(image, ntiles_x=8, ntiles_y=8, clip_limit=0.01,
|
||||
nbins=256):
|
||||
"""Contrast Limited Adaptive Histogram Equalization.
|
||||
@@ -65,25 +67,12 @@ def equalize_adapthist(image, ntiles_x=8, ntiles_y=8, clip_limit=0.01,
|
||||
.. [1] http://tog.acm.org/resources/GraphicsGems/gems.html#gemsvi
|
||||
.. [2] https://en.wikipedia.org/wiki/CLAHE#CLAHE
|
||||
"""
|
||||
ndim = image.ndim
|
||||
if ndim == 3:
|
||||
if image.shape[2] == 4:
|
||||
image = image[:, :, :3]
|
||||
image = skimage.img_as_float(image)
|
||||
image = rescale_intensity(image)
|
||||
hsv_img = color.rgb2hsv(image)
|
||||
image = hsv_img[:, :, 2].copy()
|
||||
image = skimage.img_as_uint(image)
|
||||
image = rescale_intensity(image, out_range=(0, NR_OF_GREY - 1))
|
||||
out = _clahe(image, ntiles_x, ntiles_y, clip_limit * nbins, nbins)
|
||||
image[:out.shape[0], :out.shape[1]] = out
|
||||
image = skimage.img_as_float(image)
|
||||
if ndim == 3:
|
||||
hsv_img[:, :, 2] = rescale_intensity(image)
|
||||
image = color.hsv2rgb(hsv_img)
|
||||
else:
|
||||
image = rescale_intensity(image)
|
||||
return image
|
||||
return rescale_intensity(image)
|
||||
|
||||
|
||||
def _clahe(image, ntiles_x, ntiles_y, clip_limit, nbins=128):
|
||||
|
||||
@@ -11,7 +11,6 @@ from skimage.exposure.exposure import intensity_range
|
||||
from skimage.color import rgb2gray
|
||||
from skimage.util.dtype import dtype_range
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Test histogram equalization
|
||||
# ===========================
|
||||
|
||||
Reference in New Issue
Block a user