PerfectVector
著者: Jay

Pythonと画像ベクター化APIでSVGを保存する方法:テストと変換の復旧

PythonからPNG、JPEG、WebPをベクター化APIに送信してSVGを保存します。テスト応答の確認と、接続切れ後に既存の変換を取り直す方法も解説します。

2 分で読める
目次

Pythonスクリプトでラスター画像をPerfectVectorのベクター化APIに送り、返されたSVGを元画像と同じ場所に保存できます。次の例では、ダウンロード前に変換IDを表示します。接続が途切れたら、そのIDまたは自分で決めたリクエストキーから結果を取り直せます。

お手持ちの画像で試す
フル機能のツールを開く
無料でお試しいただけます。画像をドロップすると、登録なしでベクターのプレビューを確認できます。

テストキーと画像を1枚用意する

Developer APIのページでテストキーを作成します。ターミナルで環境変数 PERFECTVECTOR_API_KEY に設定し、スクリプトやバージョン管理には入れないでください。プロジェクトのPython環境で python -m pip install requests を実行します。

最初は logo.png という512 × 512ピクセルのロゴなど、単一フレームのPNG、JPEG、WebPを使います。APIは5 MiB以下で、各辺が100~4,096ピクセルの画像を受け付けます。テストモードはアップロードを検証し、「TEST MODE」と記された固定のSVGを無料で返します。このファイルでリクエストとダウンロードを確認できます。トレースの品質は、自分の画像を本番変換して評価します。PerfectVector APIのテストモード

テストキーでPythonスクリプトを実行すると、ダウンロードしたSVGを正常に解析できました。同じリクエストキーで同じアップロードを再送すると、どちらも同じ変換IDが返りました。

処理の流れ
ローカルのラスター画像が識別タグ付きのクラウドリクエストを経由し、ベクターファイルとしてディスクに戻ります。
リクエストキーをアップロードとひも付け、SVGをダウンロードする前に変換IDを保存します。

Pythonで画像を送信してSVGを保存する

次のコードを vectorize.py として保存します。Requestsの files 引数でマルチパートのアップロードを作成し、元画像のデータを image フォームフィールドに入れます。Requestsのマルチパートアップロード

import os
import sys
import time
from pathlib import Path
 
import requests
 
API = "https://api.perfectvector.com/v1"
AUTH = {"Authorization": f"Bearer {os.environ['PERFECTVECTOR_API_KEY']}"}
 
 
def checked(response):
    content_type = response.headers.get("Content-Type", "").split(";")[0]
    if response.status_code >= 400 and content_type == "application/problem+json":
        problem = response.json()
        raise RuntimeError(f"{problem['code']} (request {problem['request_id']})")
    response.raise_for_status()
    return response
 
 
def vectorize(source, destination, operation_id):
    with source.open("rb") as image:
        response = checked(requests.post(
            f"{API}/vectorizations",
            headers={**AUTH, "Idempotency-Key": operation_id, "Prefer": "wait=10"},
            files={"image": (source.name, image)},
            timeout=30,
        ))
 
    conversion = response.json()
    conversion_id = conversion["id"]
    print(f"Conversion ID: {conversion_id}", flush=True)
    deadline = time.monotonic() + 720
    while conversion["status"] in {"queued", "processing"}:
        if time.monotonic() >= deadline:
            raise TimeoutError(f"Stopped polling {conversion_id}")
        time.sleep(2)
        conversion = checked(requests.get(
            f"{API}/vectorizations/{conversion_id}", headers=AUTH, timeout=30,
        )).json()
 
    if conversion["status"] != "succeeded":
        error = conversion["results"][0].get("error") or {}
        raise RuntimeError(f"{conversion_id}: {error.get('code', conversion['status'])}")
 
    svg = checked(requests.get(
        f"{API}/vectorizations/{conversion_id}/result", headers=AUTH, timeout=30,
    ))
    if svg.headers.get("Content-Type", "").split(";")[0] != "image/svg+xml":
        raise ValueError("Expected an SVG response")
    partial = destination.with_suffix(".svg.part")
    partial.write_bytes(svg.content)
    partial.replace(destination)
    print(f"{destination} saved ({len(svg.content)} bytes)")
 
 
if __name__ == "__main__":
    source = Path(sys.argv[1])
    if source.suffix.lower() not in {".png", ".jpg", ".jpeg", ".webp"}:
        raise SystemExit("Choose a PNG, JPEG or WebP file")
    if source.stat().st_size > 5 * 1024 * 1024:
        raise SystemExit("Choose a file no larger than 5 MiB")
    vectorize(source, source.with_suffix(".svg"), sys.argv[2])

logo.png があるディレクトリから実行します。コマンドを実行する前に、変換用のIDを決めて記録します。APIではこれを冪等性キーと呼び、同じアップロードの再送を1つの変換にひも付けます。例では logo-asset-0001 を使います。画像や操作が異なる場合は、別の値を使います。PerfectVectorの安全な再試行

python vectorize.py logo.png logo-asset-0001

テストが成功すると変換IDとバイト数が表示され、logo.png の隣に logo.svg が保存されます。logo.svg を開いて、サンプルが表示されるか確認します。スクリプトは新しいSVGのダウンロードが終わると、同名の既存SVGを置き換えます。初回は作業用フォルダーで試してください。

スクリプトはサーバーに、未完了の変換を返すまで最大10秒待つよう求めます。queued と processing の変換は約2秒間隔で確認し、succeeded になってからダウンロードします。テスト変換はすぐに終わります。本番ワーカーに組み込む前に、ローカルのテストデータで未完了の応答を再現して、ポーリングの分岐を確認します。PerfectVectorの待機と非同期処理

タイムアウト後に既存の変換を取り直す

Pythonが変換IDを表示していれば、GET /v1/vectorizations/{id} でそのIDの状態を調べます。成功後、同じアカウントとモードで GET /v1/vectorizations/{id}/result を呼び、SVGをダウンロードします。結果のリンクは完了から24時間で期限が切れるため、後から使うなら自分のストレージにコピーしてください。PerfectVectorの結果ダウンロード

IDが表示される前にPythonが止まったら、最初のリクエストから24時間以内に、同じファイルのデータと同じ冪等性キーを使ってコマンドを実行し直します。最初の送信が受理されていれば、APIは既存の変換を返します。この期間を過ぎると、同じキーで送っても新しい変換が始まる場合があります。PerfectVectorの安全な再試行

表示された内容確認すること
401 invalid_api_keyPythonが使う環境に有効なテストキーを設定します。キーを失効させた場合は、同じモードで作り直します。
415 unsupported_media_type または 422 invalid_image元ファイルを開き、実際の形式とピクセル寸法を確認します。修正した画像を保存し、そのアップロードには別のキーを使います。
変換IDの後に processing_failedAPIは変換の記録を作成しましたが、画像をトレースできませんでした。元画像を調べ、修正したファイルを別の操作として送ります。
409 idempotency_conflict同じキーで異なる画像データまたはオプションを使っています。元ファイルで復旧するか、新しい入力に新しいキーを割り当てます。

サポートに連絡する場合は、例外に表示されたリクエストIDか、スクリプトが表示した変換IDを控えます。APIエラーのリファレンスで安全に再試行できるエラーを確認できます。

よくある質問

同じPythonスクリプトでJPEGやWebPもベクター化できますか?

はい。最初の引数にJPEGまたはWebPのファイルを指定します。ファイルサイズ、寸法、単一フレームの条件は同じで、出力の拡張子はSVGです。

このPythonの例でフォルダー全体を処理できますか?

まず1件のアップロードとダウンロードを確認してください。フォルダーを処理する場合は、各ファイルのキー、変換ID、出力パスを永続的なキューに記録します。PerfectVectorは既定で1アカウントにつき同時に1件の本番変換を許可するので、次を送る前に前の変換が終わるまで待ちます。PerfectVector APIの制限

参考資料

  1. Requests — クイックスタート — マルチパートのファイル、タイムアウト、バイト応答。
  2. PerfectVector — テストモード — アップロードの制限、固定のテストSVG、クレジットの扱い。
  3. PerfectVector — 待機と非同期処理 — サーバーの待機指定、ポーリング、テストリクエストの完了時点。
  4. PerfectVector — 冪等性 — キーの有効期間、適用範囲、競合、再送条件。
  5. PerfectVector — 結果 — 結果のエンドポイント、認証、有効期限。
  6. PerfectVector — エラー — HTTPの問題コードと復旧方法。
  7. PerfectVector — 制限 — 本番の既定同時実行数とファイル制限。

PerfectVector APIのページでテストキーを取得し、手元の画像1枚でスクリプトを実行します。保存されたSVGが開けることと、ターミナルに変換IDが表示されることを確認してください。

ブログのその他の記事

PerfectVector

画像を
きれいで編集可能なベクターパスに変換

ベクター化とプレビューの確認は無料で、1日あたりの上限はありません。