20200117のGoに関する記事は5件です。

GoCVを用いてGoで集めた画像で壁紙を作ってみました

概要

集めた画像からGo言語を用いて集めた画像から壁紙を作ってみようと思います。ライブラリとしてはGoCVを利用してみました。
以前、同じことをPythonとJuliaで行いました。

実際にコードを書いてみた私的な感想としては、PythonやJuliaで書いたほうが書きやすかったですね...( *´艸`)

実行環境

  • Windows10 Pro
  • go version go1.13.1 windows/amd64
  • OpenCV 4.1.2
  • GoCV

GoCVのインストール方法

画像を操作するためにGoCVを利用するために事前に準備する必要があります。以下の公式サイトのインストール手順に従っていけばインストールできると思います。

注意点としては上記のセットアップの途中で再起動しないと認識されない場合がありました。

ソースコード

パッケージのインポート

package main

import (
    "fmt"
    "gocv.io/x/gocv"
    "image"
    "image/jpeg"
    "os"
    "path/filepath"
    "math/rand"
    "log"
    "strconv"
    "time"
)

指定した範囲で被りなしの整数を生成するための関数

以下のリンクを参考にさせていただきました。
- min-maxの範囲からn個の整数をランダム抽出する方法(ただし重複は含まない)

func allKeys(m map[int]bool) []int {
    i := 0
    result := make([]int, len(m))
    for key, _ := range m {
        result[i] = key
        i++
    }
    return result
}

func pickup(min int, max int, num int) []int {
    numRange := max - min

    selected := make(map[int]bool)
    rand.Seed(time.Now().UnixNano())
    for counter := 0; counter < num; {
        n := rand.Intn(numRange) + min
        if selected[n] == false {
            selected[n] = true
            counter++
        }
    }
    keys := allKeys(selected)

    // sort.Sort(sort.IntSlice(keys))
    return keys
}

実際の処理

func main(){

    startT1 := time.Now()
    datapath := "../../../100.data/1.Input/photo/fankiru/*.jpg"
    dirpath := "../../../100.data/2.Output/go/photo/fankiru/"
    files, err := filepath.Glob(datapath)
    if err != nil {
        log.Fatal(err)
    }

    name := "fankiru"
    sizeHeight := 3024
    sizeWidth := 4032
    rh := 5
    rw := 9

    rowSize := int(float64(sizeHeight) * float64(rh))
    colSize := int(float64(sizeWidth) * float64(rw))
    outputImg := gocv.NewMatWithSize(rowSize, colSize, gocv.MatTypeCV8UC3)

    for count := 1; count <= 100; count++ {
        selectnums := pickup(0, len(files)-1, rh*rw)
        selectnum := 0
        for row := 0; row <= sizeHeight * rh - sizeHeight; row += sizeHeight {
            for col := 0; col <= sizeWidth * rw - sizeWidth; col += sizeWidth {
                img := gocv.IMRead(files[selectnums[selectnum]], gocv.IMReadColor)
                if img.Empty() {
                    fmt.Printf("cannot read image \n")
                    return
                }
                defer img.Close()

                rect := outputImg.Region(image.Rectangle{ 
                    image.Point{X: col, Y: row}, 
                    image.Point{X: col + sizeWidth, Y: row + sizeHeight},
                })
                img.CopyTo(&rect)
                selectnum++
            }
        }

        image, err := outputImg.ToImage()
        if err != nil {
            log.Fatal(err)
        }

        if err := os.MkdirAll(dirpath, 0777); err != nil {
            log.Fatal(err)
        }

        file, err := os.Create(dirpath + name + "_" + strconv.Itoa(count) + ".jpg")
        if err != nil {
            log.Fatal(err)
        }
        defer file.Close()

        jpeg.Encode(file, image, &jpeg.Options{ Quality:100, })
    }
    endT1 := time.Now()
    fmt.Printf("making wallpaper done %f[sec]\n", (endT1.Sub(startT1)).Seconds())
}

出力された画像

イベントで撮影してきた写真を素材にして、以下のように出力されました。生成された画像の解像度が大きすぎたので縮小した画像を載せました。
edit-001.jpg

感想

GoCVを利用すれば私のやりたかったことはできました。しかし、やはりPythonやJuliaのほうが書きやすかったですね。ただ、通常の画像処理で利用するならGoCVでいろいろできそうだなーと思いました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Goの環境構築

Go 開発環境構築

最近Goの勉強をはじめました。
このあたりは、ググったら同じような記事が出てくるので、自分の備忘録も兼ねて記事にしています。
便利なパッケージ等があったら更新していくかもしれません。
ほんの少しでも、Goを始めようと思っている方への力になれたら幸いです。

環境

  • macOS Mojave (10.14.6)
  • zsh
  • Homebrew 2.2.1
  • IntelliJ IDEA

Go のインストール

  1. Homebrew でインストール
% brew install go
  1. バージョン確認
# バージョンの確認
% go version
go version go1.13.5 darwin/amd64

# GOROOTの確認
% go env GOROOT
/usr/local/Cellar/go/1.13.5/libexec

# GOPATHの確認
% go env GOPATH
/path/to/gopath

IntelliJ の設定

  1. プラグインのインストール
    Preferences > Pluginから、JetBrains 製の、Go のプラグインをインストール

  2. GOROOT の設定
    Preferences > Language & Frameworks > GO > GOROOT で設定する。

  3. GOPATH の設定
    Preferences > Language & Frameworks > GO > GOPATH で設定する。

    go getのパッケージダウンロード先を指定する。

    デフォルトは$HOME/goが自動的に指定される。

パッケージを探す

Goのパッケージは以下のサイトなどでドキュメントが見れるので、検索すると良さそう。
https://godoc.org/

Docについて

いろいろ調べてまとめようとしたが、この記事を読んでおいたら良さそう。
とても勉強になりました。ありがとうございます🙏
チョットできるGoプログラマーになるための詳解GoDoc

最後に

Goの環境構築、思っていたよりも簡単にサクッとできました。
環境構築の際にぶつかる壁がほとんどないので、よかったら始めてみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Go×CloudWatchでファイル取得状況をカスタムメトリクス化する

今回はAWS-SDKを用いて、大量のデータを取得するEC2インスタンスの1時間当たりのファイル取得量をCloudWatchでカスタムメトリクス化して取得状況を監視するシステムを作成したので、一部をご紹介します。

AWS関連の部分以外はGoの標準パッケージのみで作るシンプルなものになっています。

Goでファイル取得数を計算する

下記のプログラムで現在のファイル取得数と経過時間を計算します。
コード自体はとてもシンプルです。

package calculator

import (
    "fmt"
    "io/ioutil"
    "math"
    "os"
    "path/filepath"
    "sort"
)

func GetFetchSpeed() (float64, error) {
    dir := `取得したいディレクトリ`
//ディレクトリからファイルを取得
    files, err := ioutil.ReadDir(filepath.Dir(dir))
    if err != nil {
        return 0, err
    }
//編集日時順にファイルをソートします。
    sort.Slice(files, func(i, j int) bool {
        return files[i].ModTime().Unix() < files[j].ModTime().Unix()
    })

    firstFile, err := os.Stat(dir + files[0].Name())
   if err != nil {
        return 0, err
    }

    lastFile, _ := os.Stat(dir + files[len(files)-1].Name())
    if err != nil {
        return 0, err
    }
//最初のファイルを取得してからの経過時間を計算
    elapsedTime := lastFile.ModTime().Sub(firstFile.ModTime())
    if elapsedTime < 0 {
        elapsedTime *= -1
    }
//1時間ごとのファイル取得数
    elapsedMinute := math.Trunc(elapsedTime.Minutes())
    fetchedPerHour := len(files) / int(elapsedMinute) * 60

    return float64(fetchedPerHour), nil
}



取得した情報をCloudWatchでメトリクス化する。

package metrics

import (
    "fmt"
    "io/ioutil"
    "net/http"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
)

func getInstanceID() string {
//このアドレスにアクセスするとEC2インスタンスのインスタンスIDを取得することができます。
    resp, err := http.Get("http://169.254.169.254/latest/meta-data/instance-id")
    if err != nil {
        fmt.Println(err)
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println(err)
    }
    return string(body)
}

//メトリクスを作成します
func CreateMetrics(value float64) {
    cw := cloudwatch.New(session.New(), &aws.Config{Region: aws.String("ap-northeast-1")})

    dimensionParam := &cloudwatch.Dimension{
        Name:  aws.String("InstanceId"),
        Value: aws.String(getInstanceID()),
    }

    metricDataParam := &cloudwatch.MetricDatum{
        Dimensions: []*cloudwatch.Dimension{dimensionParam},
        MetricName: aws.String("Files per 1Hour"),
        Unit:       aws.String("Count"),
        Value:      aws.Float64(value),
    }

    putMetricsDataInput := &cloudwatch.PutMetricDataInput{
        MetricData: []*cloudwatch.MetricDatum{metricDataParam},
//メトリクスの名前空間
        Namespace:  aws.String("EC2"),
    }
    cw.PutMetricData(putMetricsDataInput)
}


この二つのパッケージをmain.goで実行します。

func main() {
    value, err := calculator.GetFetchSpeed()
    if err != nil {
        fmt.Printf("Error while calculatoring speed:%s\n", err)
    }
    if value > 0 {
        metrics.CreateMetrics(value)
    }
}

以上になります。

このメトリクス化する仕組みを用いればいろんなものを可視化することができるので、監視や運用に役立つかと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Golang wire】なんとなく理解した備忘録

Git

https://github.com/google/wire/tree/master/_tutorial

何をしているか

struct間の依存関係を記述するのを自動で生成してくれる。

作るべきファイル

1. 依存関係を実装しているファイル(main.goとか)
2. 自動生成するにあたってヒントを与えるファイル(wire.go)

↓を実行

$ wire gen
  1. 同じディレクトリにwire_gen.goが出来上がっている。

多分、実際の処理で利用していくファイルはwire_gen.goを使って処理をしてくはず。
wire genを実行するまではmain.goのInitialEventが無いっておこられているから。
wire_gen.goが出来上がるとそのエラーはなくなる。

  1. 結果
src
 main.go
 wire.go
 wire_gen.go // wire getで生成されたファイル
main.go
package main

import (
    "fmt"
)

type Event struct {
    Greeter Greeter
}

type Greeter struct {
    Message Message
}

type Message string

func NewEvent(g Greeter) (Event) {
    return Event{Greeter: g}
}

func NewGreeter(m Message) Greeter {
    return Greeter{Message: m}
}

func NewMessage(p string) Message {
    return Message(p)
}

func (e Event) Start() {
    msg := e.Greeter.Great()
    fmt.Println(msg)
}

func (g Greeter) Great() Message {
    return g.Message
}

func main() {
    e := InitializeEvent("takutakutakujiro")

    e.Start()
}
wire.go
//+build wireinject

package test

import "github.com/google/wire"

func InitializeEvent(phrase string) Event {
    wire.Build(NewEvent, NewGreeter, NewMessage)
    return Event{}
}
wire_gen.go
// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package test

// Injectors from wire.go:

func InitializeEvent(phrase string) Event {
    message := NewMessage(phrase)
    greeter := NewGreeter(message)
    event := NewEvent(greeter)
    return event
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google App Engine に Go言語 1.13 の複数サービスを deploy する

はじめに

ちょうど3日前に Google App Engine (GAE) と Go言語を初めて触ってみました。
Go言語がバージョン 11 以降、少し勝手が違ったことで、構築時に若干迷ったので記事にしてみました。

いろんなドキュメントを見て判断はしていますが、なんせ経験が浅いので間違い等あれば指摘ください。

はじめる前に…

Go言語と GAE について私が学んだサイト等まとめておきます。

環境

OS

  • Windows 10

言語

  • go version go1.13.6 windows/amd64

どういう構成にしようか。

まずは GAE の複数サービス構築についてのドキュメントを読んでみる。

まず、私が見たのは下記サイト。

英語は自身無いのですが、要約すると、下記のような感じしょうか。
- app.yaml でアプリケーションを定義します
- service1.yaml, service2.yaml みたいにサービス分けることができる
- ディレクトリ構造はサービス毎にディスプレイ切ってもいいし、yaml をルートに置いてソースコードだけディレクトリ切ってもいい
- dispatch.yaml, index.yaml, cron.yaml などの定義ファイルはルートに置いてね
- default サービス (app.yaml) は最初に必ず deploy してね

どうやら Go言語にはディレクトリ構造の流儀みたいなのがあるらしい

詳しくは下記を参照ください。
- Goにはディレクトリ構成のスタンダードがあるらしい。
- Golangプロジェクトのディレクトリ構成について考えてみた
- Golangパッケージの配置ルールとディレクトリ構成

目標のディレクトリ構成

GAE と Go言語の流儀から、私はこんな感じがいいんでないかな?と思いました。

gae-sample (application root directory)
+-- dispatch.yaml
+-- app.yaml
+-- service1.yaml
+-- src
    +-- cmd
    |   +-- default
    |   |   +-- main.go
    |   |   +-- main_test.go
    |   +-- service1
    |       +-- main.go
    |       +-- main_test.go
    +-- api
    +-- pkg
    +-- lib

※ api, pkg, lib はこの記事では使用しません。

環境構築

アプリケーションのルートディレクトリに移動して git init

$ cd gae-sample
$ git init

Go言語の初期化(モジュール化)

$ go mod init gae-sample

パッと書いていますが、ここも迷った点で、
Go言語の 11 以降はモジュール化すれば $GOPATH 配下にいなくても OK ?

GAE の yaml ファイル作成

$ type NUL > app.yaml
$ type NUL > dispatch.yaml
$ type NUL > service1.yaml
app.yaml
# デフォルト app.yaml です。

service: default
runtime: go113
main: ./src/cmd/default
service1.yaml
# service1 の app.yaml です。

service: service1
runtime: go113
main: ./src/cmd/service1
dispatch.yaml
dispatch:
  - url: "*/service1/*"
    service: service1

Go言語の必要ディレクトリを作成

※必要なければスキップしてください。

$ mkdir src
$ cd src
$ mkdir cmd
$ mkdir pkg
$ mkdir lib

main package を作成

$ cd cmd
$ mkdir default
$ mkdir service1
$ cd default
$ echo package main > main.go
$ echo package main > main_test.go
$ cd ..\service1
$ echo package main > main.go
$ echo package main > main_test.go

main.go は GAE の Quickstart でも動かした helloworld さんを使います。(service1 で若干改修)
ほんと、コピメでごめんなさい。

cmd/default/main.go
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [START gae_go111_app]

// Sample helloworld is an App Engine app.
package main

// [START import]
import (
    "fmt"
    "log"
    "net/http"
    "os"
)

// [END import]
// [START main_func]

func main() {
    http.HandleFunc("/", indexHandler)

    // [START setting_port]
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }

    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
    }
    // [END setting_port]
}

// [END main_func]

// [START indexHandler]

// indexHandler responds to requests with our greeting.
func indexHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.NotFound(w, r)
        return
    }
    fmt.Fprint(w, "Hello, World!")
}

// [END indexHandler]
// [END gae_go111_app]
cmd/default/main_test.go
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestIndexHandler(t *testing.T) {
    req, err := http.NewRequest("GET", "/", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(indexHandler)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf(
            "unexpected status: got (%v) want (%v)",
            status,
            http.StatusOK,
        )
    }

    expected := "Hello, World!"
    if rr.Body.String() != expected {
        t.Errorf(
            "unexpected body: got (%v) want (%v)",
            rr.Body.String(),
            "Hello, World!",
        )
    }
}

func TestIndexHandlerNotFound(t *testing.T) {
    req, err := http.NewRequest("GET", "/404", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(indexHandler)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusNotFound {
        t.Errorf(
            "unexpected status: got (%v) want (%v)",
            status,
            http.StatusNotFound,
        )
    }
}
cmd/service1/main.go
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// [START gae_go111_app]

// Sample helloworld is an App Engine app.
package main

// [START import]
import (
    "fmt"
    "log"
    "net/http"
    "os"
)

// [END import]
// [START main_func]

func main() {
    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/service1/", service1Handler)

    // [START setting_port]
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }

    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
        log.Fatal(err)
    }
    // [END setting_port]
}

// [END main_func]

// [START indexHandler]

// indexHandler responds to requests with our greeting.
func indexHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        http.NotFound(w, r)
        return
    }
    fmt.Fprint(w, "Hello, service1 World!")
}

// [END indexHandler]
// [START service1Handler]

// indexHandler responds to requests with our greeting.
func service1Handler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/service1/" {
        http.NotFound(w, r)
        return
    }
    fmt.Fprint(w, "This is the service1 page!")
}

// [END service1Handler]
// [END gae_go111_app]
cmd/service1/main_test.go
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestService1Handler(t *testing.T) {
    req, err := http.NewRequest("GET", "/service1/", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(service1Handler)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusOK {
        t.Errorf(
            "unexpected status: got (%v) want (%v)",
            status,
            http.StatusOK,
        )
    }

    expected := "This is the service1 page!"
    if rr.Body.String() != expected {
        t.Errorf(
            "unexpected body: got (%v) want (%v)",
            rr.Body.String(),
            "This is the service1 page!",
        )
    }
}

func TestService1HandlerNotFound(t *testing.T) {
    req, err := http.NewRequest("GET", "/service1/404", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(service1Handler)
    handler.ServeHTTP(rr, req)

    if status := rr.Code; status != http.StatusNotFound {
        t.Errorf(
            "unexpected status: got (%v) want (%v)",
            status,
            http.StatusNotFound,
        )
    }
}

deploy apps

$ cd <application root directory>
$ gcloud app deploy app.yaml service1.yaml dispatch.yaml
$ gcloud app browse
$ gcloud app browse -s service1
  • gcloud app browse でブラウザが開き、「Hello, World」が表示されたら OK です。
  • gcloud app browse -s service1 でブラウザが開き、「Hello, service1 World」が表示されたら OK です。
  • 最後に default サービスの URL 末尾に /service1/ を付け、その URL にアクセスすると、「This is the service1 page!」と表示されれば、dispatch.yaml も正常に機能しているはずです。

さいごに

Go言語 ver 1.13 で複数サービスを作成し、GAE にアップロードしてみました。
まだ Go言語の GOPATH と Module が曖昧な感じもあるので、むしろ助言いただけると助かります。
ご参考までに…。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む