プログラミング素人のはてなブログ

技術屋の末端。プログラミングも電気回路も専門外です。 コードに間違いなど見つけられたら、気軽にコメントください。 VC#、python3、ラズパイ始めました。

ミニチュア風加工写真をpythonで

実物の景色を撮影したのに、ミニチュア(ジオラマ)風に見える写真というものがあります。
スマホのアプリも多数あるようですが、これをpythonでやってみました。
f:id:s51517765:20181216173442p:plain

ミニチュア・ジオラマ風の写真を撮りたい!簡単にチルトシフト写真を作る5つの方法 | 東京上野のWeb制作会社LIG

以下のような特性を持たせると、ミニチュア風に見えるそうです。

①アオリ(見下ろし)で遠景を撮る
②端をぼかす
③彩度を上げる

このうちの①は撮影時の設定ですが、②と③に関しては後から加工が可能です。
これをpythonでPILとNumpyで実施してみました。
画像加工のライブラリを使えば一発ですが、ここでは画像加工の仕組みを調べることも合わせて、加工部分は自分で実装してみました。
ですが、やってみるとそもそも①の要素を満たした写真はミニチュア風に加工しやすいこともわかりました。

ぼかし加工

ぼかしとは、画素を周囲の色と平均化することで実現できます。
f:id:s51517765:20181216175557j:plain
https://howto.clip-studio.com/library/page/view/illuststudio_tora_filter_050

filter_sizeを例えば5pxにして、5x5=25pxの範囲のRGBの色要素をそれぞれ足して、25で割ると平均化できます。
このとき、基準とするpxを中心として、上下左右を平均化します。

 for i in range(filter_size):
       for j in range(filter_size):
            filterOutput += image_input[x + i - int(filter_size / 2), y + j - int(filter_size / 2), RGB]
       image_output1[x, y, RGB] = filterOutput / filter_size ** 2

これを画像の端部にだけかけるようにするので、画像の中心からの距離が一定以上の部分、ここでは画像の中心から楕円形の外にのみぼかしをかけるようにしました。

# redion_x , redion_y ぼかし範囲半径
# size_x, size_y 画像の範囲 
hankei = ((x - size_x / 2) / redion_x) ** 2 + ((y - size_y / 2) / redion_y) ** 2 #楕円形
 if filter_region(x, y, size_x, size_y, filter_size, hankei) == True:  # 外側がフィルター範囲
 # ぼかし加工

def filter_region(x, y, size_x, size_y, filter_size, hankei):
    if x > filter_size / 2 and y > filter_size / 2 and x < size_x - filter_size / 2 and y < size_y - filter_size / 2 and hankei > 1:
        return True
    else:
        return False

彩度を上げる

彩度とは画像のコントラストのことで、コントラストとは色味がある(明るい)色から色味がない(暗く黒に近い)までの幅のことを言います。
f:id:s51517765:20181216175436p:plain
http://aceartacademy.net/hpgen/HPB/entries/365.html

色味がある状態とは、RGBの要素が255に近い状態で、色味がないとはRGBがの要素が0に近い状態です。
ただし、RGB=255,255,255は"白"を表し、RGB=0,0,0は"黒"を表します。

彩度を上げるには、色味がある部分をより色味を強くし、色味がない部分をより色味がない状態にすることで最大限に引き出すことができますが、暗い部分が多くなると画像が暗くつぶれてしまったので色味がある部分のみを上げるようにしました。
要素が40以上の部分を比例して上げるようにして、255を超えてしまう場合は255にしました。

def tone_function(value, x, y, RGB):  # 彩度が高いところをより高く
    lowerLimit = 40
    outputValue = (value - lowerLimit) * 1.4 + lowerLimit
    if outputValue > 255:
        return 255
    elif value > lowerLimit:
        return outputValue
    else:
        value

結果

加工前
f:id:s51517765:20181216181301j:plain
加工後
f:id:s51517765:20181216173442p:plain
https://www.pakutaso.com/traffic/train/

加工前
f:id:s51517765:20181216181859j:plain
加工後
f:id:s51517765:20181216181927p:plain
ジオラマ風(ミニチュア風)写真: てーへんカメラマンの日々 -seimas-

加工前
f:id:s51517765:20181216181427j:plain
加工後
f:id:s51517765:20181216181446p:plain
※最後の写真は自身の撮影(ミニチュア加工する前提の構図じゃないのが厳しい)

github.com