From a61fe467aa6ed98364d7a83a71ea83d26c04af8d Mon Sep 17 00:00:00 2001 From: q Date: Sun, 8 Mar 2026 12:27:25 +0300 Subject: [PATCH] update --- README.md | 2 +- __pycache__/app.cpython-313.pyc | Bin 19880 -> 25816 bytes app.py | 97 +++++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e386be9..df235dd 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ OpenCV from JetPack repos (4.x) usually ships without CUDA dnn. To use CUDA, bui - `RETURN_BOXES` (default `true`) - `USE_CUDA_DNN` (default `false`, set `true` to attempt CUDA backend for EAST if OpenCV built with CUDA) - `DEBUG_VISUAL` (default `false`; if true, response includes `debug_image_base64`. Per-request: add `?debug=1` or form field `debug=1`.) -- `SHOW_GUI` (default `false`; if true and OpenCV GUI available, service opens a window with overlays. Requires X/Wayland/VDI that supports GUI.) +- `SHOW_GUI` (default `false`; if true and OpenCV GUI available, service opens a text-centric window: left = detected text regions, right = OCR block text/conf. Requires X/Wayland/VDI that supports GUI.) ## Run ```bash diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index c0b5dabbc976890874478f3561aecc9edb603d55..bf2035cace211c2ad047ef62dd17280fd06efb42 100644 GIT binary patch delta 6511 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 0 else 1600 + if frame_w < 900: + frame_w = 900 + + left_w = int(frame_w * 0.64) + scale = float(left_w) / float(max(1, w)) + left_h = max(360, int(h * scale)) + left = cv2.resize(src, (left_w, left_h), interpolation=cv2.INTER_AREA if scale < 1.0 else cv2.INTER_CUBIC) + + # text detector regions + for idx, b in enumerate(boxes or []): + try: + x1, y1, x2, y2 = [int(v * scale) for v in b] + except Exception: + continue + cv2.rectangle(left, (x1, y1), (x2, y2), (255, 90, 0), 2) + cv2.putText(left, "T{}".format(idx + 1), (x1, max(12, y1 - 4)), + cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 90, 0), 1, cv2.LINE_AA) + + # OCR blocks (where text was actually extracted) + for idx, blk in enumerate(blocks or []): + try: + x1, y1, x2, y2 = [int(v * scale) for v in blk.get("bbox", [0, 0, 0, 0])] + except Exception: + continue + cv2.rectangle(left, (x1, y1), (x2, y2), (0, 160, 255), 2) + cv2.putText(left, "R{}".format(idx + 1), (x1, min(left_h - 8, y2 + 14)), + cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 160, 255), 1, cv2.LINE_AA) + + right_w = frame_w - left_w + if right_w < 320: + right_w = 320 + left = cv2.resize(left, (frame_w - right_w, left_h)) + left_w = left.shape[1] + right = np.ones((left_h, right_w, 3), dtype=np.uint8) * 245 + + y = 28 + line_h = 22 + cv2.putText(right, "OCR text panel", (14, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (30, 30, 30), 2, cv2.LINE_AA) + y += line_h + 4 + cv2.putText(right, "detected regions: {}".format(len(boxes or [])), (14, y), + cv2.FONT_HERSHEY_SIMPLEX, 0.55, (70, 70, 70), 1, cv2.LINE_AA) + y += line_h + cv2.putText(right, "recognized blocks: {}".format(len(blocks or [])), (14, y), + cv2.FONT_HERSHEY_SIMPLEX, 0.55, (70, 70, 70), 1, cv2.LINE_AA) + y += line_h + 4 + cv2.line(right, (12, y), (right_w - 12, y), (200, 200, 200), 1) + y += 18 + + if not blocks: + cv2.putText(right, "No OCR text blocks", (14, y), + cv2.FONT_HERSHEY_SIMPLEX, 0.56, (100, 100, 100), 1, cv2.LINE_AA) + else: + visible = blocks[:10] + max_chars = max(18, int((right_w - 24) / 8)) + for idx, blk in enumerate(visible): + conf = blk.get("conf", -1) + conf_str = "{:.1f}".format(conf) if isinstance(conf, (float, int)) and conf >= 0 else "n/a" + header = "#{:02d} conf={}".format(idx + 1, conf_str) + cv2.putText(right, header, (14, y), cv2.FONT_HERSHEY_SIMPLEX, 0.50, (20, 20, 20), 1, cv2.LINE_AA) + y += line_h - 2 + raw = (blk.get("text", "") or "").replace("\n", " ").replace("\r", " ").strip() + txt = " ".join(raw.split()) + if len(txt) > max_chars: + txt = txt[:max_chars - 1] + "..." + cv2.putText(right, txt if txt else "", (14, y), + cv2.FONT_HERSHEY_SIMPLEX, 0.52, (30, 30, 30), 1, cv2.LINE_AA) + y += line_h + if y > left_h - 18: + break + + frame = np.concatenate([left, right], axis=1) + return frame + except Exception: + return None + # ---- Routes ---- @app.route('/health', methods=['GET']) @@ -382,9 +466,18 @@ def image2text(): target_width=target_w, return_image=SHOW_GUI, ) - if SHOW_GUI and cv2 is not None and debug_image_arr is not None: + if SHOW_GUI and cv2 is not None: try: - _gui_show(debug_image_arr) + gui_frame = build_text_gui_image( + img_rgb, + boxes, + blocks if RETURN_BOXES else [], + target_width=GUI_TARGET_WIDTH, + ) + if gui_frame is None: + gui_frame = debug_image_arr + if gui_frame is not None: + _gui_show(gui_frame) except Exception: pass