JavaでPDFファイルをOCR処理してみた

#始めに
PDFに含まれているイメージファイルをOCR処理するJavaのプログラムを書いてみた
当初はpythonで実装しようとしたが、使用するライブラリの依存関係が意味わからめだったので、仕方なく使い慣れたJavaで実装することにした

#使用したライブラリ

- [PDF BOX](https://pdfbox.apache.org/)
- [tess4j](http://tess4j.sourceforge.net/)

#工夫した点などなど
- PDFの解析のサンプルプログラムはどれもループ使いすぎてソースが見づらかったので、自分はmapとreduceを駆使してみて書いてみたよ
- Iteratorからstreamへの変換はもっとスマートな方法がないものだろうか・・・

#ソース
##PDFからイメージを取得するクラス
```PDFmaker.java
package pdf;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.graphics.PDXObject;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;

public class PDFMaker {
	public static void main(String args[])throws Exception{
		//読み込むPDFファイル
		PDDocument document = PDDocument.load(new File("C:/python/img/pdf/2017h29a_sc_pm2_qs.pdf"));
		//PDFページを処理する
		Stream<PDPage>stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(
				document.getDocumentCatalog().getPages().iterator(),
						Spliterator.ORDERED),false);
		
		System.out.println("start");
		Files.write(Paths.get("parse.txt"), 
				stream.map(s->exePDFpage(s)).collect(Collectors.toList()), 
				Charset.forName("MS932"),
				StandardOpenOption.CREATE);
		System.out.println("end");
	}
	
	//PDFのPageを処理する
	public static String exePDFpage(PDPage p){
		Stream<COSName>stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(
				p.getResources().getXObjectNames().iterator(),Spliterator.ORDERED),false);
		return stream.map(s->exeImage(s,p.getResources()))
		.reduce((s,v)->s+v).get();
	}
	
	//PDFのPageをJpgに変換する
	public static String exeImage(COSName n,PDResources resources){
		try{
			PDXObject xobject = resources.getXObject(n);
			if(xobject instanceof PDImageXObject){
				PDImageXObject image2 = (PDImageXObject) resources.getXObject(n);
				return PDFtoImg.extractFromPDF(image2.getImage());
			}
			return "";
		}catch(Exception e){
			e.printStackTrace();
			return "";
		}	
	}
}
```
##イメージファイルをOCR読み込みする処理
```PDFtoImg.java
package pdf;

import java.awt.image.BufferedImage;

import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;

public class PDFtoImg {
	private static final String  DICTIONARY_PATH ="C:/Users/takayoshi/workspace/PDF/tessdata";
    public static String extractFromPDF(BufferedImage img) {
		ITesseract instance = new Tesseract();
		try {
		    instance.setLanguage("jpn");
		    instance.setDatapath(DICTIONARY_PATH);
		    String result = instance.doOCR(img);
		    return result;
		} catch (TesseractException ex) {
		    ex.printStackTrace();
		    return "";
		}
    }
}
```

#感想
- 情報処理技術者試験の情報処理安全確保支援士試験（SC）の問題を改正するのに大体10分～20分くらいかかる
- 最初の1ページ目の解析結果は以下の通り 

```
平成 29 年度 秋期
{ ニ `
情報処理女全確保支援士試験
ノ
午後 = 問題
試験時聞 ー4:30 ~ ー6:30 (2 時間)
注意事項
ー・ 試験開始及び終了は, 監督員の時計が基準です。 監督員の指示に従つてく だ さい〟
2~ 試験開始の合図がある まで, 問題冊子を開いて中を見てはいけません。
3~ 答案用紙への受験番号などの記入は, 試験開始の合図がぁってから始めてください。
4_ 問題は, 吹の表に従つて解答してください。
```

OCRがフリーだということを考えても、日本語でも結構いい線いっていると思う