如何用Haskell的hip库复现R包imager的RGBtoHSI功能?
实现从Haskell hip库RGB图到imager风格HSI图的转换
一、解析R imager包RGBtoHSI的核心逻辑
imager的RGBtoHSI核心是标准RGB到HSI的颜色空间转换,关键要注意数值范围和细节处理:
- 输入预处理:将0-255的整数RGB值归一化到0-1的浮点数
- 亮度(I)计算:
I = (R + G + B) / 3 - 饱和度(S)计算:若
I=0则S=0,否则S = 1 - (min(R,G,B) / I) - 色相(H)计算:
- 先算
delta = max(R,G,B) - min(R,G,B),若delta=0则H=0(灰度) - 否则根据最大值所在通道计算:
- 最大值为R:
H = 60 * ((G - B)/delta) mod 6,结果为负则加360转正 - 最大值为G:
H = 60 * ((B - R)/delta + 2) - 最大值为B:
H = 60 * ((R - G)/delta + 4)
- 最大值为R:
- 最终H范围为0-360度
- 先算
- 输出处理:H保留0-360范围,S、I保留0-1范围,可视化时将H映射为色相,S/I对应饱和度、亮度
二、在Haskell hip库中实现该逻辑
hip库无直接对应内置函数,需手动实现转换,以下是完整步骤:
1. 类型定义与归一化
先将hip生成的Word8类型RGB图(0-255)转为0-1范围的浮点数:
import Graphics.HIP as HIP import Data.Word (Word8) -- 自定义HSI类型 data HSI a = HSI { h :: a, s :: a, i :: a } deriving (Show, Eq, Functor) -- 归一化RGB通道到0-1浮点数 normalizeRGB :: Image RGB Word8 -> Image RGB Float normalizeRGB = HIP.map (\(RGB r g b) -> RGB (fromIntegral r / 255) (fromIntegral g / 255) (fromIntegral b / 255))
2. 实现RGB到HSI的转换函数
rgbToHSI :: RGB Float -> HSI Float rgbToHSI (RGB r g b) = HSI h' s' i' where i' = (r + g + b) / 3 minVal = minimum [r, g, b] maxVal = maximum [r, g, b] delta = maxVal - minVal -- 计算饱和度S s' = if i' == 0 then 0 else 1 - (minVal / i') -- 计算色相H(0-360度) h' = case () of _ | delta == 0 -> 0 | maxVal == r -> let temp = ((g - b) / delta) * 60 in if temp < 0 then temp + 360 else temp | maxVal == g -> ((b - r) / delta + 2) * 60 | maxVal == b -> ((r - g) / delta + 4) * 60 | otherwise -> 0 -- 分支不可达
3. 应用转换到整图并转回可视化RGB
imager的HSI可视化是将HSI转回RGB,需实现反向转换:
-- HSI转RGB(0-1浮点数) hsiToRGB :: HSI Float -> RGB Float hsiToRGB (HSI h' s' i') = RGB r g b where hNormalized = h' / 60 -- 转为0-6范围 c = i' * s' x = c * (1 - abs (mod hNormalized 2 - 1)) (r1, g1, b1) = case floor hNormalized of 0 -> (c, x, 0) 1 -> (x, c, 0) 2 -> (0, c, x) 3 -> (0, x, c) 4 -> (x, 0, c) 5 -> (c, 0, x) _ -> (0, 0, 0) m = i' - c r = r1 + m g = g1 + m b = b1 + m -- 转换为可保存的Word8类型RGB图 convertAndVisualize :: Image RGB Word8 -> Image RGB Word8 convertAndVisualize img = HIP.map (\hsi -> let RGB r g b = hsiToRGB hsi in RGB (round $ r * 255) (round $ g * 255) (round $ b * 255)) (HIP.map rgbToHSI $ normalizeRGB img)
4. 完整使用示例
main :: IO () main = do -- 读取hip生成的RGB图 rgbImg <- HIP.readImage "hip_generated_rgb.jpg" -- 执行转换并保存结果 let resultImg = convertAndVisualize rgbImg HIP.writeImage "converted_hsi_image.png" resultImg
关键注意点
- hip内置的
rgbToHsi是将H转为0-2π的弧度值,而imager用0-360角度值,这是两者核心差异,需手动实现角度版计算 - 严格控制数值范围的归一化与反归一化,避免颜色偏差
- 若hip生成的图像已是0-1范围的Float类型,可跳过
normalizeRGB步骤
内容的提问来源于stack exchange,提问作者Stéphane Laurent
相关产品推荐
相关产品推荐

