Skip to content

v0.27.0

Choose a tag to compare

@github-actions github-actions released this 24 Sep 06:30
· 74 commits to main since this release
57bdf16

What changed and why

Cropping is its own tool now — CvCropOpt with CvImageOps.Crop. CvImageProcessOpt / Preprocess only
resample and denoise.
This is a breaking change on purpose; the old call sites stop compiling.

A consumer inspects parts that are small in the field of view and sit off-centre, and wants to cut them out
before inspecting and to show the result on the cut-out. Cropping and image cleanup are different jobs, so
they are separate tools even though a chain gets one tool longer.

  • CvCropOpt — UseCrop, CropX/CropY/CropW/CropH in original-image coordinates, with its edit shape.
    The property names are the ones CvImageProcessOpt used to carry, so an old image-process file read as a
    CvCropOpt gives the crop back, and handlers keyed by property name keep working on the new tool.
  • CvImageOps.Crop(Mat src, CvCropOpt opt, out Rect used) — returns a copy and the rect it used (the whole
    image when the crop is off or falls back). A view would share memory with a CamFrame wrapped by AsMat():
    writing into it rewrites pixels that history and display still hold (a regression checks exactly this).
    The copy is one pass over the region, or over the whole frame when the crop is off.
  • CvImageOps.CropRectOf(Mat src, CvCropOpt opt) — the rect alone; same fallback as before (outside the image or
    under 8 px → whole image plus a warning).
  • CvImageOps.MapOf(Rect used, Mat pre) — the stage → original map, carrying both the crop origin and the scale.
  • Removed: CvImageOps.CropRectOf(Mat, CvImageProcessOpt) and CvImageOps.MapOf(Mat, Mat, CvImageProcessOpt).
    Keeping them as obsolete would have let a call compile against an image that was never cropped and add the
    old offset to it. CvImageProcessOpt no longer implements ICvShapeSource — it has no geometry left.

Migrating a call site:

// before
using var pre = CvImageOps.Preprocess(gray, ip);
var map = CvImageOps.MapOf(gray, pre, ip);

// after
using var cut = CvImageOps.Crop(gray, crop, out var used);
using var pre = CvImageOps.Preprocess(cut, ip);
var map = CvImageOps.MapOf(used, pre);

// no crop in this chain: pass the whole image rect
var map = CvImageOps.MapOf(new Rect(0, 0, gray.Cols, gray.Rows), pre);

There is deliberately no MapOf(Mat, Mat): a cut image does not know where it was cut from, so such an overload
would get the scale right and silently drop the offset — every result drawn off by the crop origin, with nothing
looking wrong. Only Preprocess takes cut: the result overlay stays in original-frame coordinates, so arguments
that bound drawing to the image (ViDraw clipping sizes, the corner ViHud branches) keep the original frame size.
The first parameter of Preprocess and CropRectOf is now named src (was gray) — calls with named arguments
need the new name.

Recipes saved before 0.27. An image-process file with the crop on still loads — the old fields stay on
CvImageProcessOpt as hidden, obsolete properties, and they are written back on save until the crop is moved, so
a host that saves before migrating does not lose it. But Preprocess throws InvalidOperationException while an
unmoved crop is on.
The tools after it were taught on the cut-out; running them on the whole image would put each
of them off by the crop origin and still produce verdicts that look normal. Move it when the recipe loads:

if (CvCropOpt.FromLegacy(ip) is { } moved) { /* use `moved` as the crop tool, then save the image-process file */ }

FromLegacy returns the crop, turns it off on ip and logs one Info line. A crop saved without its size keys moves
at the old default size, 640×480, as 0.26 cut it. Save the image-process file again afterwards, or the crop is
moved again on the next load. With System.Text.Json that save also drops the old keys. Json.NET keeps writing
them as false/0, which reads back as a crop that is off. A file whose crop was off loads as before. The old
properties are [Obsolete]: code that sets them gets CS0618.

Migration is one-way. Once a migrated recipe is saved, a host on 0.26.x or earlier finds no crop in the
image-process file and does not know the crop tool. It runs uncropped, with no error. Roll recipes back together
with the host, and keep a copy from before the first 0.27 save.

Teaching screens: the image-process tool no longer shows the crop checkbox or rectangle. Add a CvCropOpt tool
in front of it.

Showing a result on the cropped region

  • ViOverlayLabel.IsHud — every ViHud.AddSummary branch sets it. Renderers ignore it. Use it to find the summary
    HUD instead of guessing from the alignment and text: corner alignment is also used for labels above detection boxes.
  • ViOverlay.CropTo(x, y, width, height) — a new overlay for the cut-out. Items move by (−x, −y). The HUD goes back
    to the same corner of the cut-out, where AddSummary would have put it on that image. A HUD given a start height
    keeps that height. The source overlay is not touched, and items outside the region are kept — clipping is the
    renderer's job. A label copied by hand without IsHud moves like any other label.
  • CamFrame.Crop(x, y, width, height) — a new frame with a tight stride. It keeps the format, DeviceTimestamp and
    TimestampUtc (it is the same shot). It throws if the region is not inside the frame, because the overlay moved
    with the same rect would no longer match. Pass the used rect from CvImageOps.Crop.

Also

  • CvInspect.Wpf README, Frame row: hiding and showing the control (a tab switch, a collapsed parent) is not a
    resize. The zoom is kept unless the size changed while the control was hidden. A regression covers both cases.
  • Demo: a Crop tool. Loading a recipe folder saved before 0.27 inserts a Crop tool just above the Preprocess that
    carried the crop.

Version

0.27.0 — minor. Added: CvCropOpt (+ FromLegacy), CvImageProcessOpt.HasLegacyCrop, CvImageOps.Crop,
CvImageOps.CropRectOf(Mat, CvCropOpt), CvImageOps.MapOf(Rect, Mat), ViOverlayLabel.IsHud, ViOverlay.CropTo,
CamFrame.Crop. Removed: CvImageOps.CropRectOf(Mat, CvImageProcessOpt),
CvImageOps.MapOf(Mat, Mat, CvImageProcessOpt), ICvShapeSource on CvImageProcessOpt. Obsolete:
CvImageProcessOpt.UseCrop/CropX/CropY/CropW/CropH. Changed: CvImageOps.Preprocess no longer crops and
throws on an unmoved crop; its first parameter is named src. CvInspect.Imaging.Gev has no change of its own.

Checks

  • dotnet build CvInspect.sln -c Release --no-incremental reports 0 warnings
  • main is an ancestor of this branch (the release-pr-guard job verifies it)
  • the tag will be created on the merge commit, main merged back into dev afterwards, and dev bumped to the next -dev version