Skip to content

Commit 95f438e

Browse files
authored
Merge pull request #1828 from shimat/FaceDetectorYN_test
Add tests for FaceDetectorYN
2 parents 259904d + 997a63f commit 95f438e

File tree

1 file changed

+170
-0
lines changed

1 file changed

+170
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using OpenCvSharp.Dnn;
2+
using Xunit;
3+
4+
namespace OpenCvSharp.Tests.ObjDetect;
5+
6+
// ReSharper disable InconsistentNaming
7+
public class FaceDetectorYNTest : TestBase
8+
{
9+
// YuNet face detection model from OpenCV Zoo
10+
// https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
11+
private const string ModelUrl =
12+
"https://github.com/opencv/opencv_zoo/raw/main/models/face_detection_yunet/face_detection_yunet_2023mar.onnx";
13+
14+
private const string ModelPath = "_data/model/face_detection_yunet_2023mar.onnx";
15+
16+
static FaceDetectorYNTest()
17+
{
18+
if (!File.Exists(ModelPath))
19+
{
20+
var contents = FileDownloader.DownloadData(new Uri(ModelUrl));
21+
File.WriteAllBytes(ModelPath, contents);
22+
}
23+
}
24+
25+
[Fact]
26+
public void Create()
27+
{
28+
using var detector = new FaceDetectorYN(
29+
ModelPath,
30+
config: "",
31+
inputSize: new Size(320, 320));
32+
33+
Assert.NotNull(detector);
34+
}
35+
36+
[Fact]
37+
public void CreateWithParameters()
38+
{
39+
using var detector = new FaceDetectorYN(
40+
ModelPath,
41+
config: "",
42+
inputSize: new Size(640, 480),
43+
scoreThreshold: 0.8f,
44+
nmsThreshold: 0.4f,
45+
topK: 3000,
46+
backendId: Backend.DEFAULT,
47+
targetId: Target.CPU);
48+
49+
Assert.NotNull(detector);
50+
}
51+
52+
[Fact]
53+
public void Dispose_Twice()
54+
{
55+
var detector = new FaceDetectorYN(
56+
ModelPath,
57+
config: "",
58+
inputSize: new Size(320, 320));
59+
60+
detector.Dispose();
61+
detector.Dispose(); // Should not throw
62+
}
63+
64+
[Fact]
65+
public void Detect_Lenna()
66+
{
67+
using var image = LoadImage("lenna.png");
68+
Assert.False(image.Empty());
69+
70+
using var detector = new FaceDetectorYN(
71+
ModelPath,
72+
config: "",
73+
inputSize: new Size(image.Cols, image.Rows),
74+
scoreThreshold: 0.9f,
75+
nmsThreshold: 0.3f,
76+
topK: 5000);
77+
78+
using var faces = new Mat();
79+
int result = detector.Detect(image, faces);
80+
81+
// Lenna image should contain at least one face
82+
Assert.Equal(1, result);
83+
Assert.False(faces.Empty());
84+
85+
// Each row in faces is a detected face:
86+
// [x, y, w, h, x_re, y_re, x_le, y_le, x_nt, y_nt, x_rcm, y_rcm, x_lcm, y_lcm, score]
87+
// 14 landmarks + 1 score = 15 columns
88+
Assert.True(faces.Rows > 0, "Expected at least one detected face");
89+
Assert.Equal(15, faces.Cols);
90+
91+
// Verify the confidence score (last column) is reasonable
92+
var score = faces.At<float>(0, 14);
93+
Assert.True(score > 0.5f, $"Face confidence score {score} is unexpectedly low");
94+
95+
ShowImagesWhenDebugMode(image);
96+
}
97+
98+
[Fact]
99+
public void Detect_NoFace()
100+
{
101+
// Create a blank image with no face
102+
using var image = new Mat(320, 320, MatType.CV_8UC3, Scalar.All(128));
103+
104+
using var detector = new FaceDetectorYN(
105+
ModelPath,
106+
config: "",
107+
inputSize: new Size(320, 320),
108+
scoreThreshold: 0.9f);
109+
110+
using var faces = new Mat();
111+
detector.Detect(image, faces);
112+
113+
// Blank image should have no detected faces
114+
Assert.True(faces.Empty() || faces.Rows == 0,
115+
$"Expected no faces but got {faces.Rows} detection(s)");
116+
}
117+
118+
[Fact]
119+
public void Detect_MultipleCalls()
120+
{
121+
using var image = LoadImage("lenna.png");
122+
using var detector = new FaceDetectorYN(
123+
ModelPath,
124+
config: "",
125+
inputSize: new Size(image.Cols, image.Rows));
126+
127+
// Verify that calling Detect multiple times produces consistent results
128+
using var faces1 = new Mat();
129+
using var faces2 = new Mat();
130+
131+
int result1 = detector.Detect(image, faces1);
132+
int result2 = detector.Detect(image, faces2);
133+
134+
Assert.Equal(result1, result2);
135+
Assert.Equal(faces1.Rows, faces2.Rows);
136+
Assert.Equal(faces1.Cols, faces2.Cols);
137+
}
138+
139+
[Fact]
140+
public void Detect_DifferentThresholds()
141+
{
142+
using var image = LoadImage("lenna.png");
143+
144+
// With high threshold
145+
using var detectorHigh = new FaceDetectorYN(
146+
ModelPath,
147+
config: "",
148+
inputSize: new Size(image.Cols, image.Rows),
149+
scoreThreshold: 0.99f);
150+
151+
using var facesHigh = new Mat();
152+
detectorHigh.Detect(image, facesHigh);
153+
154+
// With low threshold
155+
using var detectorLow = new FaceDetectorYN(
156+
ModelPath,
157+
config: "",
158+
inputSize: new Size(image.Cols, image.Rows),
159+
scoreThreshold: 0.1f);
160+
161+
using var facesLow = new Mat();
162+
detectorLow.Detect(image, facesLow);
163+
164+
// Lower threshold should yield >= detections than higher threshold
165+
int highCount = facesHigh.Empty() ? 0 : facesHigh.Rows;
166+
int lowCount = facesLow.Empty() ? 0 : facesLow.Rows;
167+
Assert.True(lowCount >= highCount,
168+
$"Low threshold detections ({lowCount}) should be >= high threshold detections ({highCount})");
169+
}
170+
}

0 commit comments

Comments
 (0)