import gradio as gr import torch from transformers import Qwen3VLForConditionalGeneration, AutoProcessor from PIL import Image import numpy as np import base64 from io import BytesIO # モデルとプロセッサの読み込み model_name = "Qwen/Qwen3-VL-4B-Instruct" print(f"Loading model: {model_name}") print(f"CUDA available: {torch.cuda.is_available()}") # デバイスとdtypeの設定 if torch.cuda.is_available(): device = "cuda" dtype = torch.bfloat16 else: device = "cpu" dtype = torch.float32 model = Qwen3VLForConditionalGeneration.from_pretrained( model_name, torch_dtype=dtype, device_map="auto" if torch.cuda.is_available() else None, ) processor = AutoProcessor.from_pretrained(model_name) # CPUの場合はモデルを明示的に移動 if not torch.cuda.is_available(): model = model.to(device) print("Model loaded successfully!") def transcribe_handwriting(image): """手書き文字画像をOCRで文字起こしする""" if image is None: return "画像をアップロードしてください。" # numpy配列の場合はPIL Imageに変換 if isinstance(image, np.ndarray): if len(image.shape) == 2: image = Image.fromarray(image).convert('RGB') else: image = Image.fromarray(image) # PIL Imageの場合 if isinstance(image, Image.Image): if image.mode == 'RGBA': background = Image.new('RGB', image.size, (255, 255, 255)) background.paste(image, mask=image.split()[3]) image = background elif image.mode == 'L': image = image.convert('RGB') elif image.mode != 'RGB': image = image.convert('RGB') # メッセージの構築 messages = [ { "role": "user", "content": [ { "type": "image", "image": image, }, { "type": "text", "text": "この画像に書かれている手書きの文字を正確に読み取って、テキストとして出力してください。日本語とアルファベットの両方に対応してください。文字以外の説明は不要です。読み取った文字のみを出力してください。", }, ], } ] # 入力の準備(Qwen3-VL用) inputs = processor.apply_chat_template( messages, tokenize=True, add_generation_prompt=True, return_dict=True, return_tensors="pt", ) inputs.pop("token_type_ids", None) inputs = inputs.to(model.device) # 推論 with torch.no_grad(): generated_ids = model.generate( **inputs, max_new_tokens=512, do_sample=False, ) generated_ids_trimmed = [ out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs["input_ids"], generated_ids) ] output_text = processor.batch_decode( generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False ) return output_text[0] if output_text else "文字を認識できませんでした。" def process_canvas(base64_data): """Canvasからのbase64データを処理""" if not base64_data or base64_data == "": return "手書きしてください。" try: # data:image/png;base64,... の形式から実際のbase64部分を取得 if "," in base64_data: base64_data = base64_data.split(",")[1] # base64デコード image_data = base64.b64decode(base64_data) image = Image.open(BytesIO(image_data)) return transcribe_handwriting(image) except Exception as e: return f"エラーが発生しました: {str(e)}" # カスタムHTML Canvasとdrawing JavaScript canvas_html = """