6枚1ページのスライド資料を分割する w /python

PDFならばスライド1枚につき1ページにすべし

オンライン業務・資料の電子化が進む昨今、スライド資料をPDFで配布・受領する機会が増えました。で、いきなり本題ですがスライド資料を6枚/1ページにして渡す人って何なんですかね?1枚/1ページにして配布くれれば受け取る側で自由にn枚/1ページにできるのに、どうして何ですかね?というかそもそも、電子デバイス上で複数枚が1ページになっているのって見にくくないですか?紙で配っていた時代から脳が止まって考えることを辞めてしまったのでしょうか。

ということでPDFを分割します

1枚のPDFを複数枚に分割するサービスが見つからなかったので、Chat-GPT4oに以下のpythonコードを書いてもらいました。
利用するにはHomeblewのインストールが必要だと思います(その他複数のモジュールも必要かもしれません。忘れました)

import os
import cv2
import numpy as np
from pdf2image import convert_from_path
from PIL import Image
from PyPDF2 import PdfMerger, PdfWriter
from io import BytesIO

# PDFを特定の座標で分割し、その場で結合する関数
def split_and_merge_pdf(input_pdf, output_pdf_name, dpi=300):
    merger = PdfMerger()

    # PDFを画像に変換(解像度設定を追加)
    images = convert_from_path(input_pdf, dpi=dpi)

    for page_num, img in enumerate(images):
        # ページが横向きの場合は自動で縦向きに回転
        if img.width > img.height:
            img = img.rotate(90, expand=True)
        
        # 画像を一時ファイルに保存
        img_path = f"temp_page_{page_num+1}.png"
        img.save(img_path)

        # OpenCVで画像を読み込み
        image = cv2.imread(img_path)
        
        # グレースケールに変換
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

        # 二値化(黒い部分を白、他を黒にする)
        _, thresh = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY_INV)

        # 輪郭を検出
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        # 四角形の座標リスト
        coordinates = []

        # 検出された四角形を確認
        for cnt in contours:
            # 輪郭を近似して四角形かどうかを確認
            epsilon = 0.02 * cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt, epsilon, True)

            # 頂点が4つなら四角形とみなす
            if len(approx) == 4:
                # 四角形の座標を取得
                x, y, w, h = cv2.boundingRect(approx)
                coordinates.append((x-30, y-30, w+60, h+60))  # 座標を保存

        # 検出した四角形をY座標とX座標でソート
        coordinates = sorted(coordinates, key=lambda coord: (coord[1], coord[0]))

        # 切り取った部分を順次メモリ内でPDFに変換し、結合
        pil_img = Image.open(img_path)
        for i, (x1, y1, width, height) in enumerate(coordinates):
            x2 = x1 + width
            y2 = y1 + height
            cropped_img = pil_img.crop((x1, y1, x2, y2))

            # メモリ内でPDFに変換
            pdf_bytes = BytesIO()
            cropped_img.save(pdf_bytes, format="PDF")
            pdf_bytes.seek(0)

            # メモリ内のPDFを結合
            merger.append(pdf_bytes)

        print(f"Page {page_num+1} processed with {len(coordinates)} parts")

    # 結合したPDFを出力
    with open(output_pdf_name, 'wb') as output_pdf_file:
        merger.write(output_pdf_file)
    
    merger.close()
    print(f"Merged PDF saved as {output_pdf_name}")

# 実行部分
import os
def get_pdfs_from_directory(directory):
    # ディレクトリ内のPDFファイルをリストアップ
    pdf_files = [f for f in os.listdir(directory) if f.endswith('.pdf')]
    # ファイルが存在しない場合はエラーメッセージを表示
    if not pdf_files:
        raise FileNotFoundError(f"No PDF files found in directory: {directory}")
    
    return pdf_files

import os

def get_pdfs_from_directory(directory):
    # ディレクトリ内のPDFファイルをリストアップ
    pdf_files = [f for f in os.listdir(directory) if f.endswith('.pdf')]
    # ファイルが存在しない場合はエラーメッセージを表示
    if not pdf_files:
        raise FileNotFoundError(f"No PDF files found in directory: {directory}")
    
    return pdf_files

if __name__ == "__main__":
    input_dir = '任意のディレクトリを追加してください'  # PDFファイルが存在するディレクトリ
    output_dir = "任意のディレクトリを追加してください"  # 出力先のディレクトリ
    
    # ディレクトリ内のPDFファイルを取得
    pdf_files = get_pdfs_from_directory(input_dir)

    # 各PDFファイルに対して処理を行う
    for pdf_file in pdf_files:
        input_pdf = os.path.join(input_dir, pdf_file)  # 各PDFファイルを指定
        
        # 入力ファイル名から拡張子を削除し、出力ファイル名を作成
        base_filename = os.path.splitext(pdf_file)[0]  # 拡張子を除いたファイル名
        output_pdf_name = os.path.join(output_dir, f"{base_filename}.pdf")  # "_merged" を付加

        # PDFを切り取って結合する処理
        split_and_merge_pdf(input_pdf, output_pdf_name, dpi=300)

        print(f"Processed and merged: {pdf_file} -> {output_pdf_name}")

黒い四角い枠を判定して、そこから少し大きめにクロップして1枚ずつ結合しています。改善点があれば教えてくださると助かります。

いいなと思ったら応援しよう!