質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.12%

python3で画像の畳み込みをすると白っぽくなる

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 1,956

nobuyo

score 46

前提・実現したいこと

pythonで次のような画像どうしの畳み込み演算を行い、手ブレを再現したい。

原画像
ブレを表現した画像

詳細は下記の通りです。
ご教授のほどよろしくお願い致します。

発生している問題

ImageJのFD Mathという機能で畳み込んだ場合と後述のPythonスクリプトでは、結果が異なります。
ImageJのように原画像の色合いを維持したいのですが、どうも畳み込みの結果を正規化する際に値がごく狭い範囲に補正され、白っぽくなっているような気がします。しかし解決方法がわかりませんでした。

  • python

イメージ説明

  • ImageJ

イメージ説明

該当のソースコード

引数には原画像のファイル名や出力先のファイル名を適宜与えています。

import sys
import numpy as np
from PIL import Image
from scipy import signal

if len(sys.argv) != 5:
    print('python blurer.py input fft_of_blur fft_of_original result')
    sys.exit(1)

# ブレを表現する画像をつくる
size = 128
half_size = int((size-1)/2)
blur = Image.new('L', (size, size))
blur_pixels = blur.load()

for x in range(half_size, half_size+5):
    for y in range(half_size, half_size+20):
        blur_pixels[half_size,y] = 255

blur.save('blur.png')

# FFTと畳み込み

# ブレ画像
blur_arr = np.array(blur)
Z = np.fft.fft2(blur_arr)
Z = np.fft.fftshift(Z)
print(Z)
P = np.log(np.abs(Z) + 1)
P_norm = (P * 255.0) / np.amax(P)
y = np.uint8(np.around(P_norm))
img_out = Image.fromarray(y)
img_out.save(sys.argv[2])

# 原画像
img_in = Image.open(sys.argv[1])
x = np.array(img_in.convert('L'))
X = np.fft.fft2(x)
X = np.fft.fftshift(X)
print(X)
P = np.log(np.abs(X) + 1)
P_norm = (P * 255.0) / np.amax(P)
y = np.uint8(np.around(P_norm))
print(y)
img_out = Image.fromarray(y)
img_out.save(sys.argv[3])

# 畳み込み結果
W = np.fft.fftshift(np.fft.ifft2(Z*X))
print(W)
P = np.log(np.abs(W) + 1)
P_norm = P / np.amax(P) * 255
y = np.uint8(np.around(P_norm))
print(y)
img_out = Image.fromarray(y)
img_out.save(sys.argv[4])

補足情報

  • Python 3.5.2 :: Anaconda 4.2.0
  • numpy 1.13.1
  • PIL.Image 1.1.7

 追記(20170820)

回答により色の補正ができるようになり、問題解決しました。
最終的なソースコードを文献として残しておきます。

import sys
import numpy as np
from PIL import Image
from matplotlib import pylab as plt
import cv2

if len(sys.argv) != 2:
    print('python blurer.py fft_of_original.png')
    sys.exit(1)

# ブレを表現する画像をつくる
size = 128
half_size = int((size - 1) / 2)
blur = Image.new('L', (size, size))
blur_pixels = blur.load()

for x in range(half_size, half_size + 5):
    for y in range(half_size, half_size + 20):
        blur_pixels[half_size, y] = 255

blur.save('blur.png')

# FFTと畳み込み

# ブレ画像
blur_arr = np.array(blur)
Z = np.fft.fft2(blur_arr)
Z = np.fft.fftshift(Z)
P = np.log(np.abs(Z) + 1)
P_norm = (P * 255.0) / np.amax(P)
P_norm = (P_norm - np.amin(P_norm)) / np.amax(P_norm) * 255
y = np.around(P_norm)
blur_img = np.around(P_norm)
print("BLUR MIN:%s"%np.min(y))
print("BLUR MAX:%s"%np.max(y))
img_out = Image.fromarray(y).convert('L')
img_out.save("blur_arr.png")

# 原画像
img_in = Image.open(sys.argv[1])
x = np.array(img_in.convert('L'))
dest = np.min(x)
X = np.fft.fft2(x)
X = np.fft.fftshift(X)
P = np.log(np.abs(X) + 1)
P_norm = (P * 255.0) / np.amax(P)
y = np.around(P_norm)
# dest = np.min(y)
fftshift = np.around(P_norm)
print("ORIGINAL MIN:%s"%np.min(y))
print("ORIGINAL MAX:%s"%np.max(y))
img_out = Image.fromarray(y).convert('L')
img_out.save("fftshift.png")

# 畳み込み結果
W = np.fft.fftshift(np.fft.ifft2(Z * X))
P = np.log(np.abs(W) + 1)
P_norm = P / np.amax(P) * 255
src = np.min(P_norm)
y = np.around(P_norm)
print("CONV MIN:%s"%np.min(y))
print("CONV MAX:%s"%np.max(y))
img_out = Image.fromarray(y).convert('L')
img_out.save("conv.png")

# ガンマ補正

gamma = np.log(src/255) / np.log(dest/255)

print(gamma)
lookUpTable = np.zeros((256, 1), dtype = 'uint8')
for i in range(256):
    if i == 0:
        lookUpTable[i][0] = 255 * pow(float(0.000001) / 255, 1.0 / gamma)
    else:
        lookUpTable[i][0] = 255 * pow(float(i) / 255, 1.0 / gamma)

img = cv2.imread('conv.png', 1)
img_gamma = cv2.LUT(img, lookUpTable)
cv2.imwrite('conv-lut.png', img_gamma)
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+4

■明るすぎる原因
恐らく、畳み込み時の* 255がおかしいのだと思います。

# 畳み込み結果
W = np.fft.fftshift(np.fft.ifft2(Z*X))
P = np.log(np.abs(W) + 1)
P_norm = P / np.amax(P) * 255

グレースケールの値を正規化するために、255をかけていますが、
255では大きすぎて「補正後61であってほしい値」が255の88%の値(約220)になってしまっている」
→一番くらい61の明るさが、約220になっていては明るすぎる、ですね。

■処理の段階別の値(目標値)

インデックス 生画像 畳み込み後(補正前) 畳み込み後(補正後)
最小値 61 0.885254911426 61
最大値 255 1.0 255

これをグラフにすると、
変換グラフ
※1 原点に近い部分は理屈抜きに勝手に0に近い値を入れました。
※2 0に近い値は「0」にされない程度に「0」に近い値(0.000000001など)を使ってください。

これならガンマ補正の式・グラフで対応できますね。
ここで、dstもrawもわかっているので、γの値が逆算できます。

これを元に畳み込み後(補正値)が計算できないでしょうか?


理屈抜きに直感でまとめているため怪しい気もしますが、これで解決できそうな気がします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/20 12:49

    無事うまくいきました!大変お世話になりました。ありがとうございました。

    キャンセル

0

pythonで次のような画像どうしの畳み込み演算を行い、手ブレを再現したい。

もし手振れ再現が最終的な目的であれば、シンプルにfilter2Dでいけるというのはインチキでしょうか?

ブレ方向を質問と同様にする場合には、kernel_motion_blur[ : , int((size-1)/2)] = np.ones(size)にしたらいけますね。

'---
FFT/畳み込みを使ってる感がいろいろ楽しそうで、質問の方法の解を見てみたい気もします。

2017-08-19 追記
python blurer.py fft_of_original.pngで同じ画像が再現できます。
まだ修正中ですが…

import sys
import numpy as np
from PIL import Image
# from scipy import signal

# Comment out
# if len(sys.argv) != 5:
#    print('python blurer.py input fft_of_blur fft_of_original result')

if len(sys.argv) != 2:
    print('python blurer.py fft_of_original.png')
    sys.exit(1)

# ブレを表現する画像をつくる
size = 128
half_size = int((size - 1) / 2)
blur = Image.new('L', (size, size))
blur_pixels = blur.load()

for x in range(half_size, half_size + 5):
    for y in range(half_size, half_size + 20):
        blur_pixels[half_size, y] = 255

blur.save('blur.png')

# FFTと畳み込み

# ブレ画像
blur_arr = np.array(blur)
Z = np.fft.fft2(blur_arr)
Z = np.fft.fftshift(Z)
P = np.log(np.abs(Z) + 1)
P_norm = (P * 255.0) / np.amax(P)
y = np.uint8(np.around(P_norm))
print("BLUR MIN:%s"%np.min(y))
print("BLUR MAX:%s"%np.max(y))
img_out = Image.fromarray(y)
img_out.save("blur_arr.png")

# 原画像
img_in = Image.open(sys.argv[1])
x = np.array(img_in.convert('L'))
X = np.fft.fft2(x)
X = np.fft.fftshift(X)
P = np.log(np.abs(X) + 1)
P_norm = (P * 255.0) / np.amax(P)
y = np.uint8(np.around(P_norm))
print("ORIGINAL MIN:%s"%np.min(y))
print("ORIGINAL MAX:%s"%np.max(y))
img_out = Image.fromarray(y)
img_out.save("fftshift.png")

# 畳み込み結果
W = np.fft.fftshift(np.fft.ifft2(Z * X))
P = np.log(np.abs(W) + 1)
P_norm = P / np.amax(P) * 255
y = np.uint8(np.around(P_norm))
print("CONV MIN:%s"%np.min(y))
print("CONV MAX:%s"%np.max(y))
img_out = Image.fromarray(y)
img_out.save("conv.png")

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/18 19:49 編集

    ご回答ありがとうございます。

    > もし手振れ再現が最終的な目的であれば、シンプルにfilter2Dでいけるというのはインチキでしょうか?
    > ブレ方向を質問と同様にする場合には、kernel_motion_blur[ : , int((size-1)/2)] = np.ones(size)にしたらいけますね。

    実は最終的な目的として、ブレている画像をブレの角度・大きさそれぞれで分類し、PSF推定の真似事を行うことを考えておりまして、この方法で大量に生成した画像をCNNを用いた分類器の学習データとするつもりでいます。

    ご教示いただいた通りfilter2Dを用いる方法も考えられたのですが、ブレをkernelとして表現する場合、分類するラベルをどのように設定するかをイメージしにくかったため敬遠していた部分がありました。
    これを機にそちらを使うことも検討したいと思います。

    > FFT/畳み込みを使ってる感がいろいろ楽しそうで、質問の方法の解を見てみたい気もします。

    もう少し自分でも調べつつ、他の回答も待ってみようと思います。
    せっかくお答えいただいたところ恐縮です。

    キャンセル

  • 2017/08/18 21:12 編集

    とりあえず動くコードかつ、同じ不具合が起きる状態にしました。
    コマンドは、python blur画像 original画像で動きます。
    色の修正は追って行います。
    コメント欄だとインデントが崩れるのでそこは汲んでください o.0

    import sys
    import numpy as np
    from PIL import Image
    from scipy import signal

    ###Comment out
    # if len(sys.argv) != 5:
    # print('python blurer.py input fft_of_blur fft_of_original result')

    if len(sys.argv) != 2:
    print('python blurer.py fft_of_original')
    sys.exit(1)

    # ブレを表現する画像をつくる
    size = 128
    half_size = int((size-1)/2)
    blur = Image.new('L', (size, size))
    blur_pixels = blur.load()

    for x in range(half_size, half_size+5):
    for y in range(half_size, half_size+20):
    blur_pixels[half_size,y] = 255

    blur.save('blur.png')

    # FFTと畳み込み

    # ブレ画像
    blur_arr = np.array(blur)
    Z = np.fft.fft2(blur_arr)
    Z = np.fft.fftshift(Z)
    P = np.log(np.abs(Z) + 1)
    P_norm = (P * 255.0) / np.amax(P)
    y = np.uint8(np.around(P_norm))
    img_out = Image.fromarray(y)
    img_out.save("blur_arr.png")

    # 原画像
    img_in = Image.open(sys.argv[1])
    x = np.array(img_in.convert('L'))
    X = np.fft.fft2(x)
    X = np.fft.fftshift(X)
    P = np.log(np.abs(X) + 1)
    P_norm = (P * 255.0) / np.amax(P)
    y = np.uint8(np.around(P_norm))
    img_out = Image.fromarray(y)
    img_out.save("fftshift.png")

    # 畳み込み結果
    W = np.fft.fftshift(np.fft.ifft2(Z*X))
    P = np.log(np.abs(W) + 1)
    P_norm = P / np.amax(P) * 255
    y = np.uint8(np.around(P_norm))
    img_out = Image.fromarray(y)
    img_out.save("conv.png")

    キャンセル

  • 2017/08/18 21:50

    > とりあえず動くコードかつ、同じ不具合が起きる状態にしました。色の修正は追って行います。

    質問投稿時に掲載したコードでも動作はすると思いますが、引数等を取り払ってファイル名をハードコードするように書き換えたという解釈でよろしいでしょうか(質問するのに引数を使うのは抽象的でなかったですね、すみません)

    色の修正にも力をお貸しいただけるとのことで、引き続きよろしくお願い致します。

    キャンセル

  • 2017/08/18 21:54

    引数の数が合わなくてちょっと触りました。
    blurの画像は関数の中で作られる見たいですので、引数から外してしまいました。保存する画像はハードコードしました。
    ---
    ちょっと修正ミスがあったので、先のコメントのコードもこの後すぐに差し替えます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.12%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる