datbkpro commited on
Commit
144e0fd
·
verified ·
1 Parent(s): 7775d9b

Update services/image_service.py

Browse files
Files changed (1) hide show
  1. services/image_service.py +195 -94
services/image_service.py CHANGED
@@ -16,46 +16,104 @@ class ImageService:
16
  self._initialize_ocr_models()
17
 
18
  def _initialize_ocr_models(self):
19
- """Khởi tạo các model OCR"""
20
  try:
21
  print("🔄 Đang khởi tạo OCR models...")
22
 
23
- # Khởi tạo EasyOCR cho đa ngôn ngữ
24
  try:
25
  import easyocr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  self.easy_ocr_reader = easyocr.Reader(
27
- settings.EASYOCR_LANGUAGES,
28
  gpu=torch.cuda.is_available()
29
  )
30
  print("✅ EasyOCR initialized successfully")
31
- except ImportError:
32
- print("❌ EasyOCR not installed, installing...")
33
- import subprocess
34
- subprocess.run(["pip", "install", "easyocr"])
35
- import easyocr
36
- self.easy_ocr_reader = easyocr.Reader(settings.EASYOCR_LANGUAGES)
37
- print("✅ EasyOCR installed and initialized")
38
 
39
- # Khởi tạo MangaOCR cho tiếng Việt và chữ in
 
 
 
 
 
 
 
 
 
 
40
  try:
41
  from manga_ocr import MangaOcr
42
  self.ocr_processor = MangaOcr()
43
  print("✅ MangaOCR initialized successfully")
44
  except ImportError:
45
- print("⚠️ MangaOCR not available, using EasyOCR only")
 
 
 
 
 
 
 
 
46
 
47
  except Exception as e:
48
- print(f"❌ Lỗi khởi tạo OCR: {e}")
49
 
50
  def preprocess_image(self, image: np.ndarray) -> np.ndarray:
51
  """Tiền xử lý ảnh để cải thiện OCR accuracy"""
52
  try:
 
 
 
 
53
  # Chuyển sang grayscale nếu là ảnh màu
54
  if len(image.shape) == 3:
 
 
55
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
56
  else:
57
  gray = image
58
 
 
 
 
 
 
 
 
 
 
59
  # Áp dụng filters để cải thiện chất lượng
60
  # Noise reduction
61
  denoised = cv2.medianBlur(gray, 3)
@@ -74,36 +132,46 @@ class ImageService:
74
  return image
75
 
76
  def extract_text_easyocr(self, image: np.ndarray) -> List[Dict[str, Any]]:
77
- """Trích xuất text sử dụng EasyOCR"""
 
 
 
 
78
  try:
 
 
79
  # Tiền xử lý ảnh
80
  processed_image = self.preprocess_image(image)
81
 
82
- # Chuyển đổi numpy array sang PIL Image cho phù hợp
83
- if processed_image is not None:
84
- pil_image = Image.fromarray(processed_image)
85
- image_np = np.array(pil_image)
86
- else:
87
- image_np = image
88
 
89
- # Chạy OCR
90
  results = self.easy_ocr_reader.readtext(
91
- image_np,
92
  detail=1,
93
  paragraph=True,
 
94
  contrast_ths=0.3,
95
- adjust_contrast=0.7
 
 
96
  )
97
 
98
  # Format kết quả
99
  extracted_texts = []
100
  for bbox, text, confidence in results:
101
- extracted_texts.append({
102
- 'text': text,
103
- 'confidence': float(confidence),
104
- 'bbox': bbox
105
- })
 
 
 
106
 
 
107
  return extracted_texts
108
 
109
  except Exception as e:
@@ -112,39 +180,75 @@ class ImageService:
112
 
113
  def extract_text_mangaocr(self, image: np.ndarray) -> List[Dict[str, Any]]:
114
  """Trích xuất text sử dụng MangaOCR (tốt cho tiếng Việt)"""
 
 
 
 
115
  try:
116
- if self.ocr_processor is None:
117
- return []
118
 
119
  # Chuyển numpy array sang PIL Image
120
- pil_image = Image.fromarray(image)
 
 
 
121
 
122
  # Chạy OCR
123
  text = self.ocr_processor(pil_image)
124
 
125
- return [{
126
- 'text': text,
127
- 'confidence': 0.9, # MangaOCR không trả về confidence
128
- 'bbox': None,
129
- 'source': 'manga_ocr'
130
- }]
 
 
 
131
 
132
  except Exception as e:
133
  print(f"❌ Lỗi MangaOCR: {e}")
134
  return []
135
 
136
- def merge_ocr_results(self, easyocr_results: List, mangaocr_results: List) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  """Kết hợp và chọn lọc kết quả từ nhiều OCR engine"""
138
  all_texts = []
139
 
140
- # Ưu tiên kết quả từ EasyOCR với confidence cao
141
- for result in easyocr_results:
142
- if result['confidence'] > 0.5: # Ngưỡng confidence
143
- all_texts.append(result['text'])
144
-
145
- # Thêm kết quả từ MangaOCR nếu có
146
- for result in mangaocr_results:
147
- all_texts.append(result['text'])
148
 
149
  # Loại bỏ trùng lặp và kết hợp
150
  unique_texts = []
@@ -152,11 +256,20 @@ class ImageService:
152
 
153
  for text in all_texts:
154
  clean_text = text.strip()
155
- if clean_text and len(clean_text) > 1 and clean_text not in seen_texts:
 
 
 
 
156
  unique_texts.append(clean_text)
157
  seen_texts.add(clean_text)
158
 
159
- return "\n".join(unique_texts) if unique_texts else "Không phát hiện được văn bản trong ảnh."
 
 
 
 
 
160
 
161
  def extract_text_from_image(self, image: np.ndarray) -> str:
162
  """Trích xuất văn bản từ ảnh sử dụng nhiều OCR engine"""
@@ -166,17 +279,24 @@ class ImageService:
166
  try:
167
  print("🔍 Đang trích xuất văn bản từ ảnh...")
168
 
169
- # Chạy cả hai OCR engine
 
 
 
170
  easyocr_results = self.extract_text_easyocr(image)
 
 
 
171
  mangaocr_results = self.extract_text_mangaocr(image)
 
172
 
173
- print(f"📊 EasyOCR found {len(easyocr_results)} text regions")
174
- print(f"📊 MangaOCR found {len(mangaocr_results)} text regions")
 
175
 
176
  # Kết hợp kết quả
177
- merged_text = self.merge_ocr_results(easyocr_results, mangaocr_results)
178
 
179
- print(f"✅ Extracted text: {merged_text[:100]}...")
180
  return merged_text
181
 
182
  except Exception as e:
@@ -186,10 +306,16 @@ class ImageService:
186
  def analyze_text_with_llm(self, extracted_text: str, user_description: str = "") -> str:
187
  """Phân tích văn bản trích xuất được bằng LLM"""
188
  try:
189
- if not extracted_text or extracted_text == "Không phát hiện được văn bản trong ảnh.":
 
190
  prompt = """
191
  Tôi đã tải lên một hình ảnh nhưng không thể trích xuất được văn bản từ đó.
192
- Hãy tả tổng quan về hình ảnh này đưa ra các phán đoán về nội dung thể có.
 
 
 
 
 
193
  """
194
  else:
195
  if user_description:
@@ -199,21 +325,21 @@ class ImageService:
199
  VĂN BẢN TRÍCH XUẤT TỪ ẢNH:
200
  {extracted_text}
201
 
202
- Dựa trên tả của người dùng và văn bản trích xuất được, hãy:
203
- 1. Phân tích và tóm tắt nội dung chính
204
- 2. Giải thích ý nghĩa của văn bản trong ngữ cảnh
205
- 3. Đưa ra thông tin bổ sung hữu ích
206
  """
207
  else:
208
  prompt = f"""
209
  VĂN BẢN TRÍCH XUẤT TỪ ẢNH:
210
  {extracted_text}
211
 
212
- Hãy phân tích cung cấp thông tin về:
213
- 1. Nội dung chính của văn bản
214
- 2. Loại văn bản (tài liệu, quảng cáo, tin nhắn, etc.)
215
- 3. Ngữ cảnh và ý nghĩa
216
- 4. Thông tin hữu ích khác
217
  """
218
 
219
  # Gọi LLM để phân tích
@@ -222,14 +348,14 @@ class ImageService:
222
  messages=[
223
  {
224
  "role": "system",
225
- "content": "Bạn là trợ phân tích hình ảnh và văn bản. Hãy trả lời bằng tiếng Việt tự nhiên, rõ ràng và hữu ích."
226
  },
227
  {
228
  "role": "user",
229
  "content": prompt
230
  }
231
  ],
232
- max_tokens=500,
233
  temperature=0.7
234
  )
235
 
@@ -261,36 +387,11 @@ class ImageService:
261
  {analysis_result}
262
 
263
  ---
264
- *Phân tích sử dụng OCR AI - Độ chính xác có thể thay đổi tùy thuộc vào chất lượng ảnh.*"""
 
265
 
266
  return result
267
 
268
  except Exception as e:
269
  print(f"❌ Lỗi phân tích ảnh: {e}")
270
- return f"❌ Lỗi trong quá trình phân tích: {str(e)}"
271
-
272
- def detect_image_type(self, image: np.ndarray) -> str:
273
- """Nhận diện loại ảnh (tài liệu, ảnh chụp, meme, etc.)"""
274
- try:
275
- # Phân tích đơn giản dựa trên đặc điểm ảnh
276
- height, width = image.shape[:2]
277
- aspect_ratio = width / height
278
-
279
- # Phân tích màu sắc
280
- if len(image.shape) == 3:
281
- color_variance = np.var(image, axis=(0,1))
282
- is_colorful = np.mean(color_variance) > 100
283
- else:
284
- is_colorful = False
285
-
286
- # Phân tích sơ bộ loại ảnh
287
- if aspect_ratio > 2.0 or aspect_ratio < 0.5:
288
- return "document" # Tài liệu thường có tỷ lệ khác thường
289
- elif not is_colorful:
290
- return "document" # Ít màu sắc -> có thể là tài liệu
291
- else:
292
- return "photo" # Ảnh chụp
293
-
294
- except Exception as e:
295
- print(f"⚠️ Lỗi nhận diện loại ảnh: {e}")
296
- return "unknown"
 
16
  self._initialize_ocr_models()
17
 
18
  def _initialize_ocr_models(self):
19
+ """Khởi tạo các model OCR với xử lý lỗi tốt hơn"""
20
  try:
21
  print("🔄 Đang khởi tạo OCR models...")
22
 
23
+ # Khởi tạo EasyOCR với xử lý lỗi ngôn ngữ
24
  try:
25
  import easyocr
26
+
27
+ # Sử dụng danh sách ngôn ngữ an toàn
28
+ safe_languages = ['en', 'vi'] # Bắt đầu với 2 ngôn ngữ cơ bản
29
+
30
+ # Thử thêm các ngôn ngữ khác nếu có thể
31
+ try:
32
+ safe_languages.append('fr')
33
+ safe_languages.append('es')
34
+ safe_languages.append('de')
35
+ except:
36
+ print("⚠️ Một số ngôn ngữ châu Âu không khả dụng")
37
+
38
+ # Thử thêm tiếng Nhật
39
+ try:
40
+ safe_languages.append('ja')
41
+ except:
42
+ print("⚠️ Tiếng Nhật không khả dụng")
43
+
44
+ # Thử thêm tiếng Hàn
45
+ try:
46
+ safe_languages.append('ko')
47
+ except:
48
+ print("⚠️ Tiếng Hàn không khả dụng")
49
+
50
+ # Thử thêm tiếng Trung (sử dụng ch_sim thay vì zh)
51
+ try:
52
+ safe_languages.append('ch_sim') # Chinese simplified
53
+ except:
54
+ print("⚠️ Tiếng Trung không khả dụng")
55
+
56
+ print(f"🎯 Ngôn ngữ OCR được hỗ trợ: {safe_languages}")
57
+
58
  self.easy_ocr_reader = easyocr.Reader(
59
+ safe_languages,
60
  gpu=torch.cuda.is_available()
61
  )
62
  print("✅ EasyOCR initialized successfully")
 
 
 
 
 
 
 
63
 
64
+ except Exception as e:
65
+ print(f"❌ Lỗi khởi tạo EasyOCR: {e}")
66
+ print("🔄 Thử khởi tạo chỉ với tiếng Anh...")
67
+ try:
68
+ self.easy_ocr_reader = easyocr.Reader(['en'])
69
+ print("✅ EasyOCR initialized with English only")
70
+ except Exception as e2:
71
+ print(f"❌ Không thể khởi tạo EasyOCR: {e2}")
72
+ self.easy_ocr_reader = None
73
+
74
+ # Khởi tạo MangaOCR
75
  try:
76
  from manga_ocr import MangaOcr
77
  self.ocr_processor = MangaOcr()
78
  print("✅ MangaOCR initialized successfully")
79
  except ImportError:
80
+ print(" MangaOCR not installed, installing...")
81
+ import subprocess
82
+ subprocess.run(["pip", "install", "manga-ocr"], check=True)
83
+ from manga_ocr import MangaOcr
84
+ self.ocr_processor = MangaOcr()
85
+ print("✅ MangaOCR installed and initialized")
86
+ except Exception as e:
87
+ print(f"❌ Lỗi khởi tạo MangaOCR: {e}")
88
+ self.ocr_processor = None
89
 
90
  except Exception as e:
91
+ print(f"❌ Lỗi khởi tạo OCR models: {e}")
92
 
93
  def preprocess_image(self, image: np.ndarray) -> np.ndarray:
94
  """Tiền xử lý ảnh để cải thiện OCR accuracy"""
95
  try:
96
+ # Kiểm tra và chuyển đổi kiểu dữ liệu
97
+ if image.dtype != np.uint8:
98
+ image = image.astype(np.uint8)
99
+
100
  # Chuyển sang grayscale nếu là ảnh màu
101
  if len(image.shape) == 3:
102
+ if image.shape[2] == 4: # RGBA
103
+ image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
104
  gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
105
  else:
106
  gray = image
107
 
108
+ # Resize ảnh nếu quá lớn (tối ưu cho OCR)
109
+ height, width = gray.shape
110
+ max_dimension = 1600
111
+ if max(height, width) > max_dimension:
112
+ scale = max_dimension / max(height, width)
113
+ new_width = int(width * scale)
114
+ new_height = int(height * scale)
115
+ gray = cv2.resize(gray, (new_width, new_height), interpolation=cv2.INTER_AREA)
116
+
117
  # Áp dụng filters để cải thiện chất lượng
118
  # Noise reduction
119
  denoised = cv2.medianBlur(gray, 3)
 
132
  return image
133
 
134
  def extract_text_easyocr(self, image: np.ndarray) -> List[Dict[str, Any]]:
135
+ """Trích xuất text sử dụng EasyOCR với xử lý lỗi"""
136
+ if self.easy_ocr_reader is None:
137
+ print("❌ EasyOCR chưa được khởi tạo")
138
+ return []
139
+
140
  try:
141
+ print("🔍 EasyOCR đang xử lý ảnh...")
142
+
143
  # Tiền xử lý ảnh
144
  processed_image = self.preprocess_image(image)
145
 
146
+ # Đảm bảo ảnh uint8
147
+ if processed_image.dtype != np.uint8:
148
+ processed_image = processed_image.astype(np.uint8)
 
 
 
149
 
150
+ # Chạy OCR với tham số tối ưu
151
  results = self.easy_ocr_reader.readtext(
152
+ processed_image,
153
  detail=1,
154
  paragraph=True,
155
+ batch_size=4,
156
  contrast_ths=0.3,
157
+ adjust_contrast=0.5,
158
+ text_threshold=0.5,
159
+ link_threshold=0.4
160
  )
161
 
162
  # Format kết quả
163
  extracted_texts = []
164
  for bbox, text, confidence in results:
165
+ clean_text = text.strip()
166
+ if clean_text and len(clean_text) > 1: # Loại bỏ text quá ngắn
167
+ extracted_texts.append({
168
+ 'text': clean_text,
169
+ 'confidence': float(confidence),
170
+ 'bbox': bbox,
171
+ 'source': 'easyocr'
172
+ })
173
 
174
+ print(f"📊 EasyOCR tìm thấy {len(extracted_texts)} vùng văn bản")
175
  return extracted_texts
176
 
177
  except Exception as e:
 
180
 
181
  def extract_text_mangaocr(self, image: np.ndarray) -> List[Dict[str, Any]]:
182
  """Trích xuất text sử dụng MangaOCR (tốt cho tiếng Việt)"""
183
+ if self.ocr_processor is None:
184
+ print("❌ MangaOCR chưa được khởi tạo")
185
+ return []
186
+
187
  try:
188
+ print("🔍 MangaOCR đang xử lý ảnh...")
 
189
 
190
  # Chuyển numpy array sang PIL Image
191
+ if len(image.shape) == 3:
192
+ pil_image = Image.fromarray(image)
193
+ else:
194
+ pil_image = Image.fromarray(image).convert('RGB')
195
 
196
  # Chạy OCR
197
  text = self.ocr_processor(pil_image)
198
 
199
+ if text and len(text.strip()) > 1:
200
+ return [{
201
+ 'text': text.strip(),
202
+ 'confidence': 0.8, # MangaOCR không trả về confidence
203
+ 'bbox': None,
204
+ 'source': 'manga_ocr'
205
+ }]
206
+ else:
207
+ return []
208
 
209
  except Exception as e:
210
  print(f"❌ Lỗi MangaOCR: {e}")
211
  return []
212
 
213
+ def extract_text_pytesseract(self, image: np.ndarray) -> List[Dict[str, Any]]:
214
+ """Trích xuất text sử dụng pytesseract (fallback)"""
215
+ try:
216
+ import pytesseract
217
+
218
+ # Tiền xử lý ảnh
219
+ processed_image = self.preprocess_image(image)
220
+
221
+ # Cấu hình pytesseract
222
+ custom_config = r'--oem 3 --psm 6 -l vie+eng'
223
+
224
+ # Chạy OCR
225
+ text = pytesseract.image_to_string(processed_image, config=custom_config)
226
+
227
+ if text and len(text.strip()) > 1:
228
+ return [{
229
+ 'text': text.strip(),
230
+ 'confidence': 0.7,
231
+ 'bbox': None,
232
+ 'source': 'pytesseract'
233
+ }]
234
+ else:
235
+ return []
236
+
237
+ except ImportError:
238
+ print("⚠️ pytesseract chưa được cài đặt")
239
+ return []
240
+ except Exception as e:
241
+ print(f"❌ Lỗi pytesseract: {e}")
242
+ return []
243
+
244
+ def merge_ocr_results(self, results_list: List[List]) -> str:
245
  """Kết hợp và chọn lọc kết quả từ nhiều OCR engine"""
246
  all_texts = []
247
 
248
+ for results in results_list:
249
+ for result in results:
250
+ if result['confidence'] > 0.4: # Ngưỡng confidence thấp hơn
251
+ all_texts.append(result['text'])
 
 
 
 
252
 
253
  # Loại bỏ trùng lặp và kết hợp
254
  unique_texts = []
 
256
 
257
  for text in all_texts:
258
  clean_text = text.strip()
259
+ # Loại bỏ text quá ngắn hoặc chỉ tự đặc biệt
260
+ if (clean_text and
261
+ len(clean_text) > 2 and
262
+ any(c.isalnum() for c in clean_text) and
263
+ clean_text not in seen_texts):
264
  unique_texts.append(clean_text)
265
  seen_texts.add(clean_text)
266
 
267
+ if unique_texts:
268
+ combined_text = "\n".join(unique_texts)
269
+ print(f"✅ Văn bản trích xuất: {combined_text[:200]}...")
270
+ return combined_text
271
+ else:
272
+ return "Không phát hiện được văn bản trong ảnh."
273
 
274
  def extract_text_from_image(self, image: np.ndarray) -> str:
275
  """Trích xuất văn bản từ ảnh sử dụng nhiều OCR engine"""
 
279
  try:
280
  print("🔍 Đang trích xuất văn bản từ ảnh...")
281
 
282
+ # Chạy tất cả OCR engine có sẵn
283
+ all_results = []
284
+
285
+ # EasyOCR
286
  easyocr_results = self.extract_text_easyocr(image)
287
+ all_results.append(easyocr_results)
288
+
289
+ # MangaOCR
290
  mangaocr_results = self.extract_text_mangaocr(image)
291
+ all_results.append(mangaocr_results)
292
 
293
+ # Pytesseract (fallback)
294
+ pytesseract_results = self.extract_text_pytesseract(image)
295
+ all_results.append(pytesseract_results)
296
 
297
  # Kết hợp kết quả
298
+ merged_text = self.merge_ocr_results(all_results)
299
 
 
300
  return merged_text
301
 
302
  except Exception as e:
 
306
  def analyze_text_with_llm(self, extracted_text: str, user_description: str = "") -> str:
307
  """Phân tích văn bản trích xuất được bằng LLM"""
308
  try:
309
+ if not extracted_text or "Không phát hiện" in extracted_text:
310
+ # Nếu không có văn bản, yêu cầu LLM mô tả ảnh
311
  prompt = """
312
  Tôi đã tải lên một hình ảnh nhưng không thể trích xuất được văn bản từ đó.
313
+ Đây thể ảnh chụp, hình minh họa, hoặc ảnh khôngchữ.
314
+
315
+ Hãy:
316
+ 1. Mô tả tổng quan về loại hình ảnh này có thể là gì
317
+ 2. Đưa ra các phán đoán về nội dung dựa trên đặc điểm chung
318
+ 3. Gợi ý loại ảnh nào thường chứa văn bản để trích xuất
319
  """
320
  else:
321
  if user_description:
 
325
  VĂN BẢN TRÍCH XUẤT TỪ ẢNH:
326
  {extracted_text}
327
 
328
+ Hãy phân tích bằng tiếng Việt:
329
+ 1. Tóm tắt nội dung chính của văn bản
330
+ 2. Giải thích ý nghĩa trong ngữ cảnh người dùng mô tả
331
+ 3. Đưa ra thông tin bổ sung hoặc gợi ý liên quan
332
  """
333
  else:
334
  prompt = f"""
335
  VĂN BẢN TRÍCH XUẤT TỪ ẢNH:
336
  {extracted_text}
337
 
338
+ Hãy phân tích bằng tiếng Việt:
339
+ 1. Loại văn bản này gì? (tài liệu, quảng cáo, tin nhắn, v.v.)
340
+ 2. Nội dung chính thông tin quan trọng
341
+ 3. Ngữ cảnh có thể và ý nghĩa
342
+ 4. Thông tin hữu ích khác từ văn bản
343
  """
344
 
345
  # Gọi LLM để phân tích
 
348
  messages=[
349
  {
350
  "role": "system",
351
+ "content": "Bạn là chuyên gia phân tích hình ảnh và văn bản. Hãy trả lời bằng tiếng Việt tự nhiên, rõ ràng và hữu ích. Nếu không có văn bản, hãy mô tả ảnh một cách thông minh."
352
  },
353
  {
354
  "role": "user",
355
  "content": prompt
356
  }
357
  ],
358
+ max_tokens=800,
359
  temperature=0.7
360
  )
361
 
 
387
  {analysis_result}
388
 
389
  ---
390
+ *Hệ thống sử dụng đa OCR engine (EasyOCR, MangaOCR) kết hợp AI*
391
+ *Độ chính xác phụ thuộc vào chất lượng ảnh và độ phức tạp của văn bản*"""
392
 
393
  return result
394
 
395
  except Exception as e:
396
  print(f"❌ Lỗi phân tích ảnh: {e}")
397
+ return f"❌ Lỗi trong quá trình phân tích: {str(e)}"