Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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
  static let loadrgb =
    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 reader = new StreamReader(stream)
    let txt = reader.ReadToEnd()
    txt

// parse the text of the rgb.txt color table into a Name: Values: list
  static let colorinfo =
      loadrgb.Split([|'\n'|])
      |> 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

//    Add new colors to the head of the ColorTable
  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
share|improve this question
    
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

2 Answers 2

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
share|improve this answer

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
  static let loadrgb =
    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 reader = new StreamReader(stream)
    let txt = reader.ReadToEnd()
    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 =
      loadrgb
      |> 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

//    Add new colors to the head of the ColorTable
  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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.