使用 PIL 而不是 imagio 库读取图像时图像无法正确显示

Image not showing properly when reading the image using PIL instead of imagio library

我有这段代码可以将图像从 RGB space 转换为 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()

但是,将代码中的行调整为:hsi = np.zeros(rgb.shape, dtype=np.uint8),然后使用PIL加载图像,并将其作为参数传递给函数:

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

结果是黑色图像,我不太清楚为什么!

函数 Image.fromarray 仅支持具有典型范围的有限 modes。如果您只是想将数据正确渲染为图像,也许最好从 HSI 转换回 RGB 值,然后输出它。像,

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()

此外,我对代码进行了一些修改,以便它可以在我的机器上运行。

  • 确保将形状为 (X, Y, 3) 的 RGB 数组传递给函数
  • calc_hue 函数中明确处理可能的除零情况。
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 很神奇不是吗?)