メインコンテンツへスキップ
\n\n## 2. 画像匿名化ツール\n\n[ysharma/OPF-Image-Anonymizer](https://huggingface.co/spaces/ysharma/OPF-Image-Anonymizer) でお試しください。\n\n**ユーザーの課題。** PII(個人識別情報)の上に黒いバーを配置した画像やスクリーンショット(Slack のスレッド、領収書、Stripe ダッシュボードなど)を共有したい場合、バーの表示・非表示を切り替えたり、ドラッグして位置を調整したり、モデルが見逃したものを手動で描画したりしてから結果をエクスポートしたいとします。\n\n**ここで Privacy Filter が行う処理。** Tesseract が OCR を実行し、単語ごとの境界ボックスを返します。バックエンドは文字オフセットからボックスへのマッピングを使用して完全なテキストを再構築し、その後 Privacy Filter を全文に対して一度実行します。検出された文字範囲は単語マッピングに対して照合され、行ごとにピクセル矩形に結合されます。\n\n**ここでgr.Serverが行っていること。** gr.ImageEditorは階層付き注釈をサポートしており、画像のモザイク処理(redaction)の妥当な出発点となります。私たちが望んでいたワークフロー(カテゴリごとのメタデータ、カテゴリ内のすべてのバーを一度に切り替える機能、サーバーとの往復通信なしでクライアントサイドで自然解像度のPNGエクスポートを行う機能)は、カスタムフロントエンド上に構築する方がより簡潔でした。gr.Serverは1つのキューイング済みエンドポイントからピクセル矩形を返し、キャンバスが他のすべてを管理できるようにしています:\n\n@server.api(name=\"anonymize_screenshot\")\ndef anonymize_screenshot(image: FileData) -> dict:\n img = Image.open(image[\"path\"]).convert(\"RGB\")\n full_text, char_to_box = ocr_image(img) # 単語ごとのボックスと文字マップ\n spans = run_privacy_filter(full_text)\n boxes = spans_to_pixel_boxes(spans, char_to_box)\n return {\n \"image_data_url\": pil_to_base64(img),\n \"width\": img.width,\n \"height\": img.height,\n \"boxes\": boxes, # [{x, y, w, h, label, text}, ...]\n }\n\nフロントエンドは client.predict(\"/anonymize_screenshot\", { image: handle_file(file) }) を用いてこれを呼び出します。これは上記と同じパターンです。切り替え、ドラッグ操作、新しいバーの描画、PNGエクスポートはすべてブラウザ内で発生し、編集内容はサーバーへの往復通信を行いません。\n\n## 3. SmartRedact Paste\n\n[ysharma/OPF-SmartRedact-Paste](https://huggingface.co/spaces/ysharma/OPF-SmartRedact-Paste) でお試しください。\n\n**ユーザーの課題。** 共有前に機密情報を伏字にするPastebin(テキスト共有サービス)が欲しい。ログ行、メールアドレス、サポートチケットなどを貼り付けると、2つのURLが返ってくる。公開用URLは、[公式ブログの例](https://openai.com/index/introducing-openai-privacy-filter/#:~:text=coherent%20masking%20boundaries.-,Example%20input%20text,-Subject%3A%20Q2%20Planning)で示された伏字の規約に従い、``、``、``というプレースホルダーを使用して伏字処理されたバージョンを提供する。プライベート用URLは保持しているトークンで保護され、元のテキストが表示されるが、特定の範囲(span)が強調表示される。\n\n**ここでPrivacy Filterが行う処理。** 検出された各範囲を、保存されたペースト内の``プレースホルダーに置き換える。これこそが伏字処理の全工程である。多言語テキスト(モデルカードの例に含まれるスペイン語、フランス語、中国語、ヒンディー語など)は、同じ呼び出しを通じて処理され、変更はない。\n\n**ここでgr.Serverが行う処理。** このアプリには、同じペーストIDに対して公開用とトークン保護用の2つの異なるGETルートが必要であり、URLの形状が重要になる。これは「表示用URL」を保持するものだからである。gr.Serverは、その内部がFastAPIアプリであるため(そのため`@server.api`と通常の`@server.get`を同じプロセス内で並置できる)、ここで機能する。なお、[FastAPIを使用してカスタムルートをマウント](https://www.gradio.app/docs/gradio/mount_gradio_app)することで、`gr.Blocks()`でもこれを構築可能である:\n\n# モデル呼び出し → キュー化されたエンドポイント。ブラウザからのアクセスは\n# client.predict(\"/create_paste\", { text, ttl }) を介して行われます。\n@server.api(name=\"create_paste\")\ndef create_paste(text: str, ttl: str = \"never\") -> dict:\n source_text, spans = run_privacy_filter(text)\n redacted = redact(source_text, spans) # プレースホルダー\n pid, reveal_token = secrets.token_urlsafe(6), secrets.token_urlsafe(22)\n PASTES[pid] = Paste(pid, reveal_token, source_text, redacted, spans,\n expires_at=_ttl(ttl)) # app.py を参照\n return {\n \"view_path\": f\"/view/{pid}\",\n \"reveal_path\": f\"/view/{pid}?token={reveal_token}\",\n }\n\n# 表示ページ → 通常の FastAPI GET リクエスト。モデル呼び出しもキュー処理も不要で、\n# キュー化されたエンドポイントでは提供できない独自な URL 形式 `/view/{pid}?token=...`\n# を実際に取得できます。\n@server.get(\"/view/{pid}\", response_class=HTMLResponse)\nasync def view_paste(pid: str, token: str | None = None):\n p = _store_get(pid) # ストアの実装は app.py を参照\n if p is None:\n return HTMLResponse(_not_found(), status_code=404)\n revealed = bool(token) and secrets.compare_digest(token, p.reveal_token)\n return HTMLResponse(_render_view(p, revealed))\n\n期限切れの投稿(paste)を削除するデーモンスレッドが、30 秒ごとに実行されます。ストレージを含むこのサービス全体は、すべての処理を単一のプロセスで完結させるため、約 200 行のアプリケーションコードで構成されています。\n\n## gradio.Server が提供する機能\n\n3 つのアプリすべてで分割は同じです。モデルにアクセスするものはすべて @server.api を経由し、それ以外は通常の FastAPI ルートに残ります:\n\n\t\t\n\nアプリ\nキュー処理された計算 (@server.api)\n通常の FastAPI ルート\n\n\t\t\nDocument Privacy Explorer\nanalyze_document — 抽出、検出、統計\nGET / はカスタムリーダービューを提供します\n\nImage Anonymizer\nanonymize_screenshot — OCR、検出、スパン → ピクセルボックス\nGET / および GET /examples/* はキャンバス UI とプリロードされた例を提供します\n\nSmartRedact Paste\ncreate_paste — 検出、赤塗り、ID の発行\nGET / はページを構成し、GET /view/{pid}?token=... は公開およびトークンゲート付きビューを提供し、GET /api/paste/{pid} は JSON ルックアップを提供します\n\n@server.api により、Gradio のキュー(直列化されたリクエスト、ZeroGPU 上の正しい @spaces.GPU コンポジション、進行状況イベント)が提供され、ブラウザは [@gradio/client](https://www.gradio.app/guides/getting-started-with-the-js-client) を通じてこれにアクセスします。同じエンドポイントは、Python から gradio_client ユーザーがアクセスするものでもあります。1 つの関数、2 つの SDK、重複コードなしです。通常の @server.get/@server.post は静的なサーフェス(HTML ページ、ファイルルックアップ、安価な辞書読み取り)に予約されています。これは [gradio.Server 入門投稿](https://huggingface.co/blog/introducing-gradio-server) の経験則であり、UI が非常に異なっていても、これら 3 つのアプリが一貫性を持って感じられる理由です。\n\n## 試してみる\n\n- Document Privacy Explorer\n\n- Image Anonymizer\n\n- SmartRedact Paste\n\n履歴書、Slack のスレッドのスクリーンショット、トークンを含むログ行などを投入してください。実際に気にしているテキストに対して、Privacy Filter が何を(そして稀に何を)検出するかを見てみるのが楽しい部分です。\n\n## 推奨読書\n\n- OpenAI のリリース記事:OpenAI Privacy Filter の紹介\n\n- Hugging Face 上のモデルカード:openai/privacy-filter\n\n- モデルカードにあるリダクション(匿名化)の例と分類体系","image":["https://news.ainew.jp/article/f24f2049-0781-434d-bbe2-376f71d52b81/opengraph-image"],"datePublished":"2026-04-27T00:00:00.000Z","dateModified":"2026-04-27T15:12:08Z","url":"https://news.ainew.jp/article/f24f2049-0781-434d-bbe2-376f71d52b81","mainEntityOfPage":"https://news.ainew.jp/article/f24f2049-0781-434d-bbe2-376f71d52b81","inLanguage":"ja","isBasedOn":"https://huggingface.co/blog/openai-privacy-filter-web-apps","publisher":{"@type":"Organization","@id":"https://news.ainew.jp/#organization","name":"ainew.jp","url":"https://ainew.jp","logo":{"@type":"ImageObject","url":"https://news.ainew.jp/icon-512.png","width":512,"height":512}},"author":{"@type":"Organization","name":"Hugging Face Blog","url":"https://huggingface.co"}}