opencv/pose_estimation_mediapipe
6
1#include <vector>2#include <string>3#include <utility>4#include <cmath>5 6#include <opencv2/opencv.hpp>7 8const long double _M_PI = 3.141592653589793238L;9using namespace std;10using namespace cv;11using namespace dnn;12 13vector< pair<dnn::Backend, dnn::Target> > backendTargetPairs = {14 std::make_pair<dnn::Backend, dnn::Target>(dnn::DNN_BACKEND_OPENCV, dnn::DNN_TARGET_CPU),15 std::make_pair<dnn::Backend, dnn::Target>(dnn::DNN_BACKEND_CUDA, dnn::DNN_TARGET_CUDA),16 std::make_pair<dnn::Backend, dnn::Target>(dnn::DNN_BACKEND_CUDA, dnn::DNN_TARGET_CUDA_FP16),17 std::make_pair<dnn::Backend, dnn::Target>(dnn::DNN_BACKEND_TIMVX, dnn::DNN_TARGET_NPU),18 std::make_pair<dnn::Backend, dnn::Target>(dnn::DNN_BACKEND_CANN, dnn::DNN_TARGET_NPU) };19 20 21Mat getMediapipeAnchor();22 23class MPPersonDet {24private:25 Net net;26 string modelPath;27 Size inputSize;28 float scoreThreshold;29 float nmsThreshold;30 dnn::Backend backendId;31 dnn::Target targetId;32 int topK;33 Mat anchors;34 35public:36 MPPersonDet(string modPath, float nmsThresh = 0.3, float scoreThresh = 0.5, int tok=5000 , dnn::Backend bId = DNN_BACKEND_DEFAULT, dnn::Target tId = DNN_TARGET_CPU) :37 modelPath(modPath), nmsThreshold(nmsThresh),38 scoreThreshold(scoreThresh), topK(tok),39 backendId(bId), targetId(tId)40 {41 this->inputSize = Size(224, 224);42 this->net = readNet(this->modelPath);43 this->net.setPreferableBackend(this->backendId);44 this->net.setPreferableTarget(this->targetId);45 this->anchors = getMediapipeAnchor();46 }47 48 pair<Mat, Size> preprocess(Mat img)49 {50 Mat blob;51 Image2BlobParams paramMediapipe;52 paramMediapipe.datalayout = DNN_LAYOUT_NCHW;53 paramMediapipe.ddepth = CV_32F;54 paramMediapipe.mean = Scalar::all(127.5);55 paramMediapipe.scalefactor = Scalar::all(1/127.5);56 paramMediapipe.size = this->inputSize;57 paramMediapipe.swapRB = true;58 paramMediapipe.paddingmode = DNN_PMODE_LETTERBOX;59 60 double ratio = min(this->inputSize.height / double(img.rows), this->inputSize.width / double(img.cols));61 Size padBias(0, 0);62 if (img.rows != this->inputSize.height || img.cols != this->inputSize.width)63 {64 // keep aspect ratio when resize65 Size ratioSize(int(img.cols * ratio), int(img.rows* ratio));66 int padH = this->inputSize.height - ratioSize.height;67 int padW = this->inputSize.width - ratioSize.width;68 padBias.width = padW / 2;69 padBias.height = padH / 2;70 }71 blob = blobFromImageWithParams(img, paramMediapipe);72 padBias = Size(int(padBias.width / ratio), int(padBias.height / ratio));73 return pair<Mat, Size>(blob, padBias);74 }75 76 Mat infer(Mat srcimg)77 {78 pair<Mat, Size> w = this->preprocess(srcimg);79 Mat inputBlob = get<0>(w);80 Size padBias = get<1>(w);81 this->net.setInput(inputBlob);82 vector<Mat> outs;83 this->net.forward(outs, this->net.getUnconnectedOutLayersNames());84 Mat predictions = this->postprocess(outs, Size(srcimg.cols, srcimg.rows), padBias);85 return predictions;86 }87 88 Mat postprocess(vector<Mat> outputs, Size orgSize, Size padBias)89 {90 Mat score = outputs[1].reshape(0, outputs[1].size[0]);91 Mat boxLandDelta = outputs[0].reshape(outputs[0].size[0], outputs[0].size[1]);92 Mat boxDelta = boxLandDelta.colRange(0, 4);93 Mat landmarkDelta = boxLandDelta.colRange(4, boxLandDelta.cols);94 float scale = float(max(orgSize.height, orgSize.width));95 Mat mask = score < -100;96 score.setTo(-100, mask);97 mask = score > 100;98 score.setTo(100, mask);99 Mat deno;100 exp(-score, deno);101 divide(1.0, 1+deno, score);102 boxDelta.colRange(0, 1) = boxDelta.colRange(0, 1) / this->inputSize.width;103 boxDelta.colRange(1, 2) = boxDelta.colRange(1, 2) / this->inputSize.height;104 boxDelta.colRange(2, 3) = boxDelta.colRange(2, 3) / this->inputSize.width;105 boxDelta.colRange(3, 4) = boxDelta.colRange(3, 4) / this->inputSize.height;106 Mat xy1 = (boxDelta.colRange(0, 2) - boxDelta.colRange(2, 4) / 2 + this->anchors) * scale;107 Mat xy2 = (boxDelta.colRange(0, 2) + boxDelta.colRange(2, 4) / 2 + this->anchors) * scale;108 Mat boxes;109 hconcat(xy1, xy2, boxes);110 vector< Rect2d > rotBoxes(boxes.rows);111 boxes.colRange(0, 1) = boxes.colRange(0, 1) - padBias.width;112 boxes.colRange(1, 2) = boxes.colRange(1, 2) - padBias.height;113 boxes.colRange(2, 3) = boxes.colRange(2, 3) - padBias.width;114 boxes.colRange(3, 4) = boxes.colRange(3, 4) - padBias.height;115 for (int i = 0; i < boxes.rows; i++)116 {117 rotBoxes[i] = Rect2d(Point2d(boxes.at<float>(i, 0), boxes.at<float>(i, 1)), Point2d(boxes.at<float>(i, 2), boxes.at<float>(i, 3)));118 }119 vector<int> keep;120 NMSBoxes(rotBoxes, score, this->scoreThreshold, this->nmsThreshold, keep, 1.0f, this->topK);121 if (keep.size() == 0)122 return Mat();123 int nbCols = landmarkDelta.cols + boxes.cols + 1;124 Mat candidates(int(keep.size()), nbCols, CV_32FC1);125 int row = 0;126 for (auto idx : keep)127 {128 candidates.at<float>(row, nbCols - 1) = score.at<float>(idx);129 boxes.row(idx).copyTo(candidates.row(row).colRange(0, 4));130 candidates.at<float>(row, 4) = (landmarkDelta.at<float>(idx, 0) / this->inputSize.width + this->anchors.at<float>(idx,0)) * scale - padBias.width;131 candidates.at<float>(row, 5) = (landmarkDelta.at<float>(idx, 1) / this->inputSize.height + this->anchors.at<float>(idx, 1))* scale - padBias.height;132 candidates.at<float>(row, 6) = (landmarkDelta.at<float>(idx, 2) / this->inputSize.width + this->anchors.at<float>(idx, 0))* scale - padBias.width;133 candidates.at<float>(row, 7) = (landmarkDelta.at<float>(idx, 3) / this->inputSize.height + this->anchors.at<float>(idx, 1))* scale - padBias.height;134 candidates.at<float>(row, 8) = (landmarkDelta.at<float>(idx, 4) / this->inputSize.width + this->anchors.at<float>(idx, 0))* scale - padBias.width;135 candidates.at<float>(row, 9) = (landmarkDelta.at<float>(idx, 5) / this->inputSize.height + this->anchors.at<float>(idx, 1))* scale - padBias.height;136 candidates.at<float>(row, 10) = (landmarkDelta.at<float>(idx, 6) / this->inputSize.width + this->anchors.at<float>(idx, 0))* scale - padBias.width;137 candidates.at<float>(row, 11) = (landmarkDelta.at<float>(idx, 7) / this->inputSize.height + this->anchors.at<float>(idx, 1))* scale - padBias.height;138 row++;139 }140 return candidates;141 142 }143 144 145};146 147class MPPose {148private:149 Net net;150 string modelPath;151 Size inputSize;152 float confThreshold;153 dnn::Backend backendId;154 dnn::Target targetId;155 float personBoxPreEnlargeFactor;156 float personBoxEnlargeFactor;157 Mat anchors;158 159public:160 MPPose(string modPath, float confThresh = 0.5, dnn::Backend bId = DNN_BACKEND_DEFAULT, dnn::Target tId = DNN_TARGET_CPU) :161 modelPath(modPath), confThreshold(confThresh),162 backendId(bId), targetId(tId)163 {164 this->inputSize = Size(256, 256);165 this->net = readNet(this->modelPath);166 this->net.setPreferableBackend(this->backendId);167 this->net.setPreferableTarget(this->targetId);168 this->anchors = getMediapipeAnchor();169 // RoI will be larger so the performance will be better, but preprocess will be slower.Default to 1.170 this->personBoxPreEnlargeFactor = 1;171 this->personBoxEnlargeFactor = 1.25;172 }173 174 tuple<Mat, Mat, float, Mat, Size> preprocess(Mat image, Mat person)175 {176 /***177 Rotate input for inference.178 Parameters:179 image - input image of BGR channel order180 face_bbox - human face bounding box found in image of format [[x1, y1], [x2, y2]] (top-left and bottom-right points)181 person_landmarks - 4 landmarks (2 full body points, 2 upper body points) of shape [4, 2]182 Returns:183 rotated_person - rotated person image for inference184 rotate_person_bbox - person box of interest range185 angle - rotate angle for person186 rotation_matrix - matrix for rotation and de-rotation187 pad_bias - pad pixels of interest range188 */189 // crop and pad image to interest range190 Size padBias(0, 0); // left, top191 Mat personKeypoints = person.colRange(4, 12).reshape(0, 4);192 Point2f midHipPoint = Point2f(personKeypoints.row(0));193 Point2f fullBodyPoint = Point2f(personKeypoints.row(1));194 // # get RoI195 double fullDist = norm(midHipPoint - fullBodyPoint);196 Mat fullBoxf,fullBox;197 Mat v1 = Mat(midHipPoint) - fullDist, v2 = Mat(midHipPoint);198 vector<Mat> vmat = { Mat(midHipPoint) - fullDist, Mat(midHipPoint) + fullDist };199 hconcat(vmat, fullBoxf);200 // enlarge to make sure full body can be cover201 Mat cBox, centerBox, whBox;202 reduce(fullBoxf, centerBox, 1, REDUCE_AVG, CV_32F);203 whBox = fullBoxf.col(1) - fullBoxf.col(0);204 Mat newHalfSize = whBox * this->personBoxPreEnlargeFactor / 2;205 vmat[0] = centerBox - newHalfSize;206 vmat[1] = centerBox + newHalfSize;207 hconcat(vmat, fullBox);208 Mat personBox;209 fullBox.convertTo(personBox, CV_32S);210 // refine person bbox211 Mat idx = personBox.row(0) < 0;212 personBox.row(0).setTo(0, idx);213 idx = personBox.row(0) >= image.cols;214 personBox.row(0).setTo(image.cols , idx);215 idx = personBox.row(1) < 0;216 personBox.row(1).setTo(0, idx);217 idx = personBox.row(1) >= image.rows;218 personBox.row(1).setTo(image.rows, idx); // crop to the size of interest219 220 image = image(Rect(personBox.at<int>(0, 0), personBox.at<int>(1, 0), personBox.at<int>(0, 1) - personBox.at<int>(0, 0), personBox.at<int>(1, 1) - personBox.at<int>(1, 0)));221 // pad to square222 int top = int(personBox.at<int>(1, 0) - fullBox.at<float>(1, 0));223 int left = int(personBox.at<int>(0, 0) - fullBox.at<float>(0, 0));224 int bottom = int(fullBox.at<float>(1, 1) - personBox.at<int>(1, 1));225 int right = int(fullBox.at<float>(0, 1) - personBox.at<int>(0, 1));226 copyMakeBorder(image, image, top, bottom, left, right, BORDER_CONSTANT, Scalar(0, 0, 0));227 padBias = Point(padBias) + Point(personBox.col(0)) - Point(left, top);228 // compute rotation229 midHipPoint -= Point2f(padBias);230 fullBodyPoint -= Point2f(padBias);231 float radians = float(_M_PI / 2 - atan2(-(fullBodyPoint.y - midHipPoint.y), fullBodyPoint.x - midHipPoint.x));232 radians = radians - 2 * float(_M_PI) * int((radians + _M_PI) / (2 * _M_PI));233 float angle = (radians * 180 / float(_M_PI));234 // get rotation matrix*235 Mat rotationMatrix = getRotationMatrix2D(midHipPoint, angle, 1.0);236 // get rotated image237 Mat rotatedImage;238 warpAffine(image, rotatedImage, rotationMatrix, Size(image.cols, image.rows));239 // get landmark bounding box240 Mat blob;241 Image2BlobParams paramPoseMediapipe;242 paramPoseMediapipe.datalayout = DNN_LAYOUT_NHWC;243 paramPoseMediapipe.ddepth = CV_32F;244 paramPoseMediapipe.mean = Scalar::all(0);245 paramPoseMediapipe.scalefactor = Scalar::all(1 / 255.);246 paramPoseMediapipe.size = this->inputSize;247 paramPoseMediapipe.swapRB = true;248 paramPoseMediapipe.paddingmode = DNN_PMODE_NULL;249 blob = blobFromImageWithParams(rotatedImage, paramPoseMediapipe); // resize INTER_AREA becomes INTER_LINEAR in blobFromImage250 Mat rotatedPersonBox = (Mat_<float>(2, 2) << 0, 0, image.cols, image.rows);251 252 return tuple<Mat, Mat, float, Mat, Size>(blob, rotatedPersonBox, angle, rotationMatrix, padBias);253 }254 255 tuple<Mat, Mat, Mat, Mat, Mat, float> infer(Mat image, Mat person)256 {257 int h = image.rows;258 int w = image.cols;259 // Preprocess260 tuple<Mat, Mat, float, Mat, Size> tw;261 tw = this->preprocess(image, person);262 Mat inputBlob = get<0>(tw);263 Mat rotatedPersonBbox = get<1>(tw);264 float angle = get<2>(tw);265 Mat rotationMatrix = get<3>(tw);266 Size padBias = get<4>(tw);267 268 // Forward269 this->net.setInput(inputBlob);270 vector<Mat> outputBlob;271 this->net.forward(outputBlob, this->net.getUnconnectedOutLayersNames());272 273 // Postprocess274 tuple<Mat, Mat, Mat, Mat, Mat, float> results;275 results = this->postprocess(outputBlob, rotatedPersonBbox, angle, rotationMatrix, padBias, Size(w, h));276 return results;// # [bbox_coords, landmarks_coords, conf]277 }278 279 tuple<Mat, Mat, Mat, Mat, Mat, float> postprocess(vector<Mat> blob, Mat rotatedPersonBox, float angle, Mat rotationMatrix, Size padBias, Size imgSize)280 {281 float valConf = blob[1].at<float>(0);282 if (valConf < this->confThreshold)283 return tuple<Mat, Mat, Mat, Mat, Mat, float>(Mat(), Mat(), Mat(), Mat(), Mat(), valConf);284 Mat landmarks = blob[0].reshape(0, 39);285 Mat mask = blob[2];286 Mat heatmap = blob[3];287 Mat landmarksWorld = blob[4].reshape(0, 39);288 289 Mat deno;290 // recover sigmoid score291 exp(-landmarks.colRange(3, landmarks.cols), deno);292 divide(1.0, 1 + deno, landmarks.colRange(3, landmarks.cols));293 // TODO: refine landmarks with heatmap. reference: https://github.com/tensorflow/tfjs-models/blob/master/pose-detection/src/blazepose_tfjs/detector.ts#L577-L582294 heatmap = heatmap.reshape(0, heatmap.size[0]);295 // transform coords back to the input coords296 Mat whRotatedPersonPbox = rotatedPersonBox.row(1) - rotatedPersonBox.row(0);297 Mat scaleFactor = whRotatedPersonPbox.clone();298 scaleFactor.col(0) /= this->inputSize.width;299 scaleFactor.col(1) /= this->inputSize.height;300 landmarks.col(0) = (landmarks.col(0) - this->inputSize.width / 2) * scaleFactor.at<float>(0);301 landmarks.col(1) = (landmarks.col(1) - this->inputSize.height / 2) * scaleFactor.at<float>(1);302 landmarks.col(2) = landmarks.col(2) * max(scaleFactor.at<float>(1), scaleFactor.at<float>(0));303 Mat coordsRotationMatrix;304 getRotationMatrix2D(Point(0, 0), angle, 1.0).convertTo(coordsRotationMatrix, CV_32F);305 Mat rotatedLandmarks = landmarks.colRange(0, 2) * coordsRotationMatrix.colRange(0, 2);306 hconcat(rotatedLandmarks, landmarks.colRange(2, landmarks.cols), rotatedLandmarks);307 Mat rotatedLandmarksWorld = landmarksWorld.colRange(0, 2) * coordsRotationMatrix.colRange(0, 2);308 hconcat(rotatedLandmarksWorld, landmarksWorld.col(2), rotatedLandmarksWorld);309 // invert rotation310 Mat rotationComponent = (Mat_<double>(2, 2) <<rotationMatrix.at<double>(0,0), rotationMatrix.at<double>(1, 0), rotationMatrix.at<double>(0, 1), rotationMatrix.at<double>(1, 1));311 Mat translationComponent = rotationMatrix(Rect(2, 0, 1, 2)).clone();312 Mat invertedTranslation = -rotationComponent * translationComponent;313 Mat inverseRotationMatrix;314 hconcat(rotationComponent, invertedTranslation, inverseRotationMatrix);315 Mat center, rc;316 reduce(rotatedPersonBox, rc, 0, REDUCE_AVG, CV_64F);317 hconcat(rc, Mat(1, 1, CV_64FC1, 1) , center);318 // get box center319 Mat originalCenter(2, 1, CV_64FC1);320 originalCenter.at<double>(0) = center.dot(inverseRotationMatrix.row(0));321 originalCenter.at<double>(1) = center.dot(inverseRotationMatrix.row(1));322 for (int idxRow = 0; idxRow < rotatedLandmarks.rows; idxRow++)323 {324 landmarks.at<float>(idxRow, 0) = float(rotatedLandmarks.at<float>(idxRow, 0) + originalCenter.at<double>(0) + padBias.width); // 325 landmarks.at<float>(idxRow, 1) = float(rotatedLandmarks.at<float>(idxRow, 1) + originalCenter.at<double>(1) + padBias.height); // 326 }327 // get bounding box from rotated_landmarks328 double vmin0, vmin1, vmax0, vmax1;329 minMaxLoc(landmarks.col(0), &vmin0, &vmax0);330 minMaxLoc(landmarks.col(1), &vmin1, &vmax1);331 Mat bbox = (Mat_<float>(2, 2) << vmin0, vmin1, vmax0, vmax1);332 Mat centerBox;333 reduce(bbox, centerBox, 0, REDUCE_AVG, CV_32F);334 Mat whBox = bbox.row(1) - bbox.row(0);335 Mat newHalfSize = whBox * this->personBoxEnlargeFactor / 2;336 vector<Mat> vmat(2);337 vmat[0] = centerBox - newHalfSize;338 vmat[1] = centerBox + newHalfSize;339 vconcat(vmat, bbox);340 // invert rotation for mask341 mask = mask.reshape(1, 256);342 Mat invertRotationMatrix = getRotationMatrix2D(Point(mask.cols / 2, mask.rows / 2), -angle, 1.0);343 Mat invertRotationMask;344 warpAffine(mask, invertRotationMask, invertRotationMatrix, Size(mask.cols, mask.rows));345 // enlarge mask346 resize(invertRotationMask, invertRotationMask, Size(int(whRotatedPersonPbox.at<float>(0)), int(whRotatedPersonPbox.at<float>(1))));347 // crop and pad mask348 int minW = -min(padBias.width, 0);349 int minH= -min(padBias.height, 0);350 int left = max(padBias.width, 0);351 int top = max(padBias.height, 0);352 Size padOver = imgSize - Size(invertRotationMask.cols, invertRotationMask.rows) - padBias;353 int maxW = min(padOver.width, 0) + invertRotationMask.cols;354 int maxH = min(padOver.height, 0) + invertRotationMask.rows;355 int right = max(padOver.width, 0);356 int bottom = max(padOver.height, 0);357 invertRotationMask = invertRotationMask(Rect(minW, minH, maxW - minW, maxH - minH)).clone();358 copyMakeBorder(invertRotationMask, invertRotationMask, top, bottom, left, right, BORDER_CONSTANT, Scalar::all(0));359 // binarize mask360 threshold(invertRotationMask, invertRotationMask, 1, 255, THRESH_BINARY);361 362 /* 2*2 person bbox: [[x1, y1], [x2, y2]]363 # 39*5 screen landmarks: 33 keypoints and 6 auxiliary points with [x, y, z, visibility, presence], z value is relative to HIP364 # Visibility is probability that a keypoint is located within the frame and not occluded by another bigger body part or another object365 # Presence is probability that a keypoint is located within the frame366 # 39*3 world landmarks: 33 keypoints and 6 auxiliary points with [x, y, z] 3D metric x, y, z coordinate367 # img_height*img_width mask: gray mask, where 255 indicates the full body of a person and 0 means background368 # 64*64*39 heatmap: currently only used for refining landmarks, requires sigmod processing before use369 # conf: confidence of prediction*/370 return tuple<Mat , Mat, Mat, Mat, Mat, float>(bbox, landmarks, rotatedLandmarksWorld, invertRotationMask, heatmap, valConf);371 }372};373 374std::string keys =375"{ help h | | Print help message. }"376"{ model m | pose_estimation_mediapipe_2023mar.onnx | Usage: Path to the model, defaults to person_detection_mediapipe_2023mar.onnx }"377"{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera.}"378"{ conf_threshold | 0.5 | Usage: Filter out hands of confidence < conf_threshold. }"379"{ top_k | 1 | Usage: Keep top_k bounding boxes before NMS. }"380"{ save s | true | Usage: Specify to save file with results (i.e. bounding box, confidence level). Invalid in case of camera input. }"381"{ vis v | true | Usage: Specify to open a new window to show results. Invalid in case of camera input. }"382"{ backend bt | 0 | Choose one of computation backends: "383"0: (default) OpenCV implementation + CPU, "384"1: CUDA + GPU (CUDA), "385"2: CUDA + GPU (CUDA FP16), "386"3: TIM-VX + NPU, "387"4: CANN + NPU}";388 389 390void drawLines(Mat image, Mat landmarks, Mat keeplandmarks, bool isDrawPoint = true, int thickness = 2)391{392 393 vector<pair<int, int>> segment = {394 make_pair(0, 1), make_pair(1, 2), make_pair(2, 3), make_pair(3, 7),395 make_pair(0, 4), make_pair(4, 5), make_pair(5, 6), make_pair(6, 8),396 make_pair(9, 10),397 make_pair(12, 14), make_pair(14, 16), make_pair(16, 22), make_pair(16, 18), make_pair(16, 20), make_pair(18, 20),398 make_pair(11, 13), make_pair(13, 15), make_pair(15, 21), make_pair(15, 19), make_pair(15, 17), make_pair(17, 19),399 make_pair(11, 12), make_pair(11, 23), make_pair(23, 24), make_pair(24, 12),400 make_pair(24, 26), make_pair(26, 28), make_pair(28, 30), make_pair(28, 32), make_pair(30, 32),401 make_pair(23, 25), make_pair(25, 27),make_pair(27, 31), make_pair(27, 29), make_pair(29, 31) };402 for (auto p : segment)403 if (keeplandmarks.at<uchar>(p.first) && keeplandmarks.at<uchar>(p.second))404 line(image, Point(landmarks.row(p.first)), Point(landmarks.row(p.second)), Scalar(255, 255, 255), thickness);405 if (isDrawPoint)406 for (int idxRow = 0; idxRow < landmarks.rows; idxRow++)407 if (keeplandmarks.at<uchar>(idxRow))408 circle(image, Point(landmarks.row(idxRow)), thickness, Scalar(0, 0, 255), -1);409}410 411 412pair<Mat, Mat> visualize(Mat image, vector<tuple<Mat, Mat, Mat, Mat, Mat, float>> poses, float fps=-1)413{414 Mat displayScreen = image.clone();415 Mat display3d(400, 400, CV_8UC3, Scalar::all(0));416 line(display3d, Point(200, 0), Point(200, 400), Scalar(255, 255, 255), 2);417 line(display3d, Point(0, 200), Point(400, 200), Scalar(255, 255, 255), 2);418 putText(display3d, "Main View", Point(0, 12), FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 255));419 putText(display3d, "Top View", Point(200, 12), FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 255));420 putText(display3d, "Left View", Point(0, 212), FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 255));421 putText(display3d, "Right View", Point(200, 212), FONT_HERSHEY_DUPLEX, 0.5, Scalar(0, 0, 255));422 bool isDraw = false; // ensure only one person is drawn423 424 for (auto pose : poses)425 {426 Mat bbox = get<0>(pose);427 if (!bbox.empty())428 {429 Mat landmarksScreen = get<1>(pose);430 Mat landmarksWord = get<2>(pose);431 Mat mask;432 get<3>(pose).convertTo(mask, CV_8U);433 Mat heatmap = get<4>(pose);434 float conf = get<5>(pose);435 Mat edges;436 Canny(mask, edges, 100, 200);437 Mat kernel(2, 2, CV_8UC1, Scalar::all(1)); // expansion edge to 2 pixels438 dilate(edges, edges, kernel);439 Mat edgesBGR;440 cvtColor(edges, edgesBGR, COLOR_GRAY2BGR);441 Mat idxSelec = edges == 255;442 edgesBGR.setTo(Scalar(0, 255, 0), idxSelec);443 444 add(edgesBGR, displayScreen, displayScreen);445 // draw box446 Mat box;447 bbox.convertTo(box, CV_32S);448 449 rectangle(displayScreen, Point(box.row(0)), Point(box.row(1)), Scalar(0, 255, 0), 2);450 putText(displayScreen, format("Conf = %4f", conf), Point(0, 35), FONT_HERSHEY_DUPLEX, 0.7,Scalar(0, 0, 255), 2);451 if (fps > 0)452 putText(displayScreen, format("FPS = %.2f", fps), Point(0, 55), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 0, 255), 2);453 // Draw line between each key points454 landmarksScreen = landmarksScreen.rowRange(0, landmarksScreen.rows - 6);455 landmarksWord = landmarksWord.rowRange(0, landmarksWord.rows - 6);456 457 Mat keepLandmarks = landmarksScreen.col(4) > 0.8; // only show visible keypoints which presence bigger than 0.8458 459 Mat landmarksXY;460 landmarksScreen.colRange(0, 2).convertTo(landmarksXY, CV_32S);461 drawLines(displayScreen, landmarksXY, keepLandmarks, false);462 463 // z value is relative to HIP, but we use constant to instead464 for (int idxRow = 0; idxRow < landmarksScreen.rows; idxRow++)465 {466 Mat landmark;// p in enumerate(landmarks_screen[:, 0 : 3].astype(np.int32))467 landmarksScreen.row(idxRow).convertTo(landmark, CV_32S);468 if (keepLandmarks.at<uchar>(idxRow))469 circle(displayScreen, Point(landmark.at<int>(0), landmark.at<int>(1)), 2, Scalar(0, 0, 255), -1);470 }471 472 if (!isDraw)473 {474 isDraw = true;475 // Main view476 Mat landmarksXY = landmarksWord.colRange(0, 2).clone();477 Mat x = landmarksXY * 100 + 100;478 x.convertTo(landmarksXY, CV_32S);479 drawLines(display3d, landmarksXY, keepLandmarks, true, 2);480 481 // Top view482 Mat landmarksXZ;483 hconcat(landmarksWord.col(0), landmarksWord.col(2), landmarksXZ);484 landmarksXZ.col(1) = -landmarksXZ.col(1);485 x = landmarksXZ * 100;486 x.col(0) += 300;487 x.col(1) += 100;488 x.convertTo(landmarksXZ, CV_32S);489 drawLines(display3d, landmarksXZ, keepLandmarks, true, 2);490 491 // Left view492 Mat landmarksYZ;493 hconcat(landmarksWord.col(2), landmarksWord.col(1), landmarksYZ);494 landmarksYZ.col(0) = -landmarksYZ.col(0);495 x = landmarksYZ * 100;496 x.col(0) += 100;497 x.col(1) += 300;498 x.convertTo(landmarksYZ, CV_32S);499 drawLines(display3d, landmarksYZ, keepLandmarks, true, 2);500 501 // Right view502 Mat landmarksZY;503 hconcat(landmarksWord.col(2), landmarksWord.col(1), landmarksZY);504 x = landmarksZY * 100;505 x.col(0) += 300;506 x.col(1) += 300;507 x.convertTo(landmarksZY, CV_32S);508 drawLines(display3d, landmarksZY, keepLandmarks, true, 2);509 }510 }511 }512 return pair<Mat, Mat>(displayScreen, display3d);513}514 515 516 517int main(int argc, char** argv)518{519 CommandLineParser parser(argc, argv, keys);520 521 parser.about("Person Detector from MediaPipe");522 if (parser.has("help"))523 {524 parser.printMessage();525 return 0;526 }527 528 string model = parser.get<String>("model");529 float confThreshold = parser.get<float>("conf_threshold");530 float scoreThreshold = 0.5f;531 float nmsThreshold = 0.3f;532 int topK = 5000;533 bool vis = parser.get<bool>("vis");534 bool save = parser.get<bool>("save");535 int backendTargetid = parser.get<int>("backend");536 537 if (model.empty())538 {539 CV_Error(Error::StsError, "Model file " + model + " not found");540 }541 VideoCapture cap;542 if (parser.has("input"))543 cap.open(samples::findFile(parser.get<String>("input")));544 else545 cap.open(0);546 Mat frame;547 // person detector548 MPPersonDet modelNet("../person_detection_mediapipe/person_detection_mediapipe_2023mar.onnx", nmsThreshold, scoreThreshold, topK,549 backendTargetPairs[backendTargetid].first, backendTargetPairs[backendTargetid].second);550 // pose estimator551 MPPose poseEstimator(model, confThreshold, backendTargetPairs[backendTargetid].first, backendTargetPairs[backendTargetid].second);552 //! [Open a video file or an image file or a camera stream]553 if (!cap.isOpened())554 CV_Error(Error::StsError, "Cannot open video or file");555 556 static const std::string kWinName = "MPPose Demo";557 while (waitKey(1) < 0)558 {559 cap >> frame;560 if (frame.empty())561 {562 if (parser.has("input"))563 {564 cout << "Frame is empty" << endl;565 break;566 }567 else568 continue;569 }570 TickMeter tm;571 tm.start();572 Mat person = modelNet.infer(frame);573 tm.stop();574 vector<tuple<Mat, Mat, Mat, Mat, Mat, float>> pose;575 for (int idxRow = 0; idxRow < person.rows; idxRow++)576 {577 tuple<Mat, Mat, Mat, Mat, Mat, float> re = poseEstimator.infer(frame, person.row(idxRow));578 if (!get<0>(re).empty())579 pose.push_back(re);580 }581 cout << "Inference time: " << tm.getTimeMilli() << " ms\n";582 pair<Mat, Mat> duoimg = visualize(frame, pose, tm.getFPS());583 if (vis)584 {585 imshow(kWinName, get<0>(duoimg));586 imshow("3d", get<1>(duoimg));587 }588 }589 return 0;590}591 592 593Mat getMediapipeAnchor()594{595 Mat anchor= (Mat_<float>(2254,2) << 0.017857142857142856, 0.017857142857142856,596 0.017857142857142856, 0.017857142857142856,597 0.05357142857142857, 0.017857142857142856,598 0.05357142857142857, 0.017857142857142856,599 0.08928571428571429, 0.017857142857142856,600 0.08928571428571429, 0.017857142857142856,601 0.125, 0.017857142857142856,602 0.125, 0.017857142857142856,603 0.16071428571428573, 0.017857142857142856,604 0.16071428571428573, 0.017857142857142856,605 0.19642857142857142, 0.017857142857142856,606 0.19642857142857142, 0.017857142857142856,607 0.23214285714285715, 0.017857142857142856,608 0.23214285714285715, 0.017857142857142856,609 0.26785714285714285, 0.017857142857142856,610 0.26785714285714285, 0.017857142857142856,611 0.30357142857142855, 0.017857142857142856,612 0.30357142857142855, 0.017857142857142856,613 0.3392857142857143, 0.017857142857142856,614 0.3392857142857143, 0.017857142857142856,615 0.375, 0.017857142857142856,616 0.375, 0.017857142857142856,617 0.4107142857142857, 0.017857142857142856,618 0.4107142857142857, 0.017857142857142856,619 0.44642857142857145, 0.017857142857142856,620 0.44642857142857145, 0.017857142857142856,621 0.48214285714285715, 0.017857142857142856,622 0.48214285714285715, 0.017857142857142856,623 0.5178571428571429, 0.017857142857142856,624 0.5178571428571429, 0.017857142857142856,625 0.5535714285714286, 0.017857142857142856,626 0.5535714285714286, 0.017857142857142856,627 0.5892857142857143, 0.017857142857142856,628 0.5892857142857143, 0.017857142857142856,629 0.625, 0.017857142857142856,630 0.625, 0.017857142857142856,631 0.6607142857142857, 0.017857142857142856,632 0.6607142857142857, 0.017857142857142856,633 0.6964285714285714, 0.017857142857142856,634 0.6964285714285714, 0.017857142857142856,635 0.7321428571428571, 0.017857142857142856,636 0.7321428571428571, 0.017857142857142856,637 0.7678571428571429, 0.017857142857142856,638 0.7678571428571429, 0.017857142857142856,639 0.8035714285714286, 0.017857142857142856,640 0.8035714285714286, 0.017857142857142856,641 0.8392857142857143, 0.017857142857142856,642 0.8392857142857143, 0.017857142857142856,643 0.875, 0.017857142857142856,644 0.875, 0.017857142857142856,645 0.9107142857142857, 0.017857142857142856,646 0.9107142857142857, 0.017857142857142856,647 0.9464285714285714, 0.017857142857142856,648 0.9464285714285714, 0.017857142857142856,649 0.9821428571428571, 0.017857142857142856,650 0.9821428571428571, 0.017857142857142856,651 0.017857142857142856, 0.05357142857142857,652 0.017857142857142856, 0.05357142857142857,653 0.05357142857142857, 0.05357142857142857,654 0.05357142857142857, 0.05357142857142857,655 0.08928571428571429, 0.05357142857142857,656 0.08928571428571429, 0.05357142857142857,657 0.125, 0.05357142857142857,658 0.125, 0.05357142857142857,659 0.16071428571428573, 0.05357142857142857,660 0.16071428571428573, 0.05357142857142857,661 0.19642857142857142, 0.05357142857142857,662 0.19642857142857142, 0.05357142857142857,663 0.23214285714285715, 0.05357142857142857,664 0.23214285714285715, 0.05357142857142857,665 0.26785714285714285, 0.05357142857142857,666 0.26785714285714285, 0.05357142857142857,667 0.30357142857142855, 0.05357142857142857,668 0.30357142857142855, 0.05357142857142857,669 0.3392857142857143, 0.05357142857142857,670 0.3392857142857143, 0.05357142857142857,671 0.375, 0.05357142857142857,672 0.375, 0.05357142857142857,673 0.4107142857142857, 0.05357142857142857,674 0.4107142857142857, 0.05357142857142857,675 0.44642857142857145, 0.05357142857142857,676 0.44642857142857145, 0.05357142857142857,677 0.48214285714285715, 0.05357142857142857,678 0.48214285714285715, 0.05357142857142857,679 0.5178571428571429, 0.05357142857142857,680 0.5178571428571429, 0.05357142857142857,681 0.5535714285714286, 0.05357142857142857,682 0.5535714285714286, 0.05357142857142857,683 0.5892857142857143, 0.05357142857142857,684 0.5892857142857143, 0.05357142857142857,685 0.625, 0.05357142857142857,686 0.625, 0.05357142857142857,687 0.6607142857142857, 0.05357142857142857,688 0.6607142857142857, 0.05357142857142857,689 0.6964285714285714, 0.05357142857142857,690 0.6964285714285714, 0.05357142857142857,691 0.7321428571428571, 0.05357142857142857,692 0.7321428571428571, 0.05357142857142857,693 0.7678571428571429, 0.05357142857142857,694 0.7678571428571429, 0.05357142857142857,695 0.8035714285714286, 0.05357142857142857,696 0.8035714285714286, 0.05357142857142857,697 0.8392857142857143, 0.05357142857142857,698 0.8392857142857143, 0.05357142857142857,699 0.875, 0.05357142857142857,700 0.875, 0.05357142857142857,701 0.9107142857142857, 0.05357142857142857,702 0.9107142857142857, 0.05357142857142857,703 0.9464285714285714, 0.05357142857142857,704 0.9464285714285714, 0.05357142857142857,705 0.9821428571428571, 0.05357142857142857,706 0.9821428571428571, 0.05357142857142857,707 0.017857142857142856, 0.08928571428571429,708 0.017857142857142856, 0.08928571428571429,709 0.05357142857142857, 0.08928571428571429,710 0.05357142857142857, 0.08928571428571429,711 0.08928571428571429, 0.08928571428571429,712 0.08928571428571429, 0.08928571428571429,713 0.125, 0.08928571428571429,714 0.125, 0.08928571428571429,715 0.16071428571428573, 0.08928571428571429,716 0.16071428571428573, 0.08928571428571429,717 0.19642857142857142, 0.08928571428571429,718 0.19642857142857142, 0.08928571428571429,719 0.23214285714285715, 0.08928571428571429,720 0.23214285714285715, 0.08928571428571429,721 0.26785714285714285, 0.08928571428571429,722 0.26785714285714285, 0.08928571428571429,723 0.30357142857142855, 0.08928571428571429,724 0.30357142857142855, 0.08928571428571429,725 0.3392857142857143, 0.08928571428571429,726 0.3392857142857143, 0.08928571428571429,727 0.375, 0.08928571428571429,728 0.375, 0.08928571428571429,729 0.4107142857142857, 0.08928571428571429,730 0.4107142857142857, 0.08928571428571429,731 0.44642857142857145, 0.08928571428571429,732 0.44642857142857145, 0.08928571428571429,733 0.48214285714285715, 0.08928571428571429,734 0.48214285714285715, 0.08928571428571429,735 0.5178571428571429, 0.08928571428571429,736 0.5178571428571429, 0.08928571428571429,737 0.5535714285714286, 0.08928571428571429,738 0.5535714285714286, 0.08928571428571429,739 0.5892857142857143, 0.08928571428571429,740 0.5892857142857143, 0.08928571428571429,741 0.625, 0.08928571428571429,742 0.625, 0.08928571428571429,743 0.6607142857142857, 0.08928571428571429,744 0.6607142857142857, 0.08928571428571429,745 0.6964285714285714, 0.08928571428571429,746 0.6964285714285714, 0.08928571428571429,747 0.7321428571428571, 0.08928571428571429,748 0.7321428571428571, 0.08928571428571429,749 0.7678571428571429, 0.08928571428571429,750 0.7678571428571429, 0.08928571428571429,751 0.8035714285714286, 0.08928571428571429,752 0.8035714285714286, 0.08928571428571429,753 0.8392857142857143, 0.08928571428571429,754 0.8392857142857143, 0.08928571428571429,755 0.875, 0.08928571428571429,756 0.875, 0.08928571428571429,757 0.9107142857142857, 0.08928571428571429,758 0.9107142857142857, 0.08928571428571429,759 0.9464285714285714, 0.08928571428571429,760 0.9464285714285714, 0.08928571428571429,761 0.9821428571428571, 0.08928571428571429,762 0.9821428571428571, 0.08928571428571429,763 0.017857142857142856, 0.125,764 0.017857142857142856, 0.125,765 0.05357142857142857, 0.125,766 0.05357142857142857, 0.125,767 0.08928571428571429, 0.125,768 0.08928571428571429, 0.125,769 0.125, 0.125,770 0.125, 0.125,771 0.16071428571428573, 0.125,772 0.16071428571428573, 0.125,773 0.19642857142857142, 0.125,774 0.19642857142857142, 0.125,775 0.23214285714285715, 0.125,776 0.23214285714285715, 0.125,777 0.26785714285714285, 0.125,778 0.26785714285714285, 0.125,779 0.30357142857142855, 0.125,780 0.30357142857142855, 0.125,781 0.3392857142857143, 0.125,782 0.3392857142857143, 0.125,783 0.375, 0.125,784 0.375, 0.125,785 0.4107142857142857, 0.125,786 0.4107142857142857, 0.125,787 0.44642857142857145, 0.125,788 0.44642857142857145, 0.125,789 0.48214285714285715, 0.125,790 0.48214285714285715, 0.125,791 0.5178571428571429, 0.125,792 0.5178571428571429, 0.125,793 0.5535714285714286, 0.125,794 0.5535714285714286, 0.125,795 0.5892857142857143, 0.125,796 0.5892857142857143, 0.125,797 0.625, 0.125,798 0.625, 0.125,799 0.6607142857142857, 0.125,800 0.6607142857142857, 0.125,801 0.6964285714285714, 0.125,802 0.6964285714285714, 0.125,803 0.7321428571428571, 0.125,804 0.7321428571428571, 0.125,805 0.7678571428571429, 0.125,806 0.7678571428571429, 0.125,807 0.8035714285714286, 0.125,808 0.8035714285714286, 0.125,809 0.8392857142857143, 0.125,810 0.8392857142857143, 0.125,811 0.875, 0.125,812 0.875, 0.125,813 0.9107142857142857, 0.125,814 0.9107142857142857, 0.125,815 0.9464285714285714, 0.125,816 0.9464285714285714, 0.125,817 0.9821428571428571, 0.125,818 0.9821428571428571, 0.125,819 0.017857142857142856, 0.16071428571428573,820 0.017857142857142856, 0.16071428571428573,821 0.05357142857142857, 0.16071428571428573,822 0.05357142857142857, 0.16071428571428573,823 0.08928571428571429, 0.16071428571428573,824 0.08928571428571429, 0.16071428571428573,825 0.125, 0.16071428571428573,826 0.125, 0.16071428571428573,827 0.16071428571428573, 0.16071428571428573,828 0.16071428571428573, 0.16071428571428573,829 0.19642857142857142, 0.16071428571428573,830 0.19642857142857142, 0.16071428571428573,831 0.23214285714285715, 0.16071428571428573,832 0.23214285714285715, 0.16071428571428573,833 0.26785714285714285, 0.16071428571428573,834 0.26785714285714285, 0.16071428571428573,835 0.30357142857142855, 0.16071428571428573,836 0.30357142857142855, 0.16071428571428573,837 0.3392857142857143, 0.16071428571428573,838 0.3392857142857143, 0.16071428571428573,839 0.375, 0.16071428571428573,840 0.375, 0.16071428571428573,841 0.4107142857142857, 0.16071428571428573,842 0.4107142857142857, 0.16071428571428573,843 0.44642857142857145, 0.16071428571428573,844 0.44642857142857145, 0.16071428571428573,845 0.48214285714285715, 0.16071428571428573,846 0.48214285714285715, 0.16071428571428573,847 0.5178571428571429, 0.16071428571428573,848 0.5178571428571429, 0.16071428571428573,849 0.5535714285714286, 0.16071428571428573,850 0.5535714285714286, 0.16071428571428573,851 0.5892857142857143, 0.16071428571428573,852 0.5892857142857143, 0.16071428571428573,853 0.625, 0.16071428571428573,854 0.625, 0.16071428571428573,855 0.6607142857142857, 0.16071428571428573,856 0.6607142857142857, 0.16071428571428573,857 0.6964285714285714, 0.16071428571428573,858 0.6964285714285714, 0.16071428571428573,859 0.7321428571428571, 0.16071428571428573,860 0.7321428571428571, 0.16071428571428573,861 0.7678571428571429, 0.16071428571428573,862 0.7678571428571429, 0.16071428571428573,863 0.8035714285714286, 0.16071428571428573,864 0.8035714285714286, 0.16071428571428573,865 0.8392857142857143, 0.16071428571428573,866 0.8392857142857143, 0.16071428571428573,867 0.875, 0.16071428571428573,868 0.875, 0.16071428571428573,869 0.9107142857142857, 0.16071428571428573,870 0.9107142857142857, 0.16071428571428573,871 0.9464285714285714, 0.16071428571428573,872 0.9464285714285714, 0.16071428571428573,873 0.9821428571428571, 0.16071428571428573,874 0.9821428571428571, 0.16071428571428573,875 0.017857142857142856, 0.19642857142857142,876 0.017857142857142856, 0.19642857142857142,877 0.05357142857142857, 0.19642857142857142,878 0.05357142857142857, 0.19642857142857142,879 0.08928571428571429, 0.19642857142857142,880 0.08928571428571429, 0.19642857142857142,881 0.125, 0.19642857142857142,882 0.125, 0.19642857142857142,883 0.16071428571428573, 0.19642857142857142,884 0.16071428571428573, 0.19642857142857142,885 0.19642857142857142, 0.19642857142857142,886 0.19642857142857142, 0.19642857142857142,887 0.23214285714285715, 0.19642857142857142,888 0.23214285714285715, 0.19642857142857142,889 0.26785714285714285, 0.19642857142857142,890 0.26785714285714285, 0.19642857142857142,891 0.30357142857142855, 0.19642857142857142,892 0.30357142857142855, 0.19642857142857142,893 0.3392857142857143, 0.19642857142857142,894 0.3392857142857143, 0.19642857142857142,895 0.375, 0.19642857142857142,896 0.375, 0.19642857142857142,897 0.4107142857142857, 0.19642857142857142,898 0.4107142857142857, 0.19642857142857142,899 0.44642857142857145, 0.19642857142857142,900 0.44642857142857145, 0.19642857142857142,901 0.48214285714285715, 0.19642857142857142,902 0.48214285714285715, 0.19642857142857142,903 0.5178571428571429, 0.19642857142857142,904 0.5178571428571429, 0.19642857142857142,905 0.5535714285714286, 0.19642857142857142,906 0.5535714285714286, 0.19642857142857142,907 0.5892857142857143, 0.19642857142857142,908 0.5892857142857143, 0.19642857142857142,909 0.625, 0.19642857142857142,910 0.625, 0.19642857142857142,911 0.6607142857142857, 0.19642857142857142,912 0.6607142857142857, 0.19642857142857142,913 0.6964285714285714, 0.19642857142857142,914 0.6964285714285714, 0.19642857142857142,915 0.7321428571428571, 0.19642857142857142,916 0.7321428571428571, 0.19642857142857142,917 0.7678571428571429, 0.19642857142857142,918 0.7678571428571429, 0.19642857142857142,919 0.8035714285714286, 0.19642857142857142,920 0.8035714285714286, 0.19642857142857142,921 0.8392857142857143, 0.19642857142857142,922 0.8392857142857143, 0.19642857142857142,923 0.875, 0.19642857142857142,924 0.875, 0.19642857142857142,925 0.9107142857142857, 0.19642857142857142,926 0.9107142857142857, 0.19642857142857142,927 0.9464285714285714, 0.19642857142857142,928 0.9464285714285714, 0.19642857142857142,929 0.9821428571428571, 0.19642857142857142,930 0.9821428571428571, 0.19642857142857142,931 0.017857142857142856, 0.23214285714285715,932 0.017857142857142856, 0.23214285714285715,933 0.05357142857142857, 0.23214285714285715,934 0.05357142857142857, 0.23214285714285715,935 0.08928571428571429, 0.23214285714285715,936 0.08928571428571429, 0.23214285714285715,937 0.125, 0.23214285714285715,938 0.125, 0.23214285714285715,939 0.16071428571428573, 0.23214285714285715,940 0.16071428571428573, 0.23214285714285715,941 0.19642857142857142, 0.23214285714285715,942 0.19642857142857142, 0.23214285714285715,943 0.23214285714285715, 0.23214285714285715,944 0.23214285714285715, 0.23214285714285715,945 0.26785714285714285, 0.23214285714285715,946 0.26785714285714285, 0.23214285714285715,947 0.30357142857142855, 0.23214285714285715,948 0.30357142857142855, 0.23214285714285715,949 0.3392857142857143, 0.23214285714285715,950 0.3392857142857143, 0.23214285714285715,951 0.375, 0.23214285714285715,952 0.375, 0.23214285714285715,953 0.4107142857142857, 0.23214285714285715,954 0.4107142857142857, 0.23214285714285715,955 0.44642857142857145, 0.23214285714285715,956 0.44642857142857145, 0.23214285714285715,957 0.48214285714285715, 0.23214285714285715,958 0.48214285714285715, 0.23214285714285715,959 0.5178571428571429, 0.23214285714285715,960 0.5178571428571429, 0.23214285714285715,961 0.5535714285714286, 0.23214285714285715,962 0.5535714285714286, 0.23214285714285715,963 0.5892857142857143, 0.23214285714285715,964 0.5892857142857143, 0.23214285714285715,965 0.625, 0.23214285714285715,966 0.625, 0.23214285714285715,967 0.6607142857142857, 0.23214285714285715,968 0.6607142857142857, 0.23214285714285715,969 0.6964285714285714, 0.23214285714285715,970 0.6964285714285714, 0.23214285714285715,971 0.7321428571428571, 0.23214285714285715,972 0.7321428571428571, 0.23214285714285715,973 0.7678571428571429, 0.23214285714285715,974 0.7678571428571429, 0.23214285714285715,975 0.8035714285714286, 0.23214285714285715,976 0.8035714285714286, 0.23214285714285715,977 0.8392857142857143, 0.23214285714285715,978 0.8392857142857143, 0.23214285714285715,979 0.875, 0.23214285714285715,980 0.875, 0.23214285714285715,981 0.9107142857142857, 0.23214285714285715,982 0.9107142857142857, 0.23214285714285715,983 0.9464285714285714, 0.23214285714285715,984 0.9464285714285714, 0.23214285714285715,985 0.9821428571428571, 0.23214285714285715,986 0.9821428571428571, 0.23214285714285715,987 0.017857142857142856, 0.26785714285714285,988 0.017857142857142856, 0.26785714285714285,989 0.05357142857142857, 0.26785714285714285,990 0.05357142857142857, 0.26785714285714285,991 0.08928571428571429, 0.26785714285714285,992 0.08928571428571429, 0.26785714285714285,993 0.125, 0.26785714285714285,994 0.125, 0.26785714285714285,995 0.16071428571428573, 0.26785714285714285,996 0.16071428571428573, 0.26785714285714285,997 0.19642857142857142, 0.26785714285714285,998 0.19642857142857142, 0.26785714285714285,999 0.23214285714285715, 0.26785714285714285,1000 0.23214285714285715, 0.26785714285714285,1001 0.26785714285714285, 0.26785714285714285,1002 0.26785714285714285, 0.26785714285714285,1003 0.30357142857142855, 0.26785714285714285,1004 0.30357142857142855, 0.26785714285714285,1005 0.3392857142857143, 0.26785714285714285,1006 0.3392857142857143, 0.26785714285714285,1007 0.375, 0.26785714285714285,1008 0.375, 0.26785714285714285,1009 0.4107142857142857, 0.26785714285714285,1010 0.4107142857142857, 0.26785714285714285,1011 0.44642857142857145, 0.26785714285714285,1012 0.44642857142857145, 0.26785714285714285,1013 0.48214285714285715, 0.26785714285714285,1014 0.48214285714285715, 0.26785714285714285,1015 0.5178571428571429, 0.26785714285714285,1016 0.5178571428571429, 0.26785714285714285,1017 0.5535714285714286, 0.26785714285714285,1018 0.5535714285714286, 0.26785714285714285,1019 0.5892857142857143, 0.26785714285714285,1020 0.5892857142857143, 0.26785714285714285,1021 0.625, 0.26785714285714285,1022 0.625, 0.26785714285714285,1023 0.6607142857142857, 0.26785714285714285,1024 0.6607142857142857, 0.26785714285714285,1025 0.6964285714285714, 0.26785714285714285,1026 0.6964285714285714, 0.26785714285714285,1027 0.7321428571428571, 0.26785714285714285,1028 0.7321428571428571, 0.26785714285714285,1029 0.7678571428571429, 0.26785714285714285,1030 0.7678571428571429, 0.26785714285714285,1031 0.8035714285714286, 0.26785714285714285,1032 0.8035714285714286, 0.26785714285714285,1033 0.8392857142857143, 0.26785714285714285,1034 0.8392857142857143, 0.26785714285714285,1035 0.875, 0.26785714285714285,1036 0.875, 0.26785714285714285,1037 0.9107142857142857, 0.26785714285714285,1038 0.9107142857142857, 0.26785714285714285,1039 0.9464285714285714, 0.26785714285714285,1040 0.9464285714285714, 0.26785714285714285,1041 0.9821428571428571, 0.26785714285714285,1042 0.9821428571428571, 0.26785714285714285,1043 0.017857142857142856, 0.30357142857142855,1044 0.017857142857142856, 0.30357142857142855,1045 0.05357142857142857, 0.30357142857142855,1046 0.05357142857142857, 0.30357142857142855,1047 0.08928571428571429, 0.30357142857142855,1048 0.08928571428571429, 0.30357142857142855,1049 0.125, 0.30357142857142855,1050 0.125, 0.30357142857142855,1051 0.16071428571428573, 0.30357142857142855,1052 0.16071428571428573, 0.30357142857142855,1053 0.19642857142857142, 0.30357142857142855,1054 0.19642857142857142, 0.30357142857142855,1055 0.23214285714285715, 0.30357142857142855,1056 0.23214285714285715, 0.30357142857142855,1057 0.26785714285714285, 0.30357142857142855,1058 0.26785714285714285, 0.30357142857142855,1059 0.30357142857142855, 0.30357142857142855,1060 0.30357142857142855, 0.30357142857142855,1061 0.3392857142857143, 0.30357142857142855,1062 0.3392857142857143, 0.30357142857142855,1063 0.375, 0.30357142857142855,1064 0.375, 0.30357142857142855,1065 0.4107142857142857, 0.30357142857142855,1066 0.4107142857142857, 0.30357142857142855,1067 0.44642857142857145, 0.30357142857142855,1068 0.44642857142857145, 0.30357142857142855,1069 0.48214285714285715, 0.30357142857142855,1070 0.48214285714285715, 0.30357142857142855,1071 0.5178571428571429, 0.30357142857142855,1072 0.5178571428571429, 0.30357142857142855,1073 0.5535714285714286, 0.30357142857142855,1074 0.5535714285714286, 0.30357142857142855,1075 0.5892857142857143, 0.30357142857142855,1076 0.5892857142857143, 0.30357142857142855,1077 0.625, 0.30357142857142855,1078 0.625, 0.30357142857142855,1079 0.6607142857142857, 0.30357142857142855,1080 0.6607142857142857, 0.30357142857142855,1081 0.6964285714285714, 0.30357142857142855,1082 0.6964285714285714, 0.30357142857142855,1083 0.7321428571428571, 0.30357142857142855,1084 0.7321428571428571, 0.30357142857142855,1085 0.7678571428571429, 0.30357142857142855,1086 0.7678571428571429, 0.30357142857142855,1087 0.8035714285714286, 0.30357142857142855,1088 0.8035714285714286, 0.30357142857142855,1089 0.8392857142857143, 0.30357142857142855,1090 0.8392857142857143, 0.30357142857142855,1091 0.875, 0.30357142857142855,1092 0.875, 0.30357142857142855,1093 0.9107142857142857, 0.30357142857142855,1094 0.9107142857142857, 0.30357142857142855,1095 0.9464285714285714, 0.30357142857142855,1096 0.9464285714285714, 0.30357142857142855,1097 0.9821428571428571, 0.30357142857142855,1098 0.9821428571428571, 0.30357142857142855,1099 0.017857142857142856, 0.3392857142857143,1100 0.017857142857142856, 0.3392857142857143,1101 0.05357142857142857, 0.3392857142857143,1102 0.05357142857142857, 0.3392857142857143,1103 0.08928571428571429, 0.3392857142857143,1104 0.08928571428571429, 0.3392857142857143,1105 0.125, 0.3392857142857143,1106 0.125, 0.3392857142857143,1107 0.16071428571428573, 0.3392857142857143,1108 0.16071428571428573, 0.3392857142857143,1109 0.19642857142857142, 0.3392857142857143,1110 0.19642857142857142, 0.3392857142857143,1111 0.23214285714285715, 0.3392857142857143,1112 0.23214285714285715, 0.3392857142857143,1113 0.26785714285714285, 0.3392857142857143,1114 0.26785714285714285, 0.3392857142857143,1115 0.30357142857142855, 0.3392857142857143,1116 0.30357142857142855, 0.3392857142857143,1117 0.3392857142857143, 0.3392857142857143,1118 0.3392857142857143, 0.3392857142857143,1119 0.375, 0.3392857142857143,1120 0.375, 0.3392857142857143,1121 0.4107142857142857, 0.3392857142857143,1122 0.4107142857142857, 0.3392857142857143,1123 0.44642857142857145, 0.3392857142857143,1124 0.44642857142857145, 0.3392857142857143,1125 0.48214285714285715, 0.3392857142857143,1126 0.48214285714285715, 0.3392857142857143,1127 0.5178571428571429, 0.3392857142857143,1128 0.5178571428571429, 0.3392857142857143,1129 0.5535714285714286, 0.3392857142857143,1130 0.5535714285714286, 0.3392857142857143,1131 0.5892857142857143, 0.3392857142857143,1132 0.5892857142857143, 0.3392857142857143,1133 0.625, 0.3392857142857143,1134 0.625, 0.3392857142857143,1135 0.6607142857142857, 0.3392857142857143,1136 0.6607142857142857, 0.3392857142857143,1137 0.6964285714285714, 0.3392857142857143,1138 0.6964285714285714, 0.3392857142857143,1139 0.7321428571428571, 0.3392857142857143,1140 0.7321428571428571, 0.3392857142857143,1141 0.7678571428571429, 0.3392857142857143,1142 0.7678571428571429, 0.3392857142857143,1143 0.8035714285714286, 0.3392857142857143,1144 0.8035714285714286, 0.3392857142857143,1145 0.8392857142857143, 0.3392857142857143,1146 0.8392857142857143, 0.3392857142857143,1147 0.875, 0.3392857142857143,1148 0.875, 0.3392857142857143,1149 0.9107142857142857, 0.3392857142857143,1150 0.9107142857142857, 0.3392857142857143,1151 0.9464285714285714, 0.3392857142857143,1152 0.9464285714285714, 0.3392857142857143,1153 0.9821428571428571, 0.3392857142857143,1154 0.9821428571428571, 0.3392857142857143,1155 0.017857142857142856, 0.375,1156 0.017857142857142856, 0.375,1157 0.05357142857142857, 0.375,1158 0.05357142857142857, 0.375,1159 0.08928571428571429, 0.375,1160 0.08928571428571429, 0.375,1161 0.125, 0.375,1162 0.125, 0.375,1163 0.16071428571428573, 0.375,1164 0.16071428571428573, 0.375,1165 0.19642857142857142, 0.375,1166 0.19642857142857142, 0.375,1167 0.23214285714285715, 0.375,1168 0.23214285714285715, 0.375,1169 0.26785714285714285, 0.375,1170 0.26785714285714285, 0.375,1171 0.30357142857142855, 0.375,1172 0.30357142857142855, 0.375,1173 0.3392857142857143, 0.375,1174 0.3392857142857143, 0.375,1175 0.375, 0.375,1176 0.375, 0.375,1177 0.4107142857142857, 0.375,1178 0.4107142857142857, 0.375,1179 0.44642857142857145, 0.375,1180 0.44642857142857145, 0.375,1181 0.48214285714285715, 0.375,1182 0.48214285714285715, 0.375,1183 0.5178571428571429, 0.375,1184 0.5178571428571429, 0.375,1185 0.5535714285714286, 0.375,1186 0.5535714285714286, 0.375,1187 0.5892857142857143, 0.375,1188 0.5892857142857143, 0.375,1189 0.625, 0.375,1190 0.625, 0.375,1191 0.6607142857142857, 0.375,1192 0.6607142857142857, 0.375,1193 0.6964285714285714, 0.375,1194 0.6964285714285714, 0.375,1195 0.7321428571428571, 0.375,1196 0.7321428571428571, 0.375,1197 0.7678571428571429, 0.375,1198 0.7678571428571429, 0.375,1199 0.8035714285714286, 0.375,1200 0.8035714285714286, 0.375,