🙆
Google Cloud Vision APIを用いて、単一ページから構成される透明テキスト付きPDFを作成する
概要
PDFを対象に、Google Cloud Vision APIを使って、透明テキスト付きPDFを作成する機会がありましたので、備忘録です。
以下、simple
で検索した例です。
背景
今回は単一ページから構成されるPDFを対象とします。
手順
画像の作成
OCRの対象とする画像を作成します。
デフォルトの設定だとボヤけた画像ができてしまったので、解像度を2倍に設定し、また後述するプロセスで、解像度を考慮した位置合わせを実施しています。
以下をインストールします。
requirements.txt
PyMuPDF
Pillow
import fitz # PyMuPDF
from PIL import Image
import json
from tqdm import tqdm
import io
# 入力PDFファイルと出力PDFファイル
input_pdf_path = "./input.pdf" # 単一ページのPDFファイル
output_pdf_path = "./output.pdf"
# 入力PDFファイルを開き、単一ページを読み込み
pdf_document = fitz.open(input_pdf_path)
page = pdf_document[0] # 最初のページを選択
# ページを画像としてレンダリングし、OCRでテキストを抽出
# pix = page.get_pixmap() # 解像度300 DPIでレンダリング
zoom = 2.0
# 解像度を上げるためにズーム設定
mat = fitz.Matrix(zoom, zoom)
pix = page.get_pixmap(matrix=mat)
img = Image.open(io.BytesIO(pix.tobytes("png")))
img.save("./image.png")
Google Cloud Vision API
出力された画像を対象に、Google Cloud Vision APIを適用します。
{
"textAnnotations": [
{
"boundingPoly": {
"vertices": [
{
"x": 141,
"y": 152
},
{
"x": 1082,
"y": 152
},
{
"x": 1082,
"y": 1410
},
{
"x": 141,
"y": 1410
}
]
},
"description": "Sample PDF...",
"locale": "la"
},
{
"boundingPoly": {
"vertices": [
{
"x": 141,
"y": 159
},
{
"x": 363,
"y": 156
},
{
"x": 364,
"y": 216
},
{
"x": 142,
"y": 219
}
]
},
"description": "Sample"
},
{
"boundingPoly": {
"vertices": [
{
"x": 382,
"y": 156
},
{
"x": 506,
"y": 154
},
{
"x": 507,
"y": 213
},
{
"x": 383,
"y": 215
}
]
},
"description": "PDF"
},
...
出力結果として得られるJSONファイルを./google_ocr.json
といった名前で保存します。
そして、以下のようにOCR結果を取得します。
json_path = "./google_ocr.json"
# JSONファイルからOCRテキストデータを読み込む
with open(json_path, "r") as f:
response = json.load(f)
texts = response["textAnnotations"]
透明テキストの作成
以下のスクリプトにより、PDFに反映します。ポイントとして、「フォントサイズを調整して収まるか確認」する必要がありました。
# ページサイズを取得
rect = page.rect
# OCRテキストを透明テキストとして追加
if texts:
for text in tqdm(texts[1:]): # texts[0]はページ全体のテキストなのでスキップ
vertices = text["boundingPoly"]["vertices"]
x_min = min([v["x"] for v in vertices if v])
y_min = min([v["y"] for v in vertices if v])
x_max = max([v["x"] for v in vertices if v])
y_max = max([v["y"] for v in vertices if v])
x_min = x_min / zoom
y_min = y_min / zoom
x_max = x_max / zoom
y_max = y_max / zoom
# バウンディングボックスを定義
bbox_rect = fitz.Rect(x_min, y_min, x_max, y_max)
content = text["description"]
# 初期フォントサイズ
fontsize = 10
fits = False
# フォントサイズを調整して収まるか確認
while fontsize > 0:
res = page.insert_textbox(bbox_rect, content, fontsize=fontsize, color=(0, 0, 0, 0), render_mode=3, align=1)
if res >= 0:
fits = True
break
fontsize -= 1 # フォントサイズを縮小
if not fits:
print(f"'{content}' could not fit in the rectangle.")
# 変更したPDFを保存
pdf_document.save(output_pdf_path)
pdf_document.close()
結果
以下で公開されているPDFを対象にします。
結果、以下のように透明テキスト付きPDFを作成することができました。
まとめ
特定のページのみOCRが必要な際などに、本記事が参考になれば幸いです。
Discussion