Skip to content

Commit 3f1f2f5

Browse files
committed
Expand OpenCvSharp usage guides
1 parent 350f1f0 commit 3f1f2f5

22 files changed

Lines changed: 1025 additions & 4 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
[![GitHub Actions Windows Status](https://github.com/shimat/opencvsharp/actions/workflows/windows.yml/badge.svg)](https://github.com/shimat/opencvsharp/actions/workflows/windows.yml) [![GitHub Actions Docker Test Status](https://github.com/shimat/opencvsharp/actions/workflows/docker-test-ubuntu.yml/badge.svg)](https://github.com/shimat/opencvsharp/actions/workflows/docker-test-ubuntu.yml) [![GitHub Actions manylinux Status](https://github.com/shimat/opencvsharp/actions/workflows/manylinux.yml/badge.svg)](https://github.com/shimat/opencvsharp/actions/workflows/manylinux.yml) [![GitHub Actions Wasm Status](https://github.com/shimat/opencvsharp/actions/workflows/wasm.yml/badge.svg)](https://github.com/shimat/opencvsharp/actions/workflows/wasm.yml) [![GitHub Actions macOS Status](https://github.com/shimat/opencvsharp/actions/workflows/macos.yml/badge.svg)](https://github.com/shimat/opencvsharp/actions/workflows/macos.yml) [![GitHub license](https://img.shields.io/github/license/shimat/opencvsharp.svg)](https://github.com/shimat/opencvsharp/blob/master/LICENSE)
66

7-
OpenCvSharp is a cross-platform .NET wrapper for OpenCV, providing a rich set of image processing and computer vision functionality.
7+
OpenCvSharp is a cross-platform .NET wrapper for OpenCV, providing a rich set of image processing and computer vision functionality. Its API is intentionally designed to stay as close as practical to the native OpenCV C++ API so that OpenCV concepts, documentation, and code can be transferred to .NET with minimal friction, while still using C# conventions such as typed enums, exceptions, and deterministic disposal.
8+
9+
See [OpenCV C++, OpenCV-Python, and OpenCvSharp](https://shimat.github.io/opencvsharp/articles/getting-started/opencv-api-comparison.html) for the main API and data-model differences.
810

911
## 🚀 [Try the Live Demo](https://shimat.github.io/opencvsharp_blazor_sample/)
1012

@@ -58,7 +60,7 @@ dotnet add package OpenCvSharp5.runtime.osx.arm64
5860
For more installation options, see the [Installation](#installation) section below, or the full [NuGet package list](#nuget).
5961

6062
## Features
61-
* OpenCvSharp is modeled on the native OpenCV C/C++ API style as much as possible.
63+
* OpenCvSharp is modeled on the native OpenCV C++ API as closely as practical.
6264
* Many classes of OpenCvSharp implement IDisposable. Unsafe resources are managed automatically.
6365
* OpenCvSharp does not force object-oriented programming style on you. You can also call native-style OpenCV functions.
6466
* OpenCvSharp provides functions for converting from `Mat` to `Bitmap` (GDI+) or `WriteableBitmap` (WPF).
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# OpenCV C++, OpenCV-Python, and OpenCvSharp
2+
3+
OpenCvSharp exposes OpenCV to .NET applications. Its API is intentionally designed to stay as close as practical to the native OpenCV C++ API so that OpenCV concepts, documentation, and code transfer naturally to .NET. Most computer vision concepts, algorithm behavior, and parameter constraints come directly from OpenCV, while the managed surface also follows C# conventions and cannot be translated mechanically from every C++ or Python example.
4+
5+
Use the official OpenCV documentation for algorithm theory and native parameter requirements. Use the OpenCvSharp guides and API reference for .NET package selection, managed types, overloads, resource ownership, and application integration.
6+
7+
## How the three APIs relate
8+
9+
OpenCV is implemented primarily in C++. Its C++ API is therefore the closest description of the native functions that OpenCvSharp eventually calls.
10+
11+
OpenCV-Python is an official generated binding over the same native implementation. Images are normally presented as NumPy arrays, and the binding adapts many C++ output parameters into Python return values.
12+
13+
OpenCvSharp is a .NET wrapper with a managed API and a native bridge. It preserves many OpenCV names and concepts while representing them with .NET classes, enums, arrays, spans, exceptions, and `IDisposable`.
14+
15+
| Concept | OpenCV C++ | OpenCV-Python | OpenCvSharp |
16+
|---|---|---|---|
17+
| Free function | `cv::GaussianBlur` | `cv.GaussianBlur` | `Cv2.GaussianBlur` |
18+
| Submodule function | `cv::dnn::readNetFromONNX` | `cv.dnn.readNetFromONNX` | `Cv2.Dnn.ReadNetFromONNX` |
19+
| Image container | `cv::Mat` | `numpy.ndarray` or `cv.Mat` | `Mat` |
20+
| Image size | `cv::Size` | `(width, height)` tuple | `Size` |
21+
| Coordinate | `cv::Point` | `(x, y)` tuple | `Point` |
22+
| Color conversion enum and value | `cv::ColorConversionCodes` and `cv::COLOR_BGR2GRAY` | `cv.COLOR_BGR2GRAY` | `ColorConversionCodes.BGR2GRAY` |
23+
| Output image | `OutputArray dst` argument | Usually a return value | Usually a caller-owned `Mat` argument |
24+
| Native failure | `cv::Exception` | `cv2.error` | `OpenCVException` |
25+
| Lifetime | RAII and reference counting | Python and NumPy ownership | `IDisposable` and `using` |
26+
27+
The correspondence is deliberately recognizable, but it is not a promise that every native overload has an identical managed overload.
28+
29+
## The same operation in three languages
30+
31+
The following examples all load a color image, validate it, and convert it to grayscale.
32+
33+
### C++
34+
35+
```cpp
36+
cv::Mat source = cv::imread("input.jpg", cv::IMREAD_COLOR);
37+
if (source.empty())
38+
{
39+
throw std::runtime_error("Could not read input.jpg.");
40+
}
41+
42+
cv::Mat grayscale;
43+
cv::cvtColor(source, grayscale, cv::COLOR_BGR2GRAY);
44+
```
45+
46+
### Python
47+
48+
```python
49+
import cv2 as cv
50+
51+
source = cv.imread("input.jpg", cv.IMREAD_COLOR)
52+
if source is None:
53+
raise RuntimeError("Could not read input.jpg.")
54+
55+
grayscale = cv.cvtColor(source, cv.COLOR_BGR2GRAY)
56+
```
57+
58+
### OpenCvSharp
59+
60+
```csharp
61+
using var source = Cv2.ImRead("input.jpg", ImreadModes.Color);
62+
if (source.Empty())
63+
{
64+
throw new InvalidOperationException("Could not read input.jpg.");
65+
}
66+
67+
using var grayscale = new Mat();
68+
Cv2.CvtColor(source, grayscale, ColorConversionCodes.BGR2GRAY);
69+
```
70+
71+
The OpenCvSharp example resembles the C++ API because `grayscale` is an explicit destination. It also adds deterministic disposal because both managed objects own native resources.
72+
73+
## Translating names from C++
74+
75+
Most free functions in the `cv` namespace are static methods on `Cv2`, with .NET-style capitalization:
76+
77+
| C++ | OpenCvSharp |
78+
|---|---|
79+
| `cv::imread` | `Cv2.ImRead` |
80+
| `cv::cvtColor` | `Cv2.CvtColor` |
81+
| `cv::findContours` | `Cv2.FindContours` |
82+
| `cv::warpPerspective` | `Cv2.WarpPerspective` |
83+
84+
Functions in an OpenCV submodule normally use a nested `Cv2` class. For example, `cv::dnn` maps to `Cv2.Dnn`, while wrapper object types remain in namespaces such as `OpenCvSharp.Dnn`.
85+
86+
OpenCvSharp enum types generally mirror named enums declared by the native C++ API. For example, OpenCV declares the unscoped enum `cv::ColorConversionCodes`; because its enumerators are injected into the enclosing `cv` namespace, C++ code normally writes `cv::COLOR_BGR2GRAY` without mentioning the enum type. OpenCvSharp exposes the same grouping through the scoped C# expression `ColorConversionCodes.BGR2GRAY`.
87+
88+
Apply the same rule when translating names such as `cv::INTER_AREA` or `cv::THRESH_OTSU`: identify the native enum declaration or inspect the parameter type, then use the corresponding OpenCvSharp enum member.
89+
90+
## Translating Python examples
91+
92+
Python tutorials are valuable explanations of OpenCV algorithms, but NumPy operations need special attention.
93+
94+
### NumPy arrays are not OpenCvSharp Mat objects
95+
96+
A Python image commonly exposes shape, data type, channels, slicing, broadcasting, and arithmetic through NumPy. OpenCvSharp keeps the native `cv::Mat` model instead:
97+
98+
| Python and NumPy | OpenCvSharp |
99+
|---|---|
100+
| `image.shape` | `image.Rows`, `image.Cols`, and `image.Channels()` |
101+
| `image.dtype` | `image.Depth()` or `image.Type()` |
102+
| `image[y, x]` | `image.At<T>(y, x)` or span-based access |
103+
| `image[y1:y2, x1:x2]` | A `Mat` region of interest |
104+
| NumPy vectorized expression | A corresponding `Cv2` operation, matrix expression, or span loop |
105+
106+
Do not translate a NumPy loop or slice one token at a time. First identify whether the operation is a native OpenCV function. Native operations usually express intent more clearly and avoid repeated managed-to-native calls.
107+
108+
See [Mat Basics](../guides/mat-basics.md) and [Pixel Access](../guides/pixel-access.md) for the OpenCvSharp data model.
109+
110+
### Python often returns C++ output arguments
111+
112+
The generated Python binding turns many C++ `OutputArray` arguments into return values. For example, Python thresholding returns both the selected threshold and the output image:
113+
114+
```python
115+
selected_threshold, binary = cv.threshold(
116+
grayscale, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
117+
```
118+
119+
OpenCvSharp keeps the destination as an argument and returns the scalar result:
120+
121+
```csharp
122+
using var binary = new Mat();
123+
double selectedThreshold = Cv2.Threshold(
124+
grayscale, binary, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
125+
```
126+
127+
Functions with several Python return values may instead use `out` parameters, arrays, tuples, or result objects in OpenCvSharp. Check the managed signature rather than assuming the Python return shape.
128+
129+
## Resource ownership is a .NET concern
130+
131+
OpenCvSharp objects that own native resources must be disposed. This includes `Mat`, `VideoCapture`, `VideoWriter`, `Net`, feature detectors, and many other wrappers.
132+
133+
Use `using` declarations for local ownership and document ownership when returning an OpenCvSharp object from a method. Garbage collection does not provide timely accounting for native image buffers.
134+
135+
Read [Resource Management](../guides/resource-management.md) before translating a long-running Python application or a C++ pipeline that relies on scope-based RAII.
136+
137+
## Find the corresponding OpenCvSharp API
138+
139+
When starting from an OpenCV C++ or Python page:
140+
141+
1. Identify the C++ function and its module in the official OpenCV reference.
142+
2. Map `cv::functionName` to `Cv2.FunctionName`, or `cv::module::functionName` to `Cv2.Module.FunctionName`.
143+
3. Replace global constants with the enum type accepted by the OpenCvSharp parameter.
144+
4. Search the [OpenCvSharp API reference](../../api/index.md) for the managed overload.
145+
5. Check whether returned images or algorithm objects need disposal.
146+
6. Confirm that the OpenCvSharp and OpenCV versions provide the same module and behavior.
147+
148+
If a direct mapping is missing, search the [issue tracker](https://github.com/shimat/opencvsharp/issues) before assuming that the native feature is unavailable.
149+
150+
## Version and package differences
151+
152+
This documentation describes OpenCvSharp5 and OpenCV 5.x. OpenCvSharp4 wraps the OpenCV 4.x line and targets older .NET applications. Some module names, APIs, data types, and package requirements differ between the two major versions.
153+
154+
See [Choose a Version and Package](package-selection.md) before combining code or documentation from different OpenCV generations.
155+
156+
## Official OpenCV references
157+
158+
- [OpenCV 5 documentation](https://docs.opencv.org/5.0/index.html)
159+
- [OpenCV 5 tutorials](https://docs.opencv.org/5.0/tutorials/tutorials.html)
160+
- [OpenCV-Python tutorials](https://docs.opencv.org/5.0/py_tutorials/py_tutorials.html)
161+
- [How OpenCV-Python bindings work](https://docs.opencv.org/5.0/py_tutorials/py_bindings/py_bindings_basics/py_bindings_basics.html)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Contours and Shape Analysis
2+
3+
Contours describe object boundaries in a binary image. They are useful for measuring area, locating bounding boxes, approximating shapes, and drawing annotations.
4+
5+
## Prepare a binary input
6+
7+
`Cv2.FindContours` expects an 8-bit single-channel image where zero is background and non-zero pixels are foreground:
8+
9+
```csharp
10+
using var source = Cv2.ImRead("objects.png", ImreadModes.Color);
11+
if (source.Empty())
12+
{
13+
throw new InvalidOperationException("Could not read objects.png.");
14+
}
15+
16+
using var grayscale = new Mat();
17+
using var binary = new Mat();
18+
19+
Cv2.CvtColor(source, grayscale, ColorConversionCodes.BGR2GRAY);
20+
Cv2.Threshold(
21+
grayscale,
22+
binary,
23+
0,
24+
255,
25+
ThresholdTypes.Binary | ThresholdTypes.Otsu);
26+
```
27+
28+
If the objects are darker than the background, use `ThresholdTypes.BinaryInv | ThresholdTypes.Otsu`.
29+
30+
## Find external contours
31+
32+
Use `RetrievalModes.External` when only the outer boundary of each foreground object matters:
33+
34+
```csharp
35+
using Mat contourInput = binary.Clone();
36+
Cv2.FindContours(
37+
contourInput,
38+
out Point[][] contours,
39+
out HierarchyIndex[] hierarchy,
40+
RetrievalModes.External,
41+
ContourApproximationModes.ApproxSimple);
42+
```
43+
44+
Each element in `contours` is a managed array of image coordinates. `ApproxSimple` compresses straight segments to their endpoints and is appropriate for most measurement and drawing tasks.
45+
46+
The cloned input makes the ownership and reuse of `binary` unambiguous across OpenCV versions and overloads.
47+
48+
## Filter and draw contours
49+
50+
Measure contour area before drawing or performing more expensive analysis:
51+
52+
```csharp
53+
Point[][] significantContours = contours
54+
.Where(contour => Cv2.ContourArea(contour) >= 500)
55+
.ToArray();
56+
57+
using Mat annotated = source.Clone();
58+
Cv2.DrawContours(
59+
annotated,
60+
significantContours,
61+
contourIdx: -1,
62+
color: new Scalar(0, 255, 0),
63+
thickness: 2);
64+
65+
foreach (Point[] contour in significantContours)
66+
{
67+
Rect bounds = Cv2.BoundingRect(contour);
68+
Cv2.Rectangle(
69+
annotated,
70+
bounds,
71+
new Scalar(0, 0, 255),
72+
thickness: 2);
73+
}
74+
```
75+
76+
`contourIdx: -1` draws every contour in the supplied collection. OpenCV colors are BGR by default, so `(0, 255, 0)` is green and `(0, 0, 255)` is red.
77+
78+
## Understand contour hierarchy
79+
80+
`HierarchyIndex` records four indices for each contour:
81+
82+
- The next contour at the same level.
83+
- The previous contour at the same level.
84+
- The first child contour.
85+
- The parent contour.
86+
87+
Missing relationships use `-1`. `RetrievalModes.External` produces no nested children. Use `RetrievalModes.CComp` for two-level component boundaries or `RetrievalModes.Tree` for the full nesting structure.
88+
89+
## Use connected components for filled regions
90+
91+
Connected-component labeling is often simpler when the goal is to enumerate filled blobs rather than analyze their boundaries:
92+
93+
```csharp
94+
ConnectedComponents components = Cv2.ConnectedComponentsEx(binary);
95+
using Mat annotated = source.Clone();
96+
97+
foreach (ConnectedComponents.Blob blob in components.Blobs.Skip(1))
98+
{
99+
if (blob.Area < 500)
100+
{
101+
continue;
102+
}
103+
104+
Cv2.Rectangle(
105+
annotated,
106+
blob.Rect,
107+
new Scalar(255, 0, 0),
108+
thickness: 2);
109+
}
110+
```
111+
112+
Label zero is the background, so typical applications skip the first blob. Each remaining blob includes its area, centroid, and bounding rectangle.
113+
114+
Choose contours when perimeter geometry and nested boundaries matter. Choose connected components when labels, areas, centroids, and axis-aligned bounds are sufficient.
115+
116+
## Common mistakes
117+
118+
- Passing a three-channel color image to `FindContours`.
119+
- Treating grayscale intensity as a mask without thresholding it first.
120+
- Forgetting that `Point.X` is the column and `Point.Y` is the row.
121+
- Using `RetrievalModes.External` when holes or nested objects are significant.
122+
- Comparing contour area with bounding-box area as if they were equivalent.
123+
- Keeping every noise contour instead of filtering by scale or applying morphology first.
124+
125+
## From C++ or Python
126+
127+
The native `std::vector<std::vector<cv::Point>>` and the Python contour sequence map naturally to `Point[][]` in the common OpenCvSharp overload. Python returns contours and hierarchy together, while OpenCvSharp uses `out` parameters.
128+
129+
## Official OpenCV references
130+
131+
- [Structural analysis and shape descriptors](https://docs.opencv.org/5.0/main_modules/imgproc_shape.html)
132+
- [OpenCV image processing tutorials](https://docs.opencv.org/5.0/tutorials/imgproc/imgproc.html)
133+
134+
## Related OpenCvSharp API
135+
136+
- [Cv2](xref:OpenCvSharp.Cv2)
137+
- [HierarchyIndex](xref:OpenCvSharp.HierarchyIndex)
138+
- [RetrievalModes](xref:OpenCvSharp.RetrievalModes)
139+
- [ContourApproximationModes](xref:OpenCvSharp.ContourApproximationModes)
140+
- [ConnectedComponents](xref:OpenCvSharp.ConnectedComponents)

0 commit comments

Comments
 (0)