7

I would like to implement a search where images can be filtered by colors. My image-model contains up to 10 UIColors that occur in that specific image, now I would like to have a filter with e.g. blue, green, red, yellow. How can I check (with a specified tolerance) if that specific image contains blue/green/...?

I tried with CIE94-difference, but that doesn't not match the perceived similarity by the human eye. I also tried to compare the hue and saturation value, but that doesn't work either.

As an example:

`#23567E` should be blue
`#7A010B` should be red as well as `#FD4E57`
`#0F8801` should be found for green as well as `#85FE97`

I have a specific instance of UIColor, e.g.

[UIColor colorWithRed:0.137 green:0.337 blue:0.494 alpha:1] // #23567E

this should be "equal" to .blue

[UIColor colorWithRed:0.478 green:0.00392 blue:0.0431 alpha:1] // #7A010B

should be "equal" to .red

and so on...

swalkner
  • 16,679
  • 31
  • 123
  • 210
  • Are you asking how to define what color a specific instance of UIColor is or how to get color information from an image? – Joakim Danielson Feb 03 '20 at 21:35
  • @JoakimDanielson please see my edit – swalkner Feb 03 '20 at 21:45
  • 1
    I am not sure it is possible to help you since you need to decide what a color is yourself and besides that I am not sure this is a programming question. But maybe [a page like this](https://www.rapidtables.com/web/color/RGB_Color.html]) can help you visualize the problem. I would use the second chart to come up with some definitions of what combinations of R, G and B defines a color – Joakim Danielson Feb 03 '20 at 22:01
  • It looks like the link in my previous comment is wrong, this is what I mean https://www.rapidtables.com/web/color/RGB_Color.html – Joakim Danielson Feb 04 '20 at 09:02
  • Do you just want to test which color component is the highest? – Sulthan Feb 07 '20 at 08:35
  • As others have mentioned, this isn't a programming question, so you ight have better luck asking in https://dsp.stackexchange.com/. Anyways, I think you were on the right track by using CIE94. Basically, convert your RGB color to the LAB colorspace, then experiment with different thresholds for the Euclidean distance between the color searched for and the colors found in your image. By the way, i think this is a similar problem in Matlab: https://www.mathworks.com/matlabcentral/answers/74168-color-matching-and-comparison – Pochi Feb 14 '20 at 07:09

3 Answers3

8

If your intend is only to check which color is your UIColor you can simply get your HSB color components and compare its hue value:

You will need a helper to convert your hexa string to UIColor

extension UIColor {
    convenience init?(hexString: String) {
        var chars = Array(hexString.hasPrefix("#") ? hexString.dropFirst() : hexString[...])
        switch chars.count {
        case 3: chars = chars.flatMap { [$0, $0] }; fallthrough
        case 6: chars = ["F","F"] + chars
        case 8: break
        default: return nil
        }
        self.init(red: .init(strtoul(String(chars[2...3]), nil, 16)) / 255,
                green: .init(strtoul(String(chars[4...5]), nil, 16)) / 255,
                 blue: .init(strtoul(String(chars[6...7]), nil, 16)) / 255,
                alpha: .init(strtoul(String(chars[0...1]), nil, 16)) / 255)
    }
}

And some helpers to extract its hue component:

extension UIColor {
    enum Color {
        case red, orange, yellow, yellowGreen, green, greenCyan, cyan, cyanBlue, blue, blueMagenta, magenta, magentaRed
    }
    func color(tolerance: Int = 15) -> Color? {
        precondition(0...15 ~= tolerance)
        guard let hsb = hsb else { return nil }
        if hsb.saturation == 0 { return nil }
        if hsb.brightness == 0 { return nil }
        let hue = Int(hsb.hue * 360)
        switch hue {
        case 0 ... tolerance: return .red
        case 30 - tolerance ... 30 + tolerance: return .orange
        case 60 - tolerance ... 60 + tolerance: return .yellow
        case 90 - tolerance ... 90 + tolerance: return .yellowGreen
        case 120 - tolerance ... 120 + tolerance: return .green
        case 150 - tolerance ... 150 + tolerance: return .greenCyan
        case 180 - tolerance ... 180 + tolerance: return .cyan
        case 210 - tolerance ... 210 + tolerance: return .cyanBlue
        case 240 - tolerance ... 240 + tolerance: return .blue
        case 270 - tolerance ... 270 + tolerance: return .blueMagenta
        case 300 - tolerance ... 300 + tolerance: return .magenta
        case 330 - tolerance ... 330 + tolerance: return .magentaRed
        case 360 - tolerance ... 360 : return .red
        default: break
        }
        return nil
    }
    var hsb: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat)? {
        var hue: CGFloat = 0, saturation: CGFloat = 0, brightness: CGFloat = 0, alpha: CGFloat = 0
        guard getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) else {
            return nil
        }
        return (hue, saturation, brightness, alpha)
    }
}

Playground testing:

let blue: UIColor.Color? = UIColor(hexString: "#23567E").color()    // r 0.137 g 0.337 b 0.494 a 1.0
let red1: UIColor.Color? = UIColor(hexString: "#7A010B").color()    // r 0.478 g 0.004 b 0.043 a 1.0
let red2: UIColor.Color? = UIColor(hexString: "#FD4E57").color()    // r 0.992 g 0.306 b 0.341 a 1.0
let green1: UIColor.Color? = UIColor(hexString: "#0F8801").color()  // r 0.059 g 0.533 b 0.004 a 1.0
let green2: UIColor.Color? = UIColor(hexString: "#85FE97").color()  // t 0.522 g 0.996 b 0.592 a 1.0

UIColor(hue: 90/360, saturation: 0, brightness: 1, alpha: 1).color()  // nil
UIColor(hue: 90/360, saturation: 1, brightness: 0, alpha: 1).color()  // nil
UIColor(hue: 90/360, saturation: 0, brightness: 0, alpha: 1).color()  // nil
UIColor.black.color()       // nil
UIColor.white.color()       // nil
UIColor.lightGray.color()   // nil
UIColor.darkGray.color()    // nil

UIColor.red.color()     // 0...15 && 346...360 = red
UIColor.orange.color()  // 16...45 = orange
UIColor.yellow.color()  // 46...75 = yellow
UIColor(hue: 90/360, saturation: 1, brightness: 1, alpha: 1).color()   // 76...105 yellowGreen
UIColor.green.color()   // 106...135 = green
UIColor(hue: 150/360, saturation: 1, brightness: 1, alpha: 1).color()  // 136...165 greenCyan
UIColor.cyan.color()    // 166...195 = cyan
UIColor(hue: 210/360, saturation: 1, brightness: 1, alpha: 1).color()  // 196...225 cyanBlue
UIColor.blue.color()    // 226...255 = blue
UIColor(hue: 270/360, saturation: 1, brightness: 1, alpha: 1).color() // 256...285 blueMagenta
UIColor.magenta.color()  // 286...315 = magenta
UIColor(hue: 330/360, saturation: 1, brightness: 1, alpha: 1).color()  // 316...345 = magentaRed
 

        
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
1

UIColor's CGColor has a components property that returns an array of red, green, blue, alpha values as [CGFloat]?

It works as per your requirement with RGB-based colors. Works with other colors too, but will return different set of values instead of r, g, b, a values

Tested with the colors you've provided:

let blueShade = UIColor.init(hexString: "23567E")
print(blueShade.cgColor.components)
//prints Optional([0.13725490196078433, 0.33725490196078434, 0.49411764705882355, 1.0])

let redShade = UIColor.init(hexString: "7A010B")
print(redShade.cgColor.components)
//prints Optional([0.47843137254901963, 0.00392156862745098, 0.043137254901960784, 1.0])

let greenShade = UIColor.init(hexString: "0F8801")
print(greenShade.cgColor.components)
//prints Optional([0.058823529411764705, 0.5333333333333333, 0.00392156862745098, 1.0])

Is this what you're looking for?

Lokesh SN
  • 1,583
  • 7
  • 23
0

You can get RGB values from UIColor instance with built-in method:

let color = UIColor(red: 0.3, green: 0.8, blue: 0.4, alpha: 0.9)
var red = CGFloat.zero
var green = CGFloat.zero
var blue = CGFloat.zero
var alpha = CGFloat.zero
color.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
print("r: \(red), g: \(green), b: \(blue)")
wzso
  • 3,482
  • 5
  • 27
  • 48