Skip to content

Commit 4779bf3

Browse files
committed
Add table_grid_fill: fill a ruling-line grid with OCR text
edge_lines.find_grid recovers a bordered table's geometry but leaves the cells empty; OCR gives the text but no structure, and nothing joined them. Drop OCR boxes into the grid (assigned by cell-centre, gated by an overlap fraction), concatenate each cell's text in reading order, flag merged-cell spans, and convert to records / CSV. Pure-stdlib over plain dicts.
1 parent 57d3087 commit 4779bf3

15 files changed

Lines changed: 376 additions & 0 deletions

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-24) — 以 OCR 文字填入框线网格(可定址表格)
4+
5+
把有框线表格的线条 + OCR 文字转成可定址的 `R x C` 表格。完整参考:[`docs/source/Zh/doc/new_features/v162_features_doc.rst`](../docs/source/Zh/doc/new_features/v162_features_doc.rst)
6+
7+
- **`populate_table` / `assign_text_to_grid` / `table_to_records` / `table_to_csv`**(`AC_populate_table`):`edge_lines.find_grid` 能还原表格的框线几何但返回的单元格是*空的*;OCR 提供文字却无结构——两者从未串接。本功能把 OCR 框放入网格(依单元格中心指派,以重叠比例把关,使横跨细框线的框不被重复计入),将每个单元格的文字依阅读顺序串接,标记合并单元格的 span,并可直接转成 records / CSV。纯标准库,作用于纯字典——不需图像、OCR 引擎或设备。不导入 `PySide6`
8+
39
## 本次更新 (2026-06-24) — 信任评分模板匹配(歧义 / PSR)
410

511
在点击前就知道某次模板匹配虽强但*有歧义*。完整参考:[`docs/source/Zh/doc/new_features/v161_features_doc.rst`](../docs/source/Zh/doc/new_features/v161_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-24) — 以 OCR 文字填入框線網格(可定址表格)
4+
5+
把有框線表格的線條 + OCR 文字轉成可定址的 `R x C` 表格。完整參考:[`docs/source/Zh/doc/new_features/v162_features_doc.rst`](../docs/source/Zh/doc/new_features/v162_features_doc.rst)
6+
7+
- **`populate_table` / `assign_text_to_grid` / `table_to_records` / `table_to_csv`**(`AC_populate_table`):`edge_lines.find_grid` 能還原表格的框線幾何但回傳的儲存格是*空的*;OCR 提供文字卻無結構——兩者從未串接。本功能把 OCR 框放入網格(依儲存格中心指派,以重疊比例把關,使橫跨細框線的框不被重複計入),將每個儲存格的文字依閱讀順序串接,標記合併儲存格的 span,並可直接轉成 records / CSV。純標準函式庫,作用於純字典——不需影像、OCR 引擎或裝置。不匯入 `PySide6`
8+
39
## 本次更新 (2026-06-24) — 信任評分樣板比對(歧義 / PSR)
410

511
在點擊前就知道某次樣板比對雖強但*有歧義*。完整參考:[`docs/source/Zh/doc/new_features/v161_features_doc.rst`](../docs/source/Zh/doc/new_features/v161_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-24) — Fill a Ruling-Line Grid With OCR Text (Addressable Tables)
4+
5+
Turn a bordered table's lines + OCR words into an addressable `R x C` table. Full reference: [`docs/source/Eng/doc/new_features/v162_features_doc.rst`](docs/source/Eng/doc/new_features/v162_features_doc.rst).
6+
7+
- **`populate_table` / `assign_text_to_grid` / `table_to_records` / `table_to_csv`** (`AC_populate_table`): `edge_lines.find_grid` recovers a table's ruling-line geometry but the cells come back *empty*; OCR gives the text but no structure — nothing joined them. This drops OCR boxes into the grid (assigned by cell-centre, gated by an overlap fraction so a box straddling a thin rule isn't double-counted), concatenates each cell's text in reading order, flags merged-cell spans, and converts straight to records / CSV. Pure-stdlib over plain dicts — no image, no OCR engine, no device. No `PySide6`.
8+
39
## What's new (2026-06-24) — Trust-Scored Template Matching (Ambiguity / PSR)
410

511
Know when a template match is strong but *ambiguous* before clicking it. Full reference: [`docs/source/Eng/doc/new_features/v161_features_doc.rst`](docs/source/Eng/doc/new_features/v161_features_doc.rst).
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Fill a Ruling-Line Grid With OCR Text (Addressable Tables)
2+
==========================================================
3+
4+
``edge_lines.find_grid`` recovers a bordered table's geometry — ``{rows: [y…], cols: [x…],
5+
cells: […]}`` — but the cells come back *empty* (just rectangles between the ruling lines).
6+
OCR gives the text but no table structure. Nothing joined the two, so reading a bordered
7+
table meant hand-rolling the box→cell assignment. ``table_grid_fill`` drops OCR text boxes
8+
into the grid and returns an addressable ``R x C`` table.
9+
10+
Each box is assigned to the cell its centre falls in (gated by an overlap fraction so a box
11+
straddling a thin rule is not double counted); text within a cell is concatenated in reading
12+
order; boxes that straddle multiple cells are reported as merged-cell candidates. The result
13+
converts straight to records or CSV.
14+
15+
Pure-stdlib geometry over plain dicts (the grid + the boxes) — no image, no OCR engine, no
16+
device. Imports no ``PySide6``.
17+
18+
Headless API
19+
------------
20+
21+
.. code-block:: python
22+
23+
from je_auto_control import (find_grid, find_text_lines, # producers
24+
populate_table, assign_text_to_grid,
25+
table_to_records, table_to_csv)
26+
27+
grid = find_grid(region=[0, 0, 800, 400]) # ruling-line geometry
28+
boxes = [{"x": 10, "y": 5, "width": 60, "height": 20, "text": "Name"}, ...]
29+
30+
table = assign_text_to_grid(grid, boxes) # [["Name","Age"], ["Ann","30"]]
31+
records = table_to_records(table) # [{"Name": "Ann", "Age": "30"}]
32+
csv_text = table_to_csv(table)
33+
34+
full = populate_table(grid, boxes) # {n_rows, n_cols, cells, spans}
35+
36+
``assign_text_to_grid`` returns the 2-D text table; ``populate_table`` returns the richer
37+
``{n_rows, n_cols, cells:[{row, col, text}], spans:[{row, col, row_span, col_span, text}]}``.
38+
``table_to_records`` uses the first row as headers; ``table_to_csv`` renders CSV. Boxes accept
39+
either ``{x, y, width, height}`` or ``{left, top, right, bottom}`` plus a ``text`` field.
40+
41+
Executor command
42+
----------------
43+
44+
``AC_populate_table`` (``grid`` / ``text_boxes`` / ``overlap`` → ``{n_rows, n_cols, cells,
45+
spans}``) is exposed as the MCP tool ``ac_populate_table`` (read-only) and as the Script
46+
Builder command **Fill Table From Grid + OCR** under **OCR**.

docs/source/Eng/eng_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ Comprehensive guides for all AutoControl features.
184184
doc/new_features/v159_features_doc
185185
doc/new_features/v160_features_doc
186186
doc/new_features/v161_features_doc
187+
doc/new_features/v162_features_doc
187188
doc/ocr_backends/ocr_backends_doc
188189
doc/observability/observability_doc
189190
doc/operations_layer/operations_layer_doc
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
以 OCR 文字填入框線網格(可定址表格)
2+
======================================
3+
4+
``edge_lines.find_grid`` 能還原有框線表格的幾何——``{rows: [y…], cols: [x…], cells: […]}``
5+
——但回傳的儲存格是*空的*(僅是框線之間的矩形)。OCR 提供文字卻無表格結構。兩者從未串接,
6+
因此讀取有框線表格只能自行撰寫 box→cell 的指派。``table_grid_fill`` 把 OCR 文字框放入網格,
7+
回傳可定址的 ``R x C`` 表格。
8+
9+
每個框依其中心落在哪個儲存格而被指派(以重疊比例把關,使橫跨細框線的框不被重複計入);
10+
同一儲存格內的文字依閱讀順序串接;橫跨多個儲存格的框則回報為合併儲存格候選。結果可直接
11+
轉成 records 或 CSV。
12+
13+
純標準函式庫幾何,作用於純字典(網格 + 框)——不需影像、不需 OCR 引擎、不需裝置。不匯入
14+
``PySide6``。
15+
16+
無頭 API
17+
--------
18+
19+
.. code-block:: python
20+
21+
from je_auto_control import (find_grid, find_text_lines, # 產生來源
22+
populate_table, assign_text_to_grid,
23+
table_to_records, table_to_csv)
24+
25+
grid = find_grid(region=[0, 0, 800, 400]) # 框線幾何
26+
boxes = [{"x": 10, "y": 5, "width": 60, "height": 20, "text": "Name"}, ...]
27+
28+
table = assign_text_to_grid(grid, boxes) # [["Name","Age"], ["Ann","30"]]
29+
records = table_to_records(table) # [{"Name": "Ann", "Age": "30"}]
30+
csv_text = table_to_csv(table)
31+
32+
full = populate_table(grid, boxes) # {n_rows, n_cols, cells, spans}
33+
34+
``assign_text_to_grid`` 回傳二維文字表格;``populate_table`` 回傳更豐富的
35+
``{n_rows, n_cols, cells:[{row, col, text}], spans:[{row, col, row_span, col_span, text}]}``。
36+
``table_to_records`` 以第一列為標頭;``table_to_csv`` 輸出 CSV。框接受 ``{x, y, width, height}``
37+
或 ``{left, top, right, bottom}`` 加上 ``text`` 欄位。
38+
39+
執行器指令
40+
----------
41+
42+
``AC_populate_table``(``grid`` / ``text_boxes`` / ``overlap`` → ``{n_rows, n_cols, cells,
43+
spans}``)以 MCP 工具 ``ac_populate_table``(唯讀)及 Script Builder 指令
44+
**Fill Table From Grid + OCR**(位於 **OCR** 分類下)形式提供。

docs/source/Zh/zh_index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ AutoControl 所有功能的完整使用指南。
184184
doc/new_features/v159_features_doc
185185
doc/new_features/v160_features_doc
186186
doc/new_features/v161_features_doc
187+
doc/new_features/v162_features_doc
187188
doc/ocr_backends/ocr_backends_doc
188189
doc/observability/observability_doc
189190
doc/operations_layer/operations_layer_doc

je_auto_control/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@
291291
from je_auto_control.utils.screen_grid import (
292292
GridCell, cell_for_point, grid_cells, point_for_cell,
293293
)
294+
# Fill a ruling-line grid with OCR text → addressable table
295+
from je_auto_control.utils.table_grid_fill import (
296+
assign_text_to_grid, populate_table, table_to_csv, table_to_records,
297+
)
294298
# Locate on-screen regions by colour (mask + connected components)
295299
from je_auto_control.utils.color_region import (
296300
find_color_region, find_color_regions,
@@ -1209,6 +1213,10 @@ def start_autocontrol_gui(*args, **kwargs):
12091213
"grid_cells",
12101214
"cell_for_point",
12111215
"point_for_cell",
1216+
"assign_text_to_grid",
1217+
"populate_table",
1218+
"table_to_records",
1219+
"table_to_csv",
12121220
"find_color_region",
12131221
"find_color_regions",
12141222
"ssim_compare",

je_auto_control/gui/script_builder/command_schema.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,19 @@ def _add_ocr_specs(specs: List[CommandSpec]) -> None:
674674
),
675675
description="Decode 1-D barcodes (EAN / UPC) in an image / screen region.",
676676
))
677+
specs.append(CommandSpec(
678+
"AC_populate_table", "OCR", "Fill Table From Grid + OCR",
679+
fields=(
680+
FieldSpec("grid", FieldType.STRING,
681+
placeholder='{"rows": [0, 30, 60], "cols": [0, 100, 200]}'),
682+
FieldSpec("text_boxes", FieldType.STRING,
683+
placeholder='[{"x": 10, "y": 5, "width": 60, "height": 20, '
684+
'"text": "Name"}]'),
685+
FieldSpec("overlap", FieldType.FLOAT, optional=True, default=0.4,
686+
min_value=0.0, max_value=1.0),
687+
),
688+
description="Drop OCR text boxes into a ruling-line grid → addressable table.",
689+
))
677690
specs.append(CommandSpec(
678691
"AC_scroll_to_find", "OCR", "Scroll Until Visible",
679692
fields=(

je_auto_control/utils/executor/action_executor.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,6 +3373,17 @@ def _point_for_cell(label: str, rows: Any, cols: Any,
33733373
return {"point": point}
33743374

33753375

3376+
def _populate_table(grid: Any, text_boxes: Any, overlap: Any = 0.4) -> Dict[str, Any]:
3377+
"""Adapter: fill a ruling-line grid with OCR text boxes → addressable table."""
3378+
import json
3379+
from je_auto_control.utils.table_grid_fill import populate_table
3380+
if isinstance(grid, str):
3381+
grid = json.loads(grid)
3382+
if isinstance(text_boxes, str):
3383+
text_boxes = json.loads(text_boxes)
3384+
return populate_table(grid, text_boxes, overlap=float(overlap))
3385+
3386+
33763387
def _find_color_region(rgb: Any, tolerance: Any = 20, min_area: Any = 50,
33773388
region: Any = None) -> Dict[str, Any]:
33783389
"""Adapter: locate coloured regions on the screen, largest first."""
@@ -5799,6 +5810,7 @@ def __init__(self):
57995810
"AC_grid_cells": _grid_cells,
58005811
"AC_cell_for_point": _cell_for_point,
58015812
"AC_point_for_cell": _point_for_cell,
5813+
"AC_populate_table": _populate_table,
58025814
"AC_ssim_compare": _ssim_compare,
58035815
"AC_ssim_changed_regions": _ssim_changed_regions,
58045816
"AC_feature_match": _feature_match,

0 commit comments

Comments
 (0)