You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
("Overhaul of threshing features and UI") changes 26 files, adding 6,347 lines and removing 663. You made the commit while I was reading. It holds exactly the set of uncommitted changes that was on dev when we started.
Pipeline (core.py)
Both methods in one run.mask_method now accepts "both" or a list of methods. Markers are detected once per image, and then each method segments and measures on its own. Each method writes its own results CSV and failure log, and its masks and QC images get a _threshold or _birefnet suffix. Target boxes are shared. A single-method run keeps the old file names.
Measuring before cleanup. With measurement_source="pre-cleanup", the run measures the raw mask after clean_raw_mask has done three things:
cleared a band along the target-box edge
kept the largest object
dropped any piece that touches that band or lies farther away than the stray gap
In this mode, width and length span every remaining foreground pixel, not just the largest contour.
Default measurements are unchanged.clean_leaf_mask(fill_holes=True) is still the old code. segment_leaf() now returns the raw mask and the cleaned mask, and create_leaf_mask wraps it.
New image exports:
raw (pre-cleanup) masks for each method, even a method that isn't measuring
overlays, cutouts and measurement axes
target boxes and cleaned masks can now be turned off
A failed write is now recorded as an EXPORT: warning instead of being lost silently.
New sidecar file. Every results CSV now gets a .meta.json file recording the measurement source, method, unit and schema. This happens on default runs too.
Batch return shape. New keys: methods, by_method, artifacts, preview_artifacts, measurement_source, clean_margin, stray_gap. The top-level results_path and result_rows keys now appear only on single-method runs.
Other behaviour changes:
A batch now raises an error on duplicate sample IDs (two images with the same basename).
The CPU-parallel BiRefNet acknowledgement is required only when execution_device == "cpu".
The BiRefNet checks, including the hybrid-mode one, now also apply to BiRefNet pre-cleanup exports.
An unknown method name raises an error. Before, anything other than threshold quietly ran BiRefNet.
New modules
Module
Purpose
thresholds.py
Threshold presets moved out of core (still re-exported there), plus parsing of a custom 1–255 cutoff. Standard library only
mask_settings.py
Defaults and range checks for clean margin (1%, up to 10) and stray gap (0.25, up to 10). Standard library only
mask_cleanup.py
Edge-margin clearing, stray-piece removal, and the speck/hole levels behind the Clean size slider. numpy and OpenCV only
app/threshold_preview.py
Live threshold and Clean size sliders in the browser
app/specimen_table.py
Searchable, sortable table with a View button and a Marked for Adjustment box per row
app/output_adjustment.py
Re-measures a threshold specimen and overwrites its mask, CSV row and dependent images. Files are staged first and rolled back on failure; the bulk version validates every specimen before writing anything
--export {pre-cleanup,overlay,cutout,axes} (repeatable) and --pre-cleanup-methods
--no-target-boxes, --no-masks, --no-failure-log
A dependent flag used without its parent flag now exits with an error.
The startup banner and the end-of-run summary report each method separately.
GUI
Home (Home.py, about 1,700 lines changed) is reorganized into four tabs: Setup, Analyze, Adjust and Export. There is a new Go to Export button in the sidebar.
Setup: segmentation is now two checkboxes. The threshold level appears only while Otsu is checked, and choosing custom shows a slider.
Analyze: a full-width measurement table, with a separate selection for each method. The viewer shows the mask that actually produced the measurements.
Adjust:
a live threshold preview with a colour panel of the masked leaf
a preview-only Clean size slider and Remove flashfill
Overwrite this specimen and Overwrite all marked specimens (N)
Export: lists the files this run saved, lets you pick files for the ZIP by method, and offers direct CSV downloads.
compute.py and the CPU Options page read the new checkbox state.
Training dataset export (dataset_export.py)
Adds a separate training ZIP from aligned target-box images and one selected method's masks. Formats are image + PNG mask, YOLO segmentation polygons, YOLO detection boxes, and COCO segmentation with run-length masks. Each ZIP includes manifest.json with the actual split counts, sample assignments, excluded pairs, and conversion notes.
CLI options are --dataset-format, --dataset-split, --dataset-seed, --dataset-method, --dataset-mask-source, and --dataset-groups. The GUI's Export tab offers the same format, method, mask-source, split, seed, and group choices. The default split is 70/20/10 with seed 42. A UTF-8 sample_id,group_id CSV keeps a group's images in one split, so actual percentages can shift.
Export validates that each pair has a readable target-box image and a readable, nonempty mask with matching dimensions. Invalid pairs appear in the manifest with reasons; an export with no usable pairs fails. Group IDs are validated against sample IDs, and only usable image/mask pairs enter the ZIP. YOLO polygons cannot retain holes, while COCO run-length masks can. MATS-generated labels need review before training.
Review tests/test_dataset_export.py for image/label alignment, reproducible grouped splits, hole preservation, conversion limitations, and invalid-pair handling. The CLI and GUI share write_dataset_zip for conversion.
Docs and tests
Docs: new CHANGELOG "Unreleased" entries, and updates to README.md, docs/cli.md, docs/faq.md, docs/gui.md, AGENTS.md and the Help page.
Tests: 5 new test files with 57 tests in all:
dual-method: 8
mask cleanup: 19
pre-cleanup exports: 14
threshold preview: 9
thresholds: 7
test_cli_args.py and test_home_app.py also grew by 180 and 1,377 lines. I didn't run the suite.
The commit message has a typo: "threshing" should be "thresholding". If you haven't pushed yet, git commit --amend will fix it.
Ported from main (fbb3c09) onto dev at e7904eb.
Agent instructions and docs
- AGENTS.md: public agent guide, the single source of truth; CLAUDE.md
imports it. docs/faq.md: human FAQ, linked from the README.
- .gitignore: commit CLAUDE.md and AGENTS.md; ignore *.local.md.
- README, FAQ, AGENTS: Git LFS is an install prerequisite (a clone made
without it yields a 134-byte pointer stub, not the model), RF-DETR arrives
with the clone, and `mats fetch-weights` is the repair path. Repair uses
`git lfs pull --exclude=weights/birefnet_leaf.pth`, which stays RF-DETR-only
whether or not .lfsconfig's fetchexclude is active.
- README: reconciled with the restructure from PR #2; its headings and TOC are
kept, and the hand-edited doctor/fetch paragraph is preserved verbatim.
Also carried over from fbb3c09 (app work, unrelated to the docs)
- src/mats/app/Home.py, branding.py, src/mats/core.py, pyproject.toml,
tests/test_home_app.py
Known and not addressed here
- .lfsconfig has fetchexclude commented out on dev, so a clone now fetches
BiRefNet (2.65 GB) as well as RF-DETR.
- tests/test_weights.py::test_status_missing_for_excluded_checkout_pointer
already fails on dev before this commit (message reworded in PR #2).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Restrict package metadata to supported Python 3.10–3.13
pyproject.toml:10
The package metadata still allows Python 3.14 and later, while the updated README and install guide define support as 3.10–3.13. On newer interpreters, pip will therefore attempt an unsupported installation instead of rejecting it up front. Match the declared compatibility range to the documented one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
("Overhaul of threshing features and UI") changes 26 files, adding 6,347 lines and removing 663. You made the commit while I was reading. It holds exactly the set of uncommitted changes that was on
devwhen we started.Pipeline (
core.py)Both methods in one run.
mask_methodnow accepts"both"or a list of methods. Markers are detected once per image, and then each method segments and measures on its own. Each method writes its own results CSV and failure log, and its masks and QC images get a_thresholdor_birefnetsuffix. Target boxes are shared. A single-method run keeps the old file names.Measuring before cleanup. With
measurement_source="pre-cleanup", the run measures the raw mask afterclean_raw_maskhas done three things:In this mode, width and length span every remaining foreground pixel, not just the largest contour.
Default measurements are unchanged.
clean_leaf_mask(fill_holes=True)is still the old code.segment_leaf()now returns the raw mask and the cleaned mask, andcreate_leaf_maskwraps it.New image exports:
A failed write is now recorded as an
EXPORT:warning instead of being lost silently.New sidecar file. Every results CSV now gets a
.meta.jsonfile recording the measurement source, method, unit and schema. This happens on default runs too.Batch return shape. New keys:
methods,by_method,artifacts,preview_artifacts,measurement_source,clean_margin,stray_gap. The top-levelresults_pathandresult_rowskeys now appear only on single-method runs.Other behaviour changes:
execution_device == "cpu".thresholdquietly ran BiRefNet.New modules
thresholds.pycore(still re-exported there), plus parsing of a custom1–255cutoff. Standard library onlymask_settings.pymask_cleanup.pyapp/threshold_preview.pyapp/specimen_table.pyapp/output_adjustment.pyapp/pages/0_Diagnostics.pyCLI (
cli.py)--mask-method both--threshold-levelnow also takes1–255--measure-pre-cleanup,--clean-margin,--stray-gap--export {pre-cleanup,overlay,cutout,axes}(repeatable) and--pre-cleanup-methods--no-target-boxes,--no-masks,--no-failure-logGUI
Home.py, about 1,700 lines changed) is reorganized into four tabs: Setup, Analyze, Adjust and Export. There is a new Go to Export button in the sidebar.compute.pyand the CPU Options page read the new checkbox state.Training dataset export (
dataset_export.py)manifest.jsonwith the actual split counts, sample assignments, excluded pairs, and conversion notes.--dataset-format,--dataset-split,--dataset-seed,--dataset-method,--dataset-mask-source, and--dataset-groups. The GUI's Export tab offers the same format, method, mask-source, split, seed, and group choices. The default split is 70/20/10 with seed 42. A UTF-8sample_id,group_idCSV keeps a group's images in one split, so actual percentages can shift.tests/test_dataset_export.pyfor image/label alignment, reproducible grouped splits, hole preservation, conversion limitations, and invalid-pair handling. The CLI and GUI sharewrite_dataset_zipfor conversion.Docs and tests
Docs: new CHANGELOG "Unreleased" entries, and updates to
README.md,docs/cli.md,docs/faq.md,docs/gui.md,AGENTS.mdand the Help page.Tests: 5 new test files with 57 tests in all:
test_cli_args.pyandtest_home_app.pyalso grew by 180 and 1,377 lines. I didn't run the suite.The commit message has a typo: "threshing" should be "thresholding". If you haven't pushed yet,
git commit --amendwill fix it.