├── LICENSE ├── README.md ├── ReadMe ├── 1-脚本效果展示 │ ├── 效果演示-2020-03-24_15.16.08.mp4 │ ├── 演示效果简介.odt │ └── 简介.odt ├── 2-先验条件和电脑环境要求 │ ├── 先验条件和电脑环境要求.odt │ └── 文档中提到的视频 │ │ ├── 关键点顺序.jpg │ │ ├── 官网动图.mp4 │ │ ├── 相关总结(可以直接跳过不看).docx │ │ └── 网络理论简述.mp4 └── 3-仰卧起坐模型 │ ├── 仰卧起做模型.odt │ ├── 关键点视频及其数据 │ ├── key_point.json │ ├── 关键点.mp4 │ └── 原视频.mp4 │ └── 相关解释.odt ├── cut.mp4 ├── demo_situp.py ├── model ├── Base-Keypoint-RCNN-FPN.yaml ├── Base-RCNN-FPN.yaml └── keypoint_rcnn_R_101_FPN_3x.yaml ├── predictor_situp.py └── 写给曾老师.odt /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # predictor_situp 2 | 基于Detectron2的仰卧起坐预测 3 | 4 | [demo](http://player.bilibili.com/player.html?aid=370262057&bvid=BV14Z4y1x7mp&cid=178694480&page=1) 5 | 6 | 运行环境配置说明:[即Detectron2的环境配置](https://github.com/facebookresearch/detectron2/blob/master/INSTALL.md) 7 | 8 | > 程序思路:请阅读 ReadMe 文件夹 9 | 10 | Run: python3 demo_situp.py --video-input cut.mp4 ( Linux ) 11 | 12 | issue: 13 | 1. AssertionError: Checkpoint model/keypoint_rcnn_R_101_FPN_3x.pkl not found! 14 | 15 | 链接: https://pan.baidu.com/s/1CIjF-GHqCk6FGW-ABxEzYA 提取码: d5uf 下载放置:model/ 16 | 17 | 2. AssertionError: Torch not compiled with CUDA enabled 18 | 19 | 解决办法:运行时 添加参数: --opts MODEL.DEVICE cpu 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ReadMe/1-脚本效果展示/效果演示-2020-03-24_15.16.08.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/1-脚本效果展示/效果演示-2020-03-24_15.16.08.mp4 -------------------------------------------------------------------------------- /ReadMe/1-脚本效果展示/演示效果简介.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/1-脚本效果展示/演示效果简介.odt -------------------------------------------------------------------------------- /ReadMe/1-脚本效果展示/简介.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/1-脚本效果展示/简介.odt -------------------------------------------------------------------------------- /ReadMe/2-先验条件和电脑环境要求/先验条件和电脑环境要求.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/2-先验条件和电脑环境要求/先验条件和电脑环境要求.odt -------------------------------------------------------------------------------- /ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/关键点顺序.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/关键点顺序.jpg -------------------------------------------------------------------------------- /ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/官网动图.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/官网动图.mp4 -------------------------------------------------------------------------------- /ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/相关总结(可以直接跳过不看).docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/相关总结(可以直接跳过不看).docx -------------------------------------------------------------------------------- /ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/网络理论简述.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/2-先验条件和电脑环境要求/文档中提到的视频/网络理论简述.mp4 -------------------------------------------------------------------------------- /ReadMe/3-仰卧起坐模型/仰卧起做模型.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/3-仰卧起坐模型/仰卧起做模型.odt -------------------------------------------------------------------------------- /ReadMe/3-仰卧起坐模型/关键点视频及其数据/关键点.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/3-仰卧起坐模型/关键点视频及其数据/关键点.mp4 -------------------------------------------------------------------------------- /ReadMe/3-仰卧起坐模型/关键点视频及其数据/原视频.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/3-仰卧起坐模型/关键点视频及其数据/原视频.mp4 -------------------------------------------------------------------------------- /ReadMe/3-仰卧起坐模型/相关解释.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/ReadMe/3-仰卧起坐模型/相关解释.odt -------------------------------------------------------------------------------- /cut.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/cut.mp4 -------------------------------------------------------------------------------- /demo_situp.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | import argparse 3 | import glob 4 | import multiprocessing as mp 5 | import os 6 | import time 7 | import cv2 8 | import tqdm 9 | 10 | from detectron2.config import get_cfg 11 | from detectron2.data.detection_utils import read_image 12 | from detectron2.utils.logger import setup_logger 13 | 14 | from predictor_situp import VisualizationDemo 15 | 16 | # constants 17 | WINDOW_NAME = "COCO detections" 18 | 19 | 20 | def setup_cfg(args): 21 | # load config from file and command-line arguments 22 | cfg = get_cfg() 23 | cfg.merge_from_file(args.config_file) 24 | cfg.merge_from_list(args.opts) 25 | # Set score_threshold for builtin models 26 | cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold 27 | cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold 28 | cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold 29 | cfg.freeze() 30 | return cfg 31 | 32 | 33 | def get_parser(): 34 | parser = argparse.ArgumentParser(description="Detectron2 Demo") 35 | parser.add_argument( 36 | "--config-file", 37 | default="model/keypoint_rcnn_R_101_FPN_3x.yaml", 38 | metavar="FILE", 39 | help="path to config file", 40 | ) 41 | parser.add_argument("--webcam", action="store_true", help="Take inputs from webcam.") 42 | parser.add_argument("--video-input", help="Path to video file.") 43 | parser.add_argument("--input", nargs="+", help="A list of space separated input images") 44 | parser.add_argument( 45 | "--output", 46 | help="A file or directory to save output visualizations. " 47 | "If not given, will show output in an OpenCV window.", 48 | ) 49 | 50 | parser.add_argument( 51 | "--confidence-threshold", 52 | type=float, 53 | default=0.5, 54 | help="Minimum score for instance predictions to be shown", 55 | ) 56 | parser.add_argument( 57 | "--opts", 58 | help="Modify config options using the command-line 'KEY VALUE' pairs", 59 | default=[], 60 | nargs=argparse.REMAINDER, 61 | ) 62 | return parser 63 | 64 | 65 | def continue_frame_judge(store_list,self_cfg): 66 | up_poll = 0 67 | sit_poll = 0 68 | judge=[0,0] 69 | # print(store_list[-3:-1],store_list) 70 | if len(store_list) > self_cfg["judge_fps"]: 71 | start_num = -1-self_cfg["judge_fps"] 72 | #判断 73 | for per_list in store_list[start_num:-1]: 74 | print(per_list) 75 | up_poll += sum(per_list[0]) 76 | sit_poll += sum(per_list[1]) 77 | 78 | if sit_poll > self_cfg["sit_poll"]: 79 | judge[1] = 1 80 | if up_poll > self_cfg["up_poll"]: 81 | judge[0]= 1 82 | else: 83 | judge = [0, 0] 84 | return judge 85 | 86 | def continue_frame(self_cfg,resulte_storage,continue_frame_resulte_list): 87 | resulte_storage.append(continue_frame_resulte_list) 88 | resulte = continue_frame_judge(resulte_storage,self_cfg) 89 | 90 | return resulte 91 | 92 | def count_situp(statistic_state_resulte, cycle_state, count): 93 | if cycle_state == 0: 94 | is_count = 0 95 | if is_count == 0: 96 | if statistic_state_resulte[0] == 1: 97 | count += 1 98 | cycle_state = 1 99 | is_count = 1 100 | if cycle_state == 1: 101 | if statistic_state_resulte[1] == 1: 102 | cycle_state = 0 103 | 104 | print(cycle_state) 105 | return count, cycle_state 106 | 107 | 108 | if __name__ == "__main__": 109 | situp_count = 0 110 | cycle_state = 1 111 | resulte_storage = [] 112 | self_cfg = { "judge_fps":5,"up_poll":20,"sit_poll":6} 113 | mp.set_start_method("spawn", force=True) 114 | args = get_parser().parse_args() 115 | logger = setup_logger() 116 | logger.info("Arguments: " + str(args)) 117 | 118 | cfg = setup_cfg(args) 119 | 120 | demo = VisualizationDemo(cfg) 121 | 122 | 123 | if args.video_input: 124 | video = cv2.VideoCapture(args.video_input) 125 | width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) 126 | height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) 127 | frames_per_second = video.get(cv2.CAP_PROP_FPS) 128 | print(width) 129 | print(height) 130 | print(frames_per_second) 131 | num_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) 132 | basename = os.path.basename(args.video_input) 133 | basename = "vedio" 134 | 135 | if args.output: 136 | if os.path.isdir(args.output): 137 | output_fname = os.path.join(args.output, basename) 138 | output_fname = os.path.splitext(output_fname)[0] + ".mp4" 139 | else: 140 | output_fname = args.output 141 | assert not os.path.isfile(output_fname), output_fname 142 | output_file = cv2.VideoWriter( 143 | filename=output_fname, 144 | # some installation of opencv may not support x264 (due to its license), 145 | # you can try other format (e.g. MPEG) 146 | fourcc=cv2.VideoWriter_fourcc(*"MPEG"), 147 | fps=float(frames_per_second), 148 | frameSize=(width, height), 149 | isColor=True, 150 | ) 151 | assert os.path.isfile(args.video_input) 152 | 153 | calculate_dictionary = { 154 | "angle":[[11,15,13],[12,16,14], [6,14,12], [5,13,11]], 155 | "distance":[[1,15],[2,16]], 156 | "judge_ankle_angle":[[12,16,14],[11,15,13]], 157 | "judge_butt_angle":[ [6,14,12], [5,13,11] ], 158 | "judge_distance_ratio":[[16,14,14,2],[15,13,13,1]], 159 | "require_ankle":{"need":"<","angle":[100,90]}, 160 | "require_butt_up":{"need":"<","angle":[110,110]}, 161 | "require_butt_down":{"need":">","angle":[150,150]}, 162 | "require_distance_ratio":{"need":">","distance":[0.5,0.6]} 163 | } 164 | # log_f = open("digital","w+") 165 | # log_f.close() 166 | 167 | for ordinal_num, resulte_dictionary in enumerate(tqdm.tqdm(demo.run_on_video(video,calculate_dictionary), total=num_frames)): 168 | vis_frame = resulte_dictionary["vis_frame"] 169 | 170 | if(resulte_dictionary["max_inform_keypoint"]!= None): 171 | continue_frame_resulte_list = resulte_dictionary["resulte"] 172 | # print(continue_frame_resulte_list) 173 | statistic_state_resulte = continue_frame(self_cfg,resulte_storage,continue_frame_resulte_list) 174 | 175 | situp_count , cycle_state= count_situp(statistic_state_resulte, cycle_state, situp_count) 176 | 177 | # 显示 178 | up_result = str(statistic_state_resulte[0]) 179 | sit_result = str(statistic_state_resulte[1]) 180 | vis_frame = vis_frame.astype('uint8') 181 | vis_frame = cv2.putText( 182 | vis_frame, "up:"+up_result, (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 2 183 | ) 184 | vis_frame = cv2.putText( 185 | vis_frame, "down:"+sit_result, (50, 120), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 2 186 | ) 187 | vis_frame = cv2.putText( 188 | vis_frame, "count:"+str(situp_count), (50, 190), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 2 189 | ) 190 | 191 | if args.output: 192 | output_file.write(vis_frame) 193 | else: 194 | cv2.namedWindow(basename, cv2.WINDOW_NORMAL) 195 | cv2.imshow(basename, vis_frame) 196 | if cv2.waitKey(1) == 27: 197 | break # esc to quit 198 | # cv2.waitKey(0) 199 | video.release() 200 | if args.output: 201 | output_file.release() 202 | else: 203 | cv2.destroyAllWindows() 204 | -------------------------------------------------------------------------------- /model/Base-Keypoint-RCNN-FPN.yaml: -------------------------------------------------------------------------------- 1 | _BASE_: "Base-RCNN-FPN.yaml" 2 | MODEL: 3 | KEYPOINT_ON: True 4 | ROI_HEADS: 5 | NUM_CLASSES: 1 6 | ROI_BOX_HEAD: 7 | SMOOTH_L1_BETA: 0.5 # Keypoint AP degrades (though box AP improves) when using plain L1 loss 8 | RPN: 9 | # Detectron1 uses 2000 proposals per-batch, but this option is per-image in detectron2. 10 | # 1000 proposals per-image is found to hurt box AP. 11 | # Therefore we increase it to 1500 per-image. 12 | POST_NMS_TOPK_TRAIN: 1500 13 | DATASETS: 14 | TRAIN: ("keypoints_coco_2017_train",) 15 | TEST: ("keypoints_coco_2017_val",) 16 | -------------------------------------------------------------------------------- /model/Base-RCNN-FPN.yaml: -------------------------------------------------------------------------------- 1 | MODEL: 2 | META_ARCHITECTURE: "GeneralizedRCNN" 3 | BACKBONE: 4 | NAME: "build_resnet_fpn_backbone" 5 | RESNETS: 6 | OUT_FEATURES: ["res2", "res3", "res4", "res5"] 7 | FPN: 8 | IN_FEATURES: ["res2", "res3", "res4", "res5"] 9 | ANCHOR_GENERATOR: 10 | SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map 11 | ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) 12 | RPN: 13 | IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] 14 | PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level 15 | PRE_NMS_TOPK_TEST: 1000 # Per FPN level 16 | # Detectron1 uses 2000 proposals per-batch, 17 | # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) 18 | # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. 19 | POST_NMS_TOPK_TRAIN: 1000 20 | POST_NMS_TOPK_TEST: 1000 21 | ROI_HEADS: 22 | NAME: "StandardROIHeads" 23 | IN_FEATURES: ["p2", "p3", "p4", "p5"] 24 | ROI_BOX_HEAD: 25 | NAME: "FastRCNNConvFCHead" 26 | NUM_FC: 2 27 | POOLER_RESOLUTION: 7 28 | ROI_MASK_HEAD: 29 | NAME: "MaskRCNNConvUpsampleHead" 30 | NUM_CONV: 4 31 | POOLER_RESOLUTION: 14 32 | DATASETS: 33 | TRAIN: ("coco_2017_train",) 34 | TEST: ("coco_2017_val",) 35 | SOLVER: 36 | IMS_PER_BATCH: 16 37 | BASE_LR: 0.02 38 | STEPS: (60000, 80000) 39 | MAX_ITER: 90000 40 | INPUT: 41 | MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) 42 | -------------------------------------------------------------------------------- /model/keypoint_rcnn_R_101_FPN_3x.yaml: -------------------------------------------------------------------------------- 1 | _BASE_: "Base-Keypoint-RCNN-FPN.yaml" 2 | MODEL: 3 | WEIGHTS: "model/keypoint_rcnn_R_101_FPN_3x.pkl" 4 | RESNETS: 5 | DEPTH: 101 6 | INPUT: 7 | MIN_SIZE_TEST: 800 8 | SOLVER: 9 | STEPS: (210000, 250000) 10 | MAX_ITER: 270000 11 | -------------------------------------------------------------------------------- /predictor_situp.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved 2 | import atexit 3 | import bisect 4 | import multiprocessing as mp 5 | from collections import deque 6 | import cv2 7 | import torch 8 | import math 9 | import numpy as np 10 | import matplotlib as mpl 11 | 12 | from detectron2.data import MetadataCatalog 13 | from detectron2.engine.defaults import DefaultPredictor 14 | from detectron2.utils.video_visualizer import VideoVisualizer 15 | from detectron2.utils.visualizer import ColorMode, Visualizer 16 | 17 | 18 | class VisualizationDemo(object): 19 | def __init__(self, cfg, instance_mode=ColorMode.IMAGE, parallel=False): 20 | """ 21 | Args: 22 | cfg (CfgNode): 23 | instance_mode (ColorMode): 24 | parallel (bool): whether to run the model in different processes from visualization. 25 | Useful since the visualization logic can be slow. 26 | """ 27 | self.metadata = MetadataCatalog.get( 28 | cfg.DATASETS.TEST[0] if len(cfg.DATASETS.TEST) else "__unused" 29 | ) 30 | self.cpu_device = torch.device("cpu") 31 | self.instance_mode = instance_mode 32 | 33 | self.parallel = parallel 34 | if parallel: 35 | num_gpu = torch.cuda.device_count() 36 | self.predictor = AsyncPredictor(cfg, num_gpus=num_gpu) 37 | else: 38 | self.predictor = DefaultPredictor(cfg) 39 | 40 | def run_on_image(self, image,path): 41 | """ 42 | Args: 43 | image (np.ndarray): an image of shape (H, W, C) (in BGR order). 44 | This is the format used by OpenCV. 45 | 46 | Returns: 47 | predictions (dict): the output of the model. 48 | vis_output (VisImage): the visualized image output. 49 | """ 50 | vis_output = None 51 | predictions = self.predictor(image) 52 | # Convert image from OpenCV BGR format to Matplotlib RGB format. 53 | image = image[:, :, ::-1] 54 | visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode) 55 | if "panoptic_seg" in predictions: 56 | panoptic_seg, segments_info = predictions["panoptic_seg"] 57 | vis_output = visualizer.draw_panoptic_seg_predictions( 58 | panoptic_seg.to(self.cpu_device), segments_info 59 | ) 60 | else: 61 | if "sem_seg" in predictions: 62 | vis_output = visualizer.draw_sem_seg( 63 | predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) 64 | ) 65 | if "instances" in predictions: 66 | instances = predictions["instances"].to(self.cpu_device) 67 | vis_output = visualizer.draw_instance_predictions(predictions=instances) 68 | 69 | extract_img = vis_output.img 70 | #画关键点 71 | keypoint_box = instances._fields["pred_keypoints"].numpy().tolist() 72 | img_name = path.split("/")[-1] 73 | if(len(keypoint_box)>0): 74 | for idx, keypoint_list in enumerate(keypoint_box): 75 | for idxx, keypoint in enumerate(keypoint_list): 76 | pass 77 | _ = self.write(extract_img,11,15,13,keypoint_list) 78 | text_img = self.write(_,12,16,14,keypoint_list) 79 | rgb = text_img[...,::-1] 80 | cv2.imwrite("/home/dooncloud/GitHub/detectron2/output/self_"+img_name,rgb) 81 | 82 | # vis_output = visualizer.draw_instance_predictions(predictions=instances) 83 | 84 | return predictions, vis_output 85 | 86 | def calculate_angle(self, point_1,point_2,point_base): 87 | vector_a = [point_1[0]-point_base[0],point_1[1]-point_base[1]] 88 | vector_b = [point_2[0]-point_base[0],point_2[1]-point_base[1]] 89 | up = np.dot(vector_a,vector_b) 90 | a = np.linalg.norm(np.array(vector_a)) 91 | b = np.linalg.norm(np.array(vector_b)) 92 | down = a*b 93 | if down ==0: 94 | cos = 0.0 95 | else: 96 | cos = up/down 97 | if(abs(cos)>1): 98 | cos = 1 99 | return math.degrees(math.acos(cos)) 100 | 101 | def calculate_distance(self, point_1,point_2): 102 | vector = [point_1[0]-point_2[0],point_1[1]-point_2[1]] 103 | distance = np.linalg.norm(np.array(vector)) 104 | return distance 105 | 106 | def where_point_write(self, n_list,keypoint_list): 107 | point_1 = keypoint_list[n_list[0]] 108 | point_2 = keypoint_list[n_list[1]] 109 | point_base = keypoint_list[n_list[2]] 110 | result = self.calculate_angle(point_1,point_2,point_base) 111 | x , y = point_base[0] , point_base[1] 112 | return result , x , y 113 | 114 | def write(self, img,need_list,keypoint_list): 115 | if len(need_list)>0: 116 | for i in need_list: 117 | result , x , y= self.where_point_write(i,keypoint_list) 118 | img = cv2.putText( 119 | img, "%.2f"%result, (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2 120 | ) 121 | else: 122 | img = img 123 | return img 124 | 125 | def where_distance_write(self, n_list,keypoint_list): 126 | point_1 = keypoint_list[n_list[0]] 127 | point_2 = keypoint_list[n_list[1]] 128 | result = self.calculate_distance(point_1,point_2) 129 | x , y = point_2[0] , point_2[1] 130 | return result , x , y 131 | 132 | def write_distance(self, img,need_list,keypoint_list): 133 | if len(need_list)>0: 134 | for i in need_list: 135 | result , x , y= self.where_distance_write(i,keypoint_list) 136 | img = cv2.putText( 137 | img, "%.2f"%result, (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2 138 | ) 139 | else: 140 | img = img 141 | return img 142 | 143 | def _frame_from_video(self, video): 144 | while video.isOpened(): 145 | success, frame = video.read() 146 | if success: 147 | yield frame 148 | else: 149 | break 150 | 151 | def run_on_video(self, video, dictionary): 152 | """ 153 | Visualizes predictions on frames of the input video. 154 | 155 | Args: 156 | video (cv2.VideoCapture): a :class:`VideoCapture` object, whose source can be 157 | either a webcam or a video file. 158 | 159 | Yields: 160 | ndarray: BGR visualizations of each video frame. 161 | """ 162 | video_visualizer = VideoVisualizer(self.metadata, self.instance_mode) 163 | 164 | def process_predictions(frame, predictions, dictionary): 165 | resulte = 0 166 | frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) 167 | if "panoptic_seg" in predictions: 168 | panoptic_seg, segments_info = predictions["panoptic_seg"] 169 | vis_frame = video_visualizer.draw_panoptic_seg_predictions( 170 | frame, panoptic_seg.to(self.cpu_device), segments_info 171 | ) 172 | 173 | elif "instances" in predictions: 174 | predictions = predictions["instances"].to(self.cpu_device) 175 | #判断框 176 | max_inform_keypoint = self.search_max_box_information(predictions) 177 | if(max_inform_keypoint != None): 178 | #画框 179 | bbox = max_inform_keypoint[0] 180 | frame = cv2.rectangle(frame,(int(bbox[0]),int(bbox[1])), (int(bbox[2]),int(bbox[3])),(0,255,0), 2) 181 | # 画关键点 182 | keypoint_list = max_inform_keypoint[1] 183 | for i, keypoint in enumerate(keypoint_list): 184 | circle_coord = (int(keypoint[0]),int(keypoint[1])) 185 | frame = cv2.putText( 186 | frame, str(i), circle_coord , cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2 187 | ) 188 | #画角度 189 | frame = self.write(frame,dictionary["angle"],keypoint_list) 190 | #画距离 191 | frame = self.write_distance(frame,dictionary["distance"],keypoint_list) 192 | #判断仰卧起坐 193 | resulte = self.poll_situp(keypoint_list,dictionary) 194 | #存结果 195 | # save_json = self.save_resulte(keypoint_list,dictionary) 196 | 197 | vis_frame = frame[...,::-1] 198 | else: 199 | vis_frame = frame[...,::-1] 200 | 201 | # vis_frame = video_visualizer.draw_instance_predictions(frame, predictions) 202 | 203 | elif "sem_seg" in predictions: 204 | vis_frame = video_visualizer.draw_sem_seg( 205 | frame, predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) 206 | ) 207 | 208 | # Converts Matplotlib RGB format to OpenCV BGR format 209 | # vis_frame = cv2.cvtColor(vis_frame.get_image(), cv2.COLOR_RGB2BGR) 210 | 211 | return {"vis_frame":vis_frame,"resulte": resulte,"max_inform_keypoint":max_inform_keypoint} 212 | 213 | frame_gen = self._frame_from_video(video) 214 | if self.parallel: 215 | buffer_size = self.predictor.default_buffer_size 216 | 217 | frame_data = deque() 218 | 219 | for cnt, frame in enumerate(frame_gen): 220 | frame_data.append(frame) 221 | self.predictor.put(frame) 222 | 223 | if cnt >= buffer_size: 224 | frame = frame_data.popleft() 225 | predictions = self.predictor.get() 226 | yield process_predictions(frame, predictions) 227 | 228 | while len(frame_data): 229 | frame = frame_data.popleft() 230 | predictions = self.predictor.get() 231 | yield process_predictions(frame, predictions) 232 | else: 233 | for frame in frame_gen: 234 | yield process_predictions(frame, self.predictor(frame), dictionary) 235 | 236 | def poll_situp(self,keypoint_list,dictionary): 237 | ankle_angle_poll = self.angle_poll(dictionary["judge_ankle_angle"],keypoint_list,dictionary["require_ankle"]) 238 | up_butt_angle_poll = self.angle_poll(dictionary["judge_butt_angle"],keypoint_list,dictionary["require_butt_up"]) 239 | down_butt_angle_poll = self.angle_poll(dictionary["judge_butt_angle"],keypoint_list,dictionary["require_butt_down"]) 240 | distance_ratio_poll = self.distance_ratio_poll(dictionary["judge_distance_ratio"],keypoint_list,dictionary["require_distance_ratio"]) 241 | 242 | up_array = ankle_angle_poll + distance_ratio_poll+up_butt_angle_poll 243 | down_array = down_butt_angle_poll 244 | 245 | return [up_array,down_array] 246 | 247 | def save_resulte(self,keypoint_list,dictionary): 248 | ankle_num_list = self.calculate_save_angle(dictionary["judge_ankle_angle"],keypoint_list) 249 | 250 | log_f = open("digital","a+") 251 | print(ankle_num_list,file = log_f) 252 | log_f.close() 253 | 254 | butt_num_list = self.calculate_save_angle(dictionary["judge_butt_angle"],keypoint_list) 255 | 256 | distance_num_list = self.distance_poll(dictionary["judge_distance"],keypoint_list,dictionary["require_distance_ratio"]) 257 | 258 | 259 | def calculate_save_angle(self,angle_list,keypoint_list): 260 | resulte = [] 261 | for i in (angle_list): 262 | point_1 = keypoint_list[i[0]] 263 | point_2 = keypoint_list[i[1]] 264 | point_base = keypoint_list[i[2]] 265 | angle_result = self.calculate_angle(point_1,point_2,point_base) 266 | resulte.append(angle_result) 267 | return resulte 268 | 269 | def angle_poll(self,angle_list,keypoint_list,requirement): 270 | poll = [] 271 | resulte = self.calculate_save_angle(angle_list,keypoint_list) 272 | for idx, per_resulte in enumerate(resulte): 273 | if "<" is requirement["need"]: 274 | if per_resulte < requirement["angle"][idx]: 275 | poll.append(1) 276 | else: 277 | poll.append(0) 278 | elif ">" is requirement["need"]: 279 | if per_resulte > requirement["angle"][idx]: 280 | poll.append(1) 281 | else: 282 | poll.append(0) 283 | elif "=" is requirement["need"]: 284 | if per_resulte == requirement["angle"][idx]: 285 | poll.append(1) 286 | else: 287 | poll.append(0) 288 | else: 289 | raise Exception("calculate_dictionary 请输入正确判断符号") 290 | return poll 291 | 292 | def calculate_save_distance_ratio(self,distance_list,keypoint_list): 293 | resulte = [] 294 | for i in (distance_list): 295 | point_1_1 = keypoint_list[i[0]] 296 | point_1_2 = keypoint_list[i[1]] 297 | point_2_1 = keypoint_list[i[2]] 298 | point_2_2 = keypoint_list[i[3]] 299 | 300 | up_result = self.calculate_distance(point_1_1,point_1_2) 301 | down_resulte = self.calculate_distance(point_2_1,point_2_2) 302 | ratio = up_result/down_resulte 303 | resulte.append(ratio) 304 | return resulte 305 | 306 | def distance_ratio_poll(self,distance_list,keypoint_list,requirement): 307 | poll = [] 308 | resulte = self.calculate_save_distance_ratio(distance_list,keypoint_list) 309 | print(resulte) 310 | for idx, per_resulte in enumerate(resulte): 311 | if "<" is requirement["need"]: 312 | if per_resulte < requirement["distance"][idx]: 313 | poll.append(1) 314 | else: 315 | poll.append(0) 316 | elif ">" is requirement["need"]: 317 | if per_resulte > requirement["distance"][idx]: 318 | poll.append(1) 319 | else: 320 | poll.append(0) 321 | elif "=" is requirement["need"]: 322 | if per_resulte == requirement["distance"][idx]: 323 | poll.append(1) 324 | else: 325 | poll.append(0) 326 | else: 327 | raise Exception("calculate_dictionary 请输入正确判断符号") 328 | print(poll) 329 | return poll 330 | 331 | def search_max_box_information(self, predictions): 332 | keypoint_box_area = predictions._fields["pred_boxes"].area().numpy().tolist() 333 | keypoint_box_coordinate = predictions._fields["pred_boxes"].tensor.numpy().tolist() 334 | keypoint_box = predictions._fields["pred_keypoints"].numpy().tolist() 335 | assert (len(keypoint_box_area) == len(keypoint_box_coordinate) and len(keypoint_box_coordinate) == len(keypoint_box)) is True , "search max box --error" 336 | if(len(keypoint_box_area) != 0 ): 337 | if len(keypoint_box_area) >1 : 338 | index = keypoint_box_area.index(max(keypoint_box_area)) 339 | return [keypoint_box_coordinate[index],keypoint_box[index]] 340 | else: 341 | return [keypoint_box_coordinate[0],keypoint_box[0]] 342 | else: 343 | pass 344 | 345 | 346 | class AsyncPredictor: 347 | """ 348 | A predictor that runs the model asynchronously, possibly on >1 GPUs. 349 | Because rendering the visualization takes considerably amount of time, 350 | this helps improve throughput when rendering videos. 351 | """ 352 | 353 | class _StopToken: 354 | pass 355 | 356 | class _PredictWorker(mp.Process): 357 | def __init__(self, cfg, task_queue, result_queue): 358 | self.cfg = cfg 359 | self.task_queue = task_queue 360 | self.result_queue = result_queue 361 | super().__init__() 362 | 363 | def run(self): 364 | predictor = DefaultPredictor(self.cfg) 365 | 366 | while True: 367 | task = self.task_queue.get() 368 | if isinstance(task, AsyncPredictor._StopToken): 369 | break 370 | idx, data = task 371 | result = predictor(data) 372 | self.result_queue.put((idx, result)) 373 | 374 | def __init__(self, cfg, num_gpus: int = 1): 375 | """ 376 | Args: 377 | cfg (CfgNode): 378 | num_gpus (int): if 0, will run on CPU 379 | """ 380 | num_workers = max(num_gpus, 1) 381 | self.task_queue = mp.Queue(maxsize=num_workers * 3) 382 | self.result_queue = mp.Queue(maxsize=num_workers * 3) 383 | self.procs = [] 384 | for gpuid in range(max(num_gpus, 1)): 385 | cfg = cfg.clone() 386 | cfg.defrost() 387 | cfg.MODEL.DEVICE = "cuda:{}".format(gpuid) if num_gpus > 0 else "cpu" 388 | self.procs.append( 389 | AsyncPredictor._PredictWorker(cfg, self.task_queue, self.result_queue) 390 | ) 391 | 392 | self.put_idx = 0 393 | self.get_idx = 0 394 | self.result_rank = [] 395 | self.result_data = [] 396 | 397 | for p in self.procs: 398 | p.start() 399 | atexit.register(self.shutdown) 400 | 401 | def put(self, image): 402 | self.put_idx += 1 403 | self.task_queue.put((self.put_idx, image)) 404 | 405 | def get(self): 406 | self.get_idx += 1 # the index needed for this request 407 | if len(self.result_rank) and self.result_rank[0] == self.get_idx: 408 | res = self.result_data[0] 409 | del self.result_data[0], self.result_rank[0] 410 | return res 411 | 412 | while True: 413 | # make sure the results are returned in the correct order 414 | idx, res = self.result_queue.get() 415 | if idx == self.get_idx: 416 | return res 417 | insert = bisect.bisect(self.result_rank, idx) 418 | self.result_rank.insert(insert, idx) 419 | self.result_data.insert(insert, res) 420 | 421 | def __len__(self): 422 | return self.put_idx - self.get_idx 423 | 424 | def __call__(self, image): 425 | self.put(image) 426 | return self.get() 427 | 428 | def shutdown(self): 429 | for _ in self.procs: 430 | self.task_queue.put(AsyncPredictor._StopToken()) 431 | 432 | @property 433 | def default_buffer_size(self): 434 | return len(self.procs) * 5 435 | -------------------------------------------------------------------------------- /写给曾老师.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iTomstudio/predictor_situp/36eb7e94e383e8231e570949d029d3a9d878e9dd/写给曾老师.odt --------------------------------------------------------------------------------