I have this code that converts an image from RGB space to HSI space:
import numpy as np
import imageio
from matplotlib import pyplot as plt
def RGB_TO_HSI(img):
with np.errstate(divide='ignore', invalid='ignore'):
rgb = np.float32(img) / 255
# Separate color channels
red = rgb[:, :, 0]
green = rgb[:, :, 1]
blue = rgb[:, :, 2]
# Calculate Intensity
def calc_intensity(red, green, blue):
intensity = (red + green + blue + 0.001) / 3
return intensity
# Calculate Saturation
def calc_saturation(red, green, blue):
minimum = np.minimum(np.minimum(red, green), blue)
saturation = 1 - (minimum / calc_intensity(red, green, blue))
return saturation
# Calculate Hue
def calc_hue(red, green, blue):
hue = np.copy(red) # Basically have our hue = red for now; we only need its size/dimensions
for i in range(0, blue.shape[0]):
for j in range(0, blue.shape[1]):
if blue[i][j] <= green[i][j]:
hue[i][j] = np.arccos(0.5 * ((red[i][j] - green[i][j]) + (red[i][j] - blue[i][j])) / (np.sqrt((red[i][j] - green[i][j]) ** 2 + (red[i][j] - blue[i][j]) * (green[i][j] - blue[i][j]))))
else:
hue[i][j] = 2 * np.pi - np.arccos(0.5 * ((red[i][j] - green[i][j]) + (red[i][j] - blue[i][j])) / (np.sqrt((red[i][j] - green[i][j]) ** 2 + (red[i][j] - blue[i][j]) * (green[i][j] - blue[i][j]))))
return hue
# Merge channels into picture and return image
hsi = np.zeros(img.shape) # instead of having 3 channels, one for each color (RGB), here we have a channel; for "hue", another for "saturation" and another for "intensity"
hsi[:, :, 0], hsi[:, :, 1], hsi[:, :, 2] = calc_hue(red, green, blue), calc_saturation(red, green, blue), calc_intensity(red, green, blue)
return hsi
cat = imageio.imread("Path...")
hsi_cat = RGB_TO_HSI(cat)
plt.imshow(hsi_cat)
plt.show()
However, adjusting the line in the code to: hsi = np.zeros(rgb.shape, dtype=np.uint8)
, then loading the image using PIL, and passing it into the function as an argument:
cat_p = Image.open("Path...") # Using PIL now
his_cat_p = RGB_TO_HSI(cat_p) # Passing the JPEG image into the function
his_cat_p = Image.fromarray(his_cat_p) # Converting the result back from an array (return hsi), into JPEG format
his_cat_p.show() # Black image appears
This results is a black image, and I'm not quite sure why!
The function Image.fromarray
only support limited modes with typical ranges. If you just want to render the data correctly as an image, maybe it is better to transform from HSI back to RGB values, then output it. Something like,
cat_hsi = RGB_TO_HSI(cat)
result = modify(cat_hsi) # I guess you want to operate on hsi instead of rgb
result_rgb = HSI_TO_RGB(result)
img = Image.fromarray(result_rgb, mode='RGB')
img.show()
Additionally I made some modifications to the code so that it runs on my machine.
calc_hue
function.import numpy as np
from numba import njit # code gets significantly faster
from matplotlib import pyplot as plt
from PIL import Image
def RGB_TO_HSI(img):
rgb = np.float32(img) / 255
red = rgb[:, :, 0]
green = rgb[:, :, 1]
blue = rgb[:, :, 2]
def calc_intensity(red, green, blue):
intensity = (red + green + blue + 0.001) / 3
return intensity
def calc_saturation(red, green, blue):
minimum = np.minimum(np.minimum(red, green), blue)
saturation = 1 - (minimum / calc_intensity(red, green, blue))
return saturation
@njit # use numba to accelerate
def calc_hue(red, green, blue):
hue = np.copy(red)
for i in range(0, blue.shape[0]):
for j in range(0, blue.shape[1]):
denominator = np.sqrt(
(red[i][j] - green[i][j]) ** 2 + (red[i][j] - blue[i][j]) * (green[i][j] - blue[i][j])
)
if abs(denominator) < 1e-10: # exliciply handle the possible zero-division cases
hue[i][j] = np.nan
else:
if blue[i][j] <= green[i][j]:
hue[i][j] = np.arccos(0.5 * ((red[i][j] - green[i][j]) + (red[i][j] - blue[i][j])) / denominator)
else:
hue[i][j] = 2 * np.pi - np.arccos(0.5 * ((red[i][j] - green[i][j]) + (red[i][j] - blue[i][j])) / denominator)
return hue
# Merge channels into picture and return image
hsi = np.zeros(img.shape) # instead of having 3 channels, one for each color (RGB), here we have a channel; for "hue", another for "saturation" and another for "intensity"
hsi[:, :, 0], hsi[:, :, 1], hsi[:, :, 2] = calc_hue(red, green, blue), calc_saturation(red, green, blue), calc_intensity(red, green, blue)
return hsi
cat = np.array(Image.open("test.png"))
hsi_cat = RGB_TO_HSI(cat[:, :, :3]) # sometims images are in RGBA format
plt.imshow(hsi_cat)
plt.show()
(numba is magical isn't it?)