Skip to content

Commit 8ae4dd5

Browse files
authored
Merge pull request #2007 from shimat/fix/window-trackbar-refactor-2005
Replace Window.ShowImages two-array overload with tuples; clean up Window/CvTrackbar
2 parents 5b62ba5 + 2f20cca commit 8ae4dd5

6 files changed

Lines changed: 46 additions & 116 deletions

File tree

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
using OpenCvSharp.Internal;
2-
31
namespace OpenCvSharp;
42

53
/// <summary>
64
/// Trackbar that is shown on an OpenCV Window
75
/// </summary>
86
public sealed class CvTrackbar
97
{
10-
private readonly int result;
11-
128
#region Properties
139

1410
/// <summary>
@@ -35,40 +31,24 @@ public int Pos
3531
set => Cv2.SetTrackbarPos(TrackbarName, WindowName, value);
3632
}
3733

38-
/// <summary>
39-
/// Result value of cv::createTrackbar
40-
/// </summary>
41-
public int Result => result;
42-
4334
#endregion
4435

4536
#region Init
4637

47-
/// <summary>
48-
/// Constructor (value=0, max=100)
49-
/// </summary>
50-
/// <param name="name">Trackbar name</param>
51-
/// <param name="window">Window name</param>
52-
/// <param name="callback">Callback handler</param>
53-
internal CvTrackbar(string name, string window, TrackbarCallback callback)
54-
: this(name, window, 0, 100, callback)
55-
{
56-
}
57-
5838
/// <summary>
5939
/// Constructor
6040
/// </summary>
6141
/// <param name="trackbarName">Trackbar name</param>
6242
/// <param name="windowName">Window name</param>
43+
/// <param name="callback">Callback handler</param>
6344
/// <param name="initialPos">Initial slider position</param>
6445
/// <param name="max">The upper limit of the range this trackbar is working with. </param>
65-
/// <param name="callback">Callback handler</param>
66-
internal CvTrackbar(string trackbarName, string windowName, int initialPos, int max, TrackbarCallback callback)
46+
internal CvTrackbar(string trackbarName, string windowName, TrackbarCallback callback, int initialPos = 0, int max = 100)
6747
{
6848
if (string.IsNullOrEmpty(trackbarName))
69-
throw new ArgumentNullException(nameof(trackbarName));
49+
throw new ArgumentException("Null or empty trackbar name.", nameof(trackbarName));
7050
if (string.IsNullOrEmpty(windowName))
71-
throw new ArgumentNullException(nameof(windowName));
51+
throw new ArgumentException("Null or empty window name.", nameof(windowName));
7252

7353
Callback = callback ?? throw new ArgumentNullException(nameof(callback));
7454
TrackbarName = trackbarName;
@@ -78,13 +58,12 @@ internal CvTrackbar(string trackbarName, string windowName, int initialPos, int
7858
// Cv2.CreateTrackbar roots the native-shaped delegate for the window's lifetime, so no
7959
// GCHandle is needed here.
8060
TrackbarCallbackNative callbackNative = (pos, _) => callback(pos);
81-
result = Cv2.CreateTrackbar(trackbarName, windowName, max, callbackNative);
61+
var result = Cv2.CreateTrackbar(trackbarName, windowName, max, callbackNative);
62+
if (result == 0)
63+
throw new OpenCvSharpException("Failed to create CvTrackbar.");
8264

8365
// Set initial trackbar position
8466
Cv2.SetTrackbarPos(trackbarName, windowName, initialPos);
85-
86-
if (result == 0)
87-
throw new OpenCvSharpException("Failed to create CvTrackbar.");
8867
}
8968

9069
#endregion
@@ -96,8 +75,7 @@ internal CvTrackbar(string trackbarName, string windowName, int initialPos, int
9675
/// <param name="maxVal">New maximum position.</param>
9776
public void SetMax(int maxVal)
9877
{
99-
NativeMethods.HandleException(
100-
NativeMethods.highgui_setTrackbarMax(TrackbarName, WindowName, maxVal));
78+
Cv2.SetTrackbarMax(TrackbarName, WindowName, maxVal);
10179
}
10280

10381
/// <summary>
@@ -107,7 +85,6 @@ public void SetMax(int maxVal)
10785
/// <param name="minVal">New minimum position.</param>
10886
public void SetMin(int minVal)
10987
{
110-
NativeMethods.HandleException(
111-
NativeMethods.highgui_setTrackbarMin(TrackbarName, WindowName, minVal));
88+
Cv2.SetTrackbarMin(TrackbarName, WindowName, minVal);
11289
}
11390
}

src/OpenCvSharp/Modules/highgui/Window.cs

Lines changed: 31 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Collections.Concurrent;
2+
using System.Threading;
13
using OpenCvSharp.Internal;
24

35
// ReSharper disable UnusedMember.Local
@@ -11,10 +13,10 @@ public sealed class Window : IDisposable
1113
{
1214
#region Field
1315

14-
internal static Dictionary<string, Window> Windows = new();
15-
private static uint windowCount;
16+
internal static readonly ConcurrentDictionary<string, Window> Windows = new();
17+
private static int windowCount;
1618

17-
private string name;
19+
private readonly string name;
1820
private Mat? image;
1921
// ReSharper disable once IdentifierTypo
2022
private readonly Dictionary<string, CvTrackbar> trackbars;
@@ -31,36 +33,6 @@ public Window()
3133
{
3234
}
3335

34-
/// <summary>
35-
/// Creates a window
36-
/// </summary>
37-
/// <param name="name">Name of the window which is used as window identifier and appears in the window caption. </param>
38-
public Window(string name)
39-
: this(name, null, WindowFlags.AutoSize)
40-
{
41-
}
42-
43-
/// <summary>
44-
/// Creates a window
45-
/// </summary>
46-
/// <param name="name">Name of the window which is used as window identifier and appears in the window caption. </param>
47-
/// <param name="flags">Flags of the window. Currently the only supported flag is WindowMode.AutoSize.
48-
/// If it is set, window size is automatically adjusted to fit the displayed image (see cvShowImage), while user can not change the window size manually. </param>
49-
public Window(string name, WindowFlags flags = WindowFlags.AutoSize)
50-
: this(name, null, flags)
51-
{
52-
}
53-
54-
/// <summary>
55-
/// Creates a window
56-
/// </summary>
57-
/// <param name="name">Name of the window which is used as window identifier and appears in the window caption. </param>
58-
/// <param name="image">Image to be shown.</param>
59-
public Window(string name, Mat image)
60-
: this(name, image ?? throw new ArgumentNullException(nameof(image)), WindowFlags.AutoSize)
61-
{
62-
}
63-
6436
/// <summary>
6537
/// Creates a window
6638
/// </summary>
@@ -82,8 +54,7 @@ public Window(string name, Mat? image = null, WindowFlags flags = WindowFlags.Au
8254

8355
trackbars = new Dictionary<string, CvTrackbar>();
8456

85-
if (!Windows.ContainsKey(name))
86-
Windows.Add(name, this);
57+
Windows[name] = this;
8758
}
8859

8960
/// <summary>
@@ -92,7 +63,7 @@ public Window(string name, Mat? image = null, WindowFlags flags = WindowFlags.Au
9263
/// <returns></returns>
9364
private static string DefaultName()
9465
{
95-
return $"window{windowCount++}";
66+
return $"window{Interlocked.Increment(ref windowCount) - 1}";
9667
}
9768

9869
/// <summary>
@@ -109,7 +80,9 @@ public void Dispose()
10980
return;
11081
IsDisposed = true;
11182

112-
Windows.Remove(name);
83+
// Only remove the map entry if it still points at this instance: another Window may have
84+
// since reused the same name (see the constructor), and that instance's entry must survive.
85+
((ICollection<KeyValuePair<string, Window>>) Windows).Remove(new KeyValuePair<string, Window>(name, this));
11386
trackbars.Clear();
11487

11588
// Destroying the window also releases OpenCV's references to its mouse/trackbar
@@ -150,11 +123,7 @@ public Mat? Image
150123
/// <summary>
151124
/// Gets window name
152125
/// </summary>
153-
public string Name
154-
{
155-
get => name;
156-
private set => name = value;
157-
}
126+
public string Name => name;
158127

159128
#endregion
160129

@@ -169,7 +138,7 @@ public string Name
169138
public CvTrackbar CreateTrackbar(string trackbarName, TrackbarCallback callback)
170139
{
171140
var trackbar = new CvTrackbar(trackbarName, name, callback);
172-
trackbars.Add(trackbarName, trackbar);
141+
trackbars[trackbarName] = trackbar;
173142
return trackbar;
174143
}
175144

@@ -183,8 +152,8 @@ public CvTrackbar CreateTrackbar(string trackbarName, TrackbarCallback callback)
183152
/// <returns></returns>
184153
public CvTrackbar CreateTrackbar(string trackbarName, int initialPos, int max, TrackbarCallback callback)
185154
{
186-
var trackbar = new CvTrackbar(trackbarName, name, initialPos, max, callback);
187-
trackbars.Add(trackbarName, trackbar);
155+
var trackbar = new CvTrackbar(trackbarName, name, callback, initialPos, max);
156+
trackbars[trackbarName] = trackbar;
188157
return trackbar;
189158
}
190159

@@ -290,52 +259,36 @@ public static void ShowImages(params Mat[] images)
290259
{
291260
if (images is null)
292261
throw new ArgumentNullException(nameof(images));
293-
if (images.Length == 0)
294-
return;
295262

296-
var windows = new List<Window>();
297-
foreach (var img in images)
298-
{
299-
windows.Add(new Window { Image = img });
300-
}
301-
302-
WaitKey();
303-
304-
foreach (var w in windows)
305-
{
306-
w.Close();
307-
}
263+
ShowImagesAndWaitKey(images.Select(img => new Window { Image = img }));
308264
}
309265

310266
/// <summary>
311-
///
267+
/// Shows each of the given (title, image) pairs in its own window.
268+
/// Pairing each title with its image in a single tuple makes a length mismatch
269+
/// between titles and images structurally impossible.
312270
/// </summary>
313-
/// <param name="images"></param>
314-
/// <param name="names"></param>
315-
public static void ShowImages(IEnumerable<Mat> images, IEnumerable<string> names)
271+
/// <param name="images">Pairs of window title and image to display</param>
272+
public static void ShowImages(params (string Title, Mat Image)[] images)
316273
{
317274
if (images is null)
318275
throw new ArgumentNullException(nameof(images));
319-
if (names is null)
320-
throw new ArgumentNullException(nameof(names));
321276

322-
var imagesArray = images.ToArray();
323-
var namesArray = names.ToArray();
277+
ShowImagesAndWaitKey(images.Select(t => new Window(t.Title, image: t.Image)));
278+
}
324279

325-
if (imagesArray.Length == 0)
280+
/// <summary>
281+
/// Opens the given windows, blocks until a key is pressed, then closes them all.
282+
/// </summary>
283+
private static void ShowImagesAndWaitKey(IEnumerable<Window> windows)
284+
{
285+
var windowList = windows.ToList();
286+
if (windowList.Count == 0)
326287
return;
327-
if (namesArray.Length < imagesArray.Length)
328-
throw new ArgumentException("names.Length < images.Length");
329288

330-
var windows = new List<Window>();
331-
for (var i = 0; i < imagesArray.Length; i++)
332-
{
333-
windows.Add(new Window(namesArray[i], image: imagesArray[i]));
334-
}
335-
336-
Cv2.WaitKey();
289+
WaitKey();
337290

338-
foreach (var w in windows)
291+
foreach (var w in windowList)
339292
{
340293
w.Close();
341294
}

test/OpenCvSharp.Tests/TestBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,11 @@ protected static void ShowImagesWhenDebugMode(params Mat[] mats)
7474
}
7575
}
7676

77-
protected static void ShowImagesWhenDebugMode(IEnumerable<Mat> mats, IEnumerable<string> names)
77+
protected static void ShowImagesWhenDebugMode(params (string Title, Mat Image)[] images)
7878
{
7979
if (Debugger.IsAttached)
8080
{
81-
Window.ShowImages(mats, names);
81+
Window.ShowImages(images);
8282
}
8383
}
8484
}

test/OpenCvSharp.Tests/highgui/TrackbarTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ public void RunTest()
7070

7171
for (;;)
7272
{
73-
openCloseTrackbar.Callback.DynamicInvoke(0, null);
74-
erodeDilateTrackbar.Callback.DynamicInvoke(0, null);
73+
openCloseTrackbar.Callback(0);
74+
erodeDilateTrackbar.Callback(0);
7575

7676
var key = Cv2.WaitKey();
7777

test/OpenCvSharp.Tests/imgproc/ImgProcTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ public void HoughLinesP()
725725
Cv2.Line(view, line.P1, line.P2, Scalar.Red);
726726
}
727727

728-
Window.ShowImages([src, binary, view], ["src", "binary", "lines"]);
728+
Window.ShowImages(("src", src), ("binary", binary), ("lines", view));
729729
}
730730
}
731731

test/OpenCvSharp.Tests/ximgproc/XimgProcTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public void Sauvola()
3535
5, 0.5,
3636
LocalBinarizationMethods.Sauvola,
3737
r);
38-
ShowImagesWhenDebugMode([dst], [$"r={r}"]);
38+
ShowImagesWhenDebugMode(($"r={r}", dst));
3939
}
4040
}
4141

0 commit comments

Comments
 (0)