Spaces:
Sleeping
Sleeping
File size: 10,082 Bytes
48ed049 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
import gradio as gr
import google.generativeai as genai
from PIL import Image
import io
import base64
import json
from typing import Optional, Tuple
class KoreanOCRApp:
def __init__(self):
self.model = None
self.api_key = None
def configure_api(self, api_key: str) -> str:
"""API 키를 설정하고 모델을 초기화합니다."""
try:
if not api_key or api_key.strip() == "":
return "❌ API 키를 입력해주세요."
genai.configure(api_key=api_key.strip())
self.model = genai.GenerativeModel('gemini-2.5-flash')
self.api_key = api_key.strip()
return "✅ API 키가 성공적으로 설정되었습니다."
except Exception as e:
return f"❌ API 키 설정 중 오류가 발생했습니다: {str(e)}"
def extract_korean_text(self, image: Image.Image, api_key: str) -> Tuple[str, Image.Image]:
"""이미지에서 한국어 텍스트를 추출합니다."""
try:
# API 키가 변경되었거나 처음 설정하는 경우
if not self.model or self.api_key != api_key.strip():
config_result = self.configure_api(api_key)
if "❌" in config_result:
return config_result, image
if not image:
return "❌ 이미지를 업로드해주세요.", None
# 이미지 전처리 (선택사항)
if image.mode != 'RGB':
image = image.convert('RGB')
# 이미지 크기 최적화 (너무 큰 경우)
max_size = 1024
if max(image.size) > max_size:
ratio = max_size / max(image.size)
new_size = tuple(int(dim * ratio) for dim in image.size)
image = image.resize(new_size, Image.Resampling.LANCZOS)
# 한국어 텍스트 추출을 위한 프롬프트
prompt = """
이 이미지에서 모든 한국어 텍스트를 추출해주세요.
다음 규칙을 따라주세요:
1. 이미지에 있는 모든 한국어 텍스트를 정확하게 읽어주세요
2. 텍스트의 위치나 순서를 고려하여 자연스럽게 배열해주세요
3. 줄바꿈이나 문단 구분이 있다면 그대로 유지해주세요
4. 영어나 숫자가 함께 있다면 그것도 포함해주세요
5. 읽을 수 없거나 불분명한 부분이 있다면 [불분명]으로 표시해주세요
추출된 텍스트만 출력해주세요:
"""
# Gemini API 호출
response = self.model.generate_content([prompt, image])
if response.text:
extracted_text = response.text.strip()
success_message = f"✅ 텍스트 추출 완료:\n\n{extracted_text}"
return success_message, image
else:
return "❌ 텍스트를 추출할 수 없습니다.", image
except Exception as e:
error_message = f"❌ 오류가 발생했습니다: {str(e)}"
return error_message, image
def verify_image_display(self, image: Image.Image) -> Tuple[str, Image.Image]:
"""업로드된 이미지와 출력창의 이미지가 같은지 확인합니다."""
if image is None:
return "❌ 이미지를 먼저 업로드해주세요.", None
try:
# 이미지 정보 확인
width, height = image.size
format_info = image.format if hasattr(image, 'format') else "Unknown"
mode = image.mode
verification_text = f"""
✅ 이미지 검증 완료
📊 이미지 정보:
- 크기: {width} x {height} 픽셀
- 포맷: {format_info}
- 색상 모드: {mode}
- 파일 크기: 약 {width * height * (3 if mode == 'RGB' else 1)} 바이트
🔍 이미지가 올바르게 표시되고 있습니다.
"""
return verification_text, image
except Exception as e:
return f"❌ 이미지 검증 중 오류가 발생했습니다: {str(e)}", image
def create_app():
"""그라디오 앱을 생성합니다."""
ocr_app = KoreanOCRApp()
with gr.Blocks(
title="한국어 OCR 텍스트 추출기",
theme=gr.themes.Soft(),
css="""
.container {
max-width: 1200px;
margin: auto;
}
.header {
text-align: center;
padding: 20px;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
margin-bottom: 20px;
}
"""
) as app:
# 헤더
gr.HTML("""
<div class="header">
<h1>🔤 한국어 OCR 텍스트 추출기</h1>
<p>Google Gemini를 사용한 고성능 한국어 텍스트 인식</p>
</div>
""")
with gr.Row():
with gr.Column(scale=1):
# API 키 입력 섹션
gr.Markdown("## 🔑 API 키 설정")
api_key_input = gr.Textbox(
label="Google Gemini API 키",
placeholder="여기에 API 키를 입력하세요...",
type="password",
info="https://makersuite.google.com에서 API 키를 발급받으세요"
)
api_status = gr.Textbox(
label="API 상태",
value="API 키를 입력하고 설정하세요",
interactive=False
)
# API 키 설정 버튼
api_config_btn = gr.Button("🔧 API 키 설정", variant="primary")
gr.Markdown("---")
# 이미지 업로드 섹션
gr.Markdown("## 📤 이미지 업로드")
image_input = gr.Image(
label="한국어 텍스트가 포함된 이미지를 업로드하세요",
type="pil",
height=300
)
# 버튼들
with gr.Row():
extract_btn = gr.Button("📖 텍스트 추출", variant="primary")
verify_btn = gr.Button("🔍 이미지 검증", variant="secondary")
with gr.Column(scale=1):
# 결과 출력 섹션
gr.Markdown("## 📄 추출 결과")
text_output = gr.Textbox(
label="추출된 텍스트",
lines=10,
placeholder="여기에 추출된 한국어 텍스트가 표시됩니다...",
interactive=False
)
gr.Markdown("## 🖼️ 이미지 확인")
image_output = gr.Image(
label="업로드된 이미지 (검증용)",
height=300
)
# 이미지 정보
image_info = gr.Textbox(
label="이미지 정보",
lines=5,
interactive=False
)
# 사용법 안내
with gr.Accordion("📋 사용법 안내", open=False):
gr.Markdown("""
### 🔧 설정 방법
1. **API 키 발급**: [Google AI Studio](https://makersuite.google.com)에서 무료 API 키를 발급받으세요
2. **API 키 입력**: 위의 입력창에 발급받은 API 키를 입력하고 '설정' 버튼을 클릭하세요
### 📖 텍스트 추출 방법
1. **이미지 업로드**: 한국어 텍스트가 포함된 이미지를 업로드하세요
2. **텍스트 추출**: '텍스트 추출' 버튼을 클릭하여 OCR을 실행하세요
3. **결과 확인**: 오른쪽 결과창에서 추출된 텍스트를 확인하세요
### 📋 지원 형식
- **이미지 형식**: PNG, JPEG, WEBP, HEIC, HEIF
- **최대 크기**: 20MB (자동으로 최적화됩니다)
- **언어**: 한국어 중심 (영어, 숫자도 인식 가능)
### 💡 팁
- 선명하고 해상도가 높은 이미지를 사용하세요
- 텍스트가 잘 보이도록 조명이 충분한 사진을 촬영하세요
- 기울어진 이미지는 자동으로 보정을 시도합니다
""")
# 이벤트 핸들러 등록
api_config_btn.click(
fn=ocr_app.configure_api,
inputs=[api_key_input],
outputs=[api_status]
)
extract_btn.click(
fn=ocr_app.extract_korean_text,
inputs=[image_input, api_key_input],
outputs=[text_output, image_output]
)
verify_btn.click(
fn=ocr_app.verify_image_display,
inputs=[image_input],
outputs=[image_info, image_output]
)
# 이미지 업로드 시 자동으로 출력창에 표시
image_input.change(
fn=lambda img: (img, "이미지가 업로드되었습니다. 텍스트 추출을 실행하세요." if img else ""),
inputs=[image_input],
outputs=[image_output, image_info]
)
return app
if __name__ == "__main__":
app = create_app()
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=True,
debug=True,
show_error=True
) |