【写真コラージュ】フォトパイル作成スクリプトを作ってみた

## 要約
* 写真を使ったコラージュの一種「[フォトパイル](https://www.google.com/search?q=%E3%82%B3%E3%83%A9%E3%83%BC%E3%82%B8%E3%83%A5+%E3%83%95%E3%82%A9%E3%83%88%E3%83%91%E3%82%A4%E3%83%AB&safe=off&hl=ja&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjGhZevtq7oAhUyBKYKHdo3C0sQ_AUoAXoECAwQAw&biw=1280&bih=666)」を作成するスクリプトの紹介です。
* 画像編集ソフトウェア [ImageMagick](https://imagemagick.org/) を使ったシェルスクリプトです。
* コードは[こちら](https://gist.github.com/reima4038/ed86c0f6382d3a1a49bb63a30ff00d76#file-photo_pile_collage-sh) （gistに移動します）

## フォトパイル（写真パイル）とは
たくさんの写真をランダムに、画面いっぱいバラけさせたように合成する表現手法です。次の画像は本記事で説明するスクリプトで作成したフォトパイルです。素材は「[写真素材が無料！プロ撮影のフリー画像素材 .foto project](https://free.foto.ne.jp/#_ebcv=3kxCAMrX.liskul2so.1,3kxCAMrX.liskul2so.1,3kxCAMrX.liskul2so.1)」様のものを利用しました。

![output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/11869/c83db12b-1913-076d-f32b-8f92c8318b7d.png)


### こういうの作れるソフトって他にないの？

Picasaがフォトパイル作成に対応していたそうですが、[サービスが終了していました](https://support.google.com/picasa/answer/6383491?hl=ja)。一部機能はGoogleフォトに引き継がれたそうなのですが、フォトパイルを作る機能はないようです。

## スクリプトの説明
bashで作られたシェルスクリプト。必要ライブラリとして、実行環境にはImageMagick(7.0.8 以降)がインストールされている必要があります。また、画像形式は“jpg“, “jpeg“, ”png”のみ対応しています。

画像を指定されたディレクトリに格納し、スクリプトを起動するとフォトパイルを生成します。画像の配置はランダムに行われるので、実行のたびに異なった結果が得られます。

枚数が多い場合は画像が重なり、下敷きになった画像が見えなくなることがあります。画像10枚ごとの差分を生成しているので、途中経過も確認しながら良さげに生成されている画像を見つけましょう。

### ディレクトリ構成
以下のようにディレクトリを作成して、srcにあるシェルスクリプトを実行します。

 * dist: 生成したフォトパイルを格納するディレクトリです。写真10枚ごとに差分を生成します。
 * img: フォトパイルの生成元の写真を格納します。拡張子はjpg, jpegのみ対応しています。 
 * src: スクリプトを格納するディレクトリです。
	 *  photo_pile_collage.sh: フォトパイル作成スクリプトです。
 * work: 処理途中のファイルを格納するディレクトリです。
	 * base
	 * modified
	 * org

### コード全文
```sh
#!/bin/sh

# instagram: 1080 x 1080
# iphone: 1920 x 1080
height=1920
width=1080

function preProcess () {
    # instagramの画像サイズに合わせてキャンバス画像を作成する
    convert -size ${width}x${height} xc:white ../work/base/canvas.png

    # 'img'ディレクトリ以下の画像に対して以下の処理を実行する
    for file in `\find ../img -name '*.jpg'`; do
        convert $file -quality 100 ${file//'jpg'/'png'}
        mv ${file//'jpg'/'png'} ../work/org
    done

    for file in `\find ../img -name '*.jpeg'`; do
        convert $file -quality 100 ${file//'jpeg'/'png'}
        mv ${file//'jpeg'/'png'} ../work/org
    done
}

# 画像のナンバリング用
i=0
function createPhotoPile () {
    for file in `\find ../work/org -name '*.png' -maxdepth 1`; do
        modifiedFile=${file//'org'/'modified'}

        # 画像を縮小する
        convert -resize $(($width/3))x$(($height/3)) $file $modifiedFile

        # 画像に枠を作る
        convert $modifiedFile -bordercolor '#FFFFFF' -border 10x10 $modifiedFile

        # ランダムに画像を傾ける
        convert -background 'rgba(0,0,0,0)' -rotate $((-20+$RANDOM%40)) $modifiedFile $modifiedFile

        # ランダムに配置位置を決定する（乱数は”https://togetter.com/li/1044668”を参考）
        x=$((-200+(($RANDOM+$RANDOM+$RANDOM+$RANDOM+$RANDOM)/5)%$width))
        y=$((-200+(($RANDOM+$RANDOM+$RANDOM+$RANDOM+$RANDOM)/5)%$height))

        # 画像をキャンバスに合成する
        composite -gravity northwest -geometry +$x+$y -type TrueColor -compose over $modifiedFile ../work/base/canvas.png ../work/base/canvas.png

        if [ $(($i%10)) = 0 ]; then
            cp ../work/base/canvas.png ../dist/output$i.png
        fi
        let i++
    done
}

function postProcess () {
    # 作成した画像を出力フォルダに配置する
    cp ../work/base/canvas.png ../dist/output.png
}

preProcess
# 画像枚数が少ないと画面がスカスカになるので、繰り返し重ねて厚みを出します
createPhotoPile
createPhotoPile
postProcess
```

### スクリプトの改善点
画像枚数が30枚程度だと、背景の白が目立ってしまう……。なので、やむなくフォトパイル作成関数”createPhotoPile”を繰り返して、無理やり背景を埋めています。もう少し自然なやり方はないものか。

また、画像合成の過程で10枚毎に途中経過を出力しているのですが、ループ回数を記録するイテレータiが関数の外に出ているのがいまいちですね。createPhotoPileの処理を終えた後、標準出力に最終的なiの値を出力して、それを次のcreatePhotoPileで読み込む方がよいでしょうか。

## 後記
今回はImageMagickを使って、フォトパイルを作成してみました。
やはり大量の画像を加工、処理するときにはImageMagickが非常に便利ですね！  シェルスクリプトの出来はさておき、出力結果には満足しています。

また全然本題と関係ないところで、乱数生成の参考にした[乱数にコクを出す方法について - Togetter](https://togetter.com/li/1044668)が非常に面白い！ 思わず導入してしまいました。

それでは、ここまで読んでくださってありがとうございました！