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

Add image enhancement measure (EME) #3776

Open
wants to merge 57 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8bec6b0
Proposition to add the following image quality assessment function
iliailmer Feb 27, 2019
d2b7135
Proposition to add the following image quality assessment function
iliailmer Feb 27, 2019
130d912
modified examples
iliailmer Mar 1, 2019
9bfe66e
pep8 corrections
iliailmer Mar 1, 2019
a798d36
pep8 corrections
iliailmer Mar 1, 2019
4c3fa15
changed name to 'enhancement_measure', placed the funciton into measu…
iliailmer Mar 1, 2019
6f02d9b
some pep8 corrections
iliailmer Mar 1, 2019
f9c5b69
change to examples
iliailmer Mar 9, 2019
d266205
added name to __all__, fixed examples"
iliailmer Mar 9, 2019
651d332
removed unnecessary files
iliailmer Mar 27, 2019
268d0e2
filter parameter is used instead of
iliailmer Mar 28, 2019
ac8e9ab
added epsilon instead of +1 to avoid division by zero.
iliailmer Mar 28, 2019
4fd9f8d
added intensity rescaling to account for images with negative intensi…
iliailmer Mar 28, 2019
86e0bed
doctest fixes
iliailmer Apr 1, 2019
f41fa48
added unit tests and fixed some numerial stability issues
Apr 9, 2019
47a73ee
some identation
Apr 10, 2019
81d8022
added explanations in doc
iliailmer Apr 17, 2019
c5300cc
gallery example for enhancement measure
iliailmer Apr 18, 2019
9dc5646
Update skimage/measure/simple_metrics.py
sciunto Apr 22, 2019
d6e30cf
added DOI, fixed name typo, improved consistency in parameter order
iliailmer Apr 23, 2019
996b73e
Update skimage/measure/simple_metrics.py
stefanv Apr 25, 2019
6676cb1
Update doc/examples/filters/plot_enhancement_measure.py
stefanv Apr 25, 2019
0a53d2e
Update doc/examples/filters/plot_enhancement_measure.py
stefanv Apr 25, 2019
755e276
Update doc/examples/filters/plot_enhancement_measure.py
stefanv Apr 25, 2019
e223b0b
Update doc/examples/filters/plot_enhancement_measure.py
stefanv Apr 25, 2019
42b4cb0
doc file corrections, reference reformatting
iliailmer Apr 25, 2019
b511c3a
doc example corrections
iliailmer Apr 25, 2019
8e6b65c
doc example corrections
iliailmer Apr 25, 2019
b6c78e4
Update doc/examples/filters/plot_enhancement_measure.py
stefanv Apr 29, 2019
40f89ee
small changes to citation format
iliailmer Apr 30, 2019
79b15d6
fixed critical error in example
iliailmer May 1, 2019
3eb1b94
plt.show -> fig.show
iliailmer May 2, 2019
ae7110e
fixed artifacts in docs and comments
iliailmer May 3, 2019
82be96e
added blank line to docs
iliailmer May 3, 2019
6b22824
Merge pull request #1 from scikit-image/master
iliailmer Jun 28, 2019
e68c091
edited formatting for the example
iliailmer Aug 22, 2019
6c90991
Merge branch 'master' into image-quality
iliailmer Aug 22, 2019
8bfe430
PEP8 corrections
iliailmer Aug 22, 2019
cf6028a
Merge remote-tracking branch 'upstream/master'
iliailmer Oct 4, 2019
085a0f7
Merge remote-tracking branch 'upstream/master'
iliailmer Nov 4, 2019
fa922a2
fixed conflict in test_simple_metrics.py
iliailmer Nov 8, 2019
cd4c3dc
added '_' to name 'correct_mesh_orientation'
iliailmer Nov 8, 2019
7077399
fixed deprecation warnings
iliailmer Nov 8, 2019
aba255c
remove unnecessary function call, changed args to call for normalized…
iliailmer Nov 8, 2019
3654f92
one more fix for normalized_root_mse
iliailmer Nov 8, 2019
91404f9
pep8 fix
iliailmer Nov 8, 2019
2cdad14
docstring fix: blank line
iliailmer Nov 8, 2019
470a87d
docstring fix: blank line
iliailmer Nov 8, 2019
4ab00af
docstring fix: blank line
iliailmer Nov 8, 2019
fd61c0c
docstring fix: blank line
iliailmer Nov 8, 2019
3d7dd31
docstring fix
iliailmer Nov 9, 2019
208c641
doi backquotes
iliailmer Nov 10, 2019
de9345a
removed doi
iliailmer Nov 11, 2019
ed4f7cf
Merge branch 'main' into pr3776-image-quality
lagru Jan 6, 2023
73a95e9
Re-add enhancement_measure after merge
lagru Jan 6, 2023
0872877
Remove type hints and improve docstring
lagru Jan 7, 2023
8fcdc8c
Add a link to the reference paper
lagru Jan 7, 2023
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
61 changes: 61 additions & 0 deletions doc/examples/filters/plot_enhancement_measure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
=========================================
Quantitative Measure of Image Enhancement
=========================================
In this example, we will quantify the effect of applying contrast enhancement
to an image using a measure called EME [1]_.

It is defined as the log of the ratio of local maximum to minimum within a
sliding window of given size. This measure aims to provide a perceptual
estimate of image quality, useful when comparing image enhancement algorithms.

"""
import numpy as np
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import numpy as np

Not used.

import matplotlib.pyplot as plt
from skimage import data
from skimage.filters import gaussian
from skimage.exposure import equalize_adapthist
from skimage.metrics import simple_metrics

img_gray = data.camera()
img_rgb = data.astronaut()


def compare_side_by_side(before: np.ndarray, after: np.ndarray):
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16, 14))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16, 14))
fig, ax = plt.subplots(nrows=1, ncols=2)

I think removing the forced figure size leads to a nicer layout. Otherwise the images seem very small.

for idx, image in enumerate([before, after]):
ax[idx].imshow(image)
ax[idx].axis('off')
ax[idx].set_title("EME = {:.2f} ".format(
simple_metrics.enhancement_measure(image, size=3)))
fig.show()


#####################################################################
# We will create a function that takes an original image as `before`
# and applies a `transform` to it. We will use a gaussian filter to
Comment on lines +35 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: the function doesn't actually apply a transform to it. It just calls enhancement_measure on both and plots the results. Rewording this might be a lot clearer to new users.

# strongly blur the image and an adaptive histogram equalization as
# transforms.


compare_side_by_side(before=img_gray, after=gaussian(image=img_gray,
sigma=10))
compare_side_by_side(before=img_rgb, after=equalize_adapthist(image=img_rgb,
nbins=256 * 3))

############################################################################
# You can see that the greyscale camera image is very blurred due to
# strong gaussian filtering. This resulted in smoothing a lot of pixel
# differences and, as a result, to lower value of EME.
# On the other hand, the `astronaut` image was enhanced by adaptive
# histogram equalization with `256*3` bins. Histogram equalization
# brought out details and the EME value is higher than before the transform.
#
# References
# ----------
#
# .. [1] Sos S. Agaian, Karen Panetta, and Artyom M. Grigoryan.
# "A new measure of image enhancement.",
# IASTED International Conference on Signal Processing
# & Communication, Citeseer, 2000,
# :DOI:`10.1.1.35.4021`
4 changes: 3 additions & 1 deletion skimage/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from ._variation_of_information import variation_of_information
from .set_metrics import hausdorff_distance, hausdorff_pair
from .simple_metrics import (mean_squared_error, normalized_mutual_information,
normalized_root_mse, peak_signal_noise_ratio)
normalized_root_mse, peak_signal_noise_ratio,
enhancement_measure)

__all__ = [
"adapted_rand_error",
Expand All @@ -14,6 +15,7 @@
"normalized_mutual_information",
"normalized_root_mse",
"peak_signal_noise_ratio",
"enhancement_measure",
"structural_similarity",
"hausdorff_distance",
"hausdorff_pair",
Expand Down
54 changes: 54 additions & 0 deletions skimage/metrics/simple_metrics.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import numpy as np
from scipy.stats import entropy
from scipy.ndimage import maximum_filter, minimum_filter

from ..exposure import rescale_intensity
from ..util import img_as_float
from ..util.dtype import dtype_range
from .._shared.utils import _supported_float_type, check_shape_equality, warn

__all__ = ['mean_squared_error',
'normalized_root_mse',
'peak_signal_noise_ratio',
'normalized_mutual_information',
'enhancement_measure',
]


Expand Down Expand Up @@ -259,3 +263,53 @@ def normalized_mutual_information(image0, image1, *, bins=100):
H01 = entropy(np.reshape(hist, -1))

return (H0 + H1) / H01


def enhancement_measure(image: np.ndarray,
size: int = 3,
eps: float = 1e-6) -> float:
""" The image enhancement measure called EME based on [1]_.
It is a way of quantifying improvement of the image after enhancement.

The function uses a sliding window of user-provided size to measure
the mean of log of maximal and minimal intensity ratio
within the window.

Parameters
----------
image : array
Input image of which the quality should be assessed.
Can be either 3-channel RGB or 1-channel grayscale.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably support only grayscale images as input to make sure that the user determines how the total luminance of a colored image is calculated. Currently, the way that maximum_filter and minimum_filter is combining color channels is not something that is covered by the paper.

size : int, optional
Size of the window.
eps : float, optional
Parameter to avoid division by zero.

Returns
-------
eme : float
The number describing image quality.

References
----------
.. [1] Sos S. Agaian, Karen Panetta, and Artyom M. Grigoryan.
"A new measure of image enhancement.",
IASTED International Conference on Signal Processing
& Communication, Citeseer, 2000,

Examples
--------
>>> from skimage.data import camera
>>> from skimage.exposure import equalize_hist
>>> img = camera()
>>> before = enhancement_measure(img)
>>> after = enhancement_measure(equalize_hist(img))
>>> before < after
True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other measure functions in this module are applied on and compare two images. As far as I understand, the EME of an image is not useful on its own but rather it's difference to the EME of an improved image. It might be worth thinking about making the function take two images and return a bool or difference between the EME of both images. That way we also prevent users from assigning to much meaning to the absolute value of the measure.

"""
image = img_as_float(image)
image = rescale_intensity(image, out_range=(0., 1.))
eme = np.divide(maximum_filter(image, size=size),
minimum_filter(image, size=size) + eps)
eme = np.mean(20 * np.log(eme + eps))
return eme
25 changes: 24 additions & 1 deletion skimage/metrics/tests/test_simple_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

from skimage import data
from skimage._shared._warnings import expected_warnings
from skimage._shared.testing import assert_less
from skimage.metrics import (peak_signal_noise_ratio, normalized_root_mse,
mean_squared_error, normalized_mutual_information)
mean_squared_error, normalized_mutual_information,
enhancement_measure)
from skimage.exposure import equalize_hist, adjust_gamma


np.random.seed(5)
Expand Down Expand Up @@ -137,3 +140,23 @@ def test_nmi_random_3d():
1,
decimal=2,
)


def test_EME_greyscale():
orig = cam
enhanced = equalize_hist(orig)
assert_less(enhancement_measure(orig),
enhancement_measure(enhanced))
assert_less(enhancement_measure(orig, size=5),
enhancement_measure(enhanced, size=5))
assert_less(enhancement_measure(orig, size=11),
enhancement_measure(enhanced, size=11))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably check the actual values of the EMEs and that they are within an expected range. That way our tests would inform us if some change leads to significantly different EMEs.



def test_EME_color():
orig = data.astronaut()
enhanced = adjust_gamma(orig, gamma=5)
assert_less(enhancement_measure(orig, size=3),
enhancement_measure(enhanced, size=3, eps=1))
assert_less(enhancement_measure(orig, size=7),
enhancement_measure(enhanced, size=7))