将 RGB 颜色快速更改为 HSV

Posted

技术标签:

【中文标题】将 RGB 颜色快速更改为 HSV【英文标题】:Change RGB Color to HSV swift 【发布时间】:2019-06-11 11:13:22 【问题描述】:

祝你有美好的一天。我正在做RGB LED变色应用。我正在使用 HSB 进行颜色选择。这些颜色具有色调、饱和度、亮度和 alpha 值。我将这些值保存在数据库中。我正在用 arduino 读取这些数据。但是如何找出这些颜色值属于哪个颜色呢?那么如何找到与我通过 arduino 选择的颜色对应的颜色呢?

import Foundation
import UIKit

// The view to edit HSB color components.
public class EFHSBView: UIView, EFColorView, UITextFieldDelegate 

    let EFColorSampleViewHeight: CGFloat = 30.0
    let EFViewMargin: CGFloat = 20.0
    let EFColorWheelDimension: CGFloat = 200.0

    private let colorWheel: EFColorWheelView = EFColorWheelView()
    let brightnessView: EFColorComponentView = EFColorComponentView()
    private let colorSample: UIView = UIView()

    private var colorComponents: HSB = HSB(1, 1, 1, 1)
    private var layoutConstraints: [NSLayoutConstraint] = []

    weak public var delegate: EFColorViewDelegate?

    public var isTouched: Bool 
        if self.colorWheel.isTouched 
            return true
        

        if self.brightnessView.isTouched 
            return true
        

        return false
    

    public var color: UIColor 
        get 
            return UIColor(
                hue: colorComponents.hue,
                saturation: colorComponents.saturation,
                brightness: colorComponents.brightness,
                alpha: colorComponents.alpha
            )
        
        set 

            colorComponents = EFRGB2HSB(rgb: EFRGBColorComponents(color: newValue))
            self.reloadData()
        
    

    override init(frame: CGRect) 
        super.init(frame: frame)
        self.ef_baseInit()
    

    required public init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        self.ef_baseInit()
    

    func reloadData() 
        colorSample.backgroundColor = self.color
        colorSample.accessibilityValue = EFHexStringFromColor(color: self.color)
        self.ef_reloadViewsWithColorComponents(colorComponents: colorComponents)
        self.colorWheel.display(self.colorWheel.layer)
    

    override public func updateConstraints() 
        self.ef_updateConstraints()
        super.updateConstraints()
    

    // MARK:- Private methods
    private func ef_baseInit() 
        self.accessibilityLabel = "hsb_view"

        colorSample.accessibilityLabel = "color_sample"
        colorSample.layer.borderColor = UIColor.black.cgColor
        colorSample.layer.borderWidth = 0.5
        colorSample.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(colorSample)

        colorWheel.translatesAutoresizingMaskIntoConstraints = false
        self.addSubview(colorWheel)

        brightnessView.title = NSLocalizedString("Brightness", comment: "")
        brightnessView.maximumValue = EFHSBColorComponentMaxValue
        brightnessView.format = "%.2f"
        brightnessView.translatesAutoresizingMaskIntoConstraints = false
        brightnessView.setColors(colors: [UIColor.black, UIColor.white])
        self.addSubview(brightnessView)

        colorWheel.addTarget(
            self, action: #selector(ef_colorDidChangeValue(sender:)), for: UIControl.Event.valueChanged
        )
        brightnessView.addTarget(
            self, action: #selector(ef_brightnessDidChangeValue(sender:)), for: UIControl.Event.valueChanged
        )

        self.setNeedsUpdateConstraints()
    

    private func ef_updateConstraints() 
        // remove all constraints first
        if !layoutConstraints.isEmpty 
            self.removeConstraints(layoutConstraints)
        

        layoutConstraints = UIUserInterfaceSizeClass.compact == self.traitCollection.verticalSizeClass
            ? self.ef_constraintsForCompactVerticalSizeClass()
            : self.ef_constraintsForRegularVerticalSizeClass()

        self.addConstraints(layoutConstraints)
    

    private func ef_constraintsForRegularVerticalSizeClass() -> [NSLayoutConstraint] 
        let metrics = [
            "margin" : EFViewMargin,
            "height" : EFColorSampleViewHeight,
            "color_wheel_dimension" : EFColorWheelDimension
        ]
        let views = [
            "colorSample" : colorSample,
            "colorWheel" : colorWheel,
            "brightnessView" : brightnessView
        ]

        var layoutConstraints: [NSLayoutConstraint] = []
        let visualFormats = [
            "H:|-margin-[colorSample]-margin-|",
            "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-|",
            "H:|-margin-[brightnessView]-margin-|",
            "V:|-margin-[colorSample(height)]-margin-[colorWheel]-margin-[brightnessView]-(>=margin@250)-|"
        ]
        for visualFormat in visualFormats 
            layoutConstraints.append(
                contentsOf: NSLayoutConstraint.constraints(
                    withVisualFormat: visualFormat,
                    options: NSLayoutConstraint.FormatOptions(rawValue: 0),
                    metrics: metrics,
                    views: views
                )
            )
        
        layoutConstraints.append(
            NSLayoutConstraint(
                item: colorWheel,
                attribute: NSLayoutConstraint.Attribute.width,
                relatedBy: NSLayoutConstraint.Relation.equal,
                toItem: colorWheel,
                attribute: NSLayoutConstraint.Attribute.height,
                multiplier: 1,
                constant: 0)
        )
        return layoutConstraints
    

    private func ef_constraintsForCompactVerticalSizeClass() -> [NSLayoutConstraint] 
        let metrics = [
            "margin" : EFViewMargin,
            "height" : EFColorSampleViewHeight,
            "color_wheel_dimension" : EFColorWheelDimension
        ]
        let views = [
            "colorSample" : colorSample,
            "colorWheel" : colorWheel,
            "brightnessView" : brightnessView
        ]

        var layoutConstraints: [NSLayoutConstraint] = []
        let visualFormats = [
            "H:|-margin-[colorSample]-margin-|",
            "H:|-margin-[colorWheel(>=color_wheel_dimension)]-margin-[brightnessView]-(margin@500)-|",
            "V:|-margin-[colorSample(height)]-margin-[colorWheel]-(margin@500)-|"
        ]
        for visualFormat in visualFormats 
            layoutConstraints.append(
                contentsOf: NSLayoutConstraint.constraints(
                    withVisualFormat: visualFormat,
                    options: NSLayoutConstraint.FormatOptions(rawValue: 0),
                    metrics: metrics,
                    views: views
                )
            )
        
        layoutConstraints.append(
            NSLayoutConstraint(
                item: colorWheel,
                attribute: NSLayoutConstraint.Attribute.width,
                relatedBy: NSLayoutConstraint.Relation.equal,
                toItem: colorWheel,
                attribute: NSLayoutConstraint.Attribute.height,
                multiplier: 1,
                constant: 0)
        )
        layoutConstraints.append(
            NSLayoutConstraint(
                item: brightnessView,
                attribute: NSLayoutConstraint.Attribute.centerY,
                relatedBy: NSLayoutConstraint.Relation.equal,
                toItem: self,
                attribute: NSLayoutConstraint.Attribute.centerY,
                multiplier: 1,
                constant: 0)
        )
        return layoutConstraints
    

    private func ef_reloadViewsWithColorComponents(colorComponents: HSB) 
        colorWheel.hue = colorComponents.hue
        colorWheel.saturation = colorComponents.saturation
        colorWheel.brightness = colorComponents.brightness
        self.ef_updateSlidersWithColorComponents(colorComponents: colorComponents)
    

    private func ef_updateSlidersWithColorComponents(colorComponents: HSB) 
        brightnessView.value = colorComponents.brightness
    

    @objc private func ef_colorDidChangeValue(sender: EFColorWheelView) 
        colorComponents.hue = sender.hue
        colorComponents.saturation = sender.saturation
        self.delegate?.colorView(self, didChangeColor: self.color)
        self.reloadData()
    

    @objc private func ef_brightnessDidChangeValue(sender: EFColorComponentView) 
        colorComponents.brightness = sender.value
        self.colorWheel.brightness = sender.value
        self.delegate?.colorView(self, didChangeColor: self.color)
        self.reloadData()
    

【问题讨论】:

【参考方案1】:

Hue-Saturation-Value 模型是一种广泛使用的颜色编码方法。

色调是一个循环参数,这就是它以度数编码的原因(360 度 = 全周期)。颜色的红色、绿色和蓝色分量沿 HUE 轴周期性变化,在 120 度内处于其较低值,然后在 60 度内上升到较高值,在较高值处保持 120 度,然后在接下来的 60 度内再次下降。循环在 360 度后重复。

红色绿色和蓝色分量相对于彼此偏移 120 度,因此可以对它们之间的任何比例进行编码。

在上图中,您可以看到,对于每个 H 值,RGB 分量中的一个处于较高值,另一个处于较低值,第三个在两者之间变化。

Value 组件对 RGB 的上限值进行编码。 IE。 0% 值等于黑色(无论色调和饱和度值是什么),而 100% - 最亮的颜色

HSV 模型的饱和度分量编码 R G B 分量上下值之间的差异,相对于 V 值。 IE。如果值为 50%,饱和度为 30%,则下限值为 35%,上限值为 50%。

饱和度是否为零,然后颜色为灰色,无论色调值是多少。

因此,HSV转RGB的代码可能如下:

float h = ..., s = ..., v = ...; // input values

float r, g, b;

float d = v * s; // difference between upper and lower value
float l = v - d; // calculating the lower value

// code below assumes 0 <= h < 360. Otherwise wrap the value before
if (h < 60)  // 0..60
  r = v;
  g = l + (h / 60.0) * d;
  b = l;
 else if (h < 120)   // 60..120
  r = u - ((h - 60) / 60.0) * d;
  g = v;
  b = l;
 else if (h < 180)   // 120..180
  r = l;
  g = v;
  b = l + ((h - 120) / 60.0) * d;
 else if (h < 240)   // 180..240
  r = l;
  g = u - ((h - 180) / 60.0) * d;
  b = v;
 else if (h < 300)   // 240..300
  r = l + ((h - 240) / 60.0) * d;
  g = l;
  b = v;
 else  // 300..360
  r = v;
  g = l;
  b = u - ((h - 300) / 60.0) * d;

Read more in Wikipedia

请注意,当使用 MCU 点亮 RGB LED 时,您可能会得到与您在显示屏上看到的颜色不同的颜色。这是因为显示器使用的是Gamma Correction,即 50% 并不意味着 50% 的发光(通常接近 25%)。因此,在将获得的 RGB 值传递给 LED PWM 之前,您可能需要对其应用相同的伽马校正。

【讨论】:

以上是关于将 RGB 颜色快速更改为 HSV的主要内容,如果未能解决你的问题,请参考以下文章

由RGB到HSV颜色空间的理解

将 `shap.summary_plot()` 的渐变颜色更改为特定的 2 或 3 RGB 渐变调色板颜色

RGB与HSV之间的转换公式及颜色表

rgb转化到hsv图像外观会变吗opencv

RGB和HSV颜色空间

javascript中的RGB到HSV颜色? [关闭]