Delete services/image_service
Browse files- services/image_service +0 -182
services/image_service
DELETED
|
@@ -1,182 +0,0 @@
|
|
| 1 |
-
import requests
|
| 2 |
-
import base64
|
| 3 |
-
from PIL import Image
|
| 4 |
-
import io
|
| 5 |
-
import json
|
| 6 |
-
|
| 7 |
-
class ImageService:
|
| 8 |
-
def __init__(self):
|
| 9 |
-
self.ocr_api_url = "https://api-inference.huggingface.co/models/deepseek-ai/DeepSeek-OCR"
|
| 10 |
-
self.llm_api_url = "https://api.groq.com/openai/v1/chat/completions"
|
| 11 |
-
self.headers_ocr = {
|
| 12 |
-
"Authorization": f"Bearer {os.getenv('HF_TOKEN')}",
|
| 13 |
-
"Content-Type": "application/octet-stream"
|
| 14 |
-
}
|
| 15 |
-
self.headers_llm = {
|
| 16 |
-
"Authorization": f"Bearer {settings.GROQ_API_KEY}",
|
| 17 |
-
"Content-Type": "application/json"
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
def analyze_image_with_description(self, image: np.ndarray, description: str = None) -> str:
|
| 21 |
-
"""Phân tích hình ảnh sử dụng OCR và LLM"""
|
| 22 |
-
try:
|
| 23 |
-
# Bước 1: OCR để trích xuất text từ hình ảnh
|
| 24 |
-
extracted_text = self._extract_text_with_ocr(image)
|
| 25 |
-
|
| 26 |
-
if not extracted_text:
|
| 27 |
-
return "Không thể trích xuất văn bản từ hình ảnh. Vui lòng thử với hình ảnh khác."
|
| 28 |
-
|
| 29 |
-
# Bước 2: Sử dụng LLM để phân tích và tạo câu trả lời
|
| 30 |
-
analysis_result = self._analyze_with_llm(extracted_text, description)
|
| 31 |
-
|
| 32 |
-
return analysis_result
|
| 33 |
-
|
| 34 |
-
except Exception as e:
|
| 35 |
-
return f"Lỗi khi phân tích hình ảnh: {str(e)}"
|
| 36 |
-
|
| 37 |
-
def _extract_text_with_ocr(self, image: np.ndarray) -> str:
|
| 38 |
-
"""Sử dụng DeepSeek-OCR để trích xuất văn bản từ hình ảnh"""
|
| 39 |
-
try:
|
| 40 |
-
# Convert numpy array to PIL Image
|
| 41 |
-
pil_image = Image.fromarray(image)
|
| 42 |
-
|
| 43 |
-
# Convert to bytes
|
| 44 |
-
img_byte_arr = io.BytesIO()
|
| 45 |
-
pil_image.save(img_byte_arr, format='PNG')
|
| 46 |
-
img_byte_arr = img_byte_arr.getvalue()
|
| 47 |
-
|
| 48 |
-
# Call Hugging Face API
|
| 49 |
-
response = requests.post(
|
| 50 |
-
self.ocr_api_url,
|
| 51 |
-
headers=self.headers_ocr,
|
| 52 |
-
data=img_byte_arr
|
| 53 |
-
)
|
| 54 |
-
|
| 55 |
-
if response.status_code == 200:
|
| 56 |
-
result = response.json()
|
| 57 |
-
# Extract text from OCR result
|
| 58 |
-
text_blocks = result.get('text', [])
|
| 59 |
-
if isinstance(text_blocks, list):
|
| 60 |
-
# Join all text blocks
|
| 61 |
-
full_text = ' '.join([block.get('text', '') for block in text_blocks if block.get('text')])
|
| 62 |
-
return full_text
|
| 63 |
-
elif isinstance(text_blocks, str):
|
| 64 |
-
return text_blocks
|
| 65 |
-
else:
|
| 66 |
-
return str(result)
|
| 67 |
-
else:
|
| 68 |
-
raise Exception(f"OCR API error: {response.status_code} - {response.text}")
|
| 69 |
-
|
| 70 |
-
except Exception as e:
|
| 71 |
-
raise Exception(f"OCR extraction failed: {str(e)}")
|
| 72 |
-
|
| 73 |
-
def _analyze_with_llm(self, extracted_text: str, user_description: str = None) -> str:
|
| 74 |
-
"""Sử dụng LLM để phân tích văn bản trích xuất và tạo câu trả lời"""
|
| 75 |
-
try:
|
| 76 |
-
# Build prompt based on user description
|
| 77 |
-
if user_description and user_description.strip():
|
| 78 |
-
prompt = f"""
|
| 79 |
-
Dựa trên văn bản được trích xuất từ hình ảnh và mô tả của người dùng, hãy cung cấp phân tích chi tiết.
|
| 80 |
-
|
| 81 |
-
VĂN BẢN TRÍCH XUẤT TỪ HÌNH ẢNH:
|
| 82 |
-
{extracted_text}
|
| 83 |
-
|
| 84 |
-
MÔ TẢ CỦA NGƯỜI DÙNG: {user_description}
|
| 85 |
-
|
| 86 |
-
Hãy phân tích và trả lời dựa trên cả văn bản trích xuất và mô tả của người dùng.
|
| 87 |
-
"""
|
| 88 |
-
else:
|
| 89 |
-
prompt = f"""
|
| 90 |
-
Hãy phân tích và tóm tắt nội dung từ văn bản được trích xuất từ hình ảnh dưới đây:
|
| 91 |
-
|
| 92 |
-
VĂN BẢN TRÍCH XUẤT:
|
| 93 |
-
{extracted_text}
|
| 94 |
-
|
| 95 |
-
Yêu cầu:
|
| 96 |
-
1. Tóm tắt nội dung chính
|
| 97 |
-
2. Phân tích ý nghĩa (nếu có)
|
| 98 |
-
3. Đưa ra nhận xét hoặc gợi ý liên quan
|
| 99 |
-
"""
|
| 100 |
-
|
| 101 |
-
payload = {
|
| 102 |
-
"model": settings.DEFAULT_LLM_MODEL,
|
| 103 |
-
"messages": [
|
| 104 |
-
{
|
| 105 |
-
"role": "system",
|
| 106 |
-
"content": "Bạn là một trợ lý AI chuyên phân tích và giải thích nội dung từ hình ảnh. Hãy cung cấp câu trả lời rõ ràng, chi tiết và hữu ích bằng tiếng Việt."
|
| 107 |
-
},
|
| 108 |
-
{
|
| 109 |
-
"role": "user",
|
| 110 |
-
"content": prompt
|
| 111 |
-
}
|
| 112 |
-
],
|
| 113 |
-
"temperature": 0.3,
|
| 114 |
-
"max_tokens": 1024
|
| 115 |
-
}
|
| 116 |
-
|
| 117 |
-
response = requests.post(
|
| 118 |
-
self.llm_api_url,
|
| 119 |
-
headers=self.headers_llm,
|
| 120 |
-
data=json.dumps(payload)
|
| 121 |
-
)
|
| 122 |
-
|
| 123 |
-
if response.status_code == 200:
|
| 124 |
-
result = response.json()
|
| 125 |
-
return result['choices'][0]['message']['content']
|
| 126 |
-
else:
|
| 127 |
-
# Fallback: return extracted text if LLM fails
|
| 128 |
-
return f"Đã trích xuất văn bản từ hình ảnh:\n\n{extracted_text}\n\n(Lưu ý: Không thể phân tích nâng cao do lỗi hệ thống)"
|
| 129 |
-
|
| 130 |
-
except Exception as e:
|
| 131 |
-
# Fallback to just returning extracted text
|
| 132 |
-
return f"Đã trích xuất văn bản từ hình ảnh:\n\n{extracted_text}\n\n(Lưu ý: {str(e)})"
|
| 133 |
-
|
| 134 |
-
def create_image_tab(image_service: ImageService):
|
| 135 |
-
gr.Markdown("## Phân tích hình ảnh")
|
| 136 |
-
with gr.Row():
|
| 137 |
-
image_input = gr.Image(type="numpy", label="Tải lên hình ảnh")
|
| 138 |
-
with gr.Row():
|
| 139 |
-
image_description = gr.Textbox(
|
| 140 |
-
label="Mô tả hình ảnh của bạn (tùy chọn)",
|
| 141 |
-
placeholder="Mô tả ngắn về hình ảnh để AI phân tích chính xác hơn..."
|
| 142 |
-
)
|
| 143 |
-
with gr.Row():
|
| 144 |
-
image_output = gr.Textbox(
|
| 145 |
-
label="Kết quả phân tích",
|
| 146 |
-
lines=10,
|
| 147 |
-
max_lines=15
|
| 148 |
-
)
|
| 149 |
-
|
| 150 |
-
with gr.Row():
|
| 151 |
-
analyze_button = gr.Button("Phân tích hình ảnh", variant="primary")
|
| 152 |
-
clear_button = gr.Button("Xóa")
|
| 153 |
-
|
| 154 |
-
# Analysis function
|
| 155 |
-
analyze_button.click(
|
| 156 |
-
image_service.analyze_image_with_description,
|
| 157 |
-
inputs=[image_input, image_description],
|
| 158 |
-
outputs=[image_output]
|
| 159 |
-
)
|
| 160 |
-
|
| 161 |
-
# Clear function
|
| 162 |
-
def clear_inputs():
|
| 163 |
-
return None, "", ""
|
| 164 |
-
|
| 165 |
-
clear_button.click(
|
| 166 |
-
clear_inputs,
|
| 167 |
-
outputs=[image_input, image_description, image_output]
|
| 168 |
-
)
|
| 169 |
-
|
| 170 |
-
# Examples
|
| 171 |
-
gr.Markdown("### Ví dụ về hình ảnh có thể phân tích:")
|
| 172 |
-
gr.Examples(
|
| 173 |
-
examples=[
|
| 174 |
-
["path/to/example1.jpg", "Hóa đơn mua hàng"],
|
| 175 |
-
["path/to/example2.jpg", "Tài liệu hướng dẫn"],
|
| 176 |
-
["path/to/example3.jpg", "Văn bản pháp luật"]
|
| 177 |
-
],
|
| 178 |
-
inputs=[image_input, image_description],
|
| 179 |
-
outputs=[image_output],
|
| 180 |
-
fn=image_service.analyze_image_with_description,
|
| 181 |
-
cache_examples=False
|
| 182 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|