pitcserverbilal commited on
Commit
9398e88
·
verified ·
1 Parent(s): 55e3d88

Upload ocr_server.py

Browse files
Files changed (1) hide show
  1. ocr_server.py +179 -0
ocr_server.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ocr_server.py - Clean natural language messages (no Reference number text)
2
+ from fastapi import FastAPI, HTTPException
3
+ from fastapi.responses import JSONResponse
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from pydantic import BaseModel
6
+ import base64
7
+ import binascii
8
+ from PIL import Image
9
+ import io
10
+ import torch
11
+ from transformers import Qwen2VLForConditionalGeneration, AutoProcessor
12
+ import re
13
+
14
+ app = FastAPI()
15
+
16
+ # Enable CORS for mobile app
17
+ app.add_middleware(
18
+ CORSMiddleware,
19
+ allow_origins=["*"],
20
+ allow_credentials=True,
21
+ allow_methods=["*"],
22
+ allow_headers=["*"],
23
+ )
24
+
25
+ # Request model for Base64
26
+ class OCRRequest(BaseModel):
27
+ image_base64: str
28
+ filename: str = "image.jpg"
29
+
30
+ # Load model
31
+ print("Loading OCR model...")
32
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
33
+ model_id = "prithivMLmods/coreOCR-7B-050325-preview"
34
+
35
+ processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
36
+ model = Qwen2VLForConditionalGeneration.from_pretrained(
37
+ model_id,
38
+ trust_remote_code=True,
39
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
40
+ ).to(device).eval()
41
+
42
+ print(f"Model loaded on {device}")
43
+
44
+ def extract_numbers(text):
45
+ numbers = re.findall(r'\d+', text)
46
+ return ''.join(numbers) if numbers else ""
47
+
48
+ def base64_to_image(base64_string):
49
+ if ',' in base64_string and base64_string.startswith('data:'):
50
+ base64_string = base64_string.split(',', 1)[1]
51
+ image_bytes = base64.b64decode(base64_string)
52
+ image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
53
+ return image
54
+
55
+ @app.post("/ocr")
56
+ async def ocr_image(request: OCRRequest):
57
+ try:
58
+ # Convert base64 to image
59
+ image = base64_to_image(request.image_base64)
60
+
61
+ # Run OCR
62
+ messages = [{
63
+ "role": "user",
64
+ "content": [
65
+ {"type": "image"},
66
+ {"type": "text", "text": "Extract all numbers from this meter reading. Return only the numbers."},
67
+ ]
68
+ }]
69
+
70
+ prompt_full = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
71
+ inputs = processor(
72
+ text=[prompt_full],
73
+ images=[image],
74
+ return_tensors="pt",
75
+ ).to(device)
76
+
77
+ with torch.no_grad():
78
+ outputs = model.generate(
79
+ **inputs,
80
+ max_new_tokens=200,
81
+ temperature=0.1,
82
+ do_sample=False
83
+ )
84
+
85
+ result = processor.decode(outputs[0], skip_special_tokens=True)
86
+ numbers_only = extract_numbers(result)
87
+
88
+ # Clean natural language messages
89
+ if numbers_only:
90
+ message = "Meter reading successfully extracted"
91
+ ref_no = numbers_only[-14:] if len(numbers_only) >= 14 else numbers_only
92
+ else:
93
+ message = "No numbers found in the image. Please provide a clear meter reading photo"
94
+ ref_no = ""
95
+
96
+ return JSONResponse({
97
+ "success": True,
98
+ "message": message,
99
+ "ref_no": ref_no,
100
+ "numbers": numbers_only
101
+ })
102
+
103
+ except binascii.Error:
104
+ return JSONResponse({
105
+ "success": False,
106
+ "message": "Invalid image format. Please send a valid Base64 encoded image"
107
+ }, status_code=400)
108
+
109
+ except Exception as e:
110
+ error_message = str(e)
111
+ if "image" in error_message.lower():
112
+ message = "Could not process the image. Please ensure it's a valid photo of a meter reading"
113
+ elif "timeout" in error_message.lower():
114
+ message = "OCR processing timed out. Please try with a smaller or clearer image"
115
+ else:
116
+ message = "Failed to process image. Please try again"
117
+
118
+ return JSONResponse({
119
+ "success": False,
120
+ "message": message
121
+ }, status_code=500)
122
+
123
+ @app.get("/health")
124
+ async def health_check():
125
+ return {
126
+ "status": "ok",
127
+ "model": "coreOCR-7B",
128
+ "message": "OCR server is running normally"
129
+ }
130
+
131
+ @app.post("/ocr-file")
132
+ async def ocr_image_file(file: bytes = None):
133
+ """Legacy file upload endpoint for testing"""
134
+ try:
135
+ image = Image.open(io.BytesIO(file)).convert("RGB")
136
+
137
+ messages = [{
138
+ "role": "user",
139
+ "content": [
140
+ {"type": "image"},
141
+ {"type": "text", "text": "Extract all numbers from this meter reading. Return only the numbers."},
142
+ ]
143
+ }]
144
+
145
+ prompt_full = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
146
+ inputs = processor(
147
+ text=[prompt_full],
148
+ images=[image],
149
+ return_tensors="pt",
150
+ ).to(device)
151
+
152
+ with torch.no_grad():
153
+ outputs = model.generate(**inputs, max_new_tokens=200, temperature=0.1, do_sample=False)
154
+
155
+ result = processor.decode(outputs[0], skip_special_tokens=True)
156
+ numbers_only = extract_numbers(result)
157
+
158
+ if numbers_only:
159
+ message = "Meter reading successfully extracted"
160
+ ref_no = numbers_only[-14:] if len(numbers_only) >= 14 else numbers_only
161
+ else:
162
+ message = "No numbers found in the image. Please provide a clear meter reading photo"
163
+ ref_no = ""
164
+
165
+ return JSONResponse({
166
+ "success": True,
167
+ "message": message,
168
+ "ref_no": ref_no,
169
+ "numbers": numbers_only
170
+ })
171
+ except Exception as e:
172
+ return JSONResponse({
173
+ "success": False,
174
+ "message": "Failed to process image. Please try again"
175
+ }, status_code=500)
176
+
177
+ if __name__ == "__main__":
178
+ import uvicorn
179
+ uvicorn.run(app, host="0.0.0.0", port=8000)