画像をドット絵化するWebアプリを作ってHerokuでデプロイしてみた
目次
はじめに
Go言語とHerokuに関して勉強してみたかった.
完成したもの.png形式しか対応していないので注意!
radiant-plains-19109.herokuapp.com
Herokuとは
いわゆるPaaS.Linuxに詳しくなくてもWebアプリを簡単にデプロイできるサービス.クレジットカードの登録を行わずにアプリケーションを作成できるので安心して無料利用ができる.
Webアプリ作成手法
サーバサイドをGo言語で実装して,フロントはHTML/CSSのみで作る.
フロント
Go言語で読み込むようのHTMLテンプレートを作成する.
Form
- 画像
- ピクセル数 縦×横
- 色数
である.これらをPOSTできるようなページを作る.
レスポンスページ
ドット絵化した画像を返すページ.Base64でエンコードした画像をHTMLに埋め込めば画像を表示することができる.エンコードした文字列が入る部分を{{.}}
とすることでサーバサイドで埋め込むことができる.
<img src="data:image/png;base64,{{.}}">
ドット絵化 - サーバサイド
リクエストから値を受け取る
リクエストしたときに実行されるHandler
関数の中で入力された値を読み取る.
height_str := r.FormValue("height") width_str := r.FormValue("width") nbits_str := r.FormValue("nbits") file, _, err := r.FormFile("image")
読み取った値はそれぞれstring
なのでint
に変換する.
// string -> int height, _ := strconv.Atoi(height_str) width, _ := strconv.Atoi(width_str) nbits, _ := strconv.Atoi(strings.Split(nbits_str, " ")[0])
画像データの処理
画像の読み込みはr.FormFile
で読み取ったものをimage
ライブラリを用いてデコード可能.
img, _, err := image.Decode(file)
さらに,form
から読み取った縦と横の指定ピクセル数にリサイズ.
img_resized := resize.Resize(width, height, img, resize.Lanczos3)
k-meansによる色数削減
リサイズされた画像の各ピクセルについてk-means
を行う.kmeans
関数の実装.
クラスタ中心を 2n 個用意して更新していく.
各ピクセルとクラスタ中心の色の距離を測る関数.まるでC言語で書いているみたいだ.ライブラリのAPIをしっかり読んでないだけで,便利な関数はあるかもしれない.
color_distance := func(color1 color.RGBA, color2 color.RGBA) float64 { r1, g1, b1, a1 := color1.R, color1.G, color1.B, color1.A r2, g2, b2, a2 := color2.R, color2.G, color2.B, color2.A r1f := float64(r1) g1f := float64(g1) b1f := float64(b1) a1f := float64(a1) r2f := float64(r2) g2f := float64(g2) b2f := float64(b2) a2f := float64(a2) distance := math.Sqrt(math.Pow(r2f-r1f, 2) + math.Pow(g2f-g1f, 2) + math.Pow(b2f-b1f, 2) + math.Pow(a2f-a1f, 2)) return distance }
各ピクセルのクラスタ中心との距離を計算してどのクラスタに属するかを計算.クラスタ中心の更新を繰り返す.
kmeansして得られた画像img
をBase64でエンコードして文字列にする.このとき,画像を保存せずにメモリ上で操作している.
func encodeBase64(img *image.RGBA) string { buf := new(bytes.Buffer) bio := bufio.NewWriter(buf) err := png.Encode(bio, img) if err != nil { log.Fatal(err) } bio.Flush() return base64.StdEncoding.EncodeToString(buf.Bytes()) }
- 画像用のバッファ
buf
の用意 buf
に書き込む用のbio
png.Encode
でbio
にimg
を書き込む.bio.Flush()
でbuf
へ書き込まれる.buf
のバイト配列をBase64エンコードして文字列にする
レスポンス 画像をHTMLに埋め込む
tmpl := template.Must(template.ParseFiles("image.html")) // 画像base64 if err := tmpl.ExecuteTemplate(w, "image.html", imgStr); err != nil { w.WriteHeader(http.StatusInternalServerError) log.Fatal(err) return }
画像表示用のhtmlテンプレートimage.html
の{{.}}
にBase64エンコードした文字列imgStr
を埋め込む.
これで完了.
Herokuでデプロイ
HerokuにGo言語で書いたソースコードをデプロイするにはgo.mod
というライブラリの情報やgo
のバージョン,モジュール名が記述されたファイルを用意するだけでよい.
コマンドラインでデプロイする
Herokuに登録した後にCLIツールのインストールをする.wslにインストールするにはこの方法
https://devcenter.heroku.com/articles/heroku-cli#standalone-installation
$ curl https://cli-assets.heroku.com/install.sh | sh
まずはherokuにログインする
$ heroku login
keyを押すとherokuのログインページがブラウザに開かれるのでログインする.
今回作成したファイルたちをgit管理し,そのフォルダで以下に続くコマンドを使用することでデプロイ可能.
まず,git remoteにherokuを登録する.
$ heroku create
git remoteにherokuが登録されていることを確認できる.
$ git remote -v
確認できたらソースコードをpushする
$ git push heroku master
pushと同時にデプロイされるので,webページを開いて確認.
$ heroku open
遊んでみた
フロントのページをスクショしたもので実際に実行してみた