画像をドット絵化するWebアプリを作ってHerokuでデプロイしてみた

目次

はじめに

Go言語とHerokuに関して勉強してみたかった.

完成したもの.png形式しか対応していないので注意!

radiant-plains-19109.herokuapp.com

ソースコード

github.com

Herokuとは

いわゆるPaaSLinuxに詳しくなくてもWebアプリを簡単にデプロイできるサービス.クレジットカードの登録を行わずにアプリケーションを作成できるので安心して無料利用ができる.

Webアプリ作成手法

サーバサイドをGo言語で実装して,フロントはHTML/CSSのみで作る.

フロント

Go言語で読み込むようのHTMLテンプレートを作成する.

Form

である.これらをPOSTできるようなページを作る.

f:id:soyukke:20190615182705p:plain
フロントページ

レスポンスページ

ドット絵化した画像を返すページ.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して得られた画像imgBase64エンコードして文字列にする.このとき,画像を保存せずにメモリ上で操作している.

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())
}
  1. 画像用のバッファbufの用意
  2. bufに書き込む用のbio
  3. png.Encodebioimgを書き込む.
  4. bio.Flush()bufへ書き込まれる.
  5. 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

遊んでみた

フロントのページをスクショしたもので実際に実行してみた

f:id:soyukke:20190615182817p:plain:w300
ドット絵化(?)した画像

おわり