EmguExtensions 0.1.6
dotnet add package EmguExtensions --version 0.1.6
NuGet\Install-Package EmguExtensions -Version 0.1.6
<PackageReference Include="EmguExtensions" Version="0.1.6" />
<PackageVersion Include="EmguExtensions" Version="0.1.6" />
<PackageReference Include="EmguExtensions" />
paket add EmguExtensions --version 0.1.6
#r "nuget: EmguExtensions, 0.1.6"
#:package EmguExtensions@0.1.6
#addin nuget:?package=EmguExtensions&version=0.1.6
#tool nuget:?package=EmguExtensions&version=0.1.6
EmguExtensions
A high-performance .NET library that extends Emgu.CV (OpenCV wrapper) with span-based Mat accessors, ROI utilities, pluggable Mat compression, structured contour hierarchies, and drawing helpers.
Features
- Span-based Mat access - Zero-copy
GetSpan<T>,GetSpan2D<T>, andGetReadOnlySpan2D<T>accessors for fast pixel manipulation - ROI utilities -
Roi,SafeRoi,ConstrainRoi, andRoiFromCenterfor bounded region-of-interest cropping with configurable empty-ROI behavior - Image transforms - Rotate with adjusted bounds, letterbox creation, crop-by-bounds, and shrink-to-fit resizing that reports whether the image changed
- Mat compression - Pluggable compressors (PNG, Deflate, GZip, ZLib, Brotli, Zstd) with
CMatfor memory-efficient compressed image storage.CMat.Compressis thread-safe — concurrent calls are serialized automatically - Contour hierarchies - Structured
EmguContour,EmguContours, andEmguContourFamilywrappers for OpenCV contour trees using hierarchy links - Drawing helpers - Polygon geometry, multi-line text rendering with alignment, SVG path generation, and predefined colors
- Disposal infrastructure -
DisposableObject,LeaveOpenDisposableObject, andGCSafeHandlefor thread-safe resource management
Requirements
Installation
NuGet Package Manager
Install-Package EmguExtensions
.NET CLI
dotnet add package EmguExtensions
Quick Start
Span-Based Mat Access
using EmguExtensions;
using Emgu.CV;
using Emgu.CV.CvEnum;
// Create or load a Mat
using var mat = new Mat(100, 100, DepthType.Cv8U, 1);
// Zero-copy span access for fast pixel reads/writes
var span = mat.GetSpan<byte>();
span[0] = 255; // Set first pixel
// 2D span access
var span2D = mat.GetSpan2D<byte>();
span2D[10, 20] = 128; // Row 10, Col 20
Safe ROI Cropping
using var source = CvInvoke.Imread("image.png");
// SafeRoi clamps to mat bounds and updates the rectangle with the effective ROI.
var roiRect = new Rectangle(-10, -10, 200, 200);
using var roi = source.SafeRoi(ref roiRect);
// Padding can be applied while still keeping the ROI inside the source bounds.
var paddedRect = new Rectangle(40, 40, 120, 80);
using var paddedRoi = source.SafeRoi(ref paddedRect, padding: 16);
// ConstrainRoi only updates the rectangle and reports whether it changed.
var candidate = new Rectangle(0, 0, 500, 500);
bool changed = source.ConstrainRoi(ref candidate);
// Empty ROI behavior is explicit when a requested ROI becomes empty.
var emptyRect = Rectangle.Empty;
using var fullSource = source.SafeRoi(ref emptyRect, EmptyRoiBehavior.CaptureSource);
// Other options:
// EmptyRoiBehavior.Default -> use OpenCV's default behavior for empty ROI
// EmptyRoiBehavior.CaptureSource -> use the full source image
// EmptyRoiBehavior.ThrowException -> throw InvalidOperationException
Image Sizing and Transforms
// Create a fixed-size letterboxed image. targetWidth and targetHeight must be positive.
using var boxed = source.CreateLetterBox(640, 480, out float scale, out int padX, out int padY);
// Shrink only when the image exceeds the target bounds. Returns true when resized.
bool wasShrunk = source.ShrinkToFitPreserveAspect(1024, 768);
// Output overloads populate dst even when no resize/rotation is needed.
using var resized = new Mat();
bool resizedWasShrunk = source.ShrinkToFitPreserveAspect(resized, 1024, 768);
using var rotated = new Mat();
source.RotateAdjustBounds(rotated, angle: 30);
Mat Compression with CMat
using EmguExtensions;
// Compress a Mat for memory-efficient storage
var cmat = new CMat();
cmat.Compress(mat); // Auto-selects best storage (compressed or raw)
// Decompress back to Mat — caller owns and must dispose the returned Mat
using var restored = cmat.Decompress();
// Use a specific compressor and level
cmat.Compressor = MatCompressorBrotli.Instance;
cmat.CompressionLevel = CompressionLevel.SmallestSize;
cmat.Compress(mat);
// Compress(Mat) and Compress(MatRoi) are thread-safe — safe to call from multiple threads
Parallel.ForEach(frames, (frame, _, i) =>
{
using var mat = frame.ToMat();
compressedFrames[i].Compress(mat);
});
// Switch compressor and re-encode existing data in one step
cmat.ChangeCompressor(MatCompressorDeflate.Instance, reEncodeWithNewCompressor: true);
// Equality is hash-based (XxHash3) — O(1) for mismatches, fast for collections
bool same = cmat1 == cmat2;
Contour Hierarchy
using EmguExtensions;
// Find contours with full hierarchy
using var contours = new EmguContours(binaryImage, RetrType.Tree);
// Iterate contour families (tree roots)
foreach (var family in contours.Families)
{
Console.WriteLine($"Area: {family.Self.Area}, Children: {family.Count}");
// Even depth = solid fill; odd depth = hole/cavity
}
Drawing Helpers
using EmguExtensions;
// Generate regular polygon vertices
var hexVertices = DrawingExtensions.GetPolygonVertices(6, new SizeF(50, 50), new PointF(100, 100));
// Multi-line text with alignment
mat.PutTextExtended("Line 1\nLine 2\nLine 3",
new Point(50, 50),
FontFace.HersheyComplex,
1.0,
EmguExtensions.WhiteColor,
lineAlignment: PutTextLineAlignment.Center);
Available Compressors
| Compressor | Class | Description |
|---|---|---|
| None | MatCompressorNone |
No compression (raw bytes) |
| PNG | MatCompressorPng |
PNG image encoding via OpenCV |
| Deflate | MatCompressorDeflate |
Deflate stream compression |
| GZip | MatCompressorGZip |
GZip stream compression |
| ZLib | MatCompressorZLib |
ZLib stream compression |
| Brotli | MatCompressorBrotli |
Brotli stream compression |
| Zstd | MatCompressorZstd |
Zstandard compression (.NET 11+) |
All compressors are singletons accessed via Instance (e.g., MatCompressorBrotli.Instance).
CMat automatically falls back to raw (uncompressed) storage when:
- The source is smaller than
ThresholdToCompress(default 512 bytes), or - Compression produces a result larger than the original.
Benchmarks
Compressor benchmark
BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8246/25H2/2025Update/HudsonValley2)
AMD Ryzen 9 7845HX with Radeon Graphics 3.00GHz, 1 CPU, 24 logical and 12 physical cores
.NET SDK 10.0.203
[Host] : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v4
Job-MTJJIS : .NET 10.0.7 (10.0.7, 10.0.726.21808), X64 RyuJIT x86-64-v4
MaxIterationCount=16
| Method | MatSize | CompressorName | Level | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated |
|----------- |-------- |--------------- |-------------- |------------:|------------:|------------:|---------:|---------:|---------:|----------:|
| Compress | 1920 | Brotli | NoCompression | 318.3 us | 14.55 us | 14.29 us | - | - | - | 3200 B |
| Compress | 1920 | Brotli | Fastest | 332.3 us | 54.77 us | 53.79 us | - | - | - | 2816 B |
| Compress | 1920 | None | NoCompression | 337.8 us | 11.88 us | 11.66 us | 194.3359 | 194.3359 | 194.3359 | 3686483 B |
| Compress | 1920 | Deflate | Fastest | 636.3 us | 55.72 us | 54.73 us | 1.9531 | - | - | 45744 B |
| Compress | 1920 | GZip | Fastest | 638.2 us | 28.39 us | 27.88 us | 2.4414 | - | - | 45792 B |
| Compress | 1920 | ZLib | Fastest | 640.3 us | 49.51 us | 48.62 us | 2.4414 | - | - | 45784 B |
| Compress | 1920 | GZip | NoCompression | 769.6 us | 13.23 us | 12.38 us | 276.3672 | 276.3672 | 276.3672 | 3688281 B |
| Compress | 1920 | ZLib | NoCompression | 770.1 us | 27.85 us | 27.35 us | 284.1797 | 284.1797 | 284.1797 | 3688256 B |
| Compress | 1920 | Deflate | NoCompression | 862.5 us | 14.73 us | 13.78 us | 280.2734 | 280.2734 | 280.2734 | 3688228 B |
| Compress | 1920 | Deflate | Optimal | 1,303.5 us | 42.47 us | 41.71 us | 0.9766 | - | - | 23512 B |
| Compress | 1920 | GZip | Optimal | 1,305.6 us | 93.40 us | 91.74 us | - | - | - | 23568 B |
| Compress | 1920 | ZLib | Optimal | 1,353.0 us | 67.70 us | 66.49 us | - | - | - | 23552 B |
| Compress | 1920 | Brotli | Optimal | 4,997.6 us | 181.04 us | 177.80 us | - | - | - | 1313 B |
| Compress | 1920 | GZip | SmallestSize | 8,240.1 us | 397.06 us | 389.97 us | - | - | - | 15552 B |
| Compress | 1920 | ZLib | SmallestSize | 8,380.9 us | 372.70 us | 366.04 us | - | - | - | 15536 B |
| Compress | 1920 | Deflate | SmallestSize | 8,492.4 us | 427.02 us | 419.39 us | - | - | - | 15504 B |
| Compress | 1920 | PNG | Fastest | 15,511.5 us | 1,415.48 us | 1,390.19 us | - | - | - | 25736 B |
| Compress | 1920 | PNG | NoCompression | 15,571.9 us | 654.92 us | 643.22 us | 375.0000 | 375.0000 | 375.0000 | 3694733 B |
| Compress | 1920 | PNG | Optimal | 16,101.5 us | 679.06 us | 666.93 us | - | - | - | 25496 B |
| Compress | 1920 | PNG | SmallestSize | 35,974.1 us | 2,452.16 us | 2,408.35 us | - | - | - | 9280 B |
| Compress | 1920 | Brotli | SmallestSize | 66,692.0 us | 9,677.48 us | 9,504.58 us | - | - | - | 692 B |
| | | | | | | | | | | |
| Decompress | 1920 | Deflate | SmallestSize | 113.4 us | 1.35 us | 1.19 us | - | - | - | 280 B |
| Decompress | 1920 | Deflate | Fastest | 119.7 us | 2.57 us | 2.52 us | - | - | - | 280 B |
| Decompress | 1920 | Deflate | Optimal | 123.6 us | 3.56 us | 3.50 us | - | - | - | 280 B |
| Decompress | 1920 | Deflate | NoCompression | 145.0 us | 2.05 us | 1.82 us | - | - | - | 280 B |
| Decompress | 1920 | None | NoCompression | 147.1 us | 1.93 us | 1.71 us | - | - | - | - |
| Decompress | 1920 | ZLib | Fastest | 157.1 us | 1.58 us | 1.32 us | - | - | - | 312 B |
| Decompress | 1920 | GZip | Fastest | 161.6 us | 2.30 us | 1.92 us | - | - | - | 312 B |
| Decompress | 1920 | GZip | Optimal | 162.2 us | 2.07 us | 1.93 us | - | - | - | 312 B |
| Decompress | 1920 | GZip | SmallestSize | 168.2 us | 8.01 us | 7.86 us | - | - | - | 312 B |
| Decompress | 1920 | ZLib | Optimal | 177.4 us | 1.59 us | 1.41 us | - | - | - | 312 B |
| Decompress | 1920 | GZip | NoCompression | 182.6 us | 1.61 us | 1.50 us | - | - | - | 312 B |
| Decompress | 1920 | ZLib | SmallestSize | 197.6 us | 8.23 us | 8.08 us | - | - | - | 312 B |
| Decompress | 1920 | ZLib | NoCompression | 225.5 us | 13.96 us | 13.71 us | - | - | - | 312 B |
| Decompress | 1920 | Brotli | NoCompression | 1,560.4 us | 24.39 us | 21.62 us | - | - | - | - |
| Decompress | 1920 | Brotli | Fastest | 2,107.5 us | 267.52 us | 262.74 us | - | - | - | - |
| Decompress | 1920 | Brotli | SmallestSize | 2,354.4 us | 49.89 us | 49.00 us | - | - | - | - |
| Decompress | 1920 | Brotli | Optimal | 3,384.4 us | 254.81 us | 250.26 us | - | - | - | - |
| Decompress | 1920 | PNG | Fastest | 6,079.8 us | 39.73 us | 35.22 us | - | - | - | 88 B |
| Decompress | 1920 | PNG | Optimal | 6,120.8 us | 43.09 us | 38.19 us | - | - | - | 88 B |
| Decompress | 1920 | PNG | NoCompression | 6,830.0 us | 47.14 us | 44.09 us | - | - | - | 88 B |
| Decompress | 1920 | PNG | SmallestSize | 8,797.8 us | 39.41 us | 34.94 us | - | - | - | 88 B |
Mat.ToArray() benchmark
BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8246/25H2/2025Update/HudsonValley2)
AMD Ryzen 9 7845HX with Radeon Graphics 3.00GHz, 1 CPU, 24 logical and 12 physical cores
.NET SDK 10.0.202
[Host] : .NET 10.0.6 (10.0.6, 10.0.626.17701), X64 RyuJIT x86-64-v4
DefaultJob : .NET 10.0.6 (10.0.6, 10.0.626.17701), X64 RyuJIT x86-64-v4
| Method | MatSize | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio |
|---------------------- |-------- |-----------:|---------:|---------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|
| GetRawData | 1920 | 617.7 us | 6.08 us | 5.08 us | 0.99 | 0.04 | 999.0234 | 999.0234 | 999.0234 | 3.52 MB | 1.00 |
| GetSpan.ToArray | 1920 | 623.8 us | 10.47 us | 9.28 us | 1.00 | 0.05 | 999.0234 | 999.0234 | 999.0234 | 3.52 MB | 1.00 |
| 'ToArray (extension)' | 1920 | 626.5 us | 11.84 us | 28.15 us | 1.00 | 0.06 | 999.0234 | 999.0234 | 999.0234 | 3.52 MB | 1.00 |
| | | | | | | | | | | | |
| 'ToArray (extension)' | 3840 | 1,444.4 us | 28.81 us | 32.02 us | 1.00 | 0.03 | 500.0000 | 500.0000 | 500.0000 | 14.06 MB | 1.00 |
| GetSpan.ToArray | 3840 | 1,551.3 us | 16.07 us | 15.04 us | 1.07 | 0.02 | 500.0000 | 500.0000 | 500.0000 | 14.06 MB | 1.00 |
| GetRawData | 3840 | 1,582.3 us | 21.02 us | 19.66 us | 1.10 | 0.03 | 500.0000 | 500.0000 | 500.0000 | 14.06 MB | 1.00 |
Building from Source
# Restore and build
dotnet restore
dotnet build
# Run tests
dotnet test EmguExtensions.Tests
# Pack for NuGet (Release only)
dotnet pack --configuration Release --output .
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
This project is licensed under the MIT License - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- CommunityToolkit.HighPerformance (>= 8.4.2)
- DotNext (>= 6.2.1)
- DotNext.IO (>= 6.2.1)
- Emgu.CV (>= 4.13.0.5924)
- StageKit.Primitives (>= 0.2.0)
- System.IO.Hashing (>= 10.0.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on EmguExtensions:
| Package | Downloads |
|---|---|
|
EmguExtensions.Avalonia
Avalonia integration for EmguExtensions: convert Emgu.CV Mat images to Avalonia WriteableBitmap with direct memory locking. |
GitHub repositories
This package is not used by any popular GitHub repositories.