水曜日, 9月 14, 2011

devquiz 2011 Go!の解答

devquizのGo!の問題で提出した解答。
問題文は以下の通り。
Go 言語で、PNG 画像を入力として受け取り、その画像が何色使っているかを返す関数

func CountColor(png io.Reader) int
を実装してください。PNG 画像は io.Reader 型で与えられます。
なお、入力の画像は R G B の各色の値が 0 から 255 までの 256 段階のいずれかであり、不透明(アルファチャンネルの値が常に 255)であることが保証されています。

答えとして送ったもの(ただし変数名は多少整理した)。素直にスキャンしているだけです。
package main

import (
 "fmt"
 "io"
 "strings"
 "image"
 "image/png"
)

func CountColor(r io.Reader) int {
 colormap := map[uint32]int{}
 image, _ := png.Decode(r)
 bounds := image.Bounds()
 w, h := bounds.Max.X, bounds.Max.Y
 var colorNum uint32 = 0
 for i := 0; i < w; i++ {
  for j := 0; j < h; j++ {
   colorNum = colorCheck(image.At(i, j))
   colormap[colorNum] = colormap[colorNum] + 1
  }
 }
 return len(colormap)
}

func colorCheck(color image.Color) uint32 {
 r, g, b, _ := color.RGBA()
 var num uint32
 num = uint32(uint8(r))
 num = num << 8
 num = num + uint32(uint8(g))
 num = num << 8
 num = num + uint32(uint8(b))
 return num
}

/* これらの関数は提出時に自動挿入されます。 */
func main() {
 png := GetPngBinary()
 cnt := CountColor(png)
 fmt.Println(cnt)
}

func GetPngBinary() io.Reader {
 // img_strの中身は提出するたびに変化します。
 // 省略
}

以下は、後から考えたgoroutine使用版。

package main

import (
 "fmt"
 "io"
 "strings"
 "image"
 "image/png"
)

func CountColor(r io.Reader) int {
 colormap := map[uint32]int{}
 img, _ := png.Decode(r)
 bounds := img.Bounds()
 w, h := bounds.Max.X, bounds.Max.Y
 result := make(chan uint32)
 go colorCheck(img, w, h, result)
 for i := 0; i < w*h; i++ {
  colormap[<-result]++
 }
 return len(colormap)
}

func colorCheck(img image.Image, w, h int, result chan uint32) {
 var num uint32
 for x := 0; x < w; x++ {
  for y := 0; y < h; y++ {
   r, g, b, _ := img.At(x, y).RGBA()
   num = uint32(uint8(r))
   num = num << 8
   num = num + uint32(uint8(g))
   num = num << 8
   num = num + uint32(uint8(b))
   result <- num
  }
 }
}

/* これらの関数は提出時に自動挿入されます。 */
func main() {
 png := GetPngBinary()
 cnt := CountColor(png)
 fmt.Println(cnt)
}

func GetPngBinary() io.Reader {
 // img_strの中身は提出するたびに変化します。
 // 省略
}
goっぽさという点ではgoroutine版のが良いのだろうけど、いきなりこれを書くのは今の私には無理でした。

改めて見ると、後者ではImageをスキャンしている部分(colorCheck)と、色数を数える部分(CountColor)が分離出来ているので、すっきりしているかも。