いろいろな局所範囲しきい値で画像の2値化を試してみた
局所範囲(注目ピクセルの近傍ピクセル)からしきい値を計算して2値化、この前は平均値をしきい値にしたものだった。今回はそれ以外の計算方法をググって、いくつか試してみた
使ったアプリは
20200422_局所しきい値で2値化.zip
- 画像ファイルドロップか、Pasteボタンでクリップボードから画像貼り付けで画像表示
- Copyボタンで表示している画像をクリップボードにコピー
- 数値変更は数値の右をクリックか、ホイール
- 画像クリックで変換前の画像表示
局所範囲の最大値、最小値を使って求める
Contrast
最大値 - 最小値
これであっているのかわからん
Bernsen
条件
- contrastMin = 15
- bgThreshold = 128
のとき
(最大値 - 最小値 < contrastMin) ? bgThreshold : (最大値 + 最小値) / 2.0;
Bernsen2
条件
- MidGray = (最大値 + 最小値) / 2.0
- contrastMin = 15
- bgThreshold = 128
- pv = 注目ピクセルの値)
のとき
if(最大値 - 最小値 < contrastMin)
(MidGray >= bgThreshold) ? 255: 0;
else
(pv >= MidGray) ? 255: 0;
Bernsenとある計算はこの2つが見つかったけど、これであっていのかわからん
MidGray
(最大値 + 最小値) / 2.0
最大値と最小値の中間をしきい値にするっては直感的にもわかる
局所範囲の並べ替えが必要
Median
中央値、計算量が多いので時間がかかる
平均値と標準偏差を使って求める
Niblack
条件
- k = -0.2
のとき
平均値 + k * 標準偏差
平均値にばらつき具合を表す標準偏差を足し算。標準偏差が大きいほど極端なしきい値になる。kの符号を変えると白と黒が逆になる
Sauvola
条件
- k = 0.5
- r = 128
のとき
平均値 * (1 + k * (標準偏差 / r - 1))
平均値に加工した標準偏差を掛け算、kとrの値で細かい調整ができる
Phansalkar
条件
- k = 0.25
- r = 0.5
- p = 2(基本的に固定?)
- q = 10(基本的に固定?)
のとき
平均値 * (1 + p * exp(-q * 平均値) + k * (標準偏差 / r -1))、これだと真っ黒になる
or
平均値 + (1 + p * exp(-q * 平均値) + k * (標準偏差 / r -1))、これだと期待通りになる
ググって出てくるのは平均値に掛け算する上の式で、これだとしきい値が大きくなりすぎて真っ黒な画像になってしまうので、掛け算じゃなくて足し算にしてみたら、それっぽい結果になったのが下の式、今回はこちらを使っている
平均値制限法
条件
- k = 1~10
- γ = 0~0.5
のとき
γ * (k - 1) + (1 - 2 * γ) * 平均値
kに設定する値がわからん、γ(ガンマ)の値で結果が大きく変わる
大津の方法
大津の2値化を局所範囲に適用、計算量がものすごく多くて他の方法の100倍くらい時間がかかる。実行前に確認ウィンドウを出すようにしたくらい
計算方法は
Auto Local Threshold - ImageJ
https://imagej.net/Auto_Local_Threshold#Phansalkar
殆どの計算式はここから
opencvsharp/Binarizer.cs at master · shimat/opencvsharp
https://github.com/shimat/opencvsharp/blob/master/src/OpenCvSharp.Extensions/Binarizer.cs
Bernsenはここから
imagej-ops/LocalPhansalkarThreshold.java at master · imagej/imagej-ops
https://github.com/imagej/imagej-ops/blob/master/src/main/java/net/imagej/ops/threshold/localPhansalkar/LocalPhansalkarThreshold.java
Phansalkarはここから
Scikit-imageで画像処理を行う(2)- filtersモジュールのうち、平滑化、二値化を使うー
http://ishidate.my.coocan.jp/vpy_6/vpy_6.htm
https://ocw.kyoto-u.ac.jp/ja/09-faculty-of-engineering-jp/image-processing/pdf/dip_04.pdf
平均値制限法はここから
2値化してみる
設定する値は初期値のまま、範囲7は7 * 2 + 1 = 15で15 * 15 = 225ピクセル
元の画像サイズは296x288ピクセル
結果
どれもそれなりにいい感じ、意外だったのは(最大値 - 最小値)をしきい値にしたContrast、ノイズだらけのような画像になると思ってたけど普通に見れる画像になっている。Bernsenも計算がわからんけどそれっぽい結果になった。平均値制限法は軽い計算なのにとてもいい結果
SauvolaとPhansalkaはちょっと違う感じなので調整して
良くなった
局所範囲3x3
範囲を1にして1 * 2 + 1 = 3で、3 * 3 = 9ピクセルに設定
エッジ抽出みたいになるのがある
範囲を20は20 * 2 + 1 = 41、41 * 41 = 1681ピクセル!
元の画像に依るのかもしれないけど、範囲を広げればいいってわけでもないみたいねえ。あとは方式での違いが少なくなった感じで、似たりよったりになった
範囲20のときの処理時間
製作と計測環境
- CPU AMD Ryzen 5 2400G(4コア8スレッド)
- MEM DDR4-2666
- Window 10 Home 64bit
- Visual Studio 2019 Community .NET Core 3.1 WPF C#
0.057 | Bernsen |
0.055 | Bernsen2 |
0.056 | Contrast |
9.366 | 大津 |
0.083 | MidGray |
0.737 | Median |
0.044 | Average |
0.063 | Niblack |
0.062 | Sauvola |
0.067 | Phansalkar |
0.052 | 平均値制限法 |
大津の方法だけ飛び抜けて時間がかかるし、処理途中でのキャンセル処理は書いていないので
実行前に確認するようにした
予想時間の計算は大雑把で
(局所ピクセル数画像の横ピクセル数画像の縦ピクセル数) / 1千万
範囲20、296x288ピクセルの画像の時
((20 * 2 + 1) ^ 2 * 296 * 288) / 10000000 = 14.330189秒
4コアCPUだと実際には10秒くらいだから、8コアCPUだと5秒とかになるかも?
これを範囲2で処理すると手のシワとか指紋が強調されて面白い
このときのエッジ抽出に似ているけど、また違った感じ
gogowaten.hatenablog.com
前回は1ヶ月前
大津の方法