Vào thẳng nội dung

Match Tool

A pyramid NCC (normalized cross-correlation) template-matching wrapper for the ECOS AI framework, ported from a C++ MatchToolDlg-style industrial vision tool. It locates a stored template inside an image with rotation search and optional sub-pixel refinement, and reports an OK/NG result plus the matched pose.

Overview

The match_tool module extends the BaseModel class. Compared to a plain cv2.matchTemplate, it adds:

  • Rotation search — the template is matched across a symmetric range of angles (angle_range), so a rotated part is still found.
  • Pyramid (coarse-to-fine) matching — matches are first found on down-scaled pyramid levels, then refined on finer levels, keeping the rotation search affordable.
  • Optional scale search — a range of template scales can be searched (scale_range/scale_steps) for when the physical object is smaller/larger than the stored template, with a cheap coarse-to-fine pre-filter so the extra search stays affordable.
  • Optional sub-pixel refinement of the final position and angle.
  • Pattern-keyed configuration — multiple templates and/or multiple ROI positions per named pattern, for panels with several identical parts to inspect in one image.

For a keypoint-based alternative that infers rotation and scale automatically (no explicit sweep), see feature_matching.

Installation

Please install ecos_core.

Requirements

No extra dependencies beyond ecos_core's base install (OpenCV, NumPy) — see requirements.txt.

Usage

Configuration

Configuration is managed through a JSON file (opt.json). Two modes are supported.

Simple mode — one template, one ROI:

{
    "template_path": "./templates/template.png",
    "roi": {"x": 90, "y": 352, "w": 966, "h": 1696},
    "max_pos": 1,
    "max_overlap": 0.2,
    "threshold": 0.45,
    "angle_range": 15.0,
    "min_area": 256,
    "subpixel": false,
    "scale_range": [0.9, 1.1],
    "scale_steps": 5,
    "scale_prefilter_top_k": 2,
    "class_names": ["OK", "NG"],
    "visualization": true
}

Pattern-keyed mode — multiple templates and/or ROI positions selected by pattern (falls back to a "default" key, or is skipped entirely, when pattern is not set):

{
    "pattern": "1",
    "template_folder": "./template_folder",
    "template_name": {"1": ["part_a.png", "part_b.png"]},
    "roi": {"1": [[794, 590, 1159, 1604], [487, 595, 883, 1594]]},
    "threshold": 0.45,
    "angle_range": 15.0
}

Every (template, ROI) combination configured for the active pattern is searched; the highest-scoring match across all of them wins. pattern can be changed after construction (e.g. model.opt.pattern = "2", or via update_opt) — the template(s)/ROI(s) for the new pattern are re-resolved automatically the next time predict() runs.

With:

  • template_path / template_name + template_folder + pattern: which template(s) to load (see the two modes above). template_path accepts either a single path or a list of paths (all searched, same as multiple template_name entries).
  • roi / roi_path: search window(s) — {"x","y","w","h"}, [x1,y1,x2,y2], or a list of either for multiple positions. Omit for full-frame search.
  • threshold: minimum NCC match score to accept (alias: score).
  • angle_range: rotation search tolerance in degrees, symmetric around 0 (alias: tolerance_angle).
  • min_area: stop building pyramid levels once the template's area would drop below this (alias: min_reduce_area).
  • subpixel: enable sub-pixel position/angle refinement at the finest pyramid level (alias: use_subpixel).
  • max_pos, max_overlap: max instances to keep per (template, ROI) pass, and the NMS overlap threshold between them.
  • scale_range, scale_steps: template scale factors to search (e.g. [0.9, 1.1] over 5 steps) when the physical object's size can differ from the stored template. Defaults to [1.0, 1.0] / 1 step (no scale search).
  • scale_prefilter_top_k: when scale-searching, only the top-K cheaply-ranked scale candidates get the full (expensive) match pass. Higher = safer but slower; 1 disables the safety margin.
  • class_names: [OK_label, NG_label] — NG is reported when no match clears threshold.
  • visualization: whether to draw the best match's outline on the saved output image.

Inference

import sys
import os
import json
from glob import glob
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), './../../')))
from ecos_core.match_tool.match_tool import Model
from easydict import EasyDict

if __name__ == "__main__":
    with open("./opt.json", "r") as f:
        opt = EasyDict(json.load(f))
    model = Model(opt)
    model.build_model()

    for image_path in glob("./input/*"):
        model.process_predictions(
            net_output=None,
            raw_image_path=image_path,
            raw_image_mat=None,
            image_transform=None,
            save_image_path=os.path.join("./output", os.path.basename(image_path)),
        )

Each call writes the annotated output image plus a {save_image_path}.json sidecar with predict_index, class_name, bbox, confidence, and extra_info (center_x, center_y, angle, scale of the best match).

APIs

Model

Bases: BaseModel

__call__(x)

Inference function Args: x: input data Returns: Inference results

__init__(opt)

Initialize the model with options Args: opt (Namespace/Dict): options for the model

build_model()

Build the model Returns: The built model

get_transform()

Get the data transformation function Returns: The data transformation function

predict(image)

Run matching against every configured (template, ROI) combination. Args: image (str | np.ndarray): Image file path, or an already-loaded image as a NumPy array (grayscale or BGR). Returns: list[dict]: All matches found, sorted by score descending. Each entry has keys x, y, w, h, score, angle, scale, center, corners (bbox and center/corners are in full-image pixel coordinates).

process_predictions(net_output, raw_image_path, raw_image_mat, image_transform, save_image_path)

Process the predictions and save the results. Args: net_output: the output from the model inference (unused) raw_image_path: path to the raw input image raw_image_mat: the raw input image in matrix form (unused, re-read from disk) image_transform: the transformed input image (unused) save_image_path: path to save the processed image Returns: str: save_image_path, once the annotated image and its JSON sidecar ({save_image_path}.json) have been written.

update_opt(option, output_path='./opt.json')

Update config Args: option (Namespace/Dict): option to update output_path (str, optional): path to save opt json. Defaults to "./opt.json".