前回までで、語レベルの認識範囲と認識された文字が取得できることが分かりました。最後に、文単位に、文の領域を描画するプログラムを考えてみます。
最初にどのようなデータ構造にすればいいか考えます。まず、文は語の集まりで、語ごとに領域が分かっているので、1文の領域には、複数の領域が含まれる事となります。これは、リストにしてしまいましょう。
1 |
List<Rect>; |
また、1文が複数行に渡ることもあるので、1行に対してリストを用意するとわかりやすそうです。
1 |
List<List<Rect>>; |
さらに、複数の文が存在すると考えられますので、さらにリストにしてしまいます。
1 |
List<List<List<Rect>>>; |
これに適当な名前(今回はrecognizedSentenceRectsPerLines)を付けることにします。
1 |
List<List<List<Rect>>> recognizedSentenceRectsPerLines = new ArrayList<List<List<Rect>>>(); |
tessOCRAPI.getWords().getBoxRects()には語単位の領域が、tessOCRAPI.getTextlines().getBoxRects()には行単位の領域が含まれるので、
語の領域がどの行の領域に含まれるかを求めれば、改行したかどうかが分かりそうです。
ここでは、律儀に行の最初から領域を調べる関数を考えてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private int getLineNumberContainsWord(List<Rect> lines, Rect word){ for(int lIdx=0;lIdx<lines.size();++lIdx){ if(isContains(lines.get(lIdx),word)){ return lIdx; } } return -1; } private boolean isContains(Rect line, Rect word){ return (line.top <= word.top && word.top <= line.bottom && line.left <= word.left && word.left <= line.right); } |
あとは、語ごとにgetLineNumberContainsWord()の値を調べて、この値が変わった時点で改行されたことがわかるので、適当なテンポラリリストに行ごとの領域を追加していき、文の終わりが来たら、recognizedSentenceRectsPerLinesにテンポラリリストを追加していけば良いでしょう。ここでは簡単に、ピリオド、エクスクラメーションマーク、クエスチョンマーク、もしくは認識範囲に文字が含まれていない場合、文が終了したと判定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
int currentLine=0; List<Rect> tmpRects = new ArrayList<Rect>(); List<List<Rect>> tmpRectsPerLines = new ArrayList<List<Rect>>(); for(int wIdx=0;wIdx<rect.size();++wIdx){ String word = itr.getUTF8Text(level); int line = getLineNumberContainsWord(rectTextLines,rect.get(wIdx)); // Rectangles // 改行が含まれた時の処理 if(line!=currentLine){ currentLine=line; if(tmpRects.size()>0){ tmpRectsPerLines.add(tmpRects); tmpRects = new ArrayList<Rect>(); } } tmpRects.add(rect.get(wIdx)); if(word.equals("") || word.endsWith(".") || word.endsWith("!") || word.endsWith("?")){ // 文変更 if(!currentSentence.equals("")){ // データの追加 // 文 recognizedSentences.add(currentSentence); currentSentence=""; // Rect if(tmpRects.size()>0){ tmpRectsPerLines.add(tmpRects); } if(tmpRectsPerLines.size()>0){ recognizedSentenceRectsPerLines.add(tmpRectsPerLines); } // テンポラリデータのクリア tmpRectsPerLines = new ArrayList<List<Rect>>(); tmpRects = new ArrayList<Rect>(); } } // 次のWordの場所にヘッドを動かす itr.next(level); } // データが残っていた時の処理 if(!currentSentence.equals("")){ // データの追加 recognizedSentences.add(currentSentence); recognizedSentenceRectsPerLines.add(tmpRectsPerLines); } |
最後に、文ごとに適当に矩形で囲んでしまえば良いですね。
さて、上記コードを修正して実行した結果が次のとおりです。
ではとりあえず、AndroidでTessearctを使うというトピックはこの辺で一区切りとします。