asj_pe_pullup.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. #include "asj_pe_pullup.h"
  2. #include "utils.h"
  3. // 根据box的x坐标进行排序
  4. static bool compareByX(const Hand_ST& b1, const Hand_ST& b2) {
  5. return b1.object.stRect.x < b2.object.stRect.x;
  6. }
  7. int Asj_PE_Pullup::init()
  8. {
  9. // 一些预设定的参数
  10. handAngle = 160.0; // 双手弯曲角度
  11. legAngle = 90.0; // 膝盖弯曲角度
  12. shouldersRatio = 0.5; // 双肩系数 双手的距离<=双肩的距离*(1+shouldersRatio)
  13. handQueueLength = 20; // 有关手部状态的队列长度
  14. overTime = 10; // 每个引体向上计数时候的超时时间/秒
  15. handKeypointDisThresh = 0.1; // 手部关键点到杠的距离阈值
  16. // 初始化标志位flag
  17. prevFrameStatus = false;
  18. isStraighten = false;
  19. isEyeoverbar = false;
  20. isFinishone = false;
  21. eyeStatus = false;
  22. // 初始化队列长度
  23. prepareStatueQueue.init(50);
  24. eyeStatusQueue.init(5);
  25. handStatusQueue.init(handQueueLength);
  26. leftHandStatusQueue.init(handQueueLength);
  27. rightHandStatusQueue.init(handQueueLength);
  28. // 初始化结果结构体
  29. std::memset(&pullupResult_st, 0, sizeof(PullUpResult_ST));
  30. return StatusCode::SUCCESS;
  31. }
  32. int Asj_PE_Pullup::set(PullUp_ST pullup_s)
  33. {
  34. std::memcpy(&pullup_st, &pullup_s, sizeof(PullUp_ST));
  35. // 初始化杠的参数
  36. leftBar = pullup_s.asjPullUpLine[0];
  37. rightBar = pullup_s.asjPullUpLine[1];
  38. centerBar.x = (leftBar.x + rightBar.x) / 2;
  39. centerBar.y = (leftBar.y + rightBar.y) / 2;
  40. centerBar.score = 0.0;
  41. std::cout << "arm_bend_flag: " << pullup_st.arm_bend_flag << std::endl;
  42. std::cout << "hand_back_flag: " << pullup_st.hand_back_flag << std::endl;
  43. std::cout << "body_straight_flag: " << pullup_st.body_straight_flag << std::endl;
  44. std::cout << "hand_off_bar_flag: " << pullup_st.hand_off_bar_flag << std::endl;
  45. std::cout << "knee_bend_flag: " << pullup_st.knee_bend_flag << std::endl;
  46. std::cout << "exam_interval_sencond: " << pullup_st.exam_interval_sencond << std::endl;
  47. return StatusCode::SUCCESS;
  48. }
  49. int Asj_PE_Pullup::get(PullUp_ST *pullup_s)
  50. {
  51. std::memcpy(pullup_s, &pullup_st, sizeof(PullUp_ST));
  52. return StatusCode::SUCCESS;
  53. }
  54. int Asj_PE_Pullup::examReset()
  55. {
  56. prepareStatueQueue.reset(); // 重置记录准备状态队列
  57. eyeStatusQueue.reset(); // 重置眼部是否过杠的队列状态
  58. handStatusQueue.reset(); // 重置手部队列状态
  59. leftHandStatusQueue.reset(); // 重置左手队列状态
  60. rightHandStatusQueue.reset(); // 重置右手队列状态
  61. // 重新初始化标志位flag
  62. prevFrameStatus = false;
  63. isStraighten = false;
  64. isEyeoverbar = false;
  65. isFinishone = false;
  66. eyeStatus = false;
  67. std::memset(&pullupResult_st, 0, sizeof(PullUpResult_ST)); // 重置相关结果参数
  68. return StatusCode::SUCCESS;
  69. }
  70. void Asj_PE_Pullup::findNearestPeople(sampleRunJoint_RESULT_S *mResults, int &personIndex)
  71. {
  72. float maxDistance = std::numeric_limits<float>::max();
  73. int personIndexTemp = -1;
  74. handBox.clear();
  75. for(int i = 0;i < mResults->nObjectSize;i++){
  76. if (mResults->pstObjectItems[i].labelId == 0)
  77. {
  78. float x = mResults->pstObjectItems[i].stRect.x;
  79. float y = mResults->pstObjectItems[i].stRect.y + mResults->pstObjectItems[i].stRect.h / 2;
  80. float distance = EUCLIDEAN_DISTANCE(centerBar.x, centerBar.y, x, y);
  81. if (maxDistance > distance){
  82. maxDistance = distance;
  83. personIndexTemp = i;
  84. }
  85. }
  86. else if (mResults->pstObjectItems[i].labelId == 1)
  87. {
  88. // 判断手是否与bar相交
  89. if (isLineIntersectRect(leftBar, rightBar, mResults->pstObjectItems[i].stRect))
  90. {
  91. Hand_ST hand;
  92. memset(&hand, 0, sizeof(Hand_ST));
  93. hand.object = mResults->pstObjectItems[i];
  94. hand.idx = i;
  95. handBox.push_back(hand);
  96. }
  97. }
  98. }
  99. personIndex = personIndexTemp;
  100. // 防止手没有识别到的情况,利用手的关键点与杠的y轴距离进行计算
  101. if (personIndex != -1)
  102. {
  103. sampleRunJoint_POINT_S *landmark = mResults->pstObjectItems[personIndex].landmark;
  104. if (handBox.size() != 2)
  105. {
  106. handBox.clear();
  107. if (std::abs(landmark[7].y - leftBar.y) < handKeypointDisThresh)
  108. {
  109. Hand_ST hand;
  110. memset(&hand, 0, sizeof(Hand_ST));
  111. handBox.push_back(hand);
  112. }
  113. if (std::abs(landmark[8].y - leftBar.y) < handKeypointDisThresh)
  114. {
  115. Hand_ST hand;
  116. memset(&hand, 0, sizeof(Hand_ST));
  117. handBox.push_back(hand);
  118. }
  119. }
  120. }
  121. }
  122. int Asj_PE_Pullup::fbHeadDetect(sampleRunJoint_POINT_S *landmark, bool isLeft)
  123. {
  124. if (isLeft)
  125. {
  126. if (landmark[1].x < landmark[5].x)
  127. {
  128. return true;
  129. } else {
  130. return false;
  131. }
  132. }
  133. else
  134. {
  135. if (landmark[1].x > landmark[5].x)
  136. {
  137. return true;
  138. } else {
  139. return false;
  140. }
  141. }
  142. }
  143. bool Asj_PE_Pullup::checkHandIsStraighten(sampleRunJoint_POINT_S *landmark)
  144. {
  145. float left_hand_angle = CALC_ANGLE(landmark[5], landmark[3], landmark[7]);
  146. float right_hand_angle = CALC_ANGLE(landmark[6], landmark[4], landmark[8]);
  147. return (right_hand_angle > handAngle && left_hand_angle > handAngle);
  148. }
  149. bool Asj_PE_Pullup::checkLegIsStraighten(sampleRunJoint_POINT_S *landmark)
  150. {
  151. float left_leg_angle = CALC_ANGLE(landmark[11], landmark[9], landmark[13]);
  152. float right_leg_angle = CALC_ANGLE(landmark[12], landmark[10], landmark[14]);
  153. return (right_leg_angle > handAngle && left_leg_angle > handAngle);
  154. }
  155. bool Asj_PE_Pullup::checkIsBackHand()
  156. {
  157. if (leftHandStatusQueue.real_size() == leftHandStatusQueue.size())
  158. {
  159. if ((float(leftHandStatusQueue.counts_one()) / leftHandStatusQueue.size()) < 0.85)
  160. {
  161. return true;
  162. }
  163. }
  164. if (rightHandStatusQueue.real_size() == rightHandStatusQueue.size())
  165. {
  166. if ((float(rightHandStatusQueue.counts_one()) / rightHandStatusQueue.size()) < 0.85)
  167. {
  168. return true;
  169. }
  170. }
  171. return false;
  172. }
  173. int Asj_PE_Pullup::processPrepareCPP(sampleRunJoint_RESULT_S *mResults, int &prepareStatus)
  174. {
  175. sampleRunJoint_POINT_S *landmark;
  176. int personIndex = -1;
  177. findNearestPeople(mResults, personIndex);
  178. if (personIndex != -1)
  179. {
  180. // 判断双手是否在杠上
  181. if (handBox.size() == 2)
  182. {
  183. // 当前帧正反手识别
  184. if (pullup_st.hand_back_flag)
  185. {
  186. std::sort(handBox.begin(), handBox.end(), compareByX);
  187. rightHandStatusQueue.push(int(fbHeadDetect(handBox[0].object.landmark, false)));
  188. leftHandStatusQueue.push(int(fbHeadDetect(handBox[1].object.landmark, true)));
  189. }
  190. // 获取这个离杠最近的人的关键点
  191. landmark = mResults->pstObjectItems[personIndex].landmark;
  192. // 判断手臂是否伸直
  193. if(pullup_st.arm_bend_flag && !checkHandIsStraighten(landmark))
  194. {
  195. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_HANDSNOTSTRAIGHTEN);
  196. }
  197. // 判断腿部是否伸直
  198. if(pullup_st.knee_bend_flag && !checkLegIsStraighten(landmark))
  199. {
  200. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_KNEENOTSTRAIGHTEN);
  201. }
  202. // 判断双手距离是否少于双肩距离*(1+shouldersRatio)
  203. if (EUCLIDEAN_DISTANCE(landmark[7].x, landmark[7].y, landmark[8].x, landmark[8].y) >
  204. (EUCLIDEAN_DISTANCE(landmark[3].x, landmark[3].y, landmark[4].x, landmark[4].y) * (1 + shouldersRatio)))
  205. {
  206. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_HANDSWIDERTHANSHOULDERS);
  207. }
  208. // 判断是否正反手
  209. if (pullup_st.hand_back_flag && checkIsBackHand())
  210. {
  211. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_BACKHAND);
  212. }
  213. }
  214. else
  215. {
  216. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_HANDSNOTONBAR);
  217. }
  218. }
  219. else
  220. {
  221. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_NOHUMAN);
  222. }
  223. if (prepareStatus == 0)
  224. {
  225. prepareStatueQueue.push(1);
  226. }
  227. else
  228. {
  229. prepareStatueQueue.push(0);
  230. }
  231. // 判断是否充满队列
  232. if (prepareStatueQueue.real_size() == prepareStatueQueue.size())
  233. {
  234. // 符合准备动作次数充满队列 (不检测正反手 或者 检测正反手且不是反手)
  235. if (prepareStatueQueue.counts_one() == prepareStatueQueue.size() && (!pullup_st.hand_back_flag || (pullup_st.hand_back_flag && !checkIsBackHand())))
  236. {
  237. prepareStatus |= (1 << PullUpPrepareCode::PULLUP_PREPARE_SUCCESS);
  238. }
  239. }
  240. return StatusCode::SUCCESS;
  241. }
  242. extern "C"{
  243. int Asj_PE_Pullup::processPrepare(sampleRunJoint_RESULT_S *mResults, int *prepareStatus)
  244. {
  245. int ret = StatusCode::SUCCESS;
  246. try{
  247. ret = processPrepareCPP(mResults, *prepareStatus);
  248. } catch (const std::exception& e) {
  249. printf("[%s Line:%d] Error:%s\n", __FUNCTION__, __LINE__, e.what());
  250. ret = StatusCode::FAILED;
  251. }
  252. return ret;
  253. }
  254. }
  255. int Asj_PE_Pullup::processExamCPP(sampleRunJoint_RESULT_S *mResults, PullUpResult_ST &result)
  256. {
  257. sampleRunJoint_POINT_S *landmark;
  258. int personIndex = -1;
  259. findNearestPeople(mResults, personIndex);
  260. // 存放手部状态
  261. if (handBox.size() == 2)
  262. {
  263. handStatusQueue.push(1);
  264. }
  265. else
  266. {
  267. handStatusQueue.push(0);
  268. }
  269. // 持续数帧检测不到双手在杠上(双手落杠检测开启的情况下)
  270. if (pullup_st.hand_off_bar_flag && handStatusQueue.real_size() == handStatusQueue.size() && handStatusQueue.real_size() == handStatusQueue.counts_zero())
  271. {
  272. pullupResult_st.pullUp_HandDropBar += 1;
  273. }
  274. // 判断是否有人(离杠最近)出现在画面 且 双手在杠上
  275. if (personIndex != -1 && handBox.size() == 2)
  276. // if (personIndex != -1)
  277. {
  278. // 当前帧正反手识别
  279. if (pullup_st.hand_back_flag)
  280. {
  281. std::sort(handBox.begin(), handBox.end(), compareByX);
  282. rightHandStatusQueue.push(int(fbHeadDetect(handBox[0].object.landmark, false)));
  283. leftHandStatusQueue.push(int(fbHeadDetect(handBox[1].object.landmark, true)));
  284. }
  285. // 获取这个离杠最近的人的关键点
  286. landmark = mResults->pstObjectItems[personIndex].landmark;
  287. printf("[%s Line:%d] -----------left:%.3f right:%.3f\n", __FUNCTION__, __LINE__, std::abs(landmark[7].y - leftBar.y), std::abs(landmark[8].y - leftBar.y));
  288. // 判断眼睛是否过杠
  289. isEyeoverbar = ((POINT_ON_SIDE(leftBar, rightBar, landmark[1]) >= 0) && (POINT_ON_SIDE(leftBar, rightBar, landmark[2]) >= 0));
  290. if (isEyeoverbar && !eyeStatus)
  291. {
  292. // 记录眼睛曾过杠
  293. eyeStatus = true;
  294. // 刷新记录眼睛是否过杠的队列
  295. eyeStatusQueue.reset();
  296. }
  297. // 把当前眼睛是否过杠的状态push到队列里
  298. eyeStatusQueue.push(int(isEyeoverbar));
  299. // 判断下颜是否过杠,这里的下颜是实际的下颜与双眼中点的中点坐标.
  300. sampleRunJoint_POINT_S faceCenter = calculateCentroid(landmark[0], calculateCentroid(landmark[1], landmark[2]));
  301. bool faceOnSide = POINT_ON_SIDE(leftBar, rightBar, faceCenter) >= 0;
  302. // 前一帧没过杠 且 目前这帧过杠 且 本次下颜没有过杠
  303. if (!prevFrameStatus && faceOnSide && !isFinishone)
  304. {
  305. // 手臂没有伸直
  306. if (pullup_st.arm_bend_flag && !isStraighten)
  307. {
  308. pullupResult_st.pullUp_HandNotStraighten += 1;
  309. pullupResult_st.pullup_illegal_flag |= (1 << PullUpExamCode::PULLUP_EXAM_HANDNOTSTRAIGHTEN);
  310. }
  311. // 反手
  312. else if (pullup_st.hand_back_flag && checkIsBackHand())
  313. {
  314. pullupResult_st.pullUp_BackHand += 1;
  315. pullupResult_st.pullup_illegal_flag |= (1 << PullUpExamCode::PULLUP_EXAM_BACKHAND);
  316. }
  317. // 满足条件
  318. else
  319. {
  320. gettimeofday(&nowTime, NULL);
  321. if (pullup_st.exam_interval_sencond && nowTime.tv_sec - preFinishTime.tv_sec > overTime)
  322. {
  323. pullupResult_st.pullup_OverTime += 1;
  324. pullupResult_st.pullup_illegal_flag |= (1 << PullUpExamCode::PULLUP_EXAM_OVERTIME);
  325. }
  326. pullupResult_st.pullUpCount += 1; // TOOD 超时的时候算不算当前这个
  327. // 记录时间
  328. preFinishTime = nowTime;
  329. }
  330. // 记录本次是下颜已经过杠
  331. isFinishone = true;
  332. // 重置手臂伸直标志位
  333. isStraighten = false;
  334. }
  335. // 眼部曾过杠的情况下(eyeStatus用于刷新眼部状态是否曾高于杠) 且 已经持续一定帧数眼部没有过杠 且 下颜没有过杠
  336. else if (eyeStatus && eyeStatusQueue.counts_zero() == eyeStatusQueue.size() && !isFinishone)
  337. {
  338. pullupResult_st.pullUp_NotOverBar += 1;
  339. pullupResult_st.pullup_illegal_flag |= (1 << PullUpExamCode::PULLUP_EXAM_NOTOVERBAR);
  340. eyeStatus = false;
  341. eyeStatusQueue.reset();
  342. isStraighten = false;
  343. }
  344. // 手臂没伸直
  345. if (!isStraighten)
  346. {
  347. // 满足条件
  348. if (checkHandIsStraighten(landmark))
  349. {
  350. isStraighten = true;
  351. }
  352. }
  353. // 眼部没过杠
  354. if (!isEyeoverbar)
  355. {
  356. if (isFinishone)
  357. {
  358. eyeStatus = false;
  359. }
  360. isFinishone = false;
  361. }
  362. // 记录前一帧的状态
  363. prevFrameStatus = faceOnSide;
  364. }
  365. std::memcpy(&result, &pullupResult_st, sizeof(PullUpResult_ST));
  366. return StatusCode::SUCCESS;
  367. }
  368. extern "C"{
  369. int Asj_PE_Pullup::processExam(sampleRunJoint_RESULT_S *mResults, PullUpResult_ST *result)
  370. {
  371. int ret = StatusCode::SUCCESS;
  372. try{
  373. ret = processExamCPP(mResults, *result);
  374. } catch (const std::exception& e) {
  375. printf("[%s Line:%d] Error:%s\n", __FUNCTION__, __LINE__, e.what());
  376. ret = StatusCode::FAILED;
  377. }
  378. return ret;
  379. }
  380. }