This repository implements the topological contour graph (TCG) extractor. Originally developed by Yuliang Guo; documented by Hongyi Fan and Chiang-Heng Chien. Used, edited, and updated by Chiang-Heng Chien.
- Edges detected on the image, e.g. SE / third-order edge detector. The edges are formatted in a
.edgfile. - Compute initial curve fragments from edges using
dborl_compute_curve_fragsto produce a.cemfile. The executive file dborl_compute_curve_frags` can be generated by compiling the curve framgment code.
The main script is main_TCG.m which undergoes several steps from curve fragements to a topological contour graph:
- loads the image,
.edg, and.cem - breaks contours at corners
- fills large gaps (DP)
- breaks at T-junctions, prunes noise, merges geometrically, classifies junctions (BP)
- breaks at corners again and prunes
The C++ code converts the MATLAB code in main_TCG.m (Step 3 and onward). It takes the original image, the edge file (.edg), and the contour fragement file (.cem) as inputs, and returns a new .cem file containing the new contour fragments of the topological contour graph.
- CMake ≥ 3.14
- C++17 compiler
- OpenCV 4 and above (
core,imgproc,imgcodecs)
Follow the standard build and compile process to produce the executable TCG.
cd CPP
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -jOnce built is successful, provide specific .edg, .cem, and the original image file. By default both .cem and .cemv are written using stem <image_name>_tcg_cpp.
./TCG <input.edg> <input.cem> <input.image> [format] [output]format is one of:
cem— write only.cemcemv— write only.cemvboth— write both (default)
Examples:
# both formats (default paths)
./build/TCG ./example_data/n03425413_14351.edg \
./example_data/n03425413_14351.cem \
./example_data/n03425413_14351.JPEG
# cem only
./build/TCG ... cem ./outputs/n03425413_14351_tcg_cpp.cem
# cemv only
./build/TCG ... cemv ./outputs/n03425413_14351_tcg_cpp.cemv
# both with explicit .cem path (sibling .cemv is derived)
./build/TCG ... both ./outputs/n03425413_14351_tcg_cpp.cemA 4th argument that is not cem|cemv|both is still treated as an output path with format=both (backward compatible).
Console output reports fragment counts and timings for each stage.
run_tcg_batch.sh walks an image root recursively and runs TCG on each image. Image, .edg, and .cem roots are specified separately; class subfolders are mirrored across those roots and under the output directory. Filename patterns use * for the image stem (needed when multiple .edg / .cem variants exist per image).
./run_tcg_batch.sh \
--image-dir /path/to/images \
--edg-dir /path/to/edges \
--cem-dir /path/to/contours \
--edg-name '*_to.edg' \
--cem-name '*_to_dborl.cem' \
-e JPEG -o /path/to/out -f bothThe three roots can also be passed positionally: ./run_tcg_batch.sh <image_dir> <edg_dir> <cem_dir> [options].
Other useful flags: -e / --ext (image extension, default JPEG), -b (path to the TCG binary), -n (dry-run). See all the input argements by running ./run_tcg_batch.sh --help.
The output .cem is readable by load_contours / draw_contours:
addpath(genpath('util'));
img = imread('../example_data/images/cable.png');
[CEM_cpp, ~, ~] = load_contours('outputs/cable_tcg_cpp.cem');
figure; imshow(img, 'border', 'tight'); hold on;
draw_contours(CEM_cpp{2}, 0, 1);
title('C++ final contours');Two sets of contour fragments (or contour maps) can be compared by using the provided evaluation code. Check the evaluation document for more information.
- "Gap fill" (step 4) uses MATLAB-faithful
bwmorph(...,'skel',Inf),bwdist(exact Euclidean EDT), andimgradient/rgb2gray(Sobel + replicate borders). On the example image, stage fragment counts match MATLAB; tiny residual map differences can still come from OpenCV vs MATLAB JPEG decoding. - Indices inside C++ are 0-based and
.cemfiles keep the usual 0-based edge IDs. MATLAB loaders convert to 1-based when needed.