インストール項目
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()

コメント