aliceblue11 commited on
Commit
776940e
·
verified ·
1 Parent(s): e0c9260

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +128 -54
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import gradio as gr
2
  import base64
3
  import requests
4
  import json
@@ -7,6 +7,8 @@ import io
7
  import os
8
  from typing import Optional, Tuple
9
  import re
 
 
10
 
11
  class KoreanOCRApp:
12
  def __init__(self):
@@ -26,22 +28,48 @@ class KoreanOCRApp:
26
  self.project_id = project_id.strip()
27
  return "✅ 인증 정보가 설정되었습니다."
28
 
29
- def encode_image_to_base64(self, image: Image.Image) -> str:
30
- """이미지를 base64로 인코딩"""
31
- buffer = io.BytesIO()
32
- # JPEG 형식으로 저장하여 파일 크기 최적화
 
 
 
 
 
 
 
 
 
33
  if image.mode == 'RGBA':
34
- # RGBA 이미지는 RGB로 변환
35
  background = Image.new('RGB', image.size, (255, 255, 255))
36
  background.paste(image, mask=image.split()[-1])
37
  image = background
 
 
38
 
39
- image.save(buffer, format='JPEG', quality=95)
 
 
 
 
 
 
 
 
40
  image_bytes = buffer.getvalue()
 
 
 
 
 
 
 
 
41
  return base64.b64encode(image_bytes).decode('utf-8')
42
 
43
- def call_gemini_api_direct(self, image_base64: str) -> str:
44
- """Gemini API 직접 호출 (Google AI Studio API 사용)"""
45
  if not self.api_key:
46
  return "❌ 먼저 API 키를 설정해주세요."
47
 
@@ -100,36 +128,62 @@ class KoreanOCRApp:
100
  ]
101
  }
102
 
103
- try:
104
- response = requests.post(url, headers=headers, json=payload, timeout=60)
105
-
106
- if response.status_code == 401:
107
- return "❌ API 키가 유효하지 않습니다. Google AI Studio에서 발급받은 올바른 API 키를 입력해주세요."
108
- elif response.status_code == 403:
109
- return "❌ API 접근 권한이 없습니다. Gemini API가 활성화되어 있는지 확인해주세요."
110
- elif response.status_code == 429:
111
- return "❌ API 호출 한도를 초과했습니다. 잠시 후 다시 시도해주세요."
112
-
113
- response.raise_for_status()
114
-
115
- result = response.json()
116
-
117
- if "candidates" in result and len(result["candidates"]) > 0:
118
- content = result["candidates"][0]["content"]["parts"][0]["text"]
119
- return content.strip()
120
- elif "error" in result:
121
- return f"❌ API 오류: {result['error'].get('message', '알 수 없는 오류')}"
122
- else:
123
- return "❌ 텍스트를 추출할 수 없습니다. 이미지에 한국어 텍스트가 포함되어 있는지 확인해주세요."
124
 
125
- except requests.exceptions.RequestException as e:
126
- return f"❌ API 호출 오류: {str(e)}"
127
- except json.JSONDecodeError:
128
- return "❌ API 응답 파싱 오류가 발생했습니다."
129
- except KeyError as e:
130
- return f"❌ 예상치 못한 API 응답 형식: {str(e)}"
131
- except Exception as e:
132
- return f"❌ 없는 오류: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  def call_vertex_ai_api(self, image_base64: str) -> str:
135
  """Vertex AI API 호출 (서비스 계정 키 사용)"""
@@ -216,22 +270,15 @@ class KoreanOCRApp:
216
  return image, auth_result
217
 
218
  try:
219
- # 이미지 크기 확인 조정
220
- img_byte_array = io.BytesIO()
221
- image.save(img_byte_array, format='JPEG', quality=95)
222
- img_size_mb = len(img_byte_array.getvalue()) / (1024 * 1024)
223
-
224
- if img_size_mb > 4: # 4MB로 제한을 낮춤
225
- # 이미지 크기가 너무 크면 리사이즈
226
- max_dimension = 1920
227
- image.thumbnail((max_dimension, max_dimension), Image.Resampling.LANCZOS)
228
 
229
  # 이미지를 base64로 인코딩
230
  image_base64 = self.encode_image_to_base64(image)
231
 
232
  # API 타입에 따라 호출
233
  if api_type == "Google AI Studio":
234
- extracted_text = self.call_gemini_api_direct(image_base64)
235
  else:
236
  extracted_text = self.call_vertex_ai_api(image_base64)
237
 
@@ -313,7 +360,7 @@ def create_interface():
313
  )
314
 
315
  # API 설정 가이드
316
- with gr.Accordion("📖 API 설정 가이드", open=False):
317
  gr.Markdown("""
318
  ### Google AI Studio API (권장)
319
  1. [Google AI Studio](https://aistudio.google.com/)에 접속
@@ -321,6 +368,12 @@ def create_interface():
321
  3. API 키 생성 및 복사
322
  4. 위의 "API 키" 필드에 붙여넣기
323
 
 
 
 
 
 
 
324
  ### Vertex AI API (고급 사용자용)
325
  1. [Google Cloud Console](https://console.cloud.google.com/)에서 프로젝트 생성
326
  2. Vertex AI API 활성화
@@ -328,13 +381,34 @@ def create_interface():
328
  4. `gcloud auth application-default login` 또는 Access Token 발급
329
  5. API 키와 프로젝트 ID 입력
330
 
331
- ### ⚠️ 주의사항
332
- - Google AI Studio는 개인 사용자에게 무료 할당량 제공
333
- - Vertex AI는 유료 서비스로 사용량에 따라 과금
334
- - API 키는 안전하게 보관하고 공유하지 마세요
 
 
 
 
 
 
 
335
  """, elem_classes="warning-box")
336
 
337
- # 이미지 업로드 및 처리 섹션
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  gr.Markdown("## 📤 이미지 업로드 및 텍스트 추출")
339
 
340
  with gr.Row():
 
1
+ # 이미지 업로드 및 처리 섹션import gradio as gr
2
  import base64
3
  import requests
4
  import json
 
7
  import os
8
  from typing import Optional, Tuple
9
  import re
10
+ import time
11
+ import random
12
 
13
  class KoreanOCRApp:
14
  def __init__(self):
 
28
  self.project_id = project_id.strip()
29
  return "✅ 인증 정보가 설정되었습니다."
30
 
31
+ def optimize_image_for_api(self, image: Image.Image) -> Image.Image:
32
+ """API 호출을 위한 이미지 최적화"""
33
+ # 이미지 크기 최적화 (토큰 사용량 감소)
34
+ max_dimension = 1024 # 작은 크기로 제한
35
+
36
+ # 현재 이미지 크기 확인
37
+ width, height = image.size
38
+
39
+ # 큰 이미지일 경우 리사이즈
40
+ if width > max_dimension or height > max_dimension:
41
+ image.thumbnail((max_dimension, max_dimension), Image.Resampling.LANCZOS)
42
+
43
+ # RGBA를 RGB로 변환 (파일 크기 감소)
44
  if image.mode == 'RGBA':
 
45
  background = Image.new('RGB', image.size, (255, 255, 255))
46
  background.paste(image, mask=image.split()[-1])
47
  image = background
48
+ elif image.mode != 'RGB':
49
+ image = image.convert('RGB')
50
 
51
+ return image
52
+ def encode_image_to_base64(self, image: Image.Image) -> str:
53
+ """이미지를 base64로 인코딩 (최적화된 버전)"""
54
+ # 이미지 최적화
55
+ image = self.optimize_image_for_api(image)
56
+
57
+ buffer = io.BytesIO()
58
+ # JPEG 형식으로 저장하여 파일 크기 최적화 (품질 80으로 낮춤)
59
+ image.save(buffer, format='JPEG', quality=80, optimize=True)
60
  image_bytes = buffer.getvalue()
61
+
62
+ # 파일 크기 확인
63
+ size_mb = len(image_bytes) / (1024 * 1024)
64
+ if size_mb > 3: # 3MB 초과 시 추가 최적화
65
+ buffer = io.BytesIO()
66
+ image.save(buffer, format='JPEG', quality=60, optimize=True)
67
+ image_bytes = buffer.getvalue()
68
+
69
  return base64.b64encode(image_bytes).decode('utf-8')
70
 
71
+ def call_gemini_api_with_retry(self, image_base64: str, max_retries: int = 3, initial_delay: float = 2.0) -> str:
72
+ """재시도 로직이 포함된 Gemini API 호출"""
73
  if not self.api_key:
74
  return "❌ 먼저 API 키를 설정해주세요."
75
 
 
128
  ]
129
  }
130
 
131
+ for attempt in range(max_retries):
132
+ try:
133
+ response = requests.post(url, headers=headers, json=payload, timeout=60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
+ if response.status_code == 401:
136
+ return "❌ API 키가 유효하지 않습니다. Google AI Studio에서 발급받은 올바른 API 키를 입력해주세요."
137
+ elif response.status_code == 403:
138
+ return "❌ API 접근 권한이 없습니다. Gemini API가 활성화되어 있는지 확인해주세요."
139
+ elif response.status_code == 429:
140
+ # 429 에러 재시도 로직
141
+ if attempt < max_retries - 1:
142
+ delay = initial_delay * (2 ** attempt) + random.uniform(0.5, 1.5) # 지수 백오프 + 랜덤 지터
143
+ return f"⏳ API 호출 한도를 초과했습니다. {delay:.1f}초 후 자동으로 재시도합니다... (시도 {attempt + 1}/{max_retries})"
144
+ else:
145
+ return """❌ API 호출 한도를 초과했습니다.
146
+
147
+ 📌 해결 방법:
148
+ 1. 잠시 기다린 후 다시 시도 (1-2분 권장)
149
+ 2. Google AI Studio에서 할당량 확인
150
+ 3. 유료 계정으로 업그레이드 고려
151
+ 4. 이미지 크기를 줄여서 재시도
152
+
153
+ 💡 팁: 높은 해상도의 이미지는 더 많은 토큰을 사용합니다."""
154
+
155
+ response.raise_for_status()
156
+
157
+ result = response.json()
158
+
159
+ if "candidates" in result and len(result["candidates"]) > 0:
160
+ content = result["candidates"][0]["content"]["parts"][0]["text"]
161
+ return content.strip()
162
+ elif "error" in result:
163
+ error_msg = result['error'].get('message', '알 수 없는 오류')
164
+ if "quota" in error_msg.lower() or "limit" in error_msg.lower():
165
+ if attempt < max_retries - 1:
166
+ delay = initial_delay * (2 ** attempt) + random.uniform(0.5, 1.5)
167
+ time.sleep(delay)
168
+ continue
169
+ return f"❌ API 오류: {error_msg}"
170
+ else:
171
+ return "❌ 텍스트를 추출할 수 없습니다. 이미지에 한국어 텍스트가 포함되어 있는지 확인해주세요."
172
+
173
+ except requests.exceptions.RequestException as e:
174
+ if "429" in str(e) and attempt < max_retries - 1:
175
+ delay = initial_delay * (2 ** attempt) + random.uniform(0.5, 1.5)
176
+ time.sleep(delay)
177
+ continue
178
+ return f"❌ API 호출 오류: {str(e)}"
179
+ except json.JSONDecodeError:
180
+ return "❌ API 응답 파싱 오류가 발생했습니다."
181
+ except KeyError as e:
182
+ return f"❌ 예상치 못한 API 응답 형식: {str(e)}"
183
+ except Exception as e:
184
+ return f"❌ 알 수 없는 오류: {str(e)}"
185
+
186
+ return "❌ 최대 재시도 횟수를 초과했습니다. 잠시 후 다시 시도해주세요."
187
 
188
  def call_vertex_ai_api(self, image_base64: str) -> str:
189
  """Vertex AI API 호출 (서비스 계정 키 사용)"""
 
270
  return image, auth_result
271
 
272
  try:
273
+ # 이미지 최적화 (토큰 사용량 감소를 위해)
274
+ image = self.optimize_image_for_api(image)
 
 
 
 
 
 
 
275
 
276
  # 이미지를 base64로 인코딩
277
  image_base64 = self.encode_image_to_base64(image)
278
 
279
  # API 타입에 따라 호출
280
  if api_type == "Google AI Studio":
281
+ extracted_text = self.call_gemini_api_with_retry(image_base64)
282
  else:
283
  extracted_text = self.call_vertex_ai_api(image_base64)
284
 
 
360
  )
361
 
362
  # API 설정 가이드
363
+ with gr.Accordion("📖 API 설정 가이드 및 할당량 정보", open=False):
364
  gr.Markdown("""
365
  ### Google AI Studio API (권장)
366
  1. [Google AI Studio](https://aistudio.google.com/)에 접속
 
368
  3. API 키 생성 및 복사
369
  4. 위의 "API 키" 필드에 붙여넣기
370
 
371
+ **📊 무료 할당량 (Google AI Studio):**
372
+ - 분당 15회 요청
373
+ - 일일 1,500회 요청
374
+ - 분당 100만 토큰
375
+ - 일일 5천만 토큰
376
+
377
  ### Vertex AI API (고급 사용자용)
378
  1. [Google Cloud Console](https://console.cloud.google.com/)에서 프로젝트 생성
379
  2. Vertex AI API 활성화
 
381
  4. `gcloud auth application-default login` 또는 Access Token 발급
382
  5. API 키와 프로젝트 ID 입력
383
 
384
+ ### ⚠️ 할당량 초과 시 해결 방법
385
+ 1. **잠시 대기**: 1-2분 다시 시도
386
+ 2. **이미지 최적화**: 작은 크기의 이미지 사용
387
+ 3. **사용량 분산**: 여러 나누어서 처리
388
+ 4. **유료 계정**: Google Cloud 유료 계정으로 업그레이드
389
+
390
+ ### 💡 토큰 절약 팁
391
+ - 이미지 해상도: 1024x1024 이하 권장
392
+ - 파일 형식: JPEG 사용 (PNG보다 작음)
393
+ - 불필요한 배경 제거
394
+ - 텍스트 영역만 크롭하여 업로드
395
  """, elem_classes="warning-box")
396
 
397
+ # 할당량 상태 표시
398
+ with gr.Row():
399
+ gr.Markdown("""
400
+ ### 📊 현재 상태
401
+
402
+ **무료 할당량 (Google AI Studio):**
403
+ - ⏱️ 분당 15회 요청 제한
404
+ - 📅 일일 1,500회 요청 제한
405
+ - 🔢 고해상도 이미지는 더 많은 토큰 사용
406
+
407
+ **💡 할당량 절약 팁:**
408
+ - 이미지 크기를 1024x1024 이하로 유지
409
+ - 텍스트가 있는 부분만 크롭하여 업로드
410
+ - 연속적인 요청 간 1-2초 간격 유지
411
+ """, elem_classes="info-box")
412
  gr.Markdown("## 📤 이미지 업로드 및 텍스트 추출")
413
 
414
  with gr.Row():