import json import numpy as np from PIL import Image import subprocess import sys import logging import gradio as gr from typing import Tuple, Dict, Optional import tempfile import os def install(package): subprocess.check_call([sys.executable, "-m", "pip", "install", package]) # 依存関係のインストール install("opencv-python-headless") install("moondream") install("openai") import cv2 import moondream as md from openai import OpenAI class PrivacyProtector: def __init__(self, moondream_api_key, deepseek_api_key): self.moon_model = md.vl(api_key=moondream_api_key) self.deepseek_client = OpenAI( api_key=deepseek_api_key, base_url="https://api.deepseek.com" ) def analyze_risk(self, image_path): """画像のリスク分析を行う""" try: pil_image = Image.open(image_path) cv_image = cv2.imread(image_path) if cv_image is None: raise ValueError("画像の読み込みに失敗しました") encoded_image = self.moon_model.encode_image(pil_image) caption = self.moon_model.caption(encoded_image)["caption"] analysis_prompt = f""" 以下の画像説明を基に個人情報漏洩リスクを分析し、厳密にJSON形式で返答してください: {{ "risk_level": "high|medium|low", "risk_reason": "リスクの具体的理由", "objects_to_remove": ["消去すべきオブジェクトリスト"] }} 画像説明: {caption} """ response = self.deepseek_client.chat.completions.create( model="deepseek-chat", messages=[ {"role": "system", "content": "あなたは優秀なセキュリティ分析AIです。"}, {"role": "user", "content": analysis_prompt} ], temperature=0.3, response_format={"type": "json_object"} ) result = json.loads(response.choices[0].message.content) return pil_image, cv_image, result except Exception as e: logging.error(f"リスク分析エラー: {str(e)}") raise def remove_objects(self, pil_image, cv_image, objects_to_remove): """オブジェクト検出と消去処理""" try: mask = np.zeros(cv_image.shape[:2], dtype=np.uint8) h, w = cv_image.shape[:2] for obj_name in objects_to_remove: detection = self.moon_model.detect(pil_image, obj_name) for obj in detection["objects"]: x_min = int(obj['x_min'] * w) y_min = int(obj['y_min'] * h) x_max = int(obj['x_max'] * w) y_max = int(obj['y_max'] * h) cv2.rectangle(mask, (x_min, y_min), (x_max, y_max), 255, -1) inpainted = cv2.inpaint(cv_image, mask, 3, cv2.INPAINT_TELEA) return inpainted except Exception as e: logging.error(f"オブジェクト削除エラー: {str(e)}") raise def process_image(self, image_path): """画像処理フロー全体""" try: pil_img, cv_img, result = self.analyze_risk(image_path) if result['risk_level'] != 'low': cleaned = self.remove_objects(pil_img, cv_img, result['objects_to_remove']) return cleaned, result return cv_img, result except Exception as e: logging.error(f"画像処理エラー: {str(e)}") raise def gradio_process(input_image): try: # 一時ファイルの処理 with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_file: input_path = tmp_file.name cv2.imwrite(input_path, cv2.cvtColor(input_image, cv2.COLOR_RGB2BGR)) # 環境変数の安全な取得 protector = PrivacyProtector( moondream_api_key=os.environ.get('MOONDREAM'), deepseek_api_key=os.environ.get('DEEPSEEK') ) # 画像処理実行 processed_image, result = protector.process_image(input_path) # 結果の整形 output_image = cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB) info_html = f"""

分析結果

生成キャプション: {result.get('caption', '')}

リスクレベル: {result['risk_level'].upper()}

理由: {result['risk_reason']}

消去対象: {', '.join(result['objects_to_remove'])}

""" return input_image, output_image, info_html except Exception as e: error_msg = f"処理中にエラーが発生しました: {str(e)}" return input_image, None, f'
{error_msg}
' finally: # 一時ファイルのクリーンアップ if 'input_path' in locals() and os.path.exists(input_path): os.remove(input_path) with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# 🛡️ プライバシー保護画像処理ツール") with gr.Row(): with gr.Column(scale=2): input_img = gr.Image(label="入力画像", type="numpy") submit_btn = gr.Button("画像を分析・処理", variant="primary") with gr.Column(scale=2): output_img = gr.Image(label="処理後の画像") info_output = gr.HTML(label="分析結果") with gr.Accordion("処理の詳細", open=False): gr.Markdown(""" **処理フロー:** 1. 画像キャプション生成 (Moondream) 2. リスク分析 (DeepSeek) 3. オブジェクト検出・消去 (Moondream + OpenCV) """) submit_btn.click( fn=gradio_process, inputs=input_img, outputs=[input_img, output_img, info_output] ) if __name__ == "__main__": demo.launch()