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

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

- [集めた画像からJuliaで壁紙を作る](https://qiita.com/reinsF82/items/560ce8bde2270ba7faef)
- [集めた画像からPythonとOpenCVで壁紙を作る](https://qiita.com/reinsF82/items/2ba347b1eaa941eb9d94)

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

# 実行環境
- Windows10 Pro
- go version go1.13.1 windows/amd64
- OpenCV 4.1.2
- GoCV

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

- [GoCVのWindowsでのインストール](https://gocv.io/getting-started/windows/)

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

# ソースコード
## パッケージのインポート
```go
package main

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

## 指定した範囲で被りなしの整数を生成するための関数
以下のリンクを参考にさせていただきました。
- [min-maxの範囲からn個の整数をランダム抽出する方法(ただし重複は含まない)](https://qiita.com/ohkawa/items/c5e703459a1fe5cb83d0)

```go
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
}
```

## 実際の処理

```go
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331462/7a5384e7-3bf5-d65f-d504-26416897fd70.jpeg)


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