PDF差分ver1

インストール項目

pip install PyMuPDF opencv-python numpy

コード

import cv2
import numpy as np
import fitz  # PyMuPDF
import tkinter as tk
from tkinter import filedialog

def select_file(title):
    root = tk.Tk()
    root.withdraw()
    file_path = filedialog.askopenfilename(title=title, filetypes=[("PDF files", "*.pdf")])
    return file_path

def pdf_to_image(pdf_path):
    """PDFの1ページ目を高解像度のグレースケール画像(NumPy配列)として読み込む"""
    doc = fitz.open(pdf_path)
    page = doc.load_page(0)  # 最初のページ
    
    # 図面の細かい線を拾うため、解像度を2倍に設定
    zoom = 2.0
    mat = fitz.Matrix(zoom, zoom)
    pix = page.get_pixmap(matrix=mat)
    
    # 配列に変換
    img_data = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n)
    
    # 読み込み時点でグレースケールに変換
    if pix.n == 4:
        gray = cv2.cvtColor(img_data, cv2.COLOR_RGBA2GRAY)
    else:
        gray = cv2.cvtColor(img_data, cv2.COLOR_RGB2GRAY)
    return gray

def compare_pdf_drawings():
    # 1. ファイル選択
    pdf_path1 = select_file("古い図面のPDFファイルを選択してください")
    if not pdf_path1: return
    
    pdf_path2 = select_file("新しい図面のPDFファイルを選択してください")
    if not pdf_path2: return

    output_path = filedialog.asksaveasfilename(
        title="結果を保存する場所とファイル名を指定してください", 
        defaultextension=".pdf", 
        filetypes=[("PDF files", "*.pdf")]
    )
    if not output_path: return

    # 2. PDFをグレースケール画像として取得
    try:
        gray1 = pdf_to_image(pdf_path1)
        gray2 = pdf_to_image(pdf_path2)
    except Exception as e:
        print(f"PDFの読み込みに失敗しました: {e}")
        return
    
    # サイズが異なる場合は、古い図面(gray1)のサイズに新しい図面(gray2)を合わせる
    if gray1.shape != gray2.shape:
        height, width = gray1.shape[:2]
        gray2 = cv2.resize(gray2, (width, height), interpolation=cv2.INTER_AREA)
    
    # 3. カラー出力用のベース画像を作成(新しいPDFのグレースケールをRGB化)
    # これにより、背景や変化のない部分はモノクロになります
    output_img = cv2.cvtColor(gray2, cv2.COLOR_GRAY2BGR)
    
    # 4. 差分の計算
    # 単純な絶対値差分(枠用)
    diff = cv2.absdiff(gray1, gray2)
    _, thresh = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)
    
    # 白背景(255)の図面を想定し、変化した画素を特定
    # 変化があり、かつ新しい図面で黒っぽい(値が小さい)= 新しく追加された要素
    added_mask = (diff > 30) & (gray2 < 200)
    # 変化があり、かつ古い図面で黒っぽい(値が小さい)= 削除された要素
    removed_mask = (diff > 30) & (gray1 < 200)
    
    # 5. 色付け処理(OpenCVはBGR順)
    output_img[added_mask] = [0, 0, 255]    # 新しい内容を「赤」に
    output_img[removed_mask] = [255, 0, 0]  # 古い内容を「青」に
    
    # 6. 緑色の枠で囲む
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for contour in contours:
        x, y, w, h = cv2.boundingRect(contour)
        if w > 10 and h > 10:  # 小さなノイズを除外
            cv2.rectangle(output_img, (x, y), (x + w, y + h), (0, 255, 0), 2)  # 緑枠(BGR: 0, 255, 0)
            
    # 7. PyMuPDFを使ってPDF形式で保存
    try:
        _, img_encoded = cv2.imencode(".png", output_img)
        img_bytes = img_encoded.tobytes()
        
        out_pdf = fitz.open()
        # 古い図面と同じサイズでPDFページを作成
        out_page = out_pdf.new_page(width=gray1.shape[1], height=gray1.shape[0])
        rect = fitz.Rect(0, 0, gray1.shape[1], gray1.shape[0])
        
        out_page.insert_image(rect, stream=img_bytes)
        out_pdf.save(output_path)
        out_pdf.close()
        print("差分の検出が完了し、PDFとして保存しました。")
    except Exception as e:
        print(f"PDFの保存中にエラーが発生しました: {e}")

# 関数の実行
compare_pdf_drawings()

コメント

タイトルとURLをコピーしました