16
16
static_assert (__OS__ == __OS_Linux__);
17
17
18
18
19
- #define TIME 0
20
- #define DRAW 0
19
+ #define TIME 0 // 是否启用时间调试
20
+ #define DRAW 0 // 是否启用绘图调试
21
21
22
22
#if TIME
23
23
#define REC_T (t ) const auto ___time_rec___##t = cv::getTickCount()
@@ -40,23 +40,24 @@ std::string createWindow(const std::string &name) {
40
40
#endif
41
41
42
42
43
+ // / 处理器, 包含NUEDC 2023 E题所有视觉处理的功能
43
44
class Handler {
44
- #define PAIR_MAT (name ) cv::Mat c_##name;cv::cuda::GpuMat g_##name
45
- #define PM_MALLOC (name ) memMapPool.malloc(c_##name, g_##name, src.size(), src.type())
45
+ #define PAIR_MAT (name ) cv::Mat c_##name;cv::cuda::GpuMat g_##name \
46
+ // / 声明一对儿cpu/gpu mat
47
+ #define PM_MALLOC (name ) memMapPool.malloc(c_##name, g_##name, src.size(), src.type()) \
48
+ // / 使用内存映射池为cpu/gpu mat构建映射关系
46
49
public:
47
50
cv::Point2d green, red;// 激光位置
48
51
std::vector<cv::Point > path;// 路径
49
52
private:
50
- int step = -1 ;
51
- cv::Point2d end;
52
- serial::Serial *serial = nullptr ;
53
+ serial::Serial *serial;
53
54
ifr::pkg::Move pkg_move;
54
55
55
- const double min_distance = std::pow(20.0 , 2 );
56
56
57
-
58
- cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size (10 , 10 ));
59
- cv::Ptr <cv::cuda::Filter> filter = cv::cuda::createMorphologyFilter(cv::MORPH_OPEN, CV_8UC1, kernel);
57
+ cv::Mat kernel1 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size (10 , 10 ));
58
+ cv::Ptr <cv::cuda::Filter> filter1 = cv::cuda::createMorphologyFilter(cv::MORPH_OPEN, CV_8UC1, kernel1);
59
+ cv::Mat kernel2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size (3 , 3 ));
60
+ cv::Ptr <cv::cuda::Filter> filter2 = cv::cuda::createMorphologyFilter(cv::MORPH_DILATE, CV_8UC1, kernel2);
60
61
private:
61
62
#if DRAW
62
63
@@ -101,7 +102,7 @@ class Handler {
101
102
REC_T (1 );
102
103
cv::threshold (c_gray, c_otsu, 30 , 255 , cv::THRESH_OTSU);
103
104
REC_T (2 );
104
- filter ->apply (g_otsu, g_bin);
105
+ filter1 ->apply (g_otsu, g_bin);
105
106
106
107
107
108
REC_T (3 );
@@ -205,19 +206,21 @@ class Handler {
205
206
PAIR_MAT (hsv);
206
207
PAIR_MAT (HSV[3 ]);
207
208
PAIR_MAT (pointer);
209
+ cv::cuda::GpuMat g_pointer_raw;
208
210
209
211
inline auto findPointer (rm_armor_finder::MemMapPool &memMapPool, const cv::Mat &src, const cv::cuda::GpuMat &gpu) {
210
212
PM_MALLOC (hsv);
211
213
#pragma unroll
212
214
for (int i = 0 ; i < 3 ; i++)
213
215
PM_MALLOC (HSV[i]);
214
216
PM_MALLOC (pointer);
217
+ if (g_pointer_raw.empty ())g_pointer_raw.create (src.size (), CV_8UC1);
215
218
216
219
cv::cuda::cvtColor (gpu, g_hsv, cv::COLOR_BGR2HSV);
217
220
cv::cuda::split (g_hsv, g_HSV);
218
221
219
- ifr::cuda::findPointer (g_HSV[2 ], has_bin ? &g_bin : nullptr , g_pointer );
220
-
222
+ ifr::cuda::findPointer (g_HSV[2 ], has_bin ? &g_bin : nullptr , g_pointer_raw );
223
+ filter2-> apply (g_pointer_raw, g_pointer);
221
224
222
225
std::vector<std::vector<cv::Point >> contours;
223
226
cv::findContours (c_pointer, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
@@ -232,7 +235,7 @@ class Handler {
232
235
double size_green = 0 , size_red = 0 ;
233
236
for (const auto &contour: contours) {
234
237
double area = cv::contourArea (contour);
235
- if (area > 300 || area < 5 )continue ;
238
+ if (area > 500 || area < 5 )continue ;
236
239
cv::Rect rect = cv::boundingRect (contour);
237
240
238
241
double aspectRatio = static_cast <double >(rect.width ) / rect.height ;
@@ -321,54 +324,137 @@ class Handler {
321
324
PRINT_T (1 , 5 );
322
325
}
323
326
324
- cv::Point2d move_step () {
325
- if (red.x < 0 || red.y < 0 )return {0 , 0 };
327
+ private:
328
+ int step = -1 ;// /< red当前目标
329
+ cv::Point2d end;// /< red最终位置
330
+ const double min_distance = 20.0 ; // /< red到达判定
331
+ const double min_distance2 = std::pow(min_distance, 2 ); // /< red到达判定
332
+ private:
333
+
334
+ /* *
335
+ * @brief 计算移动向量
336
+ * @param go 目标点
337
+ * @param now 当前点
338
+ * @param mask 可用路径遮罩
339
+ * @param[out] finish 是否完成
340
+ */
341
+ inline auto calcMove (cv::Point2d go, const cv::Point2d &now, const cv::Mat &mask, bool &finish) const {
342
+ static constexpr const auto r90 = 90 * CV_PI / 180.0 ;
343
+ go -= now;
344
+ if ((finish = (go.ddot (go) < min_distance2)))return go;
345
+ go /= cv::norm (go);
346
+ cv::Rect rect{0 , 0 , mask.cols , mask.rows };
347
+ if (!now.inside (rect))return go * min_distance; // 不太可能
348
+
349
+ const double angle = std::atan2 (go.y - now.y , go.x - now.x ) + r90;
350
+ const auto in_path = mask.at <uchar>(int (now.y ), int (now.x ));
351
+ const cv::Point2d detla{std::cos (angle), std::sin (angle)};
352
+
353
+ int dis_l = -1 , dis_r = -1 ;
354
+ for (int i = 0 , max_val = mask.size ().area (); i < max_val; i++) {// 从now开始向垂直于前进方向两侧寻找第一个不同点
355
+ if (dis_l < 0 ) {
356
+ auto p1 = now - (i * detla);
357
+ if (p1.inside (rect) && mask.at <uchar>(int (p1.y ), int (p1.x )) != in_path) {
358
+ dis_l = i;
359
+ if (!in_path)break ;// now 本身在黑条上
360
+ }
361
+ }
362
+ if (dis_r < 0 ) {
363
+ auto p2 = now + (i * detla);
364
+ if (p2.inside (rect) && mask.at <uchar>(int (p2.y ), int (p2.x )) != in_path) {
365
+ dis_r = i;
366
+ if (!in_path)break ;// now 本身在黑条上
367
+ }
368
+ }
369
+ if (dis_l >= 0 && dis_r >= 0 )break ;
370
+ }
371
+
372
+ if (in_path) {// 在路径中, 以小纠正得到结果
373
+ if (dis_l >= 0 && dis_r >= 0 ) {
374
+ auto fix = (dis_l < dis_r ? 1 : -1 ) * detla;
375
+ go = go * 4 + fix;
376
+ }
377
+ } else {// 不在路径中, 以大偏移量最快返回
378
+ if (dis_l >= 0 || dis_r >= 0 ) {
379
+ static constexpr const auto max_val = std::numeric_limits<int >::max ();
380
+ if (dis_l < 0 )dis_l = max_val;
381
+ if (dis_r < 0 )dis_r = max_val;
382
+ auto fix = (dis_l < dis_r ? -1 : 1 ) * detla;
383
+ go = fix * 2 + go;
384
+ }
385
+ }
386
+ go /= cv::norm (go) * min_distance;
387
+ return go;
388
+ }
389
+
390
+ // / red 帮助函数
391
+ // / 尝试计算到目标点的距离, 并自动更新step
392
+ cv::Point2d move_step (bool &finish) {
326
393
if (step < 0 ) {
327
394
end = red;
328
395
step = 0 ;
329
396
}
330
- while (step < path.size ()) {
397
+ if (step < path.size ()) {
398
+ #if 0
399
+ cv::Point2d go = calcMove(path[step], red, c_bin, finish);
400
+ #else
331
401
cv::Point2d go = path[step];
332
402
go -= red;
333
- if (go.ddot (go) < min_distance)
334
- step++;
335
- else
336
- return go;
403
+ go = -go;
404
+ finish = go.ddot (go) < min_distance2;
405
+ #endif
406
+ if (finish) step++;
407
+ finish = false ;// 单步完成不是全部完成
408
+ return go;
337
409
}
338
- return end - red;
410
+ auto go = end - red;
411
+ finish = go.ddot (go) < min_distance;
412
+ return go;
339
413
}
340
414
415
+ // / green 帮助函数
341
416
CV_NODISCARD_STD cv::Point2d follow () const {
342
417
if (red.x < 0 || red.y < 0 || green.x < 0 || green.y < 0 )return {};
343
418
return green - red;
344
419
}
345
420
421
+ public:
422
+ // / @brief 执行一次绿色激光笔的代码逻辑: 计算移动+设置数据
346
423
void do_green () {
347
424
auto go = follow ();
348
425
pkg_move.set (go.x , go.y );
349
426
IFR_LOG_STREAM (" DO" , go << " , red = " << red << " , green = " << green << " , path = " << path.size ());
350
- serial->writeT (pkg_move);
351
427
}
352
428
429
+ // / @brief 执行一次红色激光笔的代码逻辑: 计算移动+串口发送
353
430
bool do_red () {
354
431
cv::Point2d go;
355
432
bool finish = false ;
356
433
if (red.x < 0 || red.y < 0 ) {
357
434
} else {
358
- go = move_step ();
359
- finish = go.ddot (go) < min_distance;
360
- if (finish)step = -1 ;
435
+ go = move_step (finish);
436
+ if (finish) {
437
+ step = -1 ;
438
+ go = {0 , 0 };
439
+ }
361
440
}
362
441
pkg_move.set (go.x , go.y );
363
- serial->writeT (pkg_move);
364
442
IFR_LOG_STREAM (" DO" ,
365
443
go << " , red = " << red << " , green = " << green << " , path = " << path.size () << " , finish = "
366
444
<< (finish ? " true" : " false" ) << " , step = " << step);
367
445
return finish;
368
446
}
369
447
448
+ void sendMove () {
449
+ serial->writeT (pkg_move);
450
+ }
451
+
452
+ void resetMove () {
453
+ pkg_move.set (0 , 0 );
454
+ }
455
+
370
456
private:
371
- std::string read_buf;
457
+ std::string read_buf;// /串口缓冲
372
458
public:
373
459
/* *
374
460
* 尝试读取字符串, 并将maps中对应值设为true
@@ -392,12 +478,13 @@ class Handler {
392
478
}
393
479
}
394
480
}
395
- if (read_buf.size () > 256 )read_buf = " " ;
481
+ if (read_buf.size () > 256 )read_buf = " " ;// 过长保护
396
482
}
397
483
}
398
484
};
399
485
400
-
486
+ // / @brief 执行完整代码逻辑
487
+ // / @details 建立 相机、串口、处理器 之间的桥接
401
488
void run () {
402
489
ifr::Camera camera (2000 , false , 100 );
403
490
camera.initCamera ();
@@ -411,6 +498,8 @@ void run() {
411
498
{" 2_9kitey3yzpd" , &is_red},
412
499
{" 3_yp4lmg19kbc" , &is_stop},
413
500
};
501
+
502
+ int64 lstTick = 0 , now;
414
503
while (true ) {
415
504
PGX_FRAME_BUFFER pFrameBuffer;
416
505
@@ -424,19 +513,23 @@ void run() {
424
513
handler.handler (camera.memMapPool , src, gpu_src, true );
425
514
426
515
handler.tryRead (keyword);
427
- if ((is_red && is_green) || is_stop)is_red = is_green = is_stop = false ;
516
+ if ((is_red && is_green) || is_stop) {
517
+ handler.resetMove ();
518
+ is_red = is_green = is_stop = false ;
519
+ }
520
+ is_red = true ;
428
521
if (is_red) {
429
522
if (handler.do_red ())is_red = false ;
430
523
}
431
524
if (is_green) {
432
525
handler.do_green ();
433
526
}
434
- // IFR_LOG_STREAM("Main", handler.path);
435
527
}
436
528
IFR_GX_CHECK (GXQBuf (camera.m_hDevice , pFrameBuffer));
437
529
438
- SLEEP (SLEEP_TIME (10.0 / 1000 ));
439
- // cv::waitKey(10);
530
+ while (double ((now = cv::getTickCount ()) - lstTick) / cv::getTickFrequency () * 1000 < 10.0 );
531
+ handler.sendMove ();
532
+ lstTick = now;
440
533
}
441
534
}
442
535
0 commit comments