Skip to content

Commit ba919ea

Browse files
authored
Merge pull request #1890 from shimat/docs/asrows-parallel-remarks
Add Parallel.For usage guidance to AsRows<T> XML docs and regression test
2 parents 7e2f060 + 887c714 commit ba919ea

3 files changed

Lines changed: 64 additions & 0 deletions

File tree

src/OpenCvSharp/Modules/core/Mat/Mat.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4283,6 +4283,19 @@ public unsafe Span<T> RowSpan<T>(int row) where T : unmanaged
42834283
/// <typeparam name="T">Element type. Must match the matrix element type (e.g. <see cref="Vec3b"/> for CV_8UC3).</typeparam>
42844284
/// <returns>A <see cref="MatRowAccessor{T}"/> over this matrix.</returns>
42854285
/// <exception cref="InvalidOperationException">Thrown when the matrix is not 2-dimensional.</exception>
4286+
/// <remarks>
4287+
/// <b>Parallel usage:</b> Because <see cref="MatRowAccessor{T}"/> is a <c>ref struct</c> it cannot be
4288+
/// captured by a lambda closure. Call this method inside each <c>Parallel.For</c> iteration to obtain
4289+
/// a per-thread local accessor:
4290+
/// <code>
4291+
/// Parallel.For(0, mat.Rows, y =&gt;
4292+
/// {
4293+
/// Span&lt;float&gt; row = mat.AsRows&lt;float&gt;()[y];
4294+
/// for (int x = 0; x &lt; row.Length; x++)
4295+
/// row[x] = ComputeValue(y, x);
4296+
/// });
4297+
/// </code>
4298+
/// </remarks>
42864299
public unsafe MatRowAccessor<T> AsRows<T>() where T : unmanaged
42874300
{
42884301
ThrowIfDisposed();

src/OpenCvSharp/Modules/core/Mat/MatRowAccessor.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@
2525
/// This is a <c>ref struct</c> and therefore cannot be stored in fields, boxed, or used as a
2626
/// generic type argument.
2727
/// </para>
28+
/// <para>
29+
/// <b>Parallel usage:</b> <see cref="MatRowAccessor{T}"/> is safe to use concurrently from multiple threads
30+
/// as long as each thread writes to a distinct set of rows. Because <see cref="MatRowAccessor{T}"/> is a
31+
/// <c>ref struct</c> it cannot be captured by a lambda closure; instead, call <see cref="Mat.AsRows{T}"/>
32+
/// inside each iteration to obtain a per-thread local accessor:
33+
/// <code>
34+
/// Parallel.For(0, mat.Rows, y =&gt;
35+
/// {
36+
/// Span&lt;float&gt; row = mat.AsRows&lt;float&gt;()[y];
37+
/// for (int x = 0; x &lt; row.Length; x++)
38+
/// row[x] = ComputeValue(y, x);
39+
/// });
40+
/// </code>
41+
/// </para>
2842
/// </remarks>
2943
public readonly ref struct MatRowAccessor<T> where T : unmanaged
3044
{

test/OpenCvSharp.Tests/core/MatTest.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,4 +1289,41 @@ public void SubmatOfMultiDimensionalMat()
12891289
}
12901290
}
12911291
}
1292+
1293+
// Regression test for https://github.com/shimat/opencvsharp/issues/1889
1294+
[Fact]
1295+
public void AsRowsParallelFor()
1296+
{
1297+
const int rows = 500;
1298+
const int cols = 1_000;
1299+
1300+
using var matX = new Mat(rows, cols, MatType.CV_32FC1);
1301+
using var matY = new Mat(rows, cols, MatType.CV_32FC1);
1302+
matX.SetTo(Scalar.All(0f));
1303+
matY.SetTo(Scalar.All(0f));
1304+
1305+
Parallel.For(0, rows, y =>
1306+
{
1307+
var rowX = matX.AsRows<float>()[y];
1308+
var rowY = matY.AsRows<float>()[y];
1309+
for (var x = 0; x < cols; x++)
1310+
{
1311+
rowX[x] = 11f;
1312+
rowY[x] = 12f;
1313+
}
1314+
});
1315+
1316+
var resultX = matX.AsRows<float>();
1317+
var resultY = matY.AsRows<float>();
1318+
for (var y = 0; y < rows; y++)
1319+
{
1320+
var rowX = resultX[y];
1321+
var rowY = resultY[y];
1322+
for (var x = 0; x < cols; x++)
1323+
{
1324+
Assert.Equal(11f, rowX[x]);
1325+
Assert.Equal(12f, rowY[x]);
1326+
}
1327+
}
1328+
}
12921329
}

0 commit comments

Comments
 (0)