PythonでPDF差分検出

# PythonでPDF差分検出＆加工を自動化するスクリプトを作成してみた

## はじめに

業務でPDFファイルの変更箇所を自動検出し、画像化してレポートを作成する必要があったため、Pythonを用いて以下の機能を実現するスクリプトを作成しました。

- **PDFの差分検出とマーキング**  
- **差分画像のPDFへの埋め込み**  
- **不要なPDFの削除**  
- **PDFの最適化**  

本記事では、このプロセスを実現するPythonコードを解説していきます。

---

## 使用するライブラリ

まず、スクリプトで使用する主なライブラリです。

```bash
# 必要なライブラリをインストール
pip install opencv-python pillow pdf2image fitz pikepdf
```

- **OpenCV**: 画像処理全般に使用  
- **Pillow**: 画像変換用  
- **pdf2image**: PDFを画像に変換する  
- **fitz**: PDF操作用（PyMuPDFのエイリアス）  
- **pikepdf**: PDFの最適化に使用  

---

## スクリプトの全体構成

以下がスクリプト全体の構成です。

### 1. **PDFを画像に変換**
まず、PDFをページごとに画像へ変換します。

```python
from pdf2image import convert_from_path

def pdf_to_images(pdf_path):
    """
    PDFファイルを画像に変換
    """
    return convert_from_path(pdf_path)
```

### 2. **差分検出とマーキング**
2つの画像の差分を検出し、差分箇所をマーキングします。

```python
import cv2
import numpy as np

def mark_differences(image1, image2, output_path):
    """
    2つの画像の差分を検出してマーキング
    """
    # OpenCV形式に変換
    img1 = cv2.cvtColor(np.array(image1), cv2.COLOR_RGB2BGR)
    img2 = cv2.cvtColor(np.array(image2), cv2.COLOR_RGB2BGR)

    # 差分計算
    diff = cv2.absdiff(img1, img2)
    gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY)

    # 差分箇所を囲む
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    blank_img = np.ones_like(img2) * 255  # 白背景画像

    for contour in contours:
        if cv2.contourArea(contour) > 0:
            cv2.drawContours(blank_img, [contour], -1, (203, 192, 255), 10)

    # 結果を保存
    cv2.imwrite(output_path, blank_img)
    return output_path
```

---

### 3. **PNG画像をPDFに埋め込む**
マーキング済みの画像を元のPDFに重ねて保存します。

```python
import fitz

def add_images_to_pdf(pdf_path, image_paths, output_pdf_path):
    """
    PNG画像をPDFに埋め込み
    """
    pdf_document = fitz.open(pdf_path)

    for i, image_path in enumerate(image_paths):
        page = pdf_document[i]
        img_rect = fitz.Rect(0, 0, page.rect.width, page.rect.height)
        page.insert_image(img_rect, filename=image_path, overlay=False)

    pdf_document.save(output_pdf_path)
```

### 4. **不要なPDFを削除**
不要なPDFファイルを削除します。

```python
import os

def delete_files_with_keyword(directory, keyword):
    """
    指定したキーワードを含むPDFファイルを削除
    """
    for filename in os.listdir(directory):
        if keyword in filename and filename.endswith(".pdf"):
            os.remove(os.path.join(directory, filename))
```

### 5. **PDFの最適化**
生成されたPDFを最適化し、空のPDFは削除します。

```python
import pikepdf

def process_and_optimize_pdf(final_output_pdf):
    """
    PDFの最適化と空ファイルの削除
    """
    if os.path.exists(final_output_pdf):
        with pikepdf.open(final_output_pdf) as pdf:
            temp_pdf_path = final_output_pdf + "_temp"
            pdf.save(temp_pdf_path, linearize=True)
            os.replace(temp_pdf_path, final_output_pdf)
```

---

## サンプルコードの実行例

```python
if __name__ == "__main__":
    # 入力PDFのパス
    old_pdf = "old_version.pdf"
    new_pdf = "new_version.pdf"
    output_folder = "./output/"
    
    # PDFを画像に変換
    old_images = pdf_to_images(old_pdf)
    new_images = pdf_to_images(new_pdf)

    # 差分検出とマーキング
    for i, (old_image, new_image) in enumerate(zip(old_images, new_images)):
        output_path = f"{output_folder}/page_{i+1}_diff.png"
        mark_differences(old_image, new_image, output_path)

    # 差分画像をPDFに埋め込み
    diff_images = [f"{output_folder}/page_{i+1}_diff.png" for i in range(len(old_images))]
    add_images_to_pdf(new_pdf, diff_images, "diff_report.pdf")
```

---

## まとめ

今回のスクリプトでは、PDFの差分検出からレポート作成までを自動化しました。この方法を応用すれば、業務の効率化が図れます。ぜひプロジェクトで活用してみてください！