-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
base: main
Are you sure you want to change the base?
Changes from 55 commits
8bec6b0
d2b7135
130d912
9bfe66e
a798d36
4c3fa15
6f02d9b
f9c5b69
d266205
651d332
268d0e2
ac8e9ab
4fd9f8d
86e0bed
f41fa48
47a73ee
81d8022
c5300cc
9dc5646
d6e30cf
996b73e
6676cb1
0a53d2e
755e276
e223b0b
42b4cb0
b511c3a
8e6b65c
b6c78e4
40f89ee
79b15d6
3eb1b94
ae7110e
82be96e
6b22824
e68c091
6c90991
8bfe430
cf6028a
085a0f7
fa922a2
cd4c3dc
7077399
aba255c
3654f92
91404f9
2cdad14
470a87d
4ab00af
fd61c0c
3d7dd31
208c641
de9345a
ed4f7cf
73a95e9
0872877
8fcdc8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||||||
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)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
# 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` |
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', | ||
] | ||
|
||
|
||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
@@ -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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not used.