borreooo commited on
Commit
fa17f1e
·
1 Parent(s): 39a6f54

Preload OCR model at startup

Browse files
Files changed (1) hide show
  1. server.py +11 -38
server.py CHANGED
@@ -9,16 +9,18 @@ from fastapi import FastAPI, UploadFile, File, Form, HTTPException, BackgroundTa
9
  from fastapi.responses import FileResponse, JSONResponse
10
  from fastapi.staticfiles import StaticFiles
11
  from fastapi.middleware.cors import CORSMiddleware
12
-
13
- # Import existing functions
14
  from barcode_scanner import scan_barcode, scan_all_barcodes
15
- from ocr import read_chassis, postprocess_with_hint, ocr_image
16
  from preprocess import preprocess_chassis
17
  from evaluate import evaluate, get_pairs
18
 
19
- app = FastAPI(title="Chassis OCR API", description="API backend for Chassis OCR PWA")
 
 
 
20
 
21
- # CORS middleware for testing
22
  app.add_middleware(
23
  CORSMiddleware,
24
  allow_origins=["*"],
@@ -33,7 +35,6 @@ CONFIG_PATH = "config.json"
33
  os.makedirs(TEMP_DIR, exist_ok=True)
34
  os.makedirs(RESULTS_DIR, exist_ok=True)
35
 
36
- # Helper to convert cv2 image to base64 jpeg
37
  def cv2_to_base64(img):
38
  _, buffer = cv2.imencode('.jpg', img)
39
  return base64.b64encode(buffer).decode('utf-8')
@@ -51,7 +52,6 @@ def get_test_pairs():
51
  chassis_dir = "images/chassis"
52
  if not os.path.exists(barcode_dir) or not os.path.exists(chassis_dir):
53
  return []
54
-
55
  barcodes = {os.path.splitext(f)[0] for f in os.listdir(barcode_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))}
56
  chassis = {os.path.splitext(f)[0] for f in os.listdir(chassis_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))}
57
  common = sorted(list(barcodes & chassis))
@@ -59,7 +59,6 @@ def get_test_pairs():
59
 
60
  @app.get("/api/scan-barcode/{key}")
61
  def scan_barcode_by_key(key: str):
62
- """Scan a barcode image from the dataset by its key (filename without extension)."""
63
  for ext in ['.jpg', '.jpeg', '.png', '.JPG', '.PNG']:
64
  path = os.path.join("images/barcode", f"{key}{ext}")
65
  if os.path.exists(path):
@@ -67,7 +66,6 @@ def scan_barcode_by_key(key: str):
67
  return {"success": result is not None, "barcode": result}
68
  raise HTTPException(status_code=404, detail=f"Barcode image for key '{key}' not found")
69
 
70
-
71
  @app.post("/api/scan-barcode")
72
  async def api_scan_barcode(file: UploadFile = File(...)):
73
  temp_filename = f"{uuid.uuid4()}_{file.filename}"
@@ -75,7 +73,6 @@ async def api_scan_barcode(file: UploadFile = File(...)):
75
  try:
76
  with open(temp_path, "wb") as buffer:
77
  shutil.copyfileobj(file.file, buffer)
78
-
79
  result = scan_barcode(temp_path)
80
  return {"success": result is not None, "barcode": result}
81
  except Exception as e:
@@ -100,7 +97,6 @@ async def save_config(config_data: dict):
100
  except Exception as e:
101
  raise HTTPException(status_code=500, detail=str(e))
102
 
103
- # Global state to keep track of batch evaluation runs
104
  eval_status = {"running": False, "progress": 0, "total": 0, "results": []}
105
 
106
  def run_evaluation_task():
@@ -108,10 +104,7 @@ def run_evaluation_task():
108
  try:
109
  eval_status["running"] = True
110
  eval_status["progress"] = 0
111
-
112
- # We can call the evaluate function but let's read the report afterwards
113
  evaluate()
114
-
115
  report_path = os.path.join(RESULTS_DIR, "report.json")
116
  if os.path.exists(report_path):
117
  with open(report_path) as f:
@@ -128,7 +121,6 @@ def trigger_evaluation(background_tasks: BackgroundTasks):
128
  global eval_status
129
  if eval_status["running"]:
130
  return {"status": "already_running", "message": "Evaluation task is currently running"}
131
-
132
  eval_status = {"running": True, "progress": 0, "total": 50, "results": []}
133
  background_tasks.add_task(run_evaluation_task)
134
  return {"status": "started", "message": "Batch evaluation started in the background"}
@@ -143,7 +135,6 @@ def get_evaluation_status():
143
  results = json.load(f)
144
  except Exception:
145
  pass
146
-
147
  return {
148
  "running": eval_status["running"],
149
  "progress": eval_status["progress"],
@@ -165,8 +156,6 @@ async def match_chassis(
165
  temp_path = None
166
 
167
  if chassis_key:
168
- # Load from test set
169
- # Check standard extensions (.jpg, .png, etc.)
170
  for ext in ['.jpg', '.jpeg', '.png', '.JPG', '.PNG']:
171
  p = os.path.join("images/chassis", f"{chassis_key}{ext}")
172
  if os.path.exists(p):
@@ -175,7 +164,6 @@ async def match_chassis(
175
  if not chassis_path:
176
  raise HTTPException(status_code=404, detail=f"Chassis image for key '{chassis_key}' not found in images/chassis")
177
  else:
178
- # Save uploaded file
179
  temp_filename = f"{uuid.uuid4()}_{chassis_file.filename}"
180
  temp_path = os.path.join(TEMP_DIR, temp_filename)
181
  with open(temp_path, "wb") as buffer:
@@ -183,18 +171,12 @@ async def match_chassis(
183
  chassis_path = temp_path
184
 
185
  try:
186
- # 1. Run Preprocessing to get variations
187
- # Use save_comparison=True so we also write the side-by-side view to results/ (useful for viewing static file later)
188
- # Note: if it's a temp file, let's create a friendly name for results comparison
189
  save_comp = True
190
  comp_filename = chassis_key if chassis_key else os.path.splitext(chassis_file.filename)[0]
191
-
192
  variations = preprocess_chassis(chassis_path, save_comparison=save_comp)
193
-
194
- # 2. Get base64 representation of original and each variation
195
  original_img = cv2.imread(chassis_path)
196
  base64_original = cv2_to_base64(original_img)
197
-
198
  base64_variations = []
199
  labels = ["CLAHE", "Bilateral", "Otsu", "Adaptive"]
200
  for idx, var in enumerate(variations):
@@ -203,11 +185,10 @@ async def match_chassis(
203
  "base64": cv2_to_base64(var)
204
  })
205
 
206
- # 3. Run OCR on each variation and compute scores, finding the best
207
  best_text, best_conf, best_score = "", 0.0, -1
208
  winning_label = ""
209
  variation_details = []
210
-
211
  for idx, var in enumerate(variations):
212
  text, conf = ocr_image(var)
213
  score = conf * max(len(text), 1)
@@ -221,16 +202,14 @@ async def match_chassis(
221
  best_text, best_conf, best_score = text, conf, score
222
  winning_label = labels[idx]
223
 
224
- # 4. Perform error correction and match check
225
  corrected_text, is_match = postprocess_with_hint(best_text, barcode_val)
226
-
227
  status = "FAILED"
228
  if best_text == barcode_val:
229
  status = "EXACT"
230
  elif is_match:
231
  status = "CORRECTED"
232
 
233
- response_data = {
234
  "success": is_match,
235
  "status": status,
236
  "barcode_val": barcode_val,
@@ -243,7 +222,6 @@ async def match_chassis(
243
  "variation_details": variation_details,
244
  "comparison_url": f"/results/{comp_filename}_comparison.jpg" if save_comp else None
245
  }
246
- return response_data
247
 
248
  except Exception as e:
249
  raise HTTPException(status_code=500, detail=str(e))
@@ -251,14 +229,9 @@ async def match_chassis(
251
  if temp_path and os.path.exists(temp_path):
252
  os.remove(temp_path)
253
 
254
- # Serve results images directly
255
  app.mount("/results", StaticFiles(directory="results"), name="results")
256
-
257
- # Serve frontend application static files
258
- # We will mount at "/" with html=True so index.html is served automatically
259
- # Make sure to run this *after* route declarations
260
  app.mount("/", StaticFiles(directory="web", html=True), name="static")
261
 
262
  if __name__ == "__main__":
263
  import uvicorn
264
- uvicorn.run("server:app", host="0.0.0.0", port=8000, reload=True)
 
9
  from fastapi.responses import FileResponse, JSONResponse
10
  from fastapi.staticfiles import StaticFiles
11
  from fastapi.middleware.cors import CORSMiddleware
12
+ from contextlib import asynccontextmanager
 
13
  from barcode_scanner import scan_barcode, scan_all_barcodes
14
+ from ocr import read_chassis, postprocess_with_hint, ocr_image, get_ocr
15
  from preprocess import preprocess_chassis
16
  from evaluate import evaluate, get_pairs
17
 
18
+ @asynccontextmanager
19
+ async def lifespan(app: FastAPI):
20
+ get_ocr()
21
+ yield
22
 
23
+ app = FastAPI(title="Chassis OCR API", description="API backend for Chassis OCR PWA", lifespan=lifespan)
24
  app.add_middleware(
25
  CORSMiddleware,
26
  allow_origins=["*"],
 
35
  os.makedirs(TEMP_DIR, exist_ok=True)
36
  os.makedirs(RESULTS_DIR, exist_ok=True)
37
 
 
38
  def cv2_to_base64(img):
39
  _, buffer = cv2.imencode('.jpg', img)
40
  return base64.b64encode(buffer).decode('utf-8')
 
52
  chassis_dir = "images/chassis"
53
  if not os.path.exists(barcode_dir) or not os.path.exists(chassis_dir):
54
  return []
 
55
  barcodes = {os.path.splitext(f)[0] for f in os.listdir(barcode_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))}
56
  chassis = {os.path.splitext(f)[0] for f in os.listdir(chassis_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))}
57
  common = sorted(list(barcodes & chassis))
 
59
 
60
  @app.get("/api/scan-barcode/{key}")
61
  def scan_barcode_by_key(key: str):
 
62
  for ext in ['.jpg', '.jpeg', '.png', '.JPG', '.PNG']:
63
  path = os.path.join("images/barcode", f"{key}{ext}")
64
  if os.path.exists(path):
 
66
  return {"success": result is not None, "barcode": result}
67
  raise HTTPException(status_code=404, detail=f"Barcode image for key '{key}' not found")
68
 
 
69
  @app.post("/api/scan-barcode")
70
  async def api_scan_barcode(file: UploadFile = File(...)):
71
  temp_filename = f"{uuid.uuid4()}_{file.filename}"
 
73
  try:
74
  with open(temp_path, "wb") as buffer:
75
  shutil.copyfileobj(file.file, buffer)
 
76
  result = scan_barcode(temp_path)
77
  return {"success": result is not None, "barcode": result}
78
  except Exception as e:
 
97
  except Exception as e:
98
  raise HTTPException(status_code=500, detail=str(e))
99
 
 
100
  eval_status = {"running": False, "progress": 0, "total": 0, "results": []}
101
 
102
  def run_evaluation_task():
 
104
  try:
105
  eval_status["running"] = True
106
  eval_status["progress"] = 0
 
 
107
  evaluate()
 
108
  report_path = os.path.join(RESULTS_DIR, "report.json")
109
  if os.path.exists(report_path):
110
  with open(report_path) as f:
 
121
  global eval_status
122
  if eval_status["running"]:
123
  return {"status": "already_running", "message": "Evaluation task is currently running"}
 
124
  eval_status = {"running": True, "progress": 0, "total": 50, "results": []}
125
  background_tasks.add_task(run_evaluation_task)
126
  return {"status": "started", "message": "Batch evaluation started in the background"}
 
135
  results = json.load(f)
136
  except Exception:
137
  pass
 
138
  return {
139
  "running": eval_status["running"],
140
  "progress": eval_status["progress"],
 
156
  temp_path = None
157
 
158
  if chassis_key:
 
 
159
  for ext in ['.jpg', '.jpeg', '.png', '.JPG', '.PNG']:
160
  p = os.path.join("images/chassis", f"{chassis_key}{ext}")
161
  if os.path.exists(p):
 
164
  if not chassis_path:
165
  raise HTTPException(status_code=404, detail=f"Chassis image for key '{chassis_key}' not found in images/chassis")
166
  else:
 
167
  temp_filename = f"{uuid.uuid4()}_{chassis_file.filename}"
168
  temp_path = os.path.join(TEMP_DIR, temp_filename)
169
  with open(temp_path, "wb") as buffer:
 
171
  chassis_path = temp_path
172
 
173
  try:
 
 
 
174
  save_comp = True
175
  comp_filename = chassis_key if chassis_key else os.path.splitext(chassis_file.filename)[0]
 
176
  variations = preprocess_chassis(chassis_path, save_comparison=save_comp)
 
 
177
  original_img = cv2.imread(chassis_path)
178
  base64_original = cv2_to_base64(original_img)
179
+
180
  base64_variations = []
181
  labels = ["CLAHE", "Bilateral", "Otsu", "Adaptive"]
182
  for idx, var in enumerate(variations):
 
185
  "base64": cv2_to_base64(var)
186
  })
187
 
 
188
  best_text, best_conf, best_score = "", 0.0, -1
189
  winning_label = ""
190
  variation_details = []
191
+
192
  for idx, var in enumerate(variations):
193
  text, conf = ocr_image(var)
194
  score = conf * max(len(text), 1)
 
202
  best_text, best_conf, best_score = text, conf, score
203
  winning_label = labels[idx]
204
 
 
205
  corrected_text, is_match = postprocess_with_hint(best_text, barcode_val)
 
206
  status = "FAILED"
207
  if best_text == barcode_val:
208
  status = "EXACT"
209
  elif is_match:
210
  status = "CORRECTED"
211
 
212
+ return {
213
  "success": is_match,
214
  "status": status,
215
  "barcode_val": barcode_val,
 
222
  "variation_details": variation_details,
223
  "comparison_url": f"/results/{comp_filename}_comparison.jpg" if save_comp else None
224
  }
 
225
 
226
  except Exception as e:
227
  raise HTTPException(status_code=500, detail=str(e))
 
229
  if temp_path and os.path.exists(temp_path):
230
  os.remove(temp_path)
231
 
 
232
  app.mount("/results", StaticFiles(directory="results"), name="results")
 
 
 
 
233
  app.mount("/", StaticFiles(directory="web", html=True), name="static")
234
 
235
  if __name__ == "__main__":
236
  import uvicorn
237
+ uvicorn.run("server:app", host="0.0.0.0", port=8000, reload=True)