テキスト内の「川」の検出


175

TeXスタック交換については、この質問の段落で「川」を検出する方法について議論してきました。

この文脈では、川はテキスト内の単語間スペースの偶発的な整列から生じる空白のバンドです。これは読者にとって非常に注意をそらす可能性があるため、悪い川はタイポグラフィの悪さの症状であると考えられています。川のあるテキストの例はこれです。2本の川が斜めに流れています。

ここに画像の説明を入力してください

これらの川を自動的に検出することに関心があり、それらを回避することができます(おそらくテキストを手動で編集することによって)。RaphinkはTeXレベル(グリフの位置と境界ボックスのみを知っている)である程度進歩していますが、川を検出する最良の方法は画像処理を使用することだと確信しています(グリフの形状は非常に重要であり、TeXでは利用できないため) 。上記の画像から川を抽出するさまざまな方法を試しましたが、少量の楕円形のぼかしを適用するという私の単純なアイデアは十分ではないようです。私もいくつか試しましたラドンハフ変換ベースのフィルタリングですが、これらのいずれにもアクセスできませんでした。川は人間の目/網膜/脳の特徴検出回路に非常に見えており、何らかの形でこれを何らかのフィルタリング操作に変換できると思いますが、機能させることはできません。何か案は?

具体的には、上記の画像で2つの河川を検出する操作を探していますが、他の誤検出はあまりありません。

編集: Endolithは、TeXではグリフの位置、間隔などにアクセスできるため、画像処理ベースのアプローチを追求している理由を尋ねました。実際のテキストを調べるアルゴリズムを使用する方がはるかに高速で信頼性が高いかもしれません。物事を別の方法で行う理由は、その形がグリフの大きさは川の目立ち方に影響を与える可能性があり、テキストレベルでは、この形状(フォント、合字などに依存)を考慮することは非常に困難です。グリフの形状がどのように重要であるかの例については、次の2つの例を検討してください。それらの違いは、いくつかのグリフをほぼ同じ幅の他のグリフに置き換えたことです。それらも同様に良い/悪い。ただし、最初の例の川は2番目の例よりもはるかに悪いことに注意してください。

ここに画像の説明を入力してください

ここに画像の説明を入力してください


5
+1この質問が好きです。私の最初の考えはハフ変換ですが、おそらく前処理が必要になるでしょう。おそらく最初に膨張フィルター
データガイスト

実際、ラドン変換が機能しなかったことに驚いています。どうやってやったの?
エンドリス

@endolith:洗練されたものはありません。ImageLines[]Mathematicaから使用しました。これは技術的にはラドン変換ではなくハフ変換を使用していると思います。適切な前処理(datageistの推奨する拡張フィルターを試していませんでした)および/またはパラメーター設定がこの機能を実行できる場合、私は驚かないでしょう。
レフビショップ

川のGoogle画像検索では、「曲がりくねった」川も表示されます。それらを見つけたいですか?cdn.ilovetypography.com/img/text-river1.gif
endolith

@endolith最終的には、人間の視覚システムの処理を複製して、空間の特定の構成が邪魔になるようにしたいと思います。これは曲がりくねった川でも発生する可能性があるので、それらをキャッチしたいと思いますが、まっすぐな川は一般的に問題のようです。さらに良いのは、テキストを読むときに川がどれほど強く見えるかに対応する方法で、川の「悪さ」を定量化する方法です。しかし、それはすべて非常に主観的であり、定量化が困難です。そもそも、あまりにも多くの誤検知なしに、本当にすべての悪い川を捕まえるだけです。
レフビショップ

回答:


135

これについてもう少し考えましたが、以下はかなり安定していると思います。これらは標準の画像処理ライブラリで利用できるはずなので、形態学的操作に限定していることに注意してください。

(1)nPix行1列のマスクで画像を開きます。ここで、nPixは文字間の垂直距離です。

#% read image
img = rgb2gray('http://i.stack.imgur.com/4ShOW.png');

%# threshold and open with a rectangle
%# that is roughly letter sized
bwImg = img > 200; %# threshold of 200 is better than 128

opImg = imopen(bwImg,ones(13,1));

ここに画像の説明を入力してください

(2)1 x mPixマスクで画像を開いて、川には狭すぎるものをすべて除去します。

opImg = imopen(opImg,ones(1,5));

ここに画像の説明を入力してください

(3)段落間のスペースまたはインデントによる水平の「川と湖」を削除します。このため、すべての真であるすべての行を削除し、以前に見つけた川に影響しないことがわかっているnPix行1列のマスクで開きます。

湖を削除するには、nPix x nPixよりわずかに大きい開口マスクを使用できます。

このステップでは、小さすぎて実際の川にならないすべてのもの、つまり(nPix + 2)*(mPix + 2)* 4(3行以内になります)よりも小さい面積をカバーするすべてのものを捨てることもできます。+2は、すべてのオブジェクトの高さが少なくともnPix、幅がmPixであり、それより少し上にしたいことがわかっているためです。

%# horizontal river: just look for rows that are all true
opImg(all(opImg,2),:) = false;
%# open with line spacing (nPix)
opImg = imopen(opImg,ones(13,1));

%# remove lakes with nPix+2
opImg = opImg & ~imopen(opImg,ones(15,15)); 

%# remove small fry
opImg = bwareaopen(opImg,7*15*4);

ここに画像の説明を入力してください

(4)川の長さだけでなく幅にも興味がある場合、距離変換とスケルトンを組み合わせることができます。

   dt = bwdist(~opImg);
   sk = bwmorph(opImg,'skel',inf);
   %# prune the skeleton a bit to remove branches
   sk = bwmorph(sk,'spur',7);

   riversWithWidth = dt.*sk;

ここに画像の説明を入力してください (色は川の幅に対応しています(ただし、カラーバーは2倍オフになっています)

接続された各コンポーネントのピクセル数をカウントすることで、おおよその川の長さを取得し、ピクセル値を平均することで平均幅を取得できます。


次に、2番目の「川のない」画像にまったく同じ分析を適用します。

ここに画像の説明を入力してください


ありがとう。私はMatlabを持っているので、他のいくつかのテキストでこれを試して、どれだけ堅牢かを確認します。
レフビショップ

それをTeXに統合することは、何らかの方法でLuaに移植できない限り、別の問題になる可能性があります。
チャフィンク

@LevBishop:私はこの問題をもう少しよく理解していると思う。新しいソリューションはかなり堅牢である必要があります。
ジョナス

@levBishop:もう1つの更新。
ジョナス

1
@LevBishop:2番目の画像に気付いただけです。形態に基づいた分析がその仕事をすることがわかります。
ジョナス

56

Mathematicaでは、侵食とハフ変換を使用します:

(*Get Your Images*)
i = Import /@ {"http://i.stack.imgur.com/4ShOW.png", 
               "http://i.stack.imgur.com/5UQwb.png"};

(*Erode and binarize*)
i1 = Binarize /@ (Erosion[#, 2] & /@ i);

(*Hough transform*)
lines = ImageLines[#, .5, "Segmented" -> True] & /@ i1;

(*Ready, show them*)
Show[#[[1]],Graphics[{Thick,Orange, Line /@ #[[2]]}]] & /@ Transpose[{i, lines}]

ここに画像の説明を入力してください

編集回答ウィザードのコメントの

水平線を取り除きたい場合は、代わりに次のようなことをしてください(おそらく誰かがもっと簡単にすることができます):

Show[#[[1]], Graphics[{Thick, Orange, Line /@ #[[2]]}]] & /@ 
 Transpose[{i, Select[Flatten[#, 1], Chop@Last@(Subtract @@ #) != 0 &] & /@ lines}]

ここに画像の説明を入力してください


1
すべての水平線を取り除いてみませんか?(+1)
Mr.Wizard

@氏。ただ、すべての行が...検出されている表示する
博士ベリサリウス

1
しかし、それは問題の一部ではありませんか?
Mr.Wizard

@氏。要求に応じて編集
ベリサリウス博士

4
@belisariusハフ変換で使用される座標系は、8.0.0以降、ラドン変換のいずれかに一致するように変更されました。これにより、ImageLinesの動作が変わりました。全体的にこれは改善ですが、この場合は以前の動作を好むでしょう。ピーク検出を実験したくない場合は、入力画像のアスペクト比を1に近づけて変更し、8.0.0:のような結果を得ることができますlines = ImageLines[ImageResize[#, {300, 300}], .6, "Segmented" -> True] & /@ i1;。言われていることはすべて、この問題では形態学的アプローチがより堅牢に思えます。
マティアスオディシオ

29

うーん... ラドン変換は簡単に抽出できないと思います。(ラドン変換は、基本的に画像を「見ながら」回転させます。これはCATスキャンの背後にある原理です。)画像の変換によりこのサイノグラムが生成され、「川」が明るいピークを形成します。

ここに画像の説明を入力してください

70度回転したものは、水平軸に沿ったスライスのこのプロットの左側のピークとしてかなりはっきりと見ることができます。

ここに画像の説明を入力してください

特に、テキストが最初にガウスぼかしされた場合:

ここに画像の説明を入力してください

しかし、これらのピークを残りのノイズから確実に抽出する方法はわかりません。サイノグラムの明るい上端と下端は、テキストの水平線の間の「川」を表しますが、これは明らかに気にしません。重み関数と角度の組み合わせは、垂直線をより強調し、水平線を最小化するでしょうか?

この画像では、単純な余弦重み関数がうまく機能します。

ここに画像の説明を入力してください

サイノグラムのグローバルな最大値である90度の垂直河川を見つけます。

ここに画像の説明を入力してください

この画像で104度の画像を見つけますが、最初にぼかしをかけるとより正確になります:

ここに画像の説明を入力してください ここに画像の説明を入力してください

(SciPyのradon()機能は一種の愚かさです。または、このピークを川の中央を通る線として元の画像にマッピングします。)

しかし、ぼかしと重み付けの後、イメージのサイノグラムの2つの主要なピークのいずれも検出されません。

ここに画像の説明を入力してください

それらは存在しますが、重み関数の中央ピーク付近のものに圧倒されます。右の重みと微調整では、この方法は、おそらく可能性が動作しますが、私は右の微調整が何であるかわかりません。おそらく、ページのスキャンのプロパティにも依存します。たぶん、重み付けはスライスの全体エネルギーまたは正規化などの何かから導出する必要があります。

from pylab import *
from scipy.misc import radon
import Image

filename = 'rivers.png'
I = asarray(Image.open(filename).convert('L').rotate(90))

# Do the radon transform and display the result
a = radon(I, theta = mgrid[0:180])

# Remove offset
a = a - min(a.flat)

# Weight it to emphasize vertical lines
b = arange(shape(a)[1]) #
d = (0.5-0.5*cos(b*pi/90))*a

figure()
imshow(d.T)
gray()
show()

# Find the global maximum, plot it, print it
peak_x, peak_y = unravel_index(argmax(d),shape(d))
plot(peak_x, peak_y,'ro')
print len(d)- peak_x, 'pixels', peak_y, 'degrees'

最初に非対称ガウスでぼかした場合はどうなりますか?すなわち、水平方向に狭く、垂直方向に広くなります。
ジョナス

@ジョナス:それはおそらく役立つでしょう。主な問題は、背景が回転によって大きく変化するときに、背景からピークを自動的に選択することです。非対称のぼかしは、ラインからラインへの水平ストライプを滑らかにすることができます。
エンドリス

:これは、テキストでは、少なくともラインの回転を検出するためにうまく機能gist.github.com/endolith/334196bac1cac45a4893
endolithを

16

異なるスケールの微分特徴(2次まで)を使用して、ピクセルの識別分類器をトレーニングしました。

私のラベル:

ラベリング

トレーニング画像の予測:

ここに画像の説明を入力してください

他の2つの画像の予測:

ここに画像の説明を入力してください

ここに画像の説明を入力してください

これは有望に見え、より多くのトレーニングデータと多分よりスマートな機能があれば、有用な結果が得られると思います。一方、これらの結果を得るのに数分しかかかりませんでした。オープンソースソフトウェアilastikを使用して、自分で結果を再現できます。[免責事項:私は主要な開発者の1人です。]


2

(申し訳ありませんが、この投稿には素晴らしいデモンストレーションは含まれていません。)

TeXがすでに持っている情報(文字と位置)を使用したい場合は、文字と文字のペアをある方向または別の方向の「傾斜」として手動で分類できます。たとえば、「w」にはSWおよびSEコーナースロープがあり、「al」コンボにはNWコーナースロープがあり、「k」にはNEコーナースロープがあります。(句読点を忘れないでください-引用符の後にグリフボックスの下半分を埋める文字が続くと、素敵な勾配が確立されます;引用符の後にqが続くのが特に強力です。)

次に、スペースの反対側の対応する勾配の出現を探します-SWからNE川の場合は「w al」、NWからSE川の場合は「k T」。線上にあるものを見つけたら、上/下の線で同様に左または右に適切にシフトしたものが発生するかどうかを確認します。これらの実行を見つけるとき、おそらく川があります。

また、明らかに、ちょうど垂直な川のために、ほぼ垂直に積み上げられたスペースを探してください。

傾斜の「強度」を測定することで、もう少し高度になります。傾斜のためにアドバンスボックスがどれだけ「空」になり、川の幅に影響するかを測定します。「w」は川に貢献するためのアドバンスボックスの小さなコーナーしか持たないため、かなり小さいですが、「V」は非常に強いです。「b」は「k」よりわずかに強い。より緩やかな曲線は、視覚的に連続した川の端を提供し、より強く視覚的に広くなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.