# RGB Color Name Matching using Euclidean Distance (Improved)

The code below stems from work on a Euclidean Distance algorithm. The color table was simply a vehicle to test the algorithm. It is perhaps reinventing the wheel, however it is useful in itself. Any 3 RGB integers (0-255) can be associated with the nearest X11 color name. Many thanks to svick for his insights.

I'm having one last problem in initializing the color table from the online version of the X11 rgb.txt file. I need to parse the text into a {Name: Values:} list. Currently, the results are in a tuple of strings. I'm working to have "colorinfo" load the "ColorTable".

// currently the color table is create via the AddColor method, however
// the initial values should be created with the loadrgb and colorinfo members
type MyFSColorTable() =

// pull the X11 rgb.txt color table off the web in text format
let url = "http://people.csail.mit.edu/jaffer/Color/rgb.txt"
let req = WebRequest.Create(url)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
txt

// parse the text of the rgb.txt color table into a Name: Values: list
static let colorinfo =
|> Seq.skip 1
|> Seq.map (fun line -> line.Split([|'\t'|]))
|> Seq.filter (fun values -> values |> Seq.length = 3)
|> Seq.map (fun values -> string values.[0], string values.[2])
|> Seq.map (fun (rgb, name) -> rgb.Split([|' '|]), name)
|> Seq.map (fun (rgb, name) -> [|name, rgb.[0], rgb.[1], rgb.[2]|])

// Mutable Color Table will be defined on-the-fly
let mutable ColorTable = []
// Euclidean distance between 2 vectors - float is overkill here
static let Dist (V1: float[]) V2 =
Array.zip V1 V2
|> Array.map (fun (v1, v2) -> pown (v1 - v2) 2)
|> Array.sum

member x.AddColor name rgb = ColorTable <- {Name = name; Values = rgb}::ColorTable

//    Find nearest color by calculating euclidean distance of all colors,
//    then calling List.minBy for the smallest
member x.FindNearestColor (rgb : float[]) =
let nearestColor =
ColorTable |> List.minBy (fun color -> Dist rgb color.Values)
nearestColor.Name

-
Wouldn't it make more sense to ask for review after you implement the code that uses colorinfo? –  svick Feb 6 '13 at 14:00
svick, when I say I'm "working" to have "colorinfo" load the "ColorTable", I should reword it to "my attempts have not worked". I can't get the correct syntax for building the ColorTable. Perhaps I should post this on StackOverFlow, although much of the code is functioning properly. –  dgp Feb 6 '13 at 15:05

This would do it:

loadrgb.Split('\n')
|> Seq.skip 1
|> Seq.choose (fun line ->
match line.Split('\t') with
| [|rgb; _; name|] ->
let values = rgb.Split(' ') |> Array.map float
Some({Name=name; Values=values})
| _ -> None)
|> Seq.toList

-

This new version works just fine with the online initialization of the colors. Perhaps I should replace the original version in the post, however I didn't want to lose the context.

// Learn more about F# at http://fsharp.net
#light

namespace ColorLibrary

open System
open System.IO
open System.Collections.Generic
open System.Collections
open Microsoft.FSharp.Math

open System.Net

module FSColorLibrary =

// Color Type
type MyColor =
{ Name: string;
Values: float[];
}

// currently the color table is create via the AddColor method, however
// the initial values should be created with the loadrgb and colorinfo members
type MyFSColorTable() =

// pull the X11 rgb.txt color table off the web in text format
let url = "http://people.csail.mit.edu/jaffer/Color/rgb.txt"
let req = WebRequest.Create(url)
let resp = req.GetResponse()
let stream = resp.GetResponseStream()
let arr =
txt.Split([|'\n'|])
|> Seq.skip 1
|> Seq.map (fun line -> line.Trim())
// remaining problem, need to compress whitespaces efficiently
|> Seq.map (fun line -> line.Replace("  "," "))
|> Seq.map (fun line -> line.Replace("  "," "))
|> Seq.map (fun line -> line.Replace("  "," "))
arr

// parse the text of the rgb.txt color table into a Name: Values: list
static let colorinfo =
|> Seq.map (fun line -> line.Split([|'\t'|]))
|> Seq.filter (fun values -> values |> Seq.length = 3)
|> Seq.map (fun values -> string values.[0], string values.[2])
|> Seq.map (fun (rgb, name) -> rgb.Split([|' '|]), name)
|> Seq.map (fun (rgb, name) -> { Name = name; Values = [| float rgb.[0]; float rgb.[1]; float rgb.[2] |] })

// Mutable Color Table will be defined on-the-fly
let mutable ColorTable = []
// Euclidean distance between 2 vectors - float is overkill here
static let Dist (V1: float[]) V2 =
Array.zip V1 V2
|> Array.map (fun (v1, v2) -> pown (v1 - v2) 2)
//          |> Array.map(fun (v1, v2) -> (v1 - v2) * (v1 - v2))
|> Array.sum

member x.AddColor name rgb = ColorTable <- {Name = name; Values = rgb}::ColorTable

//    Find nearest color by calculating euclidean distance of all colors,
//    then calling List.minBy for the smallest
member x.FindNearestColor (rgb : float[]) =
let nearestColor =
ColorTable |> List.minBy (fun color -> Dist rgb color.Values)
nearestColor.Name

// This new method uses the colorinfo structure created from the online rgb.txt file,
// instead of building a ColorTable on the fly.
member x.FindNearestColorII (rgb : float[]) =
let nearestColor =
colorinfo |> Seq.minBy (fun color -> Dist rgb color.Values)
nearestColor.Name

-