# app.py (HF Space) } def ocr_and_parse(img) -> Dict[str, Any]: """Run OCR then parse fields with basic validation.""" # OCR output is a list of dicts with 'generated_text' raw = ocr(img)[0]["generated_text"] fields = parse_fields(raw) # basic validation messages warn = [] if not fields["drug_name"]: warn.append("약 이름 인식이 불확실합니다.") if not fields["times_per_day"]: warn.append("1일 횟수를 찾지 못했습니다 (예: 1일 3회).") return {"raw_text": raw, "fields": fields, "warnings": warn} def render_card(fields: Dict[str, Any]) -> Image.Image: """Draw a simple schedule card (PNG) from parsed fields.""" W, H = 720, 400 img = Image.new("RGB", (W, H), "white") d = ImageDraw.Draw(img) title = "오늘 복용 일정" d.rectangle((0, 0, W, 60), fill=(230, 240, 255)) d.text((24, 18), title, fill=(0, 0, 0)) y = 90 def line(label, value): nonlocal y d.text((24, y), f"{label}", fill=(60, 60, 60)) d.text((180, y), f": {value if value else '-'}", fill=(0, 0, 0)) y += 34 line("약 이름", fields.get("drug_name")) line("1회 용량", fields.get("dose_per_intake")) line("1일 횟수", fields.get("times_per_day")) slots = ", ".join(fields.get("time_slots") or []) line("시간대", slots if slots else None) d.text((24, H-60), "※ 의료진 처방 우선, 본 앱은 안내용입니다.", fill=(120, 120, 120)) return img def run_pipeline(image): if image is None: return "이미지를 업로드하세요.", None, None out = ocr_and_parse(image) card = render_card(out["fields"]) csv_row = to_csv_row(out) return json.dumps(out, ensure_ascii=False, indent=2), card, csv_row def to_csv_row(out: Dict[str, Any]) -> str: f = out["fields"] row = [ f.get("drug_name") or "", f.get("dose_per_intake") or "", str(f.get("times_per_day") or ""), ";".join(f.get("time_slots") or []), ] return ",".join(row) with gr.Blocks(theme=gr.themes.Soft()) as demo: gr.Markdown("# MedCard-KR · 약봉투 OCR → 복용 일정 카드") with gr.Row(): with gr.Column(): img_in = gr.Image(type="pil", label="약 봉투/라벨 사진") btn = gr.Button("인식 & 카드 생성", variant="primary") csv = gr.Textbox(label="CSV(약명,1회용량,1일횟수,시간대)") with gr.Column(): json_out = gr.Code(label="인식 결과(JSON)") card = gr.Image(type="pil", label="일정 카드(미리보기)") btn.click(run_pipeline, inputs=img_in, outputs=[json_out, card, csv]) if __name__ == "__main__": demo.queue().launch()