Java
PDF
OCR

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

始めに

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

使用したライブラリ

工夫した点などなど

  • 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がフリーだということを考えても、日本語でも結構いい線いっていると思う