0

下面的代码源于欧几里德距离算法的工作。颜色表只是测试算法的工具。它可能是在重新发明轮子,但它本身很有用。任何 3 个 RGB 整数 (0-255) 都可以与最接近的 X11 颜色名称相关联。非常感谢svick的见解。

在当前代码中,ColorTable 在创建实例后通过 AddColor 方法进行初始化。但是,可以使用 loadrgb/colorinfo 组合从 Web 上拉下 X11 颜色表。

我在从 X11 rgb.txt 文件的在线版本初始化颜色表时遇到了最后一个问题。我需要将文本解析为 {Name: Values:} 列表。目前,结果在字符串元组中。我正在努力让“colorinfo”加载“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
4

2 回答 2

2

目前,您的构造代码colorinfo会生成一个序列,其中包含一个数组,该数组只有一个元素,该元素是一个元组(包含四个字符串)。这意味着总体结果的类型是 - 在您当前的版本中 - seq<(string * string * string * string) []>

  (...)
  |> Seq.map (fun (rgb, name) -> 
       [|name, rgb.[0], rgb.[1], rgb.[2] |]

这可能不是有意的——如果你想创建一个包含四个字符串的数组,你需要使用分号而不是逗号[| name; rgb.[0]; ... |],如果你想创建四元素元组,那么你可以省略元组周围的[|and 。|]

无法自动将数组或元组转换为命名记录类型(假设您有一个带有Name:stringand的记录Values:float[]),因此最好的选择是在管道的最后一步进行此转换。您可以将上述内容替换为:

  |> Seq.map (fun (rgb, name) -> 
        { Name = name
          Values = [| float rgb.[0]; float rgb.[1]; float rgb.[2] |] })
  |> List.ofSeq

我还在List.ofSeq片段的末尾添加了一个列表,以便您返回一个列表 - 这与您目前使用的类型相同ColorTable

(或者,您可以说Values = Array.map float rgb,它将所有字符串转换rgb为浮点数并适用于数组的任何长度。)

于 2013-02-06T17:41:13.717 回答
0

您可以缩短代码,并{Name: string; Values: float[]} list根据需要返回,使用以下内容:

static let colorinfo =
  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
于 2013-02-06T18:21:56.333 回答