JAVA图像处理系列(六)—— 色彩转换RGB vs HSL

RGB色彩

RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红、绿、蓝三个通道的颜色。

HSL色彩( HSL color )

HSL色彩模式是工业界的一种颜色标准,是通过对色相(H)、饱和度(S)、亮度(L)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,HSL即是代表色相,饱和度,亮度三个通道的颜色。
H: Hue 色相
S:Saturation 饱和度
L :Lightness 亮度
HSL的H(hue)分量,代表的是人眼所能感知的颜色范围,这些颜色分布在一个平面的色相环上,取值范围是0°到360°的圆心角,每个角度可以代表一种颜色。色相值的意义在于,我们可以在不改变光感的情况下,通过旋转色相环来改变颜色。
HSL的S(saturation)分量,指的是色彩的饱和度,它用0%至100%的值描述了相同色相、亮度下色彩纯度的变化。数值越大,颜色中的灰色越少,颜色越鲜艳,呈现一种从理性(灰度)到感性(纯色)的变化。
HSL的L(lightness)分量,指的是色彩的亮度,作用是控制色彩的亮暗变化。它同样使用了0%至100%的取值范围。数值越小,色彩越暗,越接近于黑色;数值越大,色彩越亮,越接近于白色。

RGB转HSL算法

(1)把RGB值转成区间[0,1]内的数值。
(2)找出R,G和B中的最大值。
(3)计算亮度:L=(maxcolor + mincolor)/2
(4)如果最大和最小的颜色值相同,即表示灰色,那么S定义为0,而H未定义并在程序中通常写成0。
(5)否则,根据亮度L计算饱和度S:
    If L<0.5, S=(maxcolor-mincolor)/(maxcolor + mincolor)
    If L>=0.5, S=(maxcolor-mincolor)/(2.0-maxcolor-mincolor)
(6)计算色调H:
    If R=maxcolor, H=(G-B)/(maxcolor-mincolor)
    If G=maxcolor, H=2.0+(B-R)/(maxcolor-mincolor)
    If B=maxcolor, H=4.0+(R-G)/(maxcolor-mincolor)
    H=H*60.0,如果H为负值,则加360。

HSL转RGB算法。

(1)If S=0,表示灰色,定义R,G和B都为L.
(2)否则,测试L:
    If L<0.5,temp2=L*(1.0+S)
    If L>=0.5,temp2=L+S-L*S
(3)temp1=2.0*L-temp2
(4)把H转换到0~1。
(5)对于R,G,B,计算另外的临时值temp3。方法如下:
    for R, temp3=H+1.0/3.0
    for G, temp3=H
    for B, temp3=H-1.0/3.0
    if temp3<0, temp3=temp3+1.0
    if temp3>1, temp3=temp3-1.0
(6)对于R,G,B做如下测试:
    If 6.0*temp3<1,color=temp1+(temp2-temp1)*6.0*temp3
    Else if 2.0*temp3<1,color=temp2
    Else if 3.0*temp3<2,
    color=temp1+(temp2-temp1)*((2.0/3.0)-temp3)*6.0
    Else color=temp1

色彩转换JAVA实现代码

public class TransformColor {
    public static final double MaxRGB = 255.0;

    public void rgbToHsl(Pixel pixel) {
        int red = pixel.red;
        int blue = pixel.blue;
        int green = pixel.green;

        double b, delta, g, max, min, r;

        double hue, saturation, luminosity;
        /*
         * Convert RGB to HSL colorspace.
         */
        r = (double) red / MaxRGB;
        g = (double) green / MaxRGB;
        b = (double) blue / MaxRGB;
        max = Math.max(r, Math.max(g, b));
        min = Math.min(r, Math.min(g, b));

        hue = 0.0;
        saturation = 0.0;
        luminosity = (min + max) / 2.0;
        delta = max - min;
        if (delta == 0.0) {
            pixel.hue = hue;
            pixel.saturation = saturation;
            pixel.luminosity = luminosity;
            return;
        }
        saturation = delta / ((luminosity <= 0.5) ? (min + max) : (2.0 - max - min));
        if (r == max)
            hue = (g == min ? 5.0 + (max - b) / delta : 1.0 - (max - g) / delta);
        else if (g == max)
            hue = (b == min ? 1.0 + (max - r) / delta : 3.0 - (max - b) / delta);
        else
            hue = (r == min ? 3.0 + (max - g) / delta : 5.0 - (max - r) / delta);
        hue /= 6.0;

        pixel.hue = hue;
        pixel.saturation = saturation;
        pixel.luminosity = luminosity;
    }

    public void hslToRgb(Pixel pixel) {
        double hue, saturation, luminosity;
        // int red, green, blue;
        double b, g, r, v, x, y, z;

        hue = pixel.hue;
        saturation = pixel.saturation;
        luminosity = pixel.luminosity;

        /*
         * Convert HSL to RGB colorspace.
         */
        v = (luminosity <= 0.5) ? (luminosity * (1.0 + saturation))
                : (luminosity + saturation - luminosity * saturation);
        if (saturation == 0.0) {
            pixel.red = (int) (MaxRGB * luminosity + 0.5);
            pixel.green = (int) (MaxRGB * luminosity + 0.5);
            pixel.blue = (int) (MaxRGB * luminosity + 0.5);
            return;
        }
        y = 2.0 * luminosity - v;
        x = y + (v - y) * (6.0 * hue - Math.floor(6.0 * hue));
        z = v - (v - y) * (6.0 * hue - Math.floor(6.0 * hue));
        switch ((int) (6.0 * hue)) {
        case 0:
            r = v;
            g = x;
            b = y;
            break;
        case 1:
            r = z;
            g = v;
            b = y;
            break;
        case 2:
            r = y;
            g = v;
            b = x;
            break;
        case 3:
            r = y;
            g = z;
            b = v;
            break;
        case 4:
            r = x;
            g = y;
            b = v;
            break;
        case 5:
            r = v;
            g = y;
            b = z;
            break;
        default:
            r = v;
            g = x;
            b = y;
            break;
        }
        pixel.red = (int) (MaxRGB * r + 0.5);
        pixel.green = (int) (MaxRGB * g + 0.5);
        pixel.blue = (int) (MaxRGB * b + 0.5);
    }
}

辅助类Pixel代码:

import java.awt.image.*;

public class Pixel {
  public int red;
  public int green;
  public int blue;
  public int alpha=0xFF;
  public double hue;
  public double saturation;
  public double luminosity;
  private int rgb;

  public Pixel() {
  }

  public void setRGB(int rgb) {
    red = (rgb & 0x00FF0000) / 0x00010000;
    green = (rgb & 0x0000FF00) / 0x00000100;
    blue = rgb & 0x000000FF;
    alpha = (rgb >> 24)&0x0ff;
    this.rgb = rgb;
  }

  public int getRGB() {
    rgb =  alpha<<24 | 0x00010000 * red | 0x00000100 * green | blue;
    return this.rgb;
  }

  public static void setRgb(BufferedImage image, int x, int y, int red, int green, int blue) {
    int rgb = 0xFF000000 | red * 0x00010000 | green * 0x00000100 | blue;
    image.setRGB(x, y, rgb);
  }

  public static int pixelIntensity(int rgb) {
    int red = (rgb&0x00FF0000)/0x00010000;
    int green = (rgb&0x0000FF00)/0x00000100;
    int blue = rgb&0x000000FF;
    return (int) (0.299 * red + 0.587 * green + 0.114 * blue + 0.5);
  }
}

改变图像亮度、对比度饱和度

我们可以通过把对HSL颜色的处理实现对图像亮度,对比度饱和度的调节

调节亮度、色相、对比度代码如下

    public static void modulate(double percent_hue, double percent_saturation, double percent_brightness, Pixel pixel) {

        TransformColor transform = new TransformColor();
        /*
         * Increase or decrease color brightness, saturation, or hue.
         */
        transform.rgbToHsl(pixel);
        pixel.luminosity *= (0.01) * percent_brightness;
        if (pixel.luminosity < 0.0)
            pixel.luminosity = 0.0;
        else if (pixel.luminosity > 1.0)
            pixel.luminosity = 1.0;
        pixel.saturation *= (0.01) * percent_saturation;
        if (pixel.saturation < 0.0)
            pixel.saturation = 0.0;
        else if (pixel.saturation > 1.0)
            pixel.saturation = 1.0;
        pixel.hue *= (0.01) * percent_hue;
        if (pixel.hue < 0.0)
            pixel.hue += 1.0;
        else if (pixel.hue > 1.0)
            pixel.hue -= 1.0;
        transform.hslToRgb(pixel);
    }

    public static BufferedImage hslImage(BufferedImage image, double satuPer, double huePer, double lumPer) {
        BufferedImage bimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
        Pixel pixel = new Pixel();

        for (int y = 0; y < bimg.getHeight(); y++) {
            for (int x = 0; x < bimg.getWidth(); x++) {
                pixel.setRGB(image.getRGB(x, y));
                modulate(huePer, satuPer, lumPer, pixel);
                bimg.setRGB(x, y, pixel.getRGB());
            }
        }
        return bimg;
    }

调节对比度代码如下:

    public static void contrast(double percent, Pixel pixel) {
        TransformColor transform = new TransformColor();
        double alpha = percent / 100.0;

        /*
         * Enhance contrast: dark color become darker, light color become lighter.
         */
        transform.rgbToHsl(pixel);
        pixel.luminosity += alpha * (alpha * (Math.sin(Math.PI * (pixel.luminosity - alpha)) + 1.0) - pixel.luminosity);
        if (pixel.luminosity > 1.0)
            pixel.luminosity = 1.0;
        else if (pixel.luminosity < 0.0)
            pixel.luminosity = 0.0;
        transform.hslToRgb(pixel);
    }

    public static BufferedImage contractImage(BufferedImage image, double percent) {
        BufferedImage bimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
        Pixel pixel = new Pixel();

        for (int y = 0; y < bimg.getHeight(); y++) {
            for (int x = 0; x < bimg.getWidth(); x++) {
                pixel.setRGB(image.getRGB(x, y));
                contrast(percent, pixel);
                bimg.setRGB(x, y, pixel.getRGB());
            }
        }
        return bimg;
    }

实现效果

以下是对图像亮度,饱和度、对比度分别增加和减少的测试代码:

    public static void main(String[] argv) throws IOException {
        BufferedImage img = read(new File("D:\\eclipse-workspace\\image-toolkit\\images\\girl.jpg"));
        
        BufferedImage img2 = hslImage(img, 0, 0.5, 0);
        ImageIO.write(img2, "jpeg", new File("girl-s+.jpg"));
        img2 = hslImage(img, 0, -0.5, 0);
        ImageIO.write(img2, "jpeg", new File("girl-s-.jpg"));
        
        img2 = hslImage(img, 0, 0, 0.5);
        ImageIO.write(img2, "jpeg", new File("girl-l+.jpg"));
        img2 = hslImage(img, 0, 0, -0.5);
        ImageIO.write(img2, "jpeg", new File(girl-l-.jpg"));
        
        img2 = contractImage(img, 0.7);
        ImageIO.write(img2, "jpeg", new File("girl-c+.jpg"));
        img2 = contractImage(img, -0.7);
        ImageIO.write(img2, "jpeg", new File("girl-c-.jpg"));
    }

实现效果

其中第一张为原始图片,第二三张为饱和度调节图片,第四五张为亮度调节图片,第六七张为对比度调节图片


girl.jpg
girl-s+.jpg

girl-s-.jpg

girl-l+.jpg

girl-l-.jpg

girl-c+.jpg

girl-c-.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容