Skip to content

Commit f18bbd3

Browse files
committed
add loss check for easy cases
1 parent 22dae01 commit f18bbd3

File tree

5 files changed

+235
-53
lines changed

5 files changed

+235
-53
lines changed

src/prm_vision/opencv_armor_detector/src/OpenCVArmorDetector.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ std::vector<_Float32> OpenCVArmorDetector::search(cv::Mat &frame)
6464
std::to_string(_frame_count) + " (" +
6565
std::to_string(_detected_frame * 100 / _frame_count) + "%)");
6666

67-
cv::waitKey(10);
67+
cv::waitKey(200);
6868
#endif
6969

7070
// If we didn't find an armor for a few frames (ROS2 param), reset the search area
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
616,652,620,694,701,649,705,686,
2+
591,652,591,694,684,651,686,692,
3+
172,705,169,755,287,699,285,751,
4+
428,689,432,737,529,683,533,723,
5+
900,679,902,728,1010,680,1013,728,
6+
684,682,688,737,800,674,806,726,
7+
657,669,659,713,755,666,758,709,
8+
569,674,566,717,663,677,661,724,
9+
781,653,780,694,880,656,879,700,
10+
945,694,953,741,1019,688,1026,725,
11+
520,658,517,701,616,658,614,703,
12+
600,676,599,721,701,676,701,723,
13+
673,671,677,718,772,664,777,707,
14+
758,666,752,709,847,674,840,727,
15+
611,673,604,710,687,680,680,725,
16+
600,677,599,721,701,677,701,723,
17+
600,676,599,721,701,676,701,723,
18+
233,682,228,723,323,685,318,733,
19+
600,677,599,721,701,676,701,723,
20+
752,668,751,721,868,670,867,725,
21+
526,671,522,706,597,676,591,721,
22+
501,680,498,724,601,682,599,728,
23+
696,674,701,718,786,669,791,709,
24+
556,673,556,722,665,672,666,723,
25+
600,676,599,721,701,676,701,723,
26+
1044,657,1054,706,1136,649,1144,691,
27+
524,672,521,717,624,674,621,725,
28+
500,663,504,706,585,656,590,695,
29+
752,662,754,705,844,660,848,698,
30+
386,653,381,693,474,657,470,701,
31+
774,652,769,695,864,658,859,710,
32+
665,659,661,697,752,664,748,707,
33+
657,690,662,737,750,682,755,725,
34+
531,656,526,698,623,662,619,706,
35+
589,680,594,726,684,672,689,715,
36+
600,676,599,721,701,676,701,723,
37+
190,678,184,715,265,682,258,730,
38+
734,678,743,732,834,667,843,713,
39+
597,676,597,722,703,673,704,720,
40+
1000,655,1006,702,1105,650,1111,696,
41+
755,670,752,718,864,675,861,730,
42+
493,671,488,714,587,676,583,723,
43+
970,687,977,736,1064,684,1070,726,
44+
684,682,688,737,800,674,806,726,
45+
631,686,628,738,744,687,742,743,
46+
1001,682,1010,737,1104,673,1112,722,
47+
570,658,569,703,670,659,670,705,
48+
790,658,787,705,898,662,896,716,
49+
775,655,770,691,846,662,840,711,
50+
612,680,608,729,714,686,710,740,
51+
592,675,591,719,692,677,691,724,
52+
614,677,615,723,717,676,719,722,
53+
600,676,599,721,701,677,701,724,
54+
583,663,581,705,678,664,677,710,
55+
918,648,920,692,1024,648,1027,693,
56+
516,669,513,709,606,672,603,713,
57+
696,674,701,718,786,669,791,709,
58+
600,677,599,721,701,677,701,723,
59+
535,676,533,722,638,679,636,726,
60+
570,658,569,703,670,659,670,705,
61+
613,678,616,725,711,672,715,715,
62+
557,678,562,724,649,668,655,711,
63+
392,673,387,709,465,677,459,718,
64+
460,686,460,733,564,684,566,727,
65+
624,651,630,691,694,646,699,677,
66+
523,671,519,704,590,675,584,720,
67+
884,665,887,720,1007,663,1011,717,
68+
617,667,617,711,718,668,718,713,
69+
582,674,579,718,678,677,677,724,
70+
556,673,556,722,665,672,666,723,
71+
0,0,0,0,0,0,0,0,
72+
545,679,550,726,638,671,644,713,
73+
755,666,763,710,828,658,834,693,
74+
748,667,755,711,825,660,831,695,
75+
428,689,432,737,529,683,533,723,
76+
283,679,286,729,396,670,400,717,
77+
555,660,551,697,639,663,634,709,
78+
523,671,519,704,590,675,584,720,
79+
526,671,522,706,597,676,591,721,
80+
413,659,412,703,511,660,509,704,
81+
549,678,547,724,651,680,650,727,
82+
548,673,544,712,632,677,628,723,
83+
708,664,717,712,795,653,802,692,
84+
600,677,599,721,701,676,701,723,
85+
748,667,755,711,825,660,831,695,
86+
498,667,493,707,576,673,570,722,
87+
190,678,184,715,265,682,258,730,
88+
559,681,564,728,652,673,657,714,
89+
597,676,597,722,703,673,704,720,
90+
536,671,531,709,613,677,608,722,
91+
680,665,687,708,750,658,755,692,
92+
415,672,424,719,498,659,506,698,
93+
172,705,169,755,287,699,285,751,
94+
596,681,597,729,700,677,702,724,
95+
752,666,760,710,827,659,833,693,
96+
380,644,374,676,449,648,442,691,
97+
616,652,620,694,701,649,705,686,
98+
612,649,615,695,713,643,717,685,
99+
493,671,488,714,587,676,583,723,
100+
749,667,756,711,825,659,831,695,
101+
884,665,887,720,1007,663,1011,717,
102+
763,666,771,709,832,659,839,691,
103+
555,660,551,697,639,663,634,709,
104+
611,673,604,710,687,680,680,725,
105+
0,0,0,0,0,0,0,0,
106+
545,679,550,726,638,671,644,713,
107+
1044,657,1054,706,1136,649,1144,691,
108+
547,676,552,723,640,669,645,710,
109+
600,677,599,721,701,676,701,723,
110+
668,666,668,709,764,668,765,707,
111+
646,668,651,718,750,659,757,705,
112+
448,665,449,708,546,663,547,704,
113+
600,677,599,721,702,677,701,724,
114+
560,652,561,696,663,649,664,693,
115+
758,666,752,709,847,674,840,727,
116+
566,674,563,717,659,678,656,725,
117+
600,676,599,721,701,676,701,723,
118+
530,649,524,685,607,655,600,699,
119+
392,673,387,709,465,677,459,718,
120+
543,672,539,711,626,677,621,723,
121+
688,656,694,701,772,651,778,689,
122+
596,664,594,711,699,666,697,716,
123+
665,659,661,697,752,664,748,707,
124+
612,649,615,695,713,643,717,685,
125+
806,667,813,720,909,660,915,700,
126+
806,667,813,720,909,660,915,700,
127+
758,670,760,723,876,669,879,718,
128+
502,671,498,706,577,675,572,724,
129+
900,679,902,728,1010,680,1013,728,
130+
0,0,0,0,0,0,0,0,
131+
614,677,615,723,717,676,719,722,
132+
600,677,599,721,701,676,701,723,
133+
600,677,599,721,701,676,701,723,
134+
544,652,539,684,609,659,603,701,
135+
748,668,755,712,825,660,831,696,
136+
548,675,543,717,638,680,633,726,
137+
105,717,98,763,205,716,199,769,
138+
707,686,715,731,782,677,789,714,
139+
413,659,412,703,511,660,509,704,
140+
0,0,0,0,0,0,0,0,
141+
355,673,362,722,456,660,463,703,
142+
598,676,597,721,699,676,699,723,
143+
582,674,579,718,678,677,677,724,
144+
530,663,537,705,606,656,612,690,
145+
0,0,0,0,0,0,0,0,
146+
753,664,747,702,825,673,817,724,
147+
484,674,479,712,563,676,559,721,
148+
948,678,954,734,1064,672,1071,724,
149+
462,666,456,706,543,673,537,720,
150+
952,682,956,731,1057,681,1062,727,
151+
734,662,742,709,820,654,827,694,
152+
597,677,596,721,699,678,699,722,
153+
605,663,607,708,705,661,707,704,
154+
707,686,715,731,782,677,789,714,
155+
87,713,80,751,173,715,164,766,
156+
532,653,527,685,596,659,590,703,
157+
768,665,777,708,835,659,841,688,
158+
490,664,484,703,569,669,563,715,
159+
0,0,0,0,0,0,0,0,

src/prm_vision/opencv_armor_detector/test/test_OpenCVArmorDetector.cpp

Lines changed: 75 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@
99
#include <sstream>
1010

1111
// Helpers
12-
void iterateThroughFolder(std::string folder_path, OpenCVArmorDetector *detector);
1312
void readGT(std::string file_path, std::vector<std::vector<cv::Point2f>> &gt);
1413
void testVideo(std::string video_path, std::string gt_path, OpenCVArmorDetector *detector, float min_detection_rate, float max_loss_pix);
14+
void testImgs(std::string folder_path, std::string gt_path, OpenCVArmorDetector *detector, float min_detection_rate, float max_loss_pix);
1515

1616
// DETECTOR REQUIREMENTS
17-
#define MIN_EASY_DETECTION_RATE 0.95 // 95% detection rate required
18-
#define MAX_EASY_LOSS_PIX 5 // 5 pixels loss allowed
19-
#define MIN_FAR_BACK_SPIN_AND_MOVE_DETECTION_RATE 0.70 // 70% detection rate required
20-
#define MAX_FAR_BACK_SPIN_AND_MOVE_LOSS_PIX 5 // 5 pixels loss allowed
21-
#define MIN_CLOSE_DETECTION_RATE 0.65 // 65% detection rate required
22-
#define MAX_CLOSE_LOSS_PIX 5 // 5 pixels loss allowed
17+
#define MIN_DETECTION_RATE_EASY 0.95 // 95% detection rate required
18+
#define MIN_DETECTION_RATE_FAR_BACK_SPIN_AND_MOVE 0.70 // 70% detection rate required
19+
#define MIN_DETECTION_RATE_CLOSE_VIDEO 0.65 // 65% detection rate required
20+
#define MAX_LOSS_EASY 5 // 5 pixels loss allowed
21+
#define MAX_LOSS_FAR_BACK_SPIN_AND_MOVE 5 // 5 pixels loss allowed
22+
#define MAX_LOSS_CLOSE_VIDEO 5 // 5 pixels loss allowed
2323

2424
// Set up the test fixture
2525
class OpenCVArmorDetectorTest : public ::testing::Test
@@ -194,6 +194,7 @@ TEST_F(OpenCVArmorDetectorTest, test_search_no_armor)
194194
// This is the key test section for the armor detection. We will test the search method with frames containing armor
195195
// We must meet a detection rate and loss threshold to pass
196196
//
197+
197198
TEST_F(OpenCVArmorDetectorTest, test_search_armor_easy)
198199
{
199200
// We install the test resources to the package share directory (done in the CMakeLists.txt)
@@ -206,13 +207,9 @@ TEST_F(OpenCVArmorDetectorTest, test_search_armor_easy)
206207
EXPECT_EQ(detector_easy->_frame_count, 0);
207208

208209
// TEST THE EASY CASES
209-
std::filesystem::path easy_path = std::filesystem::path(package_share_dir) / "resources/easy";
210-
iterateThroughFolder(easy_path.string(), detector_easy);
211-
EXPECT_EQ(detector_easy->_frame_count, 159); // 159 frames in the easy folder
212-
double detection_rate = static_cast<double>(detector_easy->_detected_frame) / static_cast<double>(detector_easy->_frame_count);
213-
EXPECT_GE(detection_rate, MIN_EASY_DETECTION_RATE);
214-
215-
// TODO: Check loss against ground truths
210+
std::string easy_path = (std::filesystem::path(package_share_dir) / "resources/easy").string();
211+
std::string gtPath = (std::filesystem::path(package_share_dir) / "resources/easy/ground_truth.csv").string();
212+
testImgs(easy_path, gtPath, detector_easy, MIN_DETECTION_RATE_EASY, MAX_LOSS_EASY);
216213

217214
delete detector_easy;
218215
}
@@ -228,30 +225,77 @@ TEST_F(OpenCVArmorDetectorTest, test_search_armor_close_video)
228225
EXPECT_EQ(detector_close->_detected_frame, 0);
229226
EXPECT_EQ(detector_close->_frame_count, 0);
230227

231-
std::string videoPath = (std::filesystem::path(package_share_dir) / "resources/close/close.avi").string();
232-
std::string gtPath = (std::filesystem::path(package_share_dir) / "resources/close/ground_truth.csv").string();
233-
testVideo(videoPath, gtPath, detector_close, MIN_CLOSE_DETECTION_RATE, MAX_CLOSE_LOSS_PIX);
228+
std::string videoPath = (std::filesystem::path(package_share_dir) / "resources/close_video/close.avi").string();
229+
std::string gtPath = (std::filesystem::path(package_share_dir) / "resources/close_video/ground_truth.csv").string();
230+
testVideo(videoPath, gtPath, detector_close, MIN_DETECTION_RATE_CLOSE_VIDEO, MAX_LOSS_CLOSE_VIDEO);
234231
}
235232

236-
// This an overall test, does up-close, far, spin, and hidden. So target accuracy is lower
233+
// This an overall test. Contains up-close, far, spin, and hidden. So target accuracy is lower
237234
TEST_F(OpenCVArmorDetectorTest, test_search_armor_far_back_spin_and_move_video)
238235
{
239236
// We install the test resources to the package share directory (done in the CMakeLists.txt)
240237
std::string package_share_dir = ament_index_cpp::get_package_share_directory("opencv_armor_detector");
241238
OpenCVArmorDetector *detector_far_back_spin_and_move = new OpenCVArmorDetector({RED, 30, 100, 150, 1, true});
239+
242240
// Should be no missed frames or detected frames at the start
243241
EXPECT_EQ(detector_far_back_spin_and_move->getMissedFrames(), 0);
244242
EXPECT_EQ(detector_far_back_spin_and_move->_detected_frame, 0);
245243
EXPECT_EQ(detector_far_back_spin_and_move->_frame_count, 0);
246244

247245
std::string videoPath = (std::filesystem::path(package_share_dir) / "resources/far_back_spin_and_move/far.avi").string();
248246
std::string gtPath = (std::filesystem::path(package_share_dir) / "resources/far_back_spin_and_move/ground_truth.csv").string();
249-
testVideo(videoPath, gtPath, detector_far_back_spin_and_move, MIN_FAR_BACK_SPIN_AND_MOVE_DETECTION_RATE, MAX_FAR_BACK_SPIN_AND_MOVE_LOSS_PIX);
247+
testVideo(videoPath, gtPath, detector_far_back_spin_and_move, MIN_DETECTION_RATE_FAR_BACK_SPIN_AND_MOVE, MAX_LOSS_FAR_BACK_SPIN_AND_MOVE);
250248
}
251249

252250
//////////////////////
253251
// HELPER FUNCTIONS //
254252
//////////////////////
253+
void testImgs(std::string folder_path, std::string gt_path, OpenCVArmorDetector *detector, float min_detection_rate, float max_loss_pix)
254+
{
255+
// Read ground truths
256+
std::vector<std::vector<cv::Point2f>> gt;
257+
readGT(gt_path, gt);
258+
259+
// Loss is the sum of the L2 norm between the detected points and the ground truth
260+
float total_loss = 0;
261+
int frame_idx = 0;
262+
263+
// Loop through all images in the folder
264+
for (const auto &entry : std::filesystem::directory_iterator(folder_path))
265+
{
266+
// if not png or jpg, skip
267+
if (entry.path().extension() != ".png" && entry.path().extension() != ".jpg")
268+
{
269+
continue;
270+
}
271+
272+
// Read the image and run the detector
273+
std::string img_path = entry.path().string();
274+
cv::Mat frame = cv::imread(img_path);
275+
cv::resize(frame, frame, cv::Size(WIDTH, HEIGHT));
276+
std::vector<_Float32> points = detector->search(frame);
277+
278+
// Loss between the detected points and the ground truth
279+
for (int i = 0; i < 4; i++)
280+
{
281+
cv::Point2f detected_point(points.at(i * 2), points.at(i * 2 + 1));
282+
cv::Point2f gt_point = gt.at(frame_idx).at(i);
283+
284+
total_loss += 0.25 * cv::norm(detected_point - gt_point);
285+
}
286+
287+
frame_idx++;
288+
}
289+
290+
// Calculate the average loss across all frames
291+
double loss = total_loss / detector->_frame_count;
292+
293+
// Check detection rate and loss
294+
double detection_rate = static_cast<double>(detector->_detected_frame) / static_cast<double>(detector->_frame_count);
295+
EXPECT_GE(detection_rate, min_detection_rate);
296+
EXPECT_LT(loss / detector->_frame_count, max_loss_pix);
297+
}
298+
255299
void testVideo(std::string video_path, std::string gt_path, OpenCVArmorDetector *detector, float min_detection_rate, float max_loss_pix)
256300
{
257301
// Read ground truths
@@ -271,64 +315,43 @@ void testVideo(std::string video_path, std::string gt_path, OpenCVArmorDetector
271315

272316
// Read the video frame by frame
273317
cv::Mat frame;
274-
int idx = 0;
275-
float loss = 0;
276318

319+
float total_loss = 0;
320+
int frame_idx = 0;
277321
while (cap.read(frame))
278322
{
279323
cv::resize(frame, frame, cv::Size(WIDTH, HEIGHT));
280324
std::vector<_Float32> points = detector->search(frame);
281325

282326
// Loss between the detected points and the ground truth
283327
// Skip if no armor detected in gt or in the frame (all zeros) since we want to check loss given a detection. Detection rate is accounted for separately
284-
if ((gt.at(idx).at(0) == cv::Point2f(0, 0) && gt.at(idx).at(1) == cv::Point2f(0, 0) && gt.at(idx).at(2) == cv::Point2f(0, 0) && gt.at(idx).at(3) == cv::Point2f(0, 0)) || (points.at(0) == 0 && points.at(1) == 0 && points.at(2) == 0 && points.at(3) == 0 && points.at(4) == 0 && points.at(5) == 0 && points.at(6) == 0 && points.at(7) == 0))
328+
if ((gt.at(frame_idx).at(0) == cv::Point2f(0, 0) && gt.at(frame_idx).at(1) == cv::Point2f(0, 0) && gt.at(frame_idx).at(2) == cv::Point2f(0, 0) && gt.at(frame_idx).at(3) == cv::Point2f(0, 0)) || (points.at(0) == 0 && points.at(1) == 0 && points.at(2) == 0 && points.at(3) == 0 && points.at(4) == 0 && points.at(5) == 0 && points.at(6) == 0 && points.at(7) == 0))
285329
{
286-
idx++;
330+
frame_idx++;
287331
continue;
288332
}
289333

290334
// Loss is the sum of the L2 norm between the detected points and the ground truth
291335
for (int i = 0; i < 4; i++)
292336
{
293-
loss += cv::norm(points.at(i * 2) - gt.at(idx).at(i).x) + cv::norm(points.at(i * 2 + 1) - gt.at(idx).at(i).y);
337+
cv::Point2f detected_point(points.at(i * 2), points.at(i * 2 + 1));
338+
cv::Point2f gt_point = gt.at(frame_idx).at(i);
339+
340+
total_loss += 0.25 * cv::norm(detected_point - gt_point);
294341
}
295342

296-
idx++;
343+
frame_idx++;
297344
}
298345

346+
// Calculate the average loss across all frames
347+
double loss = total_loss / detector->_frame_count;
348+
299349
// Check detection rate and loss
300350
double detection_rate = static_cast<double>(detector->_detected_frame) / static_cast<double>(detector->_frame_count);
301351
EXPECT_GE(detection_rate, min_detection_rate);
302352
EXPECT_LT(loss / detector->_frame_count, max_loss_pix);
303353
}
304354

305-
void iterateThroughFolder(std::string folder_path, OpenCVArmorDetector *detector)
306-
{
307-
namespace fs = std::filesystem;
308-
309-
// Iterate through all .png files in the easy tests folder
310-
for (const auto &entry : fs::directory_iterator(folder_path))
311-
{
312-
if (entry.is_regular_file() && entry.path().extension() == ".png")
313-
{
314-
std::string file_path = entry.path().string();
315-
316-
// Load the image
317-
cv::Mat frame = cv::imread(file_path, cv::IMREAD_COLOR);
318-
if (frame.empty())
319-
{
320-
std::cerr << "Failed to read image: " << file_path << std::endl;
321-
continue;
322-
}
323-
324-
cv::resize(frame, frame, cv::Size(WIDTH, HEIGHT));
325-
326-
// Call the detector's search function
327-
std::vector<_Float32> points = detector->search(frame);
328-
}
329-
}
330-
}
331-
332355
void readGT(std::string file_path, std::vector<std::vector<cv::Point2f>> &gt)
333356
{
334357
std::ifstream file(file_path);

0 commit comments

Comments
 (0)