2016-07-19 32 views
0

場合によっては、フルページの高解像度画像(文書スキャンの結果)でいっぱいの非常に大きなPDFに遭遇することがあります。たとえば、私は3500 +画像で1.7GBのPDFを持っています。ドキュメントの読み込みには約50秒かかりますが、イメージのカウントには約15分かかります。PDFBox 2.xを使用してPDF画像を数える最速の方法

これは、イメージバイトがAPI呼び出しの一部として読み取られるためです。実際にイメージバイトを読み取らずにイメージカウントを抽出する方法はありますか?

PDFBoxバージョン:2.0.2

コード例:

@Test 
public void imageCountIsCorrect() throws Exception { 
    PDDocument pdf = readPdf(); 
    try { 
     assertEquals(3558, countImages(pdf)); 
     // assertEquals(3558, countImagesWithExtractor(pdf)); 
    } finally { 
     if (pdf != null) { 
      pdf.close(); 
     } 
    } 
} 

protected PDDocument readPdf() throws IOException { 
    StopWatch stopWatch = new StopWatch(); 
    stopWatch.start(); 

    FileInputStream stream = new FileInputStream("large.pdf"); 
    PDDocument pdf; 
    try { 
     pdf = PDDocument.load(stream, MemoryUsageSetting.setupMixed(1024 * 1024 * 250)); 
    } finally { 
     stream.close(); 
    } 

    stopWatch.stop(); 
    log.info("PDF loaded: time={}s", stopWatch.getTime()/1000); 
    return pdf; 
} 


protected int countImages(PDDocument pdf) throws IOException { 
    StopWatch stopWatch = new StopWatch(); 
    stopWatch.start(); 

    int imageCount = 0; 
    for (PDPage pdPage : pdf.getPages()) { 
     PDResources pdResources = pdPage.getResources(); 
     for (COSName cosName : pdResources.getXObjectNames()) { 
      PDXObject xobject = pdResources.getXObject(cosName); 
      if (xobject instanceof PDImageXObject) { 
       imageCount++; 
       if (imageCount % 100 == 0) { 
        log.info("Found image: #" + imageCount); 
       } 
      } 
     } 
    } 

    stopWatch.stop(); 
    log.info("Images counted: time={}s,imageCount={}", stopWatch.getTime()/1000, imageCount); 
    return imageCount; 
} 

私はCOSNameに頼るcountImages方法を変更した場合、カウントは1秒未満で完了しますが、私は少し不確かです名前の接頭辞に頼っていること。 (など、インライン画像を欠場する可能性が

if (cosName.getName().startsWith("QuickPDFIm")) { 
    imageCount++; 
} 
+1

注意点として、あなたのコードは、ページごとに*即時ビットマップイメージ*リソースをカウント。どちらも、オブジェクトまたはパターンに含まれるインラインイメージやイメージではありません。一方、イメージリソースはページ上で使用する必要はありません。したがって、時にはあまりにも多くの画像を数えます。一般的なソリューションでは、コンテンツストリームを検討する必要があります。 – mkl

+0

ああ、PDFGraphicsStreamEngineのカスタム実装を使用してイメージを数えたときにイメージ数の間に見つかった矛盾のいくつかを説明します。私はそのコードを掘り下げて私が逃しているものを見つけ出すでしょう。ありがとう! –

+1

私が念頭に置いておきたいのは、ExtractImagesの例を修正し、イメージオブジェクトを作成するすべてのオブジェクトを削除し、xobjectsが作成されていない場合は 'DrawObject extends GraphicsOperatorProcessor'プロセッサで' addOperator(new DrawObject());フォームに従います。 org.apache.pdfbox.contentstream.operator.DrawObjectのソースコードを参照してください。 –

答えて

0

だから、以前のアプローチはいくつかの追加の欠陥を持っていた:これは、PDFエンコーダの副産物ではなくPDFBox(私は自分のコードでは、それへの参照を見つけることができなかった)ように見えます)。フィードバックのためにmklとTilman Hausherrに感謝します!

TIL - PDF object streams contain useful operator codes!

私の新しいアプローチがPDFStreamEngineを拡張し、PDFコンテンツストリームで見つかったすべての(描画オブジェクト)「を実行」演算子のためimageCountをインクリメントします。画像のみカウントはこの方法で数百ミリ秒かかる:

public class PdfImageCounter extends PDFStreamEngine { 
    protected int documentImageCount = 0; 

    public int getDocumentImageCount() { 
     return documentImageCount; 
    } 

    public PdfImageCounter() { 
     addOperator(new OperatorProcessor() { 
      @Override 
      public void process(Operator operator, List<COSBase> arguments) throws IOException { 
       if (arguments.size() < 1) { 
        throw new MissingOperandException(operator, arguments); 
       } 
       if (isImage(arguments.get(0))) { 
        documentImageCount++; 
       } 
      } 

      protected Boolean isImage(COSBase base) { 
       return (base instanceof COSName) && 
         context.getResources().isImageXObject((COSName)base); 
      } 

      @Override 
      public String getName() { 
       return "Do"; 
      } 
     }); 
    } 
} 

各ページのためにそれを起動します。

protected int countImagesWithProcessor(PDDocument pdf) throws IOException { 
    StopWatch stopWatch = new StopWatch(); 
    stopWatch.start(); 

    PdfImageCounter counter = new PdfImageCounter(); 
    for (PDPage pdPage : pdf.getPages()) { 
     counter.processPage(pdPage); 
    } 

    stopWatch.stop(); 
    int imageCount = counter.getDocumentImageCount(); 
    log.info("Images counted: time={}s,imageCount={}", stopWatch.getTime()/1000, imageCount); 
    return imageCount; 
} 
+1

しかし、イメージではないオペランドをクロールすることはありません。 PDFormXObject。 org.apache.pdfbox.contentstream.operator.DrawObjectを見てください。これは、画像の作成を避けるための面白い戦略があります。 –

+0

ありがとう!私は本当に画像ではないオブジェクトをスキップするように更新された答えを更新しました。 –

+0

私が意味することはそれ以上です。フォームや透明度グループをヒットした場合は、org.apache.pdfbox.contentstream.operator.DrawObjectのように、フォームまたは透過グループを処理する必要があります。これらは画像も含むことができます。次に行うべきことは、画像が一意であることを確認することです。セットを使用してください。 –

関連する問題