Skip to content

Commit e365728

Browse files
add functions to process images
1 parent daca536 commit e365728

File tree

7 files changed

+238
-2
lines changed

7 files changed

+238
-2
lines changed

pyllusion/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
from .Ponzo import *
2020
from .psychopy import *
2121
from .RodFrame import *
22+
from .utilities import *
2223
from .VerticalHorizontal import *
2324
from .White import *
2425
from .Zollner import *
2526

26-
2727
# Maintainer info
2828
__author__ = "The Reality Bending League"
2929
__email__ = "[email protected]"

pyllusion/image/profile.stats

-7.09 KB
Binary file not shown.

pyllusion/utilities/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""
2+
Pyllusion submodule.
3+
"""
4+
5+
from .analyze_color import analyze_color
6+
from .analyze_image import analyze_image
7+
from .analyze_luminance import analyze_luminance
8+
9+
__all__ = [
10+
"analyze_luminance",
11+
"analyze_color",
12+
"analyze_image",
13+
]

pyllusion/utilities/analyze_color.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import numpy as np
2+
3+
4+
def analyze_color(image, average=True):
5+
"""Amount of Color in an image
6+
7+
Compute the amount of color in an image (redness, greenness, blueness).
8+
9+
Parameters
10+
----------
11+
image : ndarray
12+
Array for R, G and B channels.
13+
average : bool
14+
If True, will average the color values of each pixel.
15+
16+
Returns
17+
----------
18+
dict
19+
Contains "Redness", "Greenness" and "Blueness" and more (TODO: document this list).
20+
21+
Example
22+
----------
23+
>>> import pyllusion
24+
>>>
25+
>>> # Generate image (random pixels)
26+
>>> image = np.random.rand(500, 500, 3) * 255
27+
>>> image = image.astype(int)
28+
>>> # Visualize: plt.imshow(image)
29+
>>>
30+
>>> # Compute color
31+
>>> out = pyllusion.analyze_color(image, average=True)
32+
>>> out["Redness"]
33+
>>>
34+
>>> # Get luminance per pixel
35+
>>> out = pyllusion.analyze_color(image, average=False)
36+
>>> # Visualize: plt.imshow(out["Redness"], cmap="Reds")
37+
38+
"""
39+
try:
40+
import skimage.color
41+
except ImportError:
42+
raise ImportError(
43+
"Pyllusion error: analyze_color(): the 'scikit-image' module is required for this function to run. ",
44+
"Please install it first (`pip install scikit-image`).",
45+
)
46+
47+
out = {}
48+
49+
# Convert to color space HSL (Hue, saturation, luminance)
50+
hsv = skimage.color.rgb2hsv(image)
51+
52+
# Saturation and Luminance
53+
out["Saturation"] = hsv[:, :, 1]
54+
out["Brightness"] = hsv[:, :, 2]
55+
56+
# Get HUE
57+
hue = np.radians(hsv[:, :, 0] * 360)
58+
59+
# Redness (peaks at 0°)
60+
redness = np.rad2deg(_difference_between_angle(hue, np.radians(0)))
61+
out["Redness"] = 1 - np.abs(redness / 180)
62+
63+
# Yellowness (peaks at 60)
64+
yellowness = np.rad2deg(_difference_between_angle(hue, np.radians(60)))
65+
out["Yellowness"] = 1 - np.abs(yellowness / 180)
66+
67+
# Blueness (peaks at 240)
68+
blueness = np.rad2deg(_difference_between_angle(hue, np.radians(240)))
69+
out["Blueness"] = 1 - np.abs(blueness / 180)
70+
71+
# Greenness (peaks at 120)
72+
greenness = np.rad2deg(_difference_between_angle(hue, np.radians(120)))
73+
out["Greenness"] = 1 - np.abs(greenness / 180)
74+
75+
lab = skimage.color.rgb2lab(image)
76+
out["RedGreen"] = lab[:, :, 1]
77+
out["BlueYellow"] = lab[:, :, 2]
78+
79+
if average is True:
80+
# Average all elements
81+
out = {key: np.mean(value) for (key, value) in out.items()}
82+
83+
return out
84+
85+
86+
def _difference_between_angle(x, y):
87+
"""Compute the angular difference between two angles (in radians)"""
88+
vals = np.array([y - x, y - x + 2 * np.pi, y - x - 2 * np.pi])
89+
idx = np.argmin(np.abs(vals), axis=0)
90+
return np.take_along_axis(vals, np.array([idx]), axis=0)[0]

pyllusion/utilities/analyze_image.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import numpy as np
2+
3+
from .analyze_color import analyze_color
4+
from .analyze_luminance import analyze_luminance
5+
6+
7+
def analyze_image(image):
8+
"""Compute Objective Characteristics of an Image
9+
10+
Compute the physical characteristics of an image.
11+
12+
Parameters
13+
----------
14+
image : ndarray
15+
Array for R, G and B channels.
16+
17+
Returns
18+
----------
19+
dict
20+
Contains elements from `analyze_luminance()`, `analyze_color()` and more (TODO: document this list).
21+
22+
23+
Example
24+
----------
25+
>>> import pyllusion
26+
>>>
27+
>>> # Generate image (random pixels)
28+
>>> image = np.random.rand(500, 500, 3) * 255
29+
>>> image = image.astype(int)
30+
>>> # Visualize: plt.imshow(image)
31+
>>>
32+
>>> # Compute color
33+
>>> out = pyllusion.analyze_image(image)
34+
>>> out["Entropy"]
35+
36+
"""
37+
try:
38+
import skimage.color
39+
import skimage.feature
40+
import skimage.filters
41+
import skimage.measure
42+
except ImportError:
43+
raise ImportError(
44+
"Pyllusion error: analyze_color(): the 'scikit-image' module is required for this function to run. ",
45+
"Please install it first (`pip install scikit-image`).",
46+
)
47+
48+
out = analyze_luminance(image, average=True)
49+
out.update(analyze_color(image, average=True))
50+
out["Entropy"] = skimage.measure.shannon_entropy(image, base=2)
51+
# SD of the HUE axis (the colors)
52+
out["Colorfulness"] = np.std(skimage.color.rgb2hsv(image)[:, :, 0])
53+
# SD of the Luminance axis
54+
out["Contrast"] = np.std(skimage.color.rgb2lab(image)[:, :, 0])
55+
56+
# Edge detection
57+
bw = skimage.color.rgb2gray(image)
58+
edges = skimage.filters.sobel(bw) # skimage.filters.roberts or skimage.feature.canny
59+
out["Structure"] = skimage.measure.shannon_entropy(edges, base=2)
60+
return out
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import numpy as np
2+
3+
4+
def analyze_luminance(image, average=True):
5+
"""Image Luminance
6+
7+
Compute the average luminance of an image.
8+
9+
- Linear Luminance (L): linear measure of light, spectrally weighted for normal vision but not adjusted for the non-linear perception of lightness.
10+
- Perceived Luminance (L*): non-linear measure of perceptual lightness that approximates the human vision non-linear response curve.
11+
12+
Parameters
13+
----------
14+
image : ndarray
15+
Array for R, G and B channels.
16+
average : bool
17+
If True, will average the luminance value of each pixel.
18+
19+
Returns
20+
----------
21+
dict
22+
Contains "Luminance" and "Luminance_Perceived".
23+
24+
See Also
25+
----------
26+
- http://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
27+
28+
Example
29+
----------
30+
>>> import pyllusion
31+
>>>
32+
>>> # Generate image (random pixels)
33+
>>> image = np.random.rand(500, 500, 3) * 255
34+
>>> image = image.astype(int)
35+
>>> # Visualize: plt.imshow(image, interpolation="nearest")
36+
>>>
37+
>>> # Compute luminance
38+
>>> out = pyllusion.analyze_luminance(image, average=True)
39+
>>> out["Luminance"]
40+
>>> out["Luminance_Perceived"]
41+
>>>
42+
>>> # Get luminance per pixel
43+
>>> out = pyllusion.analyze_luminance(image, average=False)
44+
>>> # Visualize: plt.imshow(-1*out["Luminance"], cmap="Greys")
45+
46+
"""
47+
out = {}
48+
# 1. Convert all sRGB integers to decimal 0-1
49+
image = image / 255.0
50+
51+
# 2. Linearize: Convert gamma encoded RGB to a linear value. sRGB (computer standard) requires
52+
# a power curve of approximately V^2.2
53+
image[image <= 0.04045] = image[image <= 0.04045] / 12.92
54+
image[image > 0.04045] = np.power((image[image > 0.04045] + 0.055) / 1.055, 2.4)
55+
56+
# 3. To find Luminance (Y) apply the standard coefficients for sRGB
57+
L = 0.2126 * image[:, :, 0] + 0.7152 * image[:, :, 1] + 0.0722 * image[:, :, 2]
58+
59+
# 4. Perceived lightness
60+
Lstar = (
61+
0.299 * image[:, :, 0] ** 2.2
62+
+ 0.587 * image[:, :, 1] ** 2.2
63+
+ 0.114 * image[:, :, 2] ** 2.2
64+
) ** (1 / 2.2)
65+
# Lstar = image.copy()
66+
# Lstar[Lstar <= 216 / 24389] = Lstar[Lstar <= 216 / 24389] * (24389 / 27)
67+
# Lstar[Lstar > 216 / 24389] = np.power(Lstar[Lstar > 216 / 24389], 1 / 3) * 116 - 16
68+
69+
if average is True:
70+
L = np.mean(L)
71+
Lstar = np.mean(Lstar)
72+
73+
return {"Luminance": L, "Luminance_Perceived": Lstar}

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def find_version():
1616

1717
dependencies = ["numpy", "pandas", "Pillow", "scipy"]
1818
setup_requirements = ["pytest-runner", "numpy"]
19-
test_requirements = dependencies + ["pytest", "matplotlib", "coverage"]
19+
test_requirements = dependencies + ["pytest", "matplotlib", "coverage", "scikit-image"]
2020

2121

2222
setup(

0 commit comments

Comments
 (0)