Pythonで大津の二値化を試す
画像を白と黒で表すことを二値化と言います。
まずグレーで表して、それを白と黒に分けるのですが、その境目、閾値をうまく指定する必要があります。
グレーだと色は256段階で表されます。
最も単純なのは真ん中の128を閾値とする方法です。しかしこれだと偏りがあるとキレイに見えません。
次に考えられるのは平均です。これだとまあまあキレイに見えます。
そして「大津の二値化」という良い手法があります。
次のページの説明がわかりやすかったです。ありがとうございました。
大津の二値化ってなんだ…ってなった. - Qiita
ある閾値を決めてすべての画素を黒側のグループと白側のグループに分けます。
次のように計算します。
r0:黒側の画素の割合
r1:白側の画素の割合
m0:黒側の画素の数値の平均
m1:白側の画素の数値の平均
として
r0*r1*(m0-m1)**2
が最大になる閾値を求めます。
実際にコードを書いてみました。
def otsu(pixels):
# 大津の二値化を使い閾値を求める。
# pixels:二次元の各点の色(0−255)
# r0*r1*(m0-m1)**2
# r0,r1:画素の割合 m0,m1:平均
max_v = 0
max_f = 0
for f in range(255):
gaso0 = []
gaso1 = []
for color in pixels:
for val in color:
if val < f:
gaso0.append(val)
else:
gaso1.append(val)
all = len(gaso0) + len(gaso1)
r0 = len(gaso0) / all
r1 = len(gaso1) / all
if len(gaso0) == 0:
m0 = 0
else:
m0 = sum(gaso0) / len(gaso0)
if len(gaso1) == 0:
m1 = 0
else:
m1 = sum(gaso1) / len(gaso1)
v = r0 * r1 * (m0 - m1) ** 2
if v > max_v:
max_v = v
max_f = f
return max_f
# 大津の二値化を使い閾値を求める。
# pixels:二次元の各点の色(0−255)
# r0*r1*(m0-m1)**2
# r0,r1:画素の割合 m0,m1:平均
max_v = 0
max_f = 0
for f in range(255):
gaso0 = []
gaso1 = []
for color in pixels:
for val in color:
if val < f:
gaso0.append(val)
else:
gaso1.append(val)
all = len(gaso0) + len(gaso1)
r0 = len(gaso0) / all
r1 = len(gaso1) / all
if len(gaso0) == 0:
m0 = 0
else:
m0 = sum(gaso0) / len(gaso0)
if len(gaso1) == 0:
m1 = 0
else:
m1 = sum(gaso1) / len(gaso1)
v = r0 * r1 * (m0 - m1) ** 2
if v > max_v:
max_v = v
max_f = f
return max_f
実験してみます。
モナリザ
平均
大津の二値化
大津の二値化のほうがキレイな感じです。
真珠の耳飾りの少女
平均
大津の二値化
こちらの方が違いが顕著だと思います。
[ 2021年8月12日 | カテゴリー: Python | タグ: Pillow , アルゴリズム ]
« Pythonで辞書型のリストをユニークにする方法 | アイドルのキャッチフレーズ »
コメントを残す