Small Changes

たった1日で基本が身に付く! Go言語 超入門@8日目


Metadata


今日も最終章の続きをやっていきます。

CHAPTER 8 Go言語の魅力を体験するプログラム

画像を処理する

Go言語には、画像を読み込んで分析したり、画像を作成して書き出すライブラリなどもある。

画像の読み込み

AI技術を活用した画像分析が盛ん。Go言語でもライブラリが用意されている。

簡単な画像分析プログラムとして、グレースケールの画像を読み込み、アスキーアートを作成する。

①ファイルからPNG画像を読み込む

②PNGフォーマットをデコードして情報を取り出す

③画素1つ1つについて、グレー情報を数値(0なら黒、255なら白)で取得する

④グレー情報の数値を3段階に分けて、各画素を文字「*」「+」「-」のいずれかに変換する

package main

import (
	"fmt"
	"image"
	"image/color"
	_ "image/png"
	"io/ioutil"
	"os"
)

func main() {
	reader, ferr := os.Open("wow.png")
	if ferr != nil {
		fmt.Println("ファイルの読み込みエラーです", ferr)
		os.Exit(1)
	}

	img, name, derr := image.Decode(reader)
	if derr != nil {
		fmt.Println("画像の変換エラーです", derr)
		os.Exit(1)
	} else {
		fmt.Println(name, "形式のデータを得ました")
	}

	defer reader.Close()

	marks := []string{"*", "+", "-"}
	var marksStr string

	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
			c := color.GrayModel.Convert(img.At(x, y)).(color.Gray)
			grayness := c.Y / (255 / 3)
			if c.Y == 255 {
				grayness = 2
			}
			marksStr += marks[grayness]
		}
		marksStr += "\n"
	}
	wdata := []byte(marksStr)
	werr := ioutil.WriteFile("ascii_art.txt", wdata, 0777)
	if werr != nil {
		fmt.Println("ファイルの書き込みエラーです", werr)
		os.Exit(1)
	} else {
		fmt.Println("ファイルを保存しました")
	}
}

importのアンダースコア

_ "image/png"

画像ファイルの読み込みには、os.Open関数を使う

  • 一つ目の戻り値はファイルの内容で、io.Readerという型をもつ

reader, ferr := os.Open("wow.png")

次にファイルのデコードには、image.Decodeを使う

  • 一つ目の戻り値はデコードした画像で、image.imageという型をもつ

  • 二つ目の戻り値はファイルの形式を示す文字列(ここではpng)

img, name, derr := image.Decode(reader)

reader.Close()でファイルを閉じるだが、その前にdeferがついている。

deferは遅延関数

deferを呼び出した関数が終了する際に実行される

defer reader.Close() // 関数の最後にファイルを閉じる

メインのロジックは説明が少なくいまは難しい

	for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ {
		for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ {
			c := color.GrayModel.Convert(img.At(x, y)).(color.Gray)
			grayness := c.Y / (255 / 3)
			if c.Y == 255 {
				grayness = 2
			}
			marksStr += marks[grayness]
		}
		marksStr += "\n"

作成されたアスキーアート!なんか色々できそうですごい!

画像の作成と書き込み

package main

import (
	"fmt"
	"image"
	"image/color"
	"image/png"
	"os"
)

func inrange(x, a, b int) bool {
	if a < b && a <= x && x < b {
		return true
	}
	return false
}

func main() {

	const width = 350
	const height = 350
	const celllen = 50

	img := image.NewNRGBA(image.Rect(0, 0, width, height))

	cells := [7][7]int{
		{0, 1, 0, 1, 0, 1, 0},
		{0, 1, 1, 1, 1, 1, 0},
		{0, 1, 1, 1, 1, 1, 0},
		{0, 0, 1, 1, 1, 0, 0},
		{0, 0, 0, 2, 0, 0, 0},
		{0, 2, 0, 2, 0, 2, 0},
		{0, 0, 2, 2, 2, 0, 0},
	}
	colors := [3]color.RGBA{
		color.RGBA{0xff, 0xff, 0xff, 0xff},
		color.RGBA{0xff, 0, 0, 0xff},
		color.RGBA{0, 0xff, 0, 0xff},
	}

	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			for ny := 0; ny < 7; ny++ {
				for nx := 0; nx < 7; nx++ {
					if inrange(x, nx * celllen, (nx + 1) * celllen) &&
					  inrange(y, ny * celllen, (ny + 1) * celllen) {
							img.Set(x, y, colors[cells[ny][nx]])
						}
				}
			}
		}
	}

	f, ferr := os.Create("flower.png")
	if ferr != nil {
		fmt.Println("ファイル作成のエラー:", ferr)
		os.Exit(1)
	}

	derr := png.Encode(f, img)

	if derr != nil {
		f.Close()
		fmt.Println("データ変換のエラー:", derr)
		os.Exit(1)
	}

	ferr = f.Close()
	if ferr != nil {
		fmt.Println("ファイル作成のエラー:", ferr)
		os.Exit(1)
	}
}

これもコード的な解説が少ないので、調べながら。

image.Rect(0, 0, width, height)で350x350の画像作成

img := image.NewNRGBA(image.Rect(0, 0, width, height)

ここで書き込んでいるらしい

derr := png.Encode(f, img)

書き出される画像

今日の学び

画像をライブラリで扱えるのはすごい!ただ、やっていることが高度すぎて今の時点で理解するのは難しそう。。。

あと、ライブラリを使うと一気にやっていることがブラックボックスに包まれるのでもっと数をこなしてライブラリのコードとかリファレンスを追えるようになりたい。

References