書店でInterface 5月号を見つけて, 画像処理が懐かしくなったので.
(つい買ってしまった.他にも車載用OSとか面白いしね.)
golangにもimageパッケージという2D画像用ライブラリが標準で備わってるようなのでちょっと使ってみる.
1. 概要
golangの他の標準パッケージと同様, このパッケージも最低限の機能しか用意されていないみたいです.
The Go Programming Language - Package image
サブディレクトリ含めてもそんなにない
ざっと見た感じだと, 以下くらいの機能なのかな?
- アルファ値
- 色空間や濃淡(グレースケール)
- 点
- 矩形
- カラーパレット(pallete)
- 上記の組み合わせ(draw)
- 画像フォーマットのデコード処理(gif/png/jpeg)
Uniformって何.まっさらなとこからやるときに使うのか?
まぁいいや.
あと, 現時点だと扱える画像フォーマットは
だそうです.
2. 何か書いてみる
とにかく使ってみましょう.
簡単なグラフィックから.
2.1. 箱を描く
簡単そうなのから.
NewRGBAでRGB形式の画像を作成できます.
package main
import (
"os"
"image"
"image/jpeg"
)
func main() {
x := 0
y := 0
width := 100
height := 50
img := image.NewRGBA(image.Rect(x, y, width, height))
file, _ := os.Create("sample.jpg")
defer file.Close()
if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
panic(err)
}
}
[実行結果]
2.2. 箱を描く(色指定)
一歩前へ.
Setでピクセル単位に色をセットしていくだけです.
画像サイズはimg.Rectの中にあるX, Yで取得できます.
package main
import (
"os"
"image"
"image/jpeg"
"image/color"
)
func fillRect(img *image.RGBA, col color.Color) {
rect := img.Rect
for h := rect.Min.Y; h < rect.Max.Y; h++ {
for v := rect.Min.X; v < rect.Max.X; v++ {
img.Set(v, h, col)
}
}
}
func main() {
x := 0
y := 0
width := 100
height := 50
img := image.NewRGBA(image.Rect(x, y, width, height))
fillRect(img, color.RGBA{255, 0, 0, 0})
file, _ := os.Create("sample.jpg")
defer file.Close()
if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
panic(err)
}
}
[実行結果]
2.3. 箱を描く(枠だけ)
せっかくなので一捻り入れてみました.
package main
import (
"os"
"image"
"image/jpeg"
"image/color"
)
func fillRect(img *image.RGBA, col color.Color) {
rect := img.Rect
for h := rect.Min.Y; h < rect.Max.Y; h++ {
for v := rect.Min.X; v < rect.Max.X; v++ {
img.Set(v, h, col)
}
}
}
func drawBounds(img *image.RGBA, col color.Color) {
rect := img.Rect
for h := 0; h < rect.Max.X; h++ {
img.Set(h, 0, col)
img.Set(h, rect.Max.Y-1, col)
}
for v := 0; v < rect.Max.Y; v++ {
img.Set(0, v, col)
img.Set(rect.Max.X-1, v, col)
}
}
func main() {
x := 0
y := 0
width := 100
height := 50
img := image.NewRGBA(image.Rect(x, y, width, height))
fillRect(img, color.RGBA{255, 255, 255, 0})
drawBounds(img, color.RGBA{255, 0, 0, 0})
file, _ := os.Create("sample.jpg")
defer file.Close()
if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
panic(err)
}
}
[実行結果]
2.4. 円を描く
package main
import (
"image"
"image/color"
"image/jpeg"
"math"
"os"
)
func fillRect(img *image.RGBA, col color.Color) {
rect := img.Rect
for h := rect.Min.Y; h < rect.Max.Y; h++ {
for v := rect.Min.X; v < rect.Max.X; v++ {
img.Set(v, h, col)
}
}
}
type Circle struct {
p image.Point
r int
}
func (c *Circle) drawBounds(img *image.RGBA, col color.Color) {
for rad := 0.0; rad < 2.0*float64(c.r); rad += 0.1 {
x := int(float64(c.p.X) + float64(c.r)*math.Cos(rad))
y := int(float64(c.p.Y) + float64(c.r)*math.Sin(rad))
img.Set(x, y, col)
}
}
func main() {
x := 0
y := 0
width := 500
height := 500
img := image.NewRGBA(image.Rect(x, y, width, height))
fillRect(img, color.RGBA{255, 255, 255, 0})
center := image.Point{250, 250}
circle := Circle{center, 50}
circle.drawBounds(img, color.RGBA{255, 0, 0, 0})
file, _ := os.Create("sample.jpg")
defer file.Close()
if err := jpeg.Encode(file, img, &jpeg.Options{100}); err != nil {
panic(err)
}
}
[実行結果]
3. ファイルから画像を読み込み加工する
グラフィックも面白いですが, 画像加工がしてみたいので色々やってみましょう.
元画像はやはりレナさんで.
3.1. 画像を読み込み, サイズを調べる
package main
import (
"image"
_ "image/png"
"os"
"fmt"
)
func main() {
file, _ := os.Open("./rena.png")
defer file.Close()
config, formatName, err := image.DecodeConfig(file)
if err != nil {
panic(err)
}
fmt.Println(formatName)
fmt.Println(config.Width)
fmt.Println(config.Height)
}
[実行結果]
$ ./image.exe
png
512
512
3.2. 画像を合成する
https://golang.org/doc/gopher/に自由に使えるgopher君の画像があるので合成してみます.
package main
import (
"image"
"image/png"
"os"
"image/draw"
)
func main() {
src, _ := os.Open("./run.png")
defer src.Close()
dst, _ := os.Open("./rena.png")
defer dst.Close()
srcImg, _, err := image.Decode(src)
if err != nil {
panic(err)
}
dstImg, _, err := image.Decode(dst)
if err != nil {
panic(err)
}
outRect := image.Rectangle{image.Pt(0, 0), dstImg.Bounds().Size()}
out := image.NewRGBA(outRect)
dstRect := image.Rectangle{image.Pt(0, 0), dstImg.Bounds().Size()}
draw.Draw(out, dstRect, dstImg, image.Pt(0, 0), draw.Src)
srcRect := image.Rectangle{image.Pt(0, 0), srcImg.Bounds().Size()}
draw.Draw(out, srcRect, srcImg, image.Pt(0, 0), draw.Over)
outfile, _ := os.Create("out.png")
defer outfile.Close()
png.Encode(outfile, out)
}
[実行結果]
なんかえらくシュールな画像になってしまった...
4. もっと画像処理する
せっかくなのでもう少し色々やります.
4.1. グレースケール化する
標準パッケージに用意されています.
package main
import (
"image"
"image/png"
"image/color"
"os"
)
func main() {
src, _ := os.Open("./rena.png")
defer src.Close()
srcImg, _, err := image.Decode(src)
if err != nil {
panic(err)
}
srcBounds := srcImg.Bounds()
dest := image.NewGray(srcBounds)
for v := srcBounds.Min.Y; v < srcBounds.Max.Y; v++ {
for h := srcBounds.Min.X; h < srcBounds.Max.X; h++ {
c := color.GrayModel.Convert(srcImg.At(h, v))
gray, _ := c.(color.Gray)
dest.Set(h, v, gray)
}
}
outfile, _ := os.Create("out.png")
defer outfile.Close()
png.Encode(outfile, dest)
}
[実行結果]
4.2. 2値化する
ちょっと強引かな.
ColorModelとConverterを自作するのが正統派なんですかね.
いずれにしても関数化ぐらいしてもよかったかも.
package main
import (
"image"
"image/png"
"image/color"
"os"
)
const threshold = 128
func main() {
src, _ := os.Open("./rena.png")
defer src.Close()
srcImg, _, err := image.Decode(src)
if err != nil {
panic(err)
}
srcBounds := srcImg.Bounds()
dest := image.NewGray(srcBounds)
for v := srcBounds.Min.Y; v < srcBounds.Max.Y; v++ {
for h := srcBounds.Min.X; h < srcBounds.Max.X; h++ {
c := color.GrayModel.Convert(srcImg.At(h, v))
gray, _ := c.(color.Gray)
if gray.Y > threshold {
gray.Y = 255
} else {
gray.Y = 0
}
dest.Set(h, v, gray)
}
}
outfile, _ := os.Create("out.png")
defer outfile.Close()
png.Encode(outfile, dest)
}
[実行結果]
まとめ
もうちょっと色々遊ぼうかと思ってましたが, この辺にしときます.
ベースは標準ライブラリで提供されているので, 画像処理アルゴリズムを試したりするのにはちょうどいい気がします.
本格的にやる際には自作でライブラリを用意するほうがいいかもしれないです.
(そしてそれが面白かったりしますよね)
参考
package - Image
package - Image/draw
package - Image/png
package - Image/jpeg
package - Image/color
The Go Blog - The Go image/draw package
とある子育てパパの日記 - 2つの画像を重ねる/golang