Skip to content

Commit ec097c9

Browse files
committed
Add MSER text-region detection (find text without OCR)
1 parent 63e1bfe commit ec097c9

15 files changed

Lines changed: 410 additions & 1 deletion

File tree

README/WHATS_NEW_zh-CN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 免模型文字区检测(MSER)
4+
5+
不跑 OCR 也能找出画面上文字的位置。完整参考:[`docs/source/Zh/doc/new_features/v140_features_doc.rst`](../docs/source/Zh/doc/new_features/v140_features_doc.rst)
6+
7+
- **`find_text_regions` / `find_text_lines`**(`AC_find_text_regions``AC_find_text_lines`):`shape_locator` 找矩形(不是文字)、`locate_text` 需要 OCR 引擎*以及*确切字串——两者都无法回答「哪里有*任何*文字?」。MSER 找出字元 / 词 / 行区块,让脚本能裁切候选框喂给 OCR(比全画面更快更准),或在未安装 OCR 相依时检测标签出现。`merge` 并集 MSER 逐字元的嵌套区域;`find_text_lines` 将字元归为逐行框;空白画面返回 `[]`。OpenCV 核心(`cv2.MSER_create`)、可注入 haystack → 无头可测。
8+
39
## 本次更新 (2026-06-23) — HSV 色彩空间分割
410

511
不论光照都能找出「任一色阶的红色」。完整参考:[`docs/source/Zh/doc/new_features/v139_features_doc.rst`](../docs/source/Zh/doc/new_features/v139_features_doc.rst)

README/WHATS_NEW_zh-TW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 本次更新 — AutoControl
22

3+
## 本次更新 (2026-06-23) — 免模型文字區偵測(MSER)
4+
5+
不跑 OCR 也能找出畫面上文字的位置。完整參考:[`docs/source/Zh/doc/new_features/v140_features_doc.rst`](../docs/source/Zh/doc/new_features/v140_features_doc.rst)
6+
7+
- **`find_text_regions` / `find_text_lines`**(`AC_find_text_regions``AC_find_text_lines`):`shape_locator` 找矩形(不是文字)、`locate_text` 需要 OCR 引擎*以及*確切字串——兩者都無法回答「哪裡有*任何*文字?」。MSER 找出字元 / 詞 / 行區塊,讓腳本能裁切候選框餵給 OCR(比全畫面更快更準),或在未安裝 OCR 相依時偵測標籤出現。`merge` 聯集 MSER 逐字元的巢狀區域;`find_text_lines` 將字元歸為逐行框;空白畫面回傳 `[]`。OpenCV 核心(`cv2.MSER_create`)、可注入 haystack → 無頭可測。
8+
39
## 本次更新 (2026-06-23) — HSV 色彩空間分割
410

511
不論光照都能找出「任一色階的紅色」。完整參考:[`docs/source/Zh/doc/new_features/v139_features_doc.rst`](../docs/source/Zh/doc/new_features/v139_features_doc.rst)

WHATS_NEW.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# What's New — AutoControl
22

3+
## What's new (2026-06-23) — Model-Free Text-Region Detection (MSER)
4+
5+
Find where text is on screen without running OCR. Full reference: [`docs/source/Eng/doc/new_features/v140_features_doc.rst`](docs/source/Eng/doc/new_features/v140_features_doc.rst).
6+
7+
- **`find_text_regions` / `find_text_lines`** (`AC_find_text_regions`, `AC_find_text_lines`): `shape_locator` finds rectangles (not text) and `locate_text` needs an OCR engine *and* the exact string — neither answers "where is *any* text?". MSER finds the glyph/word/line blobs, so a script can crop candidate boxes to feed OCR (faster + more accurate than full-frame) or detect a label appeared with no OCR dependency. `merge` unions MSER's nested per-glyph regions; `find_text_lines` groups glyphs into per-line boxes; a blank screen returns `[]`. Base OpenCV (`cv2.MSER_create`), injectable haystack → headless-testable.
8+
39
## What's new (2026-06-23) — HSV Colour-Space Segmentation
410

511
Find "any shade of red" regardless of lighting. Full reference: [`docs/source/Eng/doc/new_features/v139_features_doc.rst`](docs/source/Eng/doc/new_features/v139_features_doc.rst).
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Model-Free Text-Region Detection (MSER)
2+
=======================================
3+
4+
``shape_locator`` finds rectangular contours (buttons / cards, not text) and
5+
``locate_text`` needs a Tesseract / Paddle engine *and* the exact string to search
6+
for. Neither answers "where is there *any* text on screen?" without running OCR or
7+
knowing the words. ``find_text_regions`` and ``find_text_lines`` use MSER (Maximally
8+
Stable Extremal Regions) to find the glyph / word / line blobs, so a script can crop
9+
the candidate text boxes to feed OCR — far faster and more accurate than full-frame
10+
OCR — or simply detect that a label appeared with no OCR dependency installed.
11+
12+
Runs on an injectable ``haystack`` (ndarray / path / PIL), so it is headless-testable
13+
on synthetic arrays. ``cv2.MSER_create`` is base OpenCV (no contrib); OpenCV + NumPy
14+
come in via ``je_open_cv``. Imports no ``PySide6``.
15+
16+
Headless API
17+
------------
18+
19+
.. code-block:: python
20+
21+
from je_auto_control import find_text_regions, find_text_lines
22+
23+
# Crop each text line and OCR just that strip.
24+
for line in find_text_lines(y_tolerance=8):
25+
text = locate_text # ... feed the cropped region to your OCR of choice
26+
print(line["x"], line["y"], line["width"], line["height"])
27+
28+
# Or per-glyph / per-word regions.
29+
for box in find_text_regions(min_area=80):
30+
highlight(box["x"], box["y"], box["width"], box["height"])
31+
32+
``find_text_regions`` returns ``{x, y, width, height, area, center}`` per region,
33+
largest first; ``merge`` unions MSER's nested per-glyph detections, ``min_area`` /
34+
``max_area`` drop specks and page-sized blobs, ``max_aspect`` rejects long thin rules.
35+
``find_text_lines`` groups glyph boxes whose vertical centres are within
36+
``y_tolerance`` pixels into one box per line, top-to-bottom. A blank screen returns an
37+
empty list (the whole-frame extremal region is filtered out).
38+
39+
Executor commands
40+
-----------------
41+
42+
``AC_find_text_regions`` (``min_area`` / ``max_area`` / ``merge`` / ``max_aspect`` /
43+
``region`` → ``{count, regions}``) and ``AC_find_text_lines`` (``y_tolerance`` /
44+
``region`` → ``{count, lines}``). They are exposed as the MCP tools
45+
``ac_find_text_regions`` / ``ac_find_text_lines`` and as Script Builder commands
46+
under **Image**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ Comprehensive guides for all AutoControl features.
162162
doc/new_features/v137_features_doc
163163
doc/new_features/v138_features_doc
164164
doc/new_features/v139_features_doc
165+
doc/new_features/v140_features_doc
165166
doc/ocr_backends/ocr_backends_doc
166167
doc/observability/observability_doc
167168
doc/operations_layer/operations_layer_doc
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
免模型文字區偵測(MSER)
2+
========================
3+
4+
``shape_locator`` 找的是矩形輪廓(按鈕 / 卡片,不是文字),``locate_text`` 需要 Tesseract / Paddle 引擎*以及*要
5+
搜尋的確切字串。兩者都無法在不跑 OCR、也不知道內容的情況下回答「畫面上哪裡有*任何*文字?」。``find_text_regions``
6+
與 ``find_text_lines`` 以 MSER(最大穩定極值區域)找出字元 / 詞 / 行的區塊,讓腳本能裁切候選文字框餵給 OCR
7+
(比全畫面 OCR 更快更準),或在未安裝 OCR 相依時單純偵測「某標籤出現了」。
8+
9+
在可注入的 ``haystack``(ndarray / 路徑 / PIL)上執行,因此可對合成陣列做無頭測試。``cv2.MSER_create`` 屬於
10+
OpenCV 核心(不需 contrib);OpenCV + NumPy 透過 ``je_open_cv`` 引入。不匯入 ``PySide6``。
11+
12+
無頭 API
13+
--------
14+
15+
.. code-block:: python
16+
17+
from je_auto_control import find_text_regions, find_text_lines
18+
19+
# 裁切每一行文字,只對該長條做 OCR。
20+
for line in find_text_lines(y_tolerance=8):
21+
print(line["x"], line["y"], line["width"], line["height"])
22+
23+
# 或逐字元 / 逐詞的區域。
24+
for box in find_text_regions(min_area=80):
25+
highlight(box["x"], box["y"], box["width"], box["height"])
26+
27+
``find_text_regions`` 為每個區域回傳 ``{x, y, width, height, area, center}``,由大到小;``merge`` 會聯集 MSER
28+
逐字元的巢狀偵測,``min_area`` / ``max_area`` 去除雜點與整頁大小的區塊,``max_aspect`` 排除長條狀的分隔線。
29+
``find_text_lines`` 將垂直中心在 ``y_tolerance`` 像素內的字元框歸為同一行,每行一個框、由上到下。空白畫面回傳空
30+
清單(整框極值區域已被濾除)。
31+
32+
執行器命令
33+
----------
34+
35+
``AC_find_text_regions``(``min_area`` / ``max_area`` / ``merge`` / ``max_aspect`` / ``region`` →
36+
``{count, regions}``)與 ``AC_find_text_lines``(``y_tolerance`` / ``region`` → ``{count, lines}``)。它們以
37+
MCP 工具 ``ac_find_text_regions`` / ``ac_find_text_lines`` 以及 Script Builder 中 **Image** 分類下的命令提供。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ AutoControl 所有功能的完整使用指南。
162162
doc/new_features/v137_features_doc
163163
doc/new_features/v138_features_doc
164164
doc/new_features/v139_features_doc
165+
doc/new_features/v140_features_doc
165166
doc/ocr_backends/ocr_backends_doc
166167
doc/observability/observability_doc
167168
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@
320320
from je_auto_control.utils.hsv_segment import (
321321
color_mask, dominant_hue_regions, segment_hsv,
322322
)
323+
# Model-free on-screen text-region detection (MSER): regions and lines
324+
from je_auto_control.utils.text_regions import (
325+
find_text_lines, find_text_regions,
326+
)
323327
# CI workflow annotations (GitHub Actions)
324328
from je_auto_control.utils.ci_annotations import (
325329
emit_annotations, format_annotation,
@@ -1156,6 +1160,8 @@ def start_autocontrol_gui(*args, **kwargs):
11561160
"segment_hsv",
11571161
"color_mask",
11581162
"dominant_hue_regions",
1163+
"find_text_regions",
1164+
"find_text_lines",
11591165
"emit_annotations", "format_annotation",
11601166
"ClipboardHistory", "default_clipboard_history",
11611167
"analyze_heal_log", "heal_stats", "scan_secrets",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,27 @@ def _add_image_specs(specs: List[CommandSpec]) -> None:
416416
),
417417
description="Locate any shade of a hue, any brightness (handles red wrap).",
418418
))
419+
specs.append(CommandSpec(
420+
"AC_find_text_regions", "Image", "Find Text Regions (MSER)",
421+
fields=(
422+
FieldSpec("min_area", FieldType.INT, optional=True, default=60),
423+
FieldSpec("max_area", FieldType.INT, optional=True),
424+
FieldSpec("merge", FieldType.BOOL, optional=True, default=True),
425+
FieldSpec("max_aspect", FieldType.FLOAT, optional=True, default=12.0),
426+
FieldSpec("region", FieldType.STRING, optional=True,
427+
placeholder=_REGION_PLACEHOLDER),
428+
),
429+
description="Locate text regions without OCR (crop to feed an OCR engine).",
430+
))
431+
specs.append(CommandSpec(
432+
"AC_find_text_lines", "Image", "Find Text Lines (MSER)",
433+
fields=(
434+
FieldSpec("y_tolerance", FieldType.INT, optional=True, default=8),
435+
FieldSpec("region", FieldType.STRING, optional=True,
436+
placeholder=_REGION_PLACEHOLDER),
437+
),
438+
description="Locate horizontal text lines without OCR.",
439+
))
419440
specs.append(CommandSpec(
420441
"AC_fuse_elements", "Image", "Fuse Element Boxes",
421442
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,30 @@ def _dominant_hue_regions(hue: Any, hue_tol: Any = 10, sat_min: Any = 80,
35373537
"best": boxes[0] if boxes else None}
35383538

35393539

3540+
def _find_text_regions(min_area: Any = 60, max_area: Any = None, merge: Any = True,
3541+
max_aspect: Any = 12.0, region: Any = None) -> Dict[str, Any]:
3542+
"""Adapter: locate text/glyph regions on screen via MSER (no OCR)."""
3543+
import json
3544+
from je_auto_control.utils.text_regions import find_text_regions
3545+
if isinstance(region, str):
3546+
region = json.loads(region) if region.strip() else None
3547+
regions = find_text_regions(
3548+
region=region, min_area=int(min_area),
3549+
max_area=int(max_area) if max_area is not None else None,
3550+
merge=bool(merge), max_aspect=float(max_aspect))
3551+
return {"count": len(regions), "regions": regions}
3552+
3553+
3554+
def _find_text_lines(y_tolerance: Any = 8, region: Any = None) -> Dict[str, Any]:
3555+
"""Adapter: locate horizontal text lines on screen via MSER (no OCR)."""
3556+
import json
3557+
from je_auto_control.utils.text_regions import find_text_lines
3558+
if isinstance(region, str):
3559+
region = json.loads(region) if region.strip() else None
3560+
lines = find_text_lines(region=region, y_tolerance=int(y_tolerance))
3561+
return {"count": len(lines), "lines": lines}
3562+
3563+
35403564
def _with_modifiers(modifiers: Any, actions: Any) -> Dict[str, Any]:
35413565
"""Adapter: run nested actions while modifier keys are held down."""
35423566
import json
@@ -5271,6 +5295,8 @@ def __init__(self):
52715295
"AC_reading_order": _reading_order,
52725296
"AC_segment_hsv": _segment_hsv,
52735297
"AC_dominant_hue_regions": _dominant_hue_regions,
5298+
"AC_find_text_regions": _find_text_regions,
5299+
"AC_find_text_lines": _find_text_lines,
52745300
"AC_tile_rect": _tile_rect,
52755301
"AC_grid_rects": _grid_rects,
52765302
"AC_cascade_rects": _cascade_rects,

0 commit comments

Comments
 (0)