有限の文字セット用の単純なOCRソリューションを書いています。つまり、アルファベットの26文字すべてがどのように見えるかを正確に知っています。私はC#を使用しており、特定のピクセルを黒または白として扱う必要があるかどうかを簡単に判断できます。
すべての文字に対して黒/白のピクセルのマトリックスを生成しています。したがって、たとえば、文字I(大文字のi)は次のようになります。
01110
00100
00100
00100
01110
注:この投稿の後半で使用するすべてのポイントは、左上のピクセルが(0、0)、右下のピクセルが(4、4)であると想定しています。1は黒のピクセルを表し、0は白のピクセルを表します。
次のように、C#で対応するマトリックスを作成します。
CreateLetter("I", new List<List<bool>>() {
new List<bool>() { false, true, true, true, false },
new List<bool>() { false, false, true, false, false },
new List<bool>() { false, false, true, false, false },
new List<bool>() { false, false, true, false, false },
new List<bool>() { false, true, true, true, false }
});
代わりに多次元配列を使用することでこの部分を最適化できる可能性があることはわかっていますが、今のところ、これは説明のためであることを無視しましょう。すべての文字はまったく同じ寸法で、10px x 11pxです(10px x 11pxは、実際のプログラムでの文字の実際の寸法です。この投稿では、0を使用して文字を「描画」する方がはるかに簡単なので、これを5pxx5pxに簡略化しました。小さい画像では1)。
これで、OCRで分析する画像の10px x 11pxの部分を指定すると、すべてのピクセル(10 * 11 = 110)のすべての文字(26)で実行する必要があります。これは、2,860(26 * 110)を意味します。すべての単一文字の反復(最悪の場合)。
すべてのキャラクターのユニークな特徴を定義することで、これを最適化できると思いました。したがって、たとえば、文字のセットが5つの異なる文字(I、A、O、B、およびL)のみで構成されていると仮定します。これらは次のようになります。
01110 00100 00100 01100 01000
00100 01010 01010 01010 01000
00100 01110 01010 01100 01000
00100 01010 01010 01010 01000
01110 01010 00100 01100 01110
すべてのキャラクターの固有の特性を分析した後、キャラクターをテストするために実行する必要のあるテストの数を大幅に減らすことができます。たとえば、「I」文字の場合、座標(3、0)に黒のピクセルがあるという独自の特性を定義できます。これは、他の文字がそのピクセルを黒として持っていないためです。そのため、「I」文字の一致について110ピクセルをテストする代わりに、1ピクセルのテストに減らしました。
これらすべてのキャラクターの場合、次のようになります。
var LetterI = new OcrLetter() {
Name = "I",
BlackPixels = new List<Point>() { new Point (3, 0) }
}
var LetterA = new OcrLetter() {
Name = "A",
WhitePixels = new List<Point>() { new Point(2, 4) }
}
var LetterO = new OcrLetter() {
Name = "O",
BlackPixels = new List<Point>() { new Point(3, 2) },
WhitePixels = new List<Point>() { new Point(2, 2) }
}
var LetterB = new OcrLetter() {
Name = "B",
BlackPixels = new List<Point>() { new Point(3, 1) },
WhitePixels = new List<Point>() { new Point(3, 2) }
}
var LetterL = new OcrLetter() {
Name = "L",
BlackPixels = new List<Point>() { new Point(1, 1), new Point(3, 4) },
WhitePixels = new List<Point>() { new Point(2, 2) }
}
これは、5文字を手動で行うのは困難であり、追加される文字の量が多いほど難しくなります。また、文字を可能な限り最適化する必要があるため、文字の固有の特性の最小セットがあることを保証する必要があります。
すべての文字の固有の特性を識別し、上記と同様のコードを生成するアルゴリズムを作成したいと思います。次に、この最適化された黒/白のマトリックスを使用して文字を識別します。
黒/白のピクセルがすべて入力されている26文字(CreateLetterコードブロックなど)を取得して、文字を定義する最適化された一意の特性セット(新しいOcrLetter()コードブロックなど)に変換するにはどうすればよいですか?そして、それが一意の特性の最も効率的な定義セットであることをどのように保証しますか(たとえば、6ポイントを一意の特性として定義する代わりに、1または2ポイントでそれを行う方法があるかもしれません。例はできました)。
私が思いついた別の解決策は、ハッシュテーブルを使用することです。これにより、ハッシュテーブルが2,860回の反復から110回の反復に削減され、26時間短縮されます。これがどのように機能するかです:
次のようなデータを入力します。
Letters["01110 00100 00100 00100 01110"] = "I";
Letters["00100 01010 01110 01010 01010"] = "A";
Letters["00100 01010 01010 01010 00100"] = "O";
Letters["01100 01010 01100 01010 01100"] = "B";
これで、処理する画像内の場所に到達したら、それを「01110 00100 00100 00100 01110」などの文字列に変換し、ハッシュテーブルで検索します。この解決策は非常に単純に見えますが、文字ごとにこの文字列を生成するには、110回の反復が必要です。
大きなO表記では、ページ上で処理するN文字に対してO(110N)= O(2860N)= O(N)であるため、アルゴリズムは同じです。ただし、それでも26の一定の係数で改善されており、大幅に改善されています(たとえば、26分かかる代わりに、1分かかります)。
更新:これまでに提供されたソリューションのほとんどは、キャラクターの固有の特性を識別する問題に対処しておらず、代わりのソリューションを提供しています。私が知る限り、最速のOCR処理を実現する唯一の方法であるこのソリューションをまだ探しています。
私はちょうど部分的な解決策を思いついた:
ピクセルごとに、グリッドに、それを含む文字を黒いピクセルとして格納します。
これらの文字の使用:
I A O B L
01110 00100 00100 01100 01000
00100 01010 01010 01010 01000
00100 01110 01010 01100 01000
00100 01010 01010 01010 01000
01110 01010 00100 01100 01110
あなたはこのようなものを持っているでしょう:
CreatePixel(new Point(0, 0), new List<Char>() { });
CreatePixel(new Point(1, 0), new List<Char>() { 'I', 'B', 'L' });
CreatePixel(new Point(2, 0), new List<Char>() { 'I', 'A', 'O', 'B' });
CreatePixel(new Point(3, 0), new List<Char>() { 'I' });
CreatePixel(new Point(4, 0), new List<Char>() { });
CreatePixel(new Point(0, 1), new List<Char>() { });
CreatePixel(new Point(1, 1), new List<Char>() { 'A', 'B', 'L' });
CreatePixel(new Point(2, 1), new List<Char>() { 'I' });
CreatePixel(new Point(3, 1), new List<Char>() { 'A', 'O', 'B' });
// ...
CreatePixel(new Point(2, 2), new List<Char>() { 'I', 'A', 'B' });
CreatePixel(new Point(3, 2), new List<Char>() { 'A', 'O' });
// ...
CreatePixel(new Point(2, 4), new List<Char>() { 'I', 'O', 'B', 'L' });
CreatePixel(new Point(3, 4), new List<Char>() { 'I', 'A', 'L' });
CreatePixel(new Point(4, 4), new List<Char>() { });
ここで、すべての文字について、固有の特性を見つけるために、それが属するバケットと、バケット内の他の文字の量を確認する必要があります。それでは、「私」を例にとってみましょう。それが属するすべてのバケット(1,0; 2,0; 3,0; ...; 3,4)に移動し、他の文字の数が最も少ないバケットが(3,0)であることを確認します。実は1文字しかないので、この場合は「I」に違いないので、独自の特徴があります。
白になるピクセルに対しても同じことができます。バケット(2,0)には、「L」を除くすべての文字が含まれていることに注意してください。これは、ホワイトピクセルテストとして使用できることを意味します。同様に、(2,4)には「A」が含まれていません。
これらのピクセルは一意の特性(例:1,1; 4,0; 0,1; 4,4)を定義するのに役立たないため、すべての文字を含むバケット、または文字を含まないバケットはすぐに破棄できます。
たとえば、「O」と「B」の場合のように、文字に対して1ピクセルのテストがない場合は、さらに注意が必要です。'O'のテストを見ていきましょう...
次のバケットに含まれています。
// Bucket Count Letters
// 2,0 4 I, A, O, B
// 3,1 3 A, O, B
// 3,2 2 A, O
// 2,4 4 I, O, B, L
さらに、役立ついくつかの白いピクセルテストもあります:(私は多くても2つ欠けているものだけをリストしました)。ミッシングカウントは(5-Bucket.Count)として計算されました。
// Bucket Missing Count Missing Letters
// 1,0 2 A, O
// 1,1 2 I, O
// 2,2 2 O, L
// 3,4 2 O, B
これで、最短の黒ピクセルバケット(3,2)を取得し、(3,2)をテストすると、それが「A」または「O」のいずれかであることがわかります。したがって、「A」と「O」の違いを簡単に見分ける方法が必要です。「O」を含むが「A」を含まない黒いピクセルバケット(例:2,4)、または「O」を含むが「A」を含まない白いピクセルバケット(例:1,1)のいずれかを探すことができます。これらのいずれかを(3,2)ピクセルと組み合わせて使用すると、2回のテストで文字「O」を一意に識別できます。
これは、5文字の場合は単純なアルゴリズムのように見えますが、26文字で、さらに多くのピクセルが重なっている場合はどうすればよいでしょうか。たとえば、(3,2)ピクセルのテスト後に、ピクセルを含む10個の異なる文字が見つかったとします(これはすべてのバケットの中で最も少なかった)。ここで、他の1文字だけでなく、他の9文字との違いを見つける必要があります。できるだけ少ない量のチェックを取得するという目標をどのように達成し、無関係なテストを実行していないことを確認するにはどうすればよいですか?