diff --git a/README.md b/README.md index a49ae12..dfb0fb8 100644 --- a/README.md +++ b/README.md @@ -1,102 +1,57 @@ # MocapNET Project -![MocapNET](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/mnet2.png) +![MocapNET](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet4/doc/method.png) +Finishing my PhD this will probably be the *final* version of MocapNET! +MocapNET 4 will deal with upperbody / lowerbody / hands / eye tracking and / facial capture +It has a written from scratch python interface, but maintain the same compatible BVH output format. +It will also be compatible with Raspberry Pi 4 and use Tensorflow /Tf-Lite / ONNX backends + +This branch is still under construction, and has been ported to Python to boost usability +so if you want the older C/C++ version of MocapNET you ignore it for now..! -## News ------------------------------------------------------------------- - -30-12-2022 - -MocapNET has a new [plugin/script](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/python/blender/blender_mocapnet.py) for the [Blender](https://www.blender.org/) 3D editor that when combined with [MPFB2](http://static.makehumancommunity.org/mpfb.html) (the MakeHuman addon for Blender) can make 3D animations using custom skinned humans with the output BVH files of MocapNET with a few clicks. The code targets recent Blender Versions 3.4+ -Watch [this video](https://www.youtube.com/watch?v=9jmTdhVjAsI) to learn how to install MPFB2 and [this video](https://www.youtube.com/watch?v=ooLRUS5j4AI) to learn how to interface the provided plugin in this repository with the MocapNET output BVH file and the generated MakeHuman armature. - -![MocapNET Blender Plugin](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/blenderscript.jpg) - -[![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/blenderytb.png)](https://www.youtube.com/watch?v=ooLRUS5j4AI) - - -30-9-2022 -MocapNET was demonstrated at the [Foundation of Research and technology of Greece](https://www.forth.gr/en/home/) as part of the [European Researcher's Night 2022 event](https://cordis.europa.eu/programme/id/HORIZON_HORIZON-MSCA-2022-CITIZENS-01-01). -![Researcher's Night 2022](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/ren2022.jpg) +![MocapNET](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet4/doc/ICCV2023_Presentation_Slide18.png) -1-6-2022 -An added [python/mediapipe utility](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/master/src/python/mediapipe) can now help with generating 2D data for experiments! -This can help you create datasets that include hands that can be processed using [MocapNETv3](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet3) +## Deploy it now on Google Colab with a single click! +------------------------------------------------------------------ -7-4-2022 - -The open call of [BONSAPPS (https://bonsapps.eu/)](https://bonsapps.eu/) for AI talents received 126 proposals from 31 EU countries. -Out of these proposals, 30 were actually accepted. -Out of the 30 running BONSAPPs projects, 10 were selected yesterday to continue into phase 2. -I am very happy to report that our AUTO-MNET MocapNET based work made it to the top ten! - -![BonsAPPs Hackathon/Stage 2 selection](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/bonsappshackathon.jpg) - +Click here for one click setup : [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/mocapnet4.ipynb) -9-3-2022 -MocapNET was one of the selected projects in the [BonsAPPs Open Call for AI talents](https://bonsapps-1oc-ai-talents.fundingbox.com/) -We are now preparing a version of MocapNET called AUTO-MNET tailored for [3D Body Tracking for automotive uses](https://s3.amazonaws.com/fundingbox-sites/gear%2F1635238346063-BonsAPPs_Guide+for+Applicants+%5BOC1%5D_published.pdf) -Due to our limited resources this has currently pushed back merging of the [mnet3 branch](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet3), however hopefully we will soon have a working MocapNET in the [Bonseyes platform](https://beta.bonseyes.com/). +## Relevant publications! +------------------------------------------------------------------ -8-11-2021 -MocapNET3 with hand pose estimation support has landed in this repository! The latest version that has been accepted in BMVC2021 is now commited [in the mnet3 branch of this repository](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet3). Since however there is considerable code-polish missing and currently the 2D joint estimator offered does not contain hands there needs to be a transition to a 2D joint estimator like [Mediapipe Holistic](https://google.github.io/mediapipe/solutions/holistic) for a better live webcam demo. MocapNET3 will appear in [the 32nd British Machine Vision Conference](http://www.bmvc2021.com/) that will be held virtually and is free to attend this year!! +| Download Paper | Year | Conference | Title | +| ------------- | ------------- | ------------- | ------------- | +| [![A Unified Approach for Occlusion Tolerant 3D Facial Pose Capture and Gaze Estimation using MocapNETs](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/pdf.png?raw=true)](http://users.ics.forth.gr/~argyros/mypapers/2023_10_AMFG_Qammaz.pdf) | 2023 | AMFG@ICCV | A Unified Approach for Occlusion Tolerant 3D Facial Pose Capture and Gaze Estimation using MocapNETs | +| [![Compacting MocapNET-based 3D Human Pose Estimation via Dimensionality Reduction](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/pdf.png?raw=true)](http://users.ics.forth.gr/~argyros/mypapers/2023_07_PETRA_Qammaz.pdf) | 2023 | PeTRA | Compacting MocapNET-based 3D Human Pose Estimation via Dimensionality Reduction | +| [![Towards Holistic Real-time Human 3D Pose Estimation using MocapNETs](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/pdf.png?raw=true)](http://users.ics.forth.gr/~argyros/mypapers/2021_11_BMVC_Qammaz.pdf) | 2021 | BMVC | Towards Holistic Real-time Human 3D Pose Estimation using MocapNETs | +| [![Occlusion-tolerant and personalized 3D human pose estimation in RGB images](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/pdf.png?raw=true)](http://users.ics.forth.gr/~argyros/mypapers/2021_01_ICPR_Qammaz.pdf) | 2021 | ICPR | Occlusion-tolerant and personalized 3D human pose estimation in RGB images | +| [![MocapNET: Ensemble of SNN Encoders for 3D Human Pose Estimation in RGB Images](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/pdf.png?raw=true)](http://users.ics.forth.gr/~argyros/mypapers/2019_09_BMVC_mocapnet.pdf) | 2019 | BMVC | MocapNET: Ensemble of SNN Encoders for 3D Human Pose Estimation in RGB Images | -An [upgraded 2020 version of MocapNET](https://github.com/FORTH-ModelBasedTracker/MocapNET/milestone/1) has landed! It contains a very big list of improvements that have been carried out during 2020 over the original work that allows higher accuracy, smoother BVH output and better occlusion robustness while maintaining realtime perfomance. MocapNET2 will appear in [the 25th International Conference on Pattern Recognition](https://www.icpr2020.it/) -If you are interested in the older MocapNET v1 release you can find it in the [mnet1 branch](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet1), -Visualization Example: -With MocapNET2 an [RGB video feed like this](https://www.youtube.com/watch?v=Orb4pawcfFY#t=10m) can be converted to BVH motion frames in real-time. The result can be easily used in your favourite 3D engine or application. -![Sample run](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/shuffle.gif) -Example Output: -| Youtube Video | MocapNET Output | Editing on Blender | -| ------------- | ------------- | ------------- | -| [![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/youtube.png)](https://www.youtube.com/watch?v=GtJct8nKjcc) | [![BVH File](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/bvh.png)](http://ammar.gr/mocapnet/mnet2/sept2020version.bvh) | [![Blender Video](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/blender.png)](http://ammar.gr/mocapnet/mnet2/sept2020versionBlender.ogv) | -## Ensemble of SNN Encoders for 3D Human Pose Estimation in RGB Images +## AMFG@ICCV 2023 Poster ------------------------------------------------------------------ -We present MocapNET v2, a real-time method that estimates the 3D human pose directly in the popular [Bio Vision Hierarchy (BVH)](https://en.wikipedia.org/wiki/Biovision_Hierarchy) format, given estimations of the 2D body joints originating from monocular color images. - -Our contributions include: - - * A novel and compact 2D pose [NSRM representation](https://www.youtube.com/watch?v=Jgz1MRq-I-k#t=27s). - * A human body orientation classifier and an ensemble of orientation-tuned neural networks that regress the 3D human pose by also allowing for the decomposition of the body to an upper and lower kinematic hierarchy. This permits the recovery of the human pose even in the case of significant occlusions. - * An efficient Inverse Kinematics solver that refines the neural-network-based solution providing 3D human pose estimations that are consistent with the limb sizes of a target person (if known). +![Our Poster in the Analysis and Modeling of Faces and Gestures Workshop @ ICCV 2023 ](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet4/doc/ICCV2023_MocapNET4_poster.png?raw=true) -All the above yield a 33\% accuracy improvement on the [Human 3.6 Million (H3.6M)](http://vision.imar.ro/human3.6m/description.php) dataset compared to the baseline method ([MocapNET v1](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet1)) while maintaining real-time performance (70 fps in CPU-only execution). -![MocapNET](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet2/doc/leedsDataset.jpg) - - -## Youtube Videos ------------------------------------------------------------------- - -| BMVC 2021 Supplementary Video | ICPR 2020 Poster Session | -| ------------- | ------------- | -| [![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/BMVC21YoutubeVideo.png) ](https://www.youtube.com/watch?v=aaLOSY_p6Zc) | [![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/ICPR2020_posterYoutubeVideoLink.png) ](https://www.youtube.com/watch?v=mns2s4xUC7c) | - -| ICPR 2020 Supplementary Video | BMVC 2019 Supplementary Video | -| ------------- | ------------- | -| [![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/youtubevideolink2.jpg) ](https://www.youtube.com/watch?v=Jgz1MRq-I-k) | [![YouTube Link](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/youtubevideolink.jpg) ](https://www.youtube.com/watch?v=fH5e-KMBvM0) | - ------------------------------------------------------------------- @@ -104,11 +59,20 @@ All the above yield a 33\% accuracy improvement on the [Human 3.6 Million (H3.6M ## Citation ------------------------------------------------------------------ -Please cite the following papers [1](http://users.ics.forth.gr/~argyros/mypapers/2021_11_BMVC_Qammaz.pdf), [2](http://users.ics.forth.gr/~argyros/mypapers/2021_01_ICPR_Qammaz.pdf), [3](http://users.ics.forth.gr/~argyros/mypapers/2019_09_BMVC_mocapnet.pdf) according to the part of this work that helps your research : - - +Please cite the following papers if this work helps your research : +``` +@inproceedings{Qammaz2023b, + author = {Qammaz, Ammar and Argyros, Antonis}, + title = {A Unified Approach for Occlusion Tolerant 3D Facial Pose Capture and Gaze Estimation using MocapNETs}, + booktitle = {International Conference on Computer Vision Workshops (AMFG 2023 - ICCVW 2023), (to appear)}, + publisher = {IEEE}, + year = {2023}, + month = {October}, + address = {Paris, France}, + projects = {VMWARE,I.C.HUMANS}, + pdflink = {http://users.ics.forth.gr/ argyros/mypapers/2023_10_AMFG_Qammaz.pdf} +} -``` @inproceedings{Qammaz2021, author = {Qammaz, Ammar and Argyros, Antonis A}, title = {Towards Holistic Real-time Human 3D Pose Estimation using MocapNETs}, @@ -119,10 +83,10 @@ Please cite the following papers [1](http://users.ics.forth.gr/~argyros/mypapers projects = {I.C.HUMANS}, videolink = {https://www.youtube.com/watch?v=aaLOSY_p6Zc} } + ``` For the BMVC21 version of MocapNET please [switch to the MNET3 branch](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet3) - ``` @inproceedings{Qammaz2020, author = {Ammar Qammaz and Antonis A. Argyros}, @@ -138,6 +102,7 @@ For the BMVC21 version of MocapNET please [switch to the MNET3 branch](https://g ``` +For the original BMVC19 version of MocapNET please [switch to the MNET1 branch](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet1), unfortunately Tensorflow 1 is not well supported in recent environments so it is difficult to set it up ``` @inproceedings{Qammaz2019, author = {Qammaz, Ammar and Argyros, Antonis A}, @@ -156,308 +121,6 @@ For the BMVC21 version of MocapNET please [switch to the MNET3 branch](https://g -## Overview, System Requirements and Dependencies ------------------------------------------------------------------- -MocapNET is a high performance 2D to 3D single person pose estimator. -This code base targets recent Linux (Ubuntu 18.04 - 20.04 +) machines, and relies on the Tensorflow C-API and OpenCV. Windows 10 users can try the [linux subsystem](https://www.microsoft.com/en-us/p/ubuntu-1804-lts/9n9tngvndl3q?rtc=1&activetab=pivot:overviewtab) that has been also [reported](https://github.com/FORTH-ModelBasedTracker/MocapNET/issues/10) to work. - -Tensorflow is used as the Neural Network framework for our work and OpenCV is used to enable the acquisition of images from webcams or video files as well as to provide an easy visualization method. - -We have provided an [initialization script](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/initialize.sh) that automatically handles most dependencies, as well as download all needed pretrained models. After running it the application should be ready for use. To examine the neural network .pb files provided you can [download](https://github.com/lutzroeder/netron/releases/) and use [Netron](https://github.com/lutzroeder/netron). - -Any issues not automatically resolved by the script can be reported on the [issues](https://github.com/FORTH-ModelBasedTracker/MocapNET/issues) section of this repository. - -This repository contains 2D joint estimators for the [MocapNET2LiveWebcamDemo](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET2/MocapNET2LiveWebcamDemo/livedemo.cpp). By giving it the [correct parameters](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET2/MocapNETLib2/applicationLogic/parseCommandlineOptions.cpp#L117) you can switch between a cut-down version of OpenPose (--openpose), VNect (--vnect) or our own MobileNet (default) based 2D joint estimator. All of these are automatically downloaded using the [initialize.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/initialize.sh) script. However in order to achieve higher accuracy estimations you are advised to set up a full [OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose) instance and use it to acquire JSON files with 2D detections that can be subsequently converted to CSV using [convertOpenPoseJSONToCSV](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/master/src/MocapNET2/Converters/Openpose) and then to 3D BVH files using the [MocapNET2CSV](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/master/src/MocapNET2/MocapNETFromCSV) binary. They will provide superior accuracy compared to the bundled 2D joint detectors which are provided for faster performance in the live demo, since 2D estimation is the bottleneck of the application. Our live demo will try to run the 2D Joint estimation on your GPU and MocapNET 3D estimation on the system CPU to achieve a combined framerate of over 30 fps which in most systems matches or surpasses the acquisition rate of web cameras. Unfortunately there are many GPU compatibility issues with Tensorflow C-API builds since recent versions have dropped CUDA 9.0 support as well as compute capabilities that might be required by your system, you can edit the [initialize.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/initialize.sh) script and change the variable TENSORFLOW_VERSION according to your needs. If you want CUDA 9.0 you should se it to 1.12.0. If you want CUDA 9.0 and have a card with older compute capabilities (5.2) then choose version 1.11.0. If all else fails you can always [recompile the tensorflow C-API](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tools/lib_package/README.md) to match your specific hardware configuration. You can also [use this script that automates building tensorflow r1.15](https://github.com/AmmarkoV/MyScripts/blob/master/Tensorflow/tensorflowBuild.sh) that might help you, dealing with the Bazel build system and all of its weirdness. Release 1.15 is the final of the 1.x tensorflow tree and is compatible with MocapNET, Tensorflow 2.x is also supported, according to the [Tensorflow site, version 2.3](https://www.tensorflow.org/install/lang_c) is the first version of the 2.x tree to re-include C bindings. The [initialize.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/initialize.sh) script will ask you which version you want to use and try to download it and set it up locally for your MocapNET installation. - - -If you are interested in generating BVH training data for your research, we have also provided the code that handles randomization and pose perturbation from the CMU dataset. After a successful compilation, dataset generation is accessible using the scripts [scripts/createRandomizedDataset.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/createRandomizedDataset.sh) and [scripts/createTestDataset.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/createTestDataset.sh). All BVH manipulation code is imported from a secondary [github project](https://github.com/AmmarkoV/RGBDAcquisition/tree/master/opengl_acquisition_shared_library/opengl_depth_and_color_renderer/src/Library/MotionCaptureLoader) that is automatically downloaded, included and built using the [initialize.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/initialize.sh) script. These [scripts/createRandomizedDataset.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/createRandomizedDataset.sh) and [scripts/createTestDataset.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/createTestDataset.sh) scripts will populate the dataset/ directory with CSV files that contain valid training samples based on the CMU dataset. It is [trivial](https://pythonspot.com/reading-csv-files-in-python/) to load these files using python. After loading them using them as training samples in conjunction with a deep learning framework like [Keras](https://keras.io/) you can facilitate learning of 2D to 3D BVH. - -## Building the library ------------------------------------------------------------------- - -To download and compile the library issue : - -``` -sudo apt-get install git build-essential cmake libopencv-dev libjpeg-dev libpng-dev libglew-dev libpthread-stubs0-dev - -git clone https://github.com/FORTH-ModelBasedTracker/MocapNET - -cd MocapNET - -./initialize.sh - -``` - -After performing changes to the source code, you do not need to rerun the initialization script. You can recompile the code by using : - -``` -cd build -cmake .. -make -cd .. -``` - - - -## Updating the library ------------------------------------------------------------------- - -The MocapNET library is under active development, the same thing is true for its dependencies. - -In order to update all the relevant parts of the code you can use the [update.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/update.sh) script provided. - -``` -./update.sh -``` - -If you made changes to the source code that you want to discard and want to revert to the master you can also use the [revert.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/revert.sh) script provided - -``` -./revert.sh -``` - - - - -## Testing the library and performing benchmarks ------------------------------------------------------------------- - -To test your OpenCV installation as well as support of your webcam issue : -``` -./OpenCVTest --from /dev/video0 -``` - -To test OpenCV support of your video files issue : -``` -./OpenCVTest --from /path/to/yourfile.mp4 -``` - -These tests only use OpenCV (without Tensorflow or any other dependencies) and are intended as a quick method that can identify and debug configuration problems on your system. -In case of problems playing back video files or your webcam you might want to consider compiling OpenCV yourself. The [scripts/getOpenCV.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/getOpenCV.sh) script has been included to automatically fetch and make OpenCV for your convinience. The CMake file provided will automatically try to set the OpenCV_DIR variable to target the locally built version made using the script. If you are having trouble switching between the system version and the downloaded version consider using the cmake-gui utility or removing the build directory and making a fresh one, once again following the Building instructions. The new build directory should reset all paths and automatically see the local OpenCV version if you used the [scripts/getOpenCV.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/getOpenCV.sh) script and use this by default. - -## Live Demo ------------------------------------------------------------------- - -Assuming that the OpenCVTest executable described previously is working correctly with your input source, to do a live test of the MocapNET library using a webcam issue : - -``` -./MocapNET2LiveWebcamDemo --from /dev/video0 --live -``` - -To dump 5000 frames from the webcam to out.bvh instead of the live directive issue : - -``` -./MocapNET2LiveWebcamDemo --from /dev/video0 --frames 5000 -``` - -To control the resolution of your webcam you can use the --size width height parameter, make sure that the resolution you provide is supported by your webcam model. You can use the v4l2-ctl tool by executing it and examining your supported sensor sizes and rates. By issuing --forth you can use our FORTH developed 2D joint estimator that performs faster but offers lower accuracy - -``` - v4l2-ctl --list-formats-ext -./MocapNET2LiveWebcamDemo --from /dev/video0 --live --forth --size 800 600 -``` - - -Testing the library using a pre-recorded video file (i.e. not live input) means you can use a slower but more precise 2D Joint estimation algorithm like the included OpenPose implementation. You should keep in mind that [this OpenPose implementation](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET1/MocapNETLiveWebcamDemo/utilities.cpp#L213) does not use PAFs and so it is still not as precise as the official OpenPose implementation. To run the demo with a prerecorded file issue : - -``` -./MocapNET2LiveWebcamDemo --from /path/to/yourfile.mp4 --openpose -``` - -We have included a [video file](http://ammar.gr/mocapnet/shuffle.webm) that should be automatically downloaded by the initialize.sh script. Issuing the following command should run it and produce an out.bvh file even if you don't have any webcam or other video files available! : - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --frames 375 -``` - - -Since high-framerate output is hard to examine, if you need some more time to elaborate on the output you can use the delay flag to add programmable delays between frames. Issuing the following will add 1 second of delay after each processed frame : - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --frames 375 --delay 1000 -``` - -If your target is a headless environment then you might consider deactivating the visualization by passing the runtime argument --novisualization. This will prevent any windows from opening and thus not cause issues even on a headless environment. - -BVH output files are stored to the "out.bvh" file by default. If you want them to be stored in a different path use the -o option. They can be easily viewed using a variety of compatible applicatons. We suggest [Blender](https://www.blender.org/) which is a very powerful open-source 3D editing and animation suite or [BVHacker](https://www.bvhacker.com/) that is freeware and compatible with [Wine](https://wiki.winehq.org/) - - -![MocapNETLiveWebcamDemo default visualization](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet2/doc/show0.jpg) - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --show 0 --frames 375 -``` - -![MocapNETLiveWebcamDemo all-in-one visualization](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet2/doc/show3.jpg) - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --show 3 --frames 375 -``` - - -![MocapNETLiveWebcamDemo rotation per joint visualization](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet2/doc/show1.jpg) - - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --show 1 --frames 375 -``` - - -By using the --show variable you can alternate between different visualizations. A particularly useful visualization is the "--show 1" one that plots the joint rotations as seen above. - -![MocapNETLiveWebcamDemo OpenGL visualization](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/mnet2/doc/show0ogl.jpg) - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --show 0 --opengl --frames 375 -``` -By executing "sudo apt-get install freeglut3-dev" to get the required libraries, then enabling the ENABLE_OPENGL CMake configuration flag during compilation and using the --opengl flag when running the MocapNET2LiveWebcamDemo you can also see the experimental OpenGL visualization illustrated above, rendering a skinned mesh that was generated using [makehuman](http://www.makehumancommunity.org/). The BVH file armature used corresponds to the [CMU+Face](http://www.makehumancommunity.org/content/cmu_plus_face.html) armature of makehuman. - - - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --openpose --gestures --frames 375 -``` -By starting the live demo using the --gestures argument you can enable an experimental simple form of gesture detection as seen in the illustration above. Gestures are stored as [BVH files](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/master/dataset/gestures) and controlled through the [gestureRecognition.hpp](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET1/MocapNETLib/gestureRecognition.hpp#L18) file. A client application can register a callback as seen in the [demo](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET1/MocapNETLiveWebcamDemo/mocapNETLiveDemo.cpp#L50). The gesture detection code is experimental and has been included as a proof of concept, since due to our high-level output you can easily facilitate gesture detections by comparing subsequent BVH frames as [seen in the code](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET1/MocapNETLib/gestureRecognition.cpp#L148). That being said gestures where not a part of the original MocapNET papers. - - -## ROS (Robot Operating System) node ------------------------------------------------------------------- - -[![mocapnet_rosnode screenshot with rviz](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/mocapnet_rosnode/main/doc/screenshot.jpg)](https://github.com/FORTH-ModelBasedTracker/mocapnet_rosnode) - -If you are interested in [ROS](https://www.ros.org/) development and looking for a 3D pose estimator for your robot, you are in luck, MocapNET has a ROS node! You can [get it here](https://github.com/FORTH-ModelBasedTracker/mocapnet_rosnode)! - - - -## Tuning Hierarchical Coordinate Descent for accuracy/performance gains ------------------------------------------------------------------- - -As described in the paper, the Hierarchical Coordinate Descent Inverse Kinematics algorithm has various hyper-parameters that have been set to default values after experiments. Depending on your deployment scenarios you might to sacrifice some performance for better accuracy. You can do this by altering the IK tuning parameters by using the --ik switch - -A default run without the --ik switch is equivalent to a run using a learning rate of 0.01, 5 iterations, 30 epochs. The iterations variable has the biggest impact in performance. - -A normal run without the --ik flag is equivalent to - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --ik 0.01 5 30 -``` - -If you want a very high accuracy run and don't care about framerate as much consider -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --ik 0.01 15 40 -``` - -The IK module supports tailoring the model used for posed estimation to your liking using the "--changeJointDimensions neckLength torsoLength chestWidth shoulderToElbowLength elbowToHandLength waistWidth hipToKneeLength kneeToFootLength shoeLength as well as the focal length of your specific camera using "--focalLength fx fy" The following example will try to track the shuffle.webm sample assuming a body with feet 150% the normal size and a focal length of 600 on x and y - -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --ik 0.01 25 40 --changeJointDimensions 1.0 1.0 1.0 1.0 1.0 1.5 1.5 1.5 1.0 --focalLength 600 600 -``` - -If you don't care about fine results and just want a rough pose estimation extracted really fast you can completely switch the IK module off using -``` -./MocapNET2LiveWebcamDemo --from shuffle.webm --noik -``` - - - - -## Headless deployment ------------------------------------------------------------------- - -When deploying the code on headless environments like [Google Colab](https://github.com/FORTH-ModelBasedTracker/MocapNET/issues/33) where there is no display available you might experience errors like -``` -(3D Points Output:xxxx): Gtk-WARNING **: cannot open display: -``` - -To overcome these errors just use the --novisualization switch to disable visualization windows - - - - - -## Higher accuracy with relatively little work using Mediapipe Holistic ------------------------------------------------------------------- -To convert video files ready for use as input to MocapNET in a *relatively* easy way I have included a python converter that uses mediapipe/opencv to create the CSV files needed for MocapNET. - -![MediaPipe Video 2 CSV utility](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/mediapipeConverter.jpg) - -You can get mediapipe using this [src/python/mediapipe/setup.sh](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/python/mediapipe/setup.sh) script or by executing - -``` -pip install --user mediapipe opencv-python -``` - -The converter utility receives an input video stream and creates an output directory with all image frames and the CSV file with 2D joint estimations. - -After going to the root directory of the project -``` -python3 src/python/mediapipe/mediapipeHolistic2CSV.py --from shuffle.webm -o tester -``` - -After the conversion finishes you can process the generated "dataset" using MocapNET2CSV - -``` -./MocapNET2CSV --from tester-mpdata/2dJoints_mediapipe.csv --show 3 -``` -Due to the higher accuracy of [mediapipe holistic](https://google.github.io/mediapipe/solutions/holistic.html) (as well as inclusion of heads and hands which makes data forward compatible with the next versions of MocapNET) this might be a very useful tool to use in conjunction with MocapNET. In particular if you use this dumper be sure to checkout [MocapNET version 3](https://github.com/FORTH-ModelBasedTracker/MocapNET/tree/mnet3) that also supports hand pose estimation! - - - - - - - -## Higher accuracy with more work deploying Caffe/OpenPose and using OpenPose JSON files ------------------------------------------------------------------- - -In order to get higher accuracy output compared to the live demo which is more performance oriented, you can use OpenPose and the 2D output JSON files produced by it. The convertOpenPoseJSONToCSV application can convert them to a BVH file. After downloading [OpenPose](https://github.com/CMU-Perceptual-Computing-Lab/openpose) and building it you can use it to acquire 2D JSON body pose data by running : - -``` -build/examples/openpose/openpose.bin -number_people_max 1 --hand --write_json /path/to/outputJSONDirectory/ -video /path/to/yourVideoFile.mp4 -``` - -This will create files in the following fashion /path/to/outputJSONDirectory/yourVideoFile_XXXXXXXXXXXX_keypoints.json Notice that the filenames generated encode the serial number by padding it up to 12 characters (marked as X). You provide this information to our executable using the --seriallength commandline option. - -The [dump_and_process_video.sh script has been included](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/scripts/dump_and_process_video.sh) that can be used to fully process a video file using openpose and then process it through MocapNET, or act as a guide for this procedure. - -A utility has been included that can convert the JSON files to a single CSV file issuing : -``` - ./convertOpenPoseJSONToCSV --from /path/to/outputJSONDirectory/ --label yourVideoFile --seriallength 12 --size 1920 1080 -o . -``` -For more information on how to use the conversion utility please [see the documentation inside the utility](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET2/Converters/Openpose/convertOpenPoseJSONToCSV.cpp) - -A CSV file has been included that can be run by issuing : -``` - ./MocapNET2CSV --from dataset/sample.csv --visualize --delay 30 -``` -The delay is added in every frame so that there is enough time for the user to see the results, of course the visualization only contains the armature since the CSV file does not have the input images. - -Check out [this guide contributed by a project user](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/doc/OpenPose.md) for more info. - -## Experimental utilities ------------------------------------------------------------------- - -The repository contains experimental utilities used for the development of the papers. - - -The CSV cluster plot utility if you choose to download the CMU-BVH dataset using the ./initialize.sh script will allow you to perform the clustering experiments described. - -![CSV cluster plot utility](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/CSVClusterPlot.jpg) - -``` -./CSVClusterPlot -``` - - -The BVHGUI2 is a very minimal utility you can use to become more familiar with the BVH armature used by the project. Using easy to use sliders you can animate the armature and it is [has a minimal source code](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/src/MocapNET2/BVHGUI2/bvhGUI2.cpp). - -![BVH GUI utility](https://raw.githubusercontent.com/FORTH-ModelBasedTracker/MocapNET/master/doc/BVHGUI2.jpg) - -``` -./BVHGUI2 --opengl -``` - - ## License ------------------------------------------------------------------ This library is provided under the [FORTH license](https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/license.txt) diff --git a/dataset/gopro1080p.calib b/dataset/gopro1080p.calib new file mode 100644 index 0000000..1f1d28a --- /dev/null +++ b/dataset/gopro1080p.calib @@ -0,0 +1,37 @@ +%Calibration File v 1.0.3 - Matlab Load() compatible, https://support.stereolabs.com/hc/en-us/articles/360007497173-What-is-the-calibration-file- Scale Unit: 1.0 +%CameraID=0 +%CameraNo=0 +%Date= +%Width +1920 +%Height +1080 +%Description= +%Intrinsics I[1,1], I[1,2], I[1,3], I[2,1], I[2,2], I[2,3], I[3,1], I[3,2] I[3,3] +%I +1055.47 +0.0 +960.0 +0.0 +1055.15 +740.0 +0.0 +0.0 +1.0 +%Distortion D[1], D[2], D[3], D[4] D[5] +%D +0.07 +-0.18 +0.0 +0.0 +0.0 +%Translation T.X, T.Y, T.Z +%T +0 +0 +0 +%Rotation Vector (Rodrigues) R.X, R.Y, R.Z +%R +0 +0 +0 diff --git a/dataset/logitechC270_1280p.calib b/dataset/logitechC270_1280p.calib new file mode 100644 index 0000000..00383f2 --- /dev/null +++ b/dataset/logitechC270_1280p.calib @@ -0,0 +1,41 @@ +%Calibration File v 1.0.3 - Matlab Load() compatible, https://support.stereolabs.com/hc/en-us/articles/360007497173-What-is-the-calibration-file- Scale Unit: 1.0 +% CALIB_INTR_FX = 0 , +% CALIB_INTR_FY = 4 , +% CALIB_INTR_CX = 2 , +% CALIB_INTR_CY = 5 +%CameraID=0 +%CameraNo=0 +%Date= +%Width +1280 +%Height +720 +%Description= +%Intrinsics I[1,1], I[1,2], I[1,3], I[2,1], I[2,2], I[2,3], I[3,1], I[3,2] I[3,3] +%I +983.02 +0.0 +640 +0.0 +983.02 +360 +0.0 +0.0 +1.0 +%Distortion D[1], D[2], D[3], D[4] D[5] +%D +0.0 +0.0 +0.0 +0.0 +0.0 +%Translation T.X, T.Y, T.Z +%T +0 +0 +0 +%Rotation Vector (Rodrigues) R.X, R.Y, R.Z +%R +0 +0 +0 diff --git a/dataset/logitech_c920_720p.calib b/dataset/logitech_c920_720p.calib new file mode 100644 index 0000000..d9851b3 --- /dev/null +++ b/dataset/logitech_c920_720p.calib @@ -0,0 +1,37 @@ +%Calibration File v 1.0.3 - Matlab Load() compatible, https://support.stereolabs.com/hc/en-us/articles/360007497173-What-is-the-calibration-file- Scale Unit: 1.0 - Logitech c920 @ 1280x720 - https://github.com/AmmarkoV/RGBDAcquisition/blob/master/tools/Calibration/GoPROCalib.py +%CameraID=0 +%CameraNo=0 +%Date= +%Width +1280 +%Height +720 +%Description= +%Intrinsics I[1,1], I[1,2], I[1,3], I[2,1], I[2,2], I[2,3], I[3,1], I[3,2] I[3,3] +%I +989.59523845 +0.0 +663.71403205 +0.0 +974.40173272 +360.94085103 +0.0 +0.0 +1.0 +%Distortion D[1], D[2], D[3], D[4] D[5] +%D +0.07971379 +-0.35296092 +0.00312104 +0.00754752 +0.30976551 +%Translation T.X, T.Y, T.Z +%T +0 +0 +0 +%Rotation Vector (Rodrigues) R.X, R.Y, R.Z +%R +0 +0 +0 diff --git a/doc/ICCV2023_MocapNET4_poster.png b/doc/ICCV2023_MocapNET4_poster.png new file mode 100644 index 0000000..78aa2f3 Binary files /dev/null and b/doc/ICCV2023_MocapNET4_poster.png differ diff --git a/doc/ICCV2023_Presentation_Slide18.png b/doc/ICCV2023_Presentation_Slide18.png new file mode 100644 index 0000000..f1f4ca2 Binary files /dev/null and b/doc/ICCV2023_Presentation_Slide18.png differ diff --git a/doc/method.png b/doc/method.png new file mode 100644 index 0000000..8fc8543 Binary files /dev/null and b/doc/method.png differ diff --git a/doc/pdf.png b/doc/pdf.png new file mode 100644 index 0000000..367c766 Binary files /dev/null and b/doc/pdf.png differ diff --git a/initialize.sh b/initialize.sh index 195fafc..b845457 100755 --- a/initialize.sh +++ b/initialize.sh @@ -18,7 +18,7 @@ if [ "$ASK_QUESTIONS" -eq "0" ]; then else #Simple dependency checker that will apt-get stuff if something is missing # sudo apt-get install build-essential cmake libopencv-dev libjpeg-dev libpng-dev libglew-dev libpthread-stubs0-dev -SYSTEM_DEPENDENCIES="wget git build-essential cmake libopencv-dev libjpeg-dev libpng-dev libglew-dev libpthread-stubs0-dev" +SYSTEM_DEPENDENCIES="python3-pip python3-venv wget git build-essential cmake unzip libopencv-dev libjpeg-dev libpng-dev libglew-dev libpthread-stubs0-dev" #------------------------------------------------------------------------------ for REQUIRED_PKG in $SYSTEM_DEPENDENCIES do @@ -42,271 +42,6 @@ fi -#We generate a Linux desktop shortcut to easily start the live demo -echo "Generating shortcut" -echo "[Desktop Entry]" > mocapnet.desktop -echo "Type=Application" >> mocapnet.desktop -echo "Name=MocapNET Demo" >> mocapnet.desktop -echo "Version=1.0" >> mocapnet.desktop -echo "GenericName=MocapNET" >> mocapnet.desktop -echo "Icon=$ORIG_DIR/doc/icon.png" >> mocapnet.desktop -echo "Exec=$ORIG_DIR/MocapNET2LiveWebcamDemo --from /dev/video0 --live --dir \"$ORIG_DIR\"" >> mocapnet.desktop -echo "Terminal=false" >> mocapnet.desktop -echo "StartupNotify=false" >> mocapnet.desktop -echo "Categories=Application;Graphics;3DGraphics;2DGraphics;" >> mocapnet.desktop -chmod +x mocapnet.desktop - - -#unfortunately 2022 has not been kind on the internet and my server so I wont use my server -CMUDATASET_WEBSERVER="http://ammar.gr/datasets/" -DATASET_WEBSERVER="http://ammar.gr/datasets/" -OTHERFILE_WEBSERVER="http://ammar.gr/mocapnet/" - -#Instead files are now located on the CVRL FORTH server -CMUDATASET_WEBSERVER="http://cvrlcode.ics.forth.gr/web_share/mocapnet/" -DATASET_WEBSERVER="http://cvrlcode.ics.forth.gr/web_share/mocapnet/" -OTHERFILE_WEBSERVER="http://cvrlcode.ics.forth.gr/web_share/mocapnet/" - - -clear - -cd "$DIR" -if [ -f dataset/MotionCapture/READMEFIRST.txt ] -then -echo "CMU BVH datasets appear to have been downloaded.." -else - echo " Do you want to download the CMU BVH datasets ? " - echo "The download is approximately 1GB and uncompressed will take 4GB of disk space " - echo "(You probably don't need this if you dont want to use the GenerateGroundTruth/CSVClusterPlot utility)" - echo - echo -n " (Y/N)?" - - #Only ask if we can answer - #_____________________________ - if [ "$ASK_QUESTIONS" -eq "0" ]; then - answer="Y" - else - read answer - fi - #_____________________________ - - if test "$answer" != "N" -a "$answer" != "n"; - then - cd "$DIR/dataset" - echo "Could not find MotionCapture" - - #This is a richer armature that also contains provisons for head and feet animation - wget "$CMUDATASET_WEBSERVER/CMUPlusHeadMotionCapture.zip" - unzip CMUPlusHeadMotionCapture.zip - mv CMUPlusHeadMotionCapture.zip MotionCapture - - cd "$DIR" - fi -fi - - -#SWITCH DOWNLOAD BEHAVIOR -USE_GOOGLE_HOSTING="yes" - -if [ "$USE_GOOGLE_HOSTING" == "yes" ]; then - #Since June 8 2023, FORTH NOC has firewalled cvrldemo.ics.forth.gr and ammar.gr, - #as a result the old way to access files is not available.. - #this is a workaround until they fix this.. - #https://github.com/FORTH-ModelBasedTracker/MocapNET/issues/96 - cd "$DIR" - echo "Using Google Drive Hosting to retrieve required files.." - mkdir -p dataset/combinedModel/mocapnet2/mode5/1.0/ - mkdir -p dataset/combinedModel/mocapnet2/mode1/1.0/ - if [ ! -f allInOneMNET2RedistMirrorICPR2020.zip ]; then - wget -O allInOneMNET2RedistMirrorICPR2020.zip "drive.google.com/u/3/uc?id=1GtmPWOpf3MzhqhqegaC8cS3_m3Drp6y3&export=download&confirm=yes" - fi - unzip allInOneMNET2RedistMirrorICPR2020.zip -else - -echo "Using FORTH Hosting to retrieve required files.." -cd "$DIR" -#Force download of a Video sample -if [ ! -f shuffle.webm ]; then - wget "$OTHERFILE_WEBSERVER/shuffle.webm" -fi -#-------------------------------------------- - -if [ ! -f dataset/makehuman.tri ]; then - cd "$DIR/dataset" - #TRI is the internal 3D format used by my 3D renderer to handle 3D meshes - #https://github.com/AmmarkoV/RGBDAcquisition/blob/master/opengl_acquisition_shared_library/opengl_depth_and_color_renderer/src/Library/ModelLoader/model_loader_tri.h - wget "$OTHERFILE_WEBSERVER/makehuman.tri" - - #Also provide the OpenCollada file in case someone wants to create their own .tri by `sudo apt-get install libassimp-dev` and then compiling and using the project - # https://github.com/AmmarkoV/RGBDAcquisition/tree/master/opengl_acquisition_shared_library/opengl_depth_and_color_renderer/submodules/Assimp - #that you will find in $ROOT_DIR/dependencies/RGBDAcquisition/opengl_acquisition_shared_library/opengl_depth_and_color_renderer/submodules/Assimp/ - #./assimpTester --convert $ROOT_DIR/dataset/makehuman.dae $ROOT_DIR/dataset/makehuman.tri --paint 123 123 123 - #This dae file has been created usign makehuman(http://www.makehumancommunity.org/) and the CMU+Face Rig (http://www.makehumancommunity.org/content/cmu_plus_face.html) - wget "$OTHERFILE_WEBSERVER/makehuman.dae" -fi - -cd "$DIR" - - - - -cd "$DIR/dataset" -mkdir -p combinedModel/mocapnet2/mode5/1.0/ -cd "$DIR/dataset/combinedModel/mocapnet2/mode5/1.0/" - - -#New ICPR pretrained networks - -LIST_OF_NETWORKS="categorize_lowerbody_all.pb lowerbody_left.pb upperbody_left.pb categorize_upperbody_all.pb lowerbody_right.pb upperbody_right.pb lowerbody_back.pb upperbody_back.pb lowerbody_front.pb upperbody_front.pb" - -for NETWORK in $LIST_OF_NETWORKS; do -if [ ! -f $NETWORK ]; then - wget "$DATASET_WEBSERVER/icpr2020/$NETWORK" -fi -done - - - - -#-------------------------------------------------------------------- -cd "$DIR/combinedModel" -#-------------------------------------------------------------------- - -#We also downloar pre-trained models for the 2D joint estimation -#We have 3D flavours available, openpose, vnect and our own 2D detector -echo "Downloading 2D Joint Estimator models" -cd "$DIR/dataset/combinedModel" - -if [ ! -f openpose_model.pb ]; then - wget "$DATASET_WEBSERVER/combinedModel/openpose_model.pb" -fi - -if [ ! -f vnect_sm_pafs_8.1k.pb ]; then - wget "$DATASET_WEBSERVER/combinedModel/vnect_sm_pafs_8.1k.pb" -fi - -if [ ! -f mobnet2_tiny_vnect_sm_1.9k.pb ]; then - wget "$DATASET_WEBSERVER/combinedModel/mobnet2_tiny_vnect_sm_1.9k.pb" -fi - -cd "$DIR" - -#END OF FORTH HOSTING FILE RETRIEVAL -fi - - - - - -#Default Tensorflow to be downloaded is 2.x with CPU only stuff to improve compatibility -TENSORFLOW_VERSION="2.3.1" -ARCHITECTURE="cpu" #can be gpu or cpu -#https://www.tensorflow.org/install/lang_c -#https://github.com/tensorflow/tensorflow/tree/master/tensorflow/c - - -#Tensorflow 2.3.1 works well with CUDA 10 and cudnn-10.0-linux-x64-v7.6.5.32.tgz -#wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-2.3.1.tar.gz - -#if you want the latest version -#you can download it from https://storage.googleapis.com/tensorflow-nightly/github/tensorflow/lib_package/libtensorflow-gpu-linux-x86_64.tar.gz - -#========================================================================== -#========================================================================== -#========================================================================== -clear - echo " Do you want to use your GPU in Tensorflow ? " - echo "If you select Y a GPU-enabled version will be downloaded " - echo "If you don't have a CUDA-enabled GPU its best to select N" - echo "GPU execution is mainly imporant for the RGB->2D neural networks" - echo - echo -n " (Y/N)?" - - #Only ask if we can answer - #_____________________________ - if [ "$ASK_QUESTIONS" -eq "0" ]; then - answer="Y" - else - read answer - fi - #_____________________________ - - if test "$answer" != "N" -a "$answer" != "n"; - then - ARCHITECTURE="gpu" - fi -#========================================================================== -#========================================================================== -#========================================================================== -clear - echo " Do you want to use Tensorflow 1.x instead of 2.x ? " - echo "The project is compatible with both but if you have an older GPU it might be better for you " - echo "to stick with Tensorflow 1.x " - echo - echo -n " (Y/N)?" - - #Only ask if we can answer - #_____________________________ - if [ "$ASK_QUESTIONS" -eq "0" ]; then - answer="N" - else - read answer - fi - #_____________________________ - - - if test "$answer" != "N" -a "$answer" != "n"; - then - TENSORFLOW_VERSION="1.14.0" # 1.12.0 for CUDA 9.0 / 1.11.0 for CUDA9 with older compute capabilities (5.2) .. / 1.8.0 for CUDA9 and a device with compute capability 3.0 / 1.4.1 for CUDA 8 - fi -#========================================================================== -#========================================================================== -#========================================================================== -echo "Selected Tensorflow version $ARCHITECTURE/$TENSORFLOW_VERSION" - - -#I have a special version of tensorflow 1.11.0 tailored for Intel Core 2 and NVIDIA 7XX cards ( compute capabilities ) that you can find here -#wget http://ammar.gr/mocapnet/libtensorflow-oldgpu-linux-x86_64-1.11.0.tar.gz - -#I have a special version of tensorflow 1.15.2 built for i7 950 CPUs without later AVX instrucitons but CUDA 10.0 compute capability 3.5 + GPUs -#wget http://ammar.gr/mocapnet/libtensorflow-1-15.2_CPUi7_970_CUDA10.tar.gz - -cd "$DIR" -if [ -f /usr/local/lib/libtensorflow.so ]; then - echo "Found a system wide tensorflow installation, not altering anything" -elif [ -f dependencies/libtensorflow/lib/libtensorflow.so ]; then - echo "Found a local tensorflow installation, not altering anything" -else - echo "Did not find tensorflow already installed..!" - if [ ! -f dependencies/libtensorflow-$ARCHITECTURE-linux-x86_64-$TENSORFLOW_VERSION.tar.gz ]; then - echo "Did not find tensorflow tarball so will have to download it..!" - cd "$DIR/dependencies" - wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-$ARCHITECTURE-linux-x86_64-$TENSORFLOW_VERSION.tar.gz - #Is the Google link down ? we have a mirror :) - #wget http://ammar.gr/mocapnet/libtensorflow-gpu-linux-x86_64-$TENSORFLOW_VERSION.tar.gz - else - echo "The tensorflow tarball was already downloaded.." - fi - - -cd "$DIR" -if [ -f dependencies/libtensorflow-$ARCHITECTURE-linux-x86_64-$TENSORFLOW_VERSION.tar.gz ]; then - #Doing a local installation that requires no SUDO - cd "$DIR/dependencies" - mkdir libtensorflow - tar -C libtensorflow -xzf libtensorflow-$ARCHITECTURE-linux-x86_64-$TENSORFLOW_VERSION.tar.gz - #echo "Please give me sudo permissions to install Tensorflow $TENSORFLOW_VERSION C Bindings.." - #sudo tar -C /usr/local -xzf libtensorflow-gpu-linux-x86_64-$TENSORFLOW_VERSION.tar.gz - else - echo "Failed to download/extract tensorflow.." -fi - -fi -#--------------------------------------------------------------------------------------------------------------------------- - - - - cd "$DIR" if [ -f dependencies/RGBDAcquisition/README.md ]; then echo "RGBDAcquisition appears to already exist .." @@ -337,33 +72,18 @@ fi +cd "$DIR" +cd src/python/mnet4 +rm -rf BVH/ +ln -s ../../../dependencies/RGBDAcquisition/opengl_acquisition_shared_library/opengl_depth_and_color_renderer/src/Applications/BVHTester/ BVH +cd BVH +./makeLibrary.sh + +cd .. +#Install rest of python stuff.. +./setup.sh -#This webserver stuff is really not needed, and just adds complexity to everything so it is disabled -#cd "$DIR" -#if [ -f dependencies/AmmarServer/README.md ] -#then -#echo "AmmarServer appears to already exist .." -#else -# echo "Do you want to download AmmarServer and enable MocapNETServer build ? " -# echo "(You probably don't need this)" -# echo -# echo -n " (Y/N)?" -# read answer -# if test "$answer" != "N" -a "$answer" != "n"; -# then -# cd "$DIR/dependencies" -# git clone https://github.com/AmmarkoV/AmmarServer -# AmmarServer/scripts/get_dependencies.sh -# cd AmmarServer -# mkdir build -# cd build -# cmake .. -# make -# cd "$DIR" -# fi -#fi - #Now that we have everything lets build.. diff --git a/mocapnet4.ipynb b/mocapnet4.ipynb new file mode 100644 index 0000000..ccb34a9 --- /dev/null +++ b/mocapnet4.ipynb @@ -0,0 +1,316 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click this play button to setup MocapNET v4!\n", + "\n", + "#This is the Total Capture version that handles body, hands, gaze, face!\n", + "#It also has been rewritten from scratch in Python for your convenience.\n", + "#If you deploy this on your PC, run : jupyter notebook mocapnet4.ipynb\n", + "# and remove --collab from the setup.sh invocation 2 lines below..!\n", + "import os\n", + "if (os.path.isfile(\"MocapNET.py\")):\n", + " !git pull\n", + " exit\n", + "!git clone -b mnet4 https://github.com/FORTH-ModelBasedTracker/MocapNET.git\n", + "!MocapNET/src/python/mnet4/setup.sh --collab\n", + "os.chdir(\"MocapNET/src/python/mnet4\")\n", + "print(\"MocapNET setup is finished, you can run the next cell now..\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to get a video from the internet and then track it ( e.g. shuffle.webm )\n", + "\n", + "#You can also upload your own by clicking the Files icon and using the menu on the left\n", + "#You need to put files in the /content/MocapNET/src/python/mnet4/ directory\n", + "\n", + "#Make sure we are at the correct directory\n", + "import os\n", + "if (not os.path.isfile(\"mediapipeHolisticWebcamMocapNET.py\")):\n", + " os.chdir(\"MocapNET/src/python/mnet4\")\n", + "\n", + "!wget http://ammar.gr/mocapnet/shuffle.webm -O shuffle.webm\n", + "\n", + "#Analyze the file shuffle.web through MediaPipe 2D + MocapNET 3D Pose Estimation!\n", + "!(python3 -m mediapipeHolisticWebcamMocapNET --from shuffle.webm --ik 0.001 99 99 --smooth 60 10 --all --save --plot --headless 2> /dev/null)\n", + "print(\"MocapNET video input processing finished, you can run the next cells now to see the results..\")\n", + "\n", + "#Outputs are :\n", + "#livelastRun3DHiRes.mp4 | A video showing the regressed output overlayed on RGB\n", + "#livelastPlot3DHiRes.mp4| A video plot of the retrieved BVH degrees of freedom\n", + "#out.bvh | the extracted BVH file \n", + "#2d_out.csv | Input 2D joints used by MocapNET as a CSV file\n", + "#3d_out.csv | Output 3D joints produced by MocapNET as a CSV file\n", + "#bvh_out.csv | Output BVH angles produced by MocapNET as a CSV file\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to run a sign-language dataset without mediapipe from a dumped Openpose/JSON/CSV source\n", + "\n", + "#Make sure we are at the correct directory\n", + "import os\n", + "if (not os.path.isfile(\"csvNET.py\")):\n", + " os.chdir(\"MocapNET/src/python/mnet4\")\n", + "\n", + "#Download a single sign (con0014) from SIGNUM\n", + "!wget http://ammar.gr/datasets/signumtest.zip -O signumtest.zip\n", + "!unzip -o signumtest.zip\n", + "\n", + "#Analyze the file con0014/2dJoints_v1.4.csv through MediaPipe 2D + MocapNET 3D Pose Estimation!\n", + "!(python3 -m csvNET --from con0014/2dJoints_v1.4.csv --ik 0.001 99 99 --upperbody --reye --mouth --hands --save --plot --headless 2> /dev/null)\n", + "print(\"MocapNET video input processing finished, you can run the next cells now to see the results..\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to see the generated BVH angle plot of the last MocapNET run!\n", + "from IPython.display import Video\n", + "import os\n", + "if (not os.path.isfile(\"livelastPlot3DHiRes.mp4\")):\n", + " print(\"Please execute the previous cells and wait for them to complete before seeing this visualization!\")\n", + " exit\n", + "\n", + "Video(\"livelastPlot3DHiRes.mp4\",embed=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to see the generated visualization of the last MocapNET run!\n", + "from IPython.display import Video\n", + "import os\n", + "if (not os.path.isfile(\"livelastRun3DHiRes.mp4\")):\n", + " print(\"Please execute the previous cells and wait for them to complete before seeing this visualization!\")\n", + " exit\n", + "\n", + "Video(\"livelastRun3DHiRes.mp4\",embed=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to download the generated BVH/3D files of the last MocapNET run!\n", + "import os \n", + "from google.colab import files\n", + "if (not os.path.isfile(\"out.bvh\")) or (not os.path.isfile(\"2d_out.csv\")) or (not os.path.isfile(\"bvh_out.csv\")) or (not os.path.isfile(\"3d_out.csv\")):\n", + " print(\"Please execute the previous cells and wait for them to complete before downloading output!\")\n", + "else:\n", + " print(\"Download Output Files!\")\n", + " files.download(\"out.bvh\")\n", + " files.download(\"2d_out.csv\")\n", + " files.download(\"3d_out.csv\")\n", + " files.download(\"bvh_out.csv\")\n", + "#To render the 3D face get Blender from https://www.blender.org/\n", + "#Download http://ammar.gr/mocapnet/mnet4/face.blend and open it using Blender\n", + "#Download http://ammar.gr/mocapnet/mnet4/headerWithHeadAndOneMotion.bvh and open it using Blender at a 0.01 scale\n", + "#Run The Loaded Python Script\n", + "#Click on the armature and on the menu right click the orange rectangle\n", + "#Select headerWithHeadAndOneMotion as Source BVH\n", + "#Select newgirl as Target Obj\n", + "#Select a directory as a target for generated dataset\n", + "#Select the path to the regressed bvh_out.csv to load pre-generated dataset\n", + "#Click Just Render CSV Dataset\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#<----- Click to attempt MocapNET live demo by streaming your camera to Google Colab\n", + "#EXPERIMENTAL, performance is impacted because of low image throughput!\n", + "\n", + "import cv2\n", + "import os\n", + "from colabStream import init_camera,take_frame,show_frame\n", + "\n", + " \n", + "from mediapipeHolisticWebcamMocapNET import MediaPipeHolistic\n", + "from MocapNET import easyMocapNETConstructor\n", + "from folderStream import resize_with_padding\n", + "\n", + "# Create instances of MediaPipeHolistic and MocapNET\n", + "mp_holistic = MediaPipeHolistic(doMediapipeVisualization=False)\n", + "mnet = easyMocapNETConstructor(\n", + " engine = \"onnx\",\n", + " doProfiling = False,\n", + " multiThreaded = False,\n", + " doHCDPostProcessing = True,\n", + " hcdLearningRate = 0.001,\n", + " hcdEpochs = 99,\n", + " hcdIterations = 99,\n", + " bvhScale = 1.0,\n", + " doBody = True,\n", + " doFace = False,\n", + " doREye = True,\n", + " doMouth = True,\n", + " doHands = True,\n", + " addNoise = 0.0\n", + ")\n", + "\n", + "\n", + "# init JavaScript code\n", + "init_camera()\n", + "\n", + "while True:\n", + " try:\n", + " image = take_frame()\n", + " \n", + " image = resize_with_padding(image,1280,720)\n", + "\n", + " # Perform image processing with MediaPipeHolistic\n", + " mocapNETInput, annotated_image = mp_holistic.convertImageToMocapNETInput(image)\n", + "\n", + " # Perform 3D joint prediction with MocapNET\n", + " mocapNET3DOutput = mnet.predict3DJoints(mocapNETInput)\n", + "\n", + " # Visualize the results on the image\n", + " #image_with_results = Image.fromarray(cv2.cvtColor(annotated_image, cv2.COLOR_BGR2RGB))\n", + " \n", + " from MocapNETVisualization import visualizeMocapNETEnsemble\n", + " image,plotImage = visualizeMocapNETEnsemble(mnet,annotated_image,plotBVHChannels=False)\n", + " \n", + " show_frame(annotated_image) # it replace previous image\n", + " \n", + " except Exception as err:\n", + " print('Exception:', err)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#D/L and compile a different version of MocapNET and evaluate it against CMUBVH test dataset\n", + "#Running this will take a while, but it is usefult to compare different trained MocapNET Models!\n", + "import os\n", + "\n", + "#Download Test Set!\n", + "if (not os.path.isfile(\"dataset/generated/bvh_body_all_test.csv\")):\n", + " print(\"Downloading test set!\")\n", + " os.system(\"wget http://ammar.gr/datasets/testBody.zip && unzip testBody.zip\")\n", + "\n", + "#Download a different MocapNET build\n", + "!python3 -m getModelFromDatabase --get 319 #<- change number to try a different version\n", + "\n", + "#Do Evaluation\n", + "!python3 -m evaluateMocapNET --config dataset/body_configuration.json --all body --skip 5 --engine onnx > lastEvaluationLog.txt\n", + "!tail -n 30 lastEvaluationLog.txt " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + "\n", + "This library is provided under the FORTH license\n", + "https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/master/license.txt\n", + "\n", + "If you use this version of MocapNET for your research please consider citing : \n", + "\n", + "@inproceedings{Qammaz2023b,\n", + " author = {Qammaz, Ammar and Argyros, Antonis},\n", + " title = {A Unified Approach for Occlusion Tolerant 3D Facial Pose Capture and Gaze Estimation using MocapNETs},\n", + " booktitle = {International Conference on Computer Vision Workshops (AMFG 2023 - ICCVW 2023), (to appear)},\n", + " publisher = {IEEE},\n", + " year = {2023},\n", + " month = {October},\n", + " address = {Paris, France},\n", + " projects = {VMWARE,I.C.HUMANS},\n", + " pdflink = {http://users.ics.forth.gr/ argyros/mypapers/2023_10_AMFG_Qammaz.pdf}\n", + "}\n", + "\n", + "@inproceedings{Qammaz2021,\n", + " author = {Qammaz, Ammar and Argyros, Antonis A},\n", + " title = {Towards Holistic Real-time Human 3D Pose Estimation using MocapNETs},\n", + " booktitle = {British Machine Vision Conference (BMVC 2021)},\n", + " publisher = {BMVA},\n", + " year = {2021},\n", + " month = {November},\n", + " projects = {I.C.HUMANS},\n", + " videolink = {https://www.youtube.com/watch?v=aaLOSY_p6Zc}\n", + "}\n", + "\"\"\" " + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/src/MocapNET2/MocapNETLib2/IO/bvh.cpp b/src/MocapNET2/MocapNETLib2/IO/bvh.cpp index 66d9f40..961f33d 100644 --- a/src/MocapNET2/MocapNETLib2/IO/bvh.cpp +++ b/src/MocapNET2/MocapNETLib2/IO/bvh.cpp @@ -552,7 +552,7 @@ int getBVHJointOffset(unsigned int currentJoint,float * x,float *y,float *z) unsigned int getBVHParentJoint(unsigned int currentJoint) { #if USE_BVH - return bhv_getJointParent(&bvhMotion,currentJoint); + return bvh_getJointParent(&bvhMotion,currentJoint); #endif return 0; } diff --git a/src/MocapNET4/MocapNET4Test/CMakeLists.txt b/src/MocapNET4/MocapNET4Test/CMakeLists.txt new file mode 100644 index 0000000..211cfab --- /dev/null +++ b/src/MocapNET4/MocapNET4Test/CMakeLists.txt @@ -0,0 +1,24 @@ +project( MocapNET4Test ) +cmake_minimum_required( VERSION 2.8.7 ) +#cmake_minimum_required(VERSION 3.5) +find_package(OpenCV REQUIRED) +INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS}) + +#set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(CMAKE_CXX_STANDARD 11) +include_directories(${TENSORFLOW_INCLUDE_ROOT}) +include_directories(../MocapNETLib4) #<- includes in test.cpp + + +add_executable(MocapNET4Test test.cpp) + +target_link_libraries(MocapNET4Test rt dl m ${OpenCV_LIBRARIES} ${OPENGL_LIBS} JointEstimator2D Tensorflow TensorflowFramework MocapNETLib4 ${NETWORK_CLIENT_LIBRARIES} ${PNG_Libs} ${JPG_Libs} ) +set_target_properties(MocapNET4Test PROPERTIES DEBUG_POSTFIX "D") + + +set_target_properties(MocapNET4Test PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}" + ) + diff --git a/src/MocapNET4/MocapNET4Test/test.cpp b/src/MocapNET4/MocapNET4Test/test.cpp new file mode 100644 index 0000000..dc2ffd3 --- /dev/null +++ b/src/MocapNET4/MocapNET4Test/test.cpp @@ -0,0 +1,122 @@ + +/** @file livedemo.cpp + * @brief This is the main "demo" offered in this repository, it will take a stream from a webcam or video file using OpenCV and run +* 2D pose estimation + MocapNET giving you a nice 3D visualization as well as an output .bvh file + * @author Ammar Qammaz (AmmarkoV) + */ +#include +#include +//----------------------------------------------------------------- + +#include "PCA/PCA.h" +#include "JSON/readModelConfiguration.h" +#include "JSON/readListFile.h" + +#include "NSxM/NSxM.h" + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + + + +void testPCA(struct PCAData * pca) +{ + float output[210]={0}; + int outputSize = 210; + + float input[458]={0}; + int inputSize = 458; + for (int i=0; i + + +/** + * @brief This is an array of names for all the new BODY25 body parts. + * This tries to mirror body_configuration.json and everything used is lowercase exactly for this reason.. + * it also has to be the same with the bvh file headerWithHeadAndOneMotion.bvh + */ +static const char * Body25Labels[] = +{ + "head", //0 + "neck", //1 + "rshoulder", //2 + "relbow", //3 + "rhand", //4 + "lshoulder", //5 + "lelbow", //6 + "lhand", //7 + "hip", //8 + "rhip", //9 + "rknee", //10 + "rfoot", //11 + "lhip", //12 + "lknee", //13 + "lfoot", //14 + "endsite_eye.r", //15 + "endsite_eye.l", //16 + "rear", //17 ========= No correspondance + "lear", //18 ========= No correspondance + "endsite_toe1-2.l",//19 + "endsite_toe5-3.l",//20 + "lheel", //21 ========= No correspondance + "endsite_toe1-2.r",//22 + "endsite_toe5-3.r",//23 + "rheel", //24 ========= No correspondance + "bkg", //25 ========= No correspondance + //================== + "End of Joint Names", + 0 +}; + + + + +#include //toupper +static int strcasecmp_route(const char * input1,const char * input2) +{ + if ( (input1==0) || (input2==0) ) + { + fprintf(stderr,"Error , calling strcasecmp_route with null parameters \n"); + return 1; + } + + #if CASE_SENSITIVE_OBJECT_NAMES + return strcmp(input1,input2); + #endif + + unsigned int len1 = strlen(input1); + unsigned int len2 = strlen(input2); + if (len1!=len2) + { + //mismatched lengths of strings , they can't be equal..! + return 1; + } + + char A; //<- character buffer for input1 + char B; //<- character buffer for input2 + unsigned int i=0; + while (iroutedValues!=0) { free(route->routedValues); route->routedValues=0; } + if (route->routingRules!=0) { free(route->routingRules); route->routingRules=0; } + route->numberOfRoutingRules=0; + route->resolved=0; + return 1; +} + + + +/** + * @brief generate Route from Labels + * @param Pointer to a model configuration + * @param Pointer to the route structure + * @param Labels to route + * @param Number of Labels to route + * @retval 1=Success/0=Failure + */ +static int generateRouteFromLabels( + struct ModelConfigurationData * config, + struct inputRouting * route, + const char * * incomingLabels, + unsigned int incomingLabelsLength + ) +{ + fprintf(stderr,GREEN "\ngenerateRouteFromLabels for %u labels and %u hierarchy elements\n" NORMAL,incomingLabelsLength,config->numberOfHierarchyElements); + + if (config==0) { return 0; } + if (incomingLabels==0) { return 0; } + if (route==0) { return 0; } + + + destroyRoute(route); + + route->numberOfRoutingRules = config->numberOfHierarchyElements; + + route->routedValues = (float*) malloc(sizeof(float) * 3 * route->numberOfRoutingRules); + route->routingRules = (int*) malloc(sizeof(int) * route->numberOfRoutingRules); + + if ( + (route->routedValues==0) || + (route->routingRules==0) + ) //Failed allocating memory.. + { + destroyRoute(route); + return 0; + } + + + memset(route->routedValues,0,sizeof(float) * 3 * route->numberOfRoutingRules); + memset(route->routingRules,0,sizeof(int) * route->numberOfRoutingRules); + + + int routingFailures = 0; + for (int trg=0; trgnumberOfHierarchyElements; trg++) + { + //fprintf(stderr,"trg %u/%u\n",trg,config->numberOfHierarchyElements); + int trgJointResolved = 0; + + for (int src=0; srchierarchyElements[trg].joint)==0) + { + fprintf(stderr,GREEN "MATCH %s (%u) to %s (%u) \n" NORMAL,incomingLabels[src],src,config->hierarchyElements[trg].joint,trg); + trgJointResolved = 1; + route->routedValues[trg*3+0] = 0.0;//Set everything to zero initially.. + route->routedValues[trg*3+1] = 0.0;//Set everything to zero initially.. + route->routedValues[trg*3+2] = 0.0;//Set everything to zero initially.. + route->routingRules[trg] = src; + } + } + + + if (!trgJointResolved) + { + fprintf(stderr,YELLOW "Could not match %s \n" NORMAL,config->hierarchyElements[trg].joint); + routingFailures+=1; + } + } + + if (routingFailures==0) + { + fprintf(stderr,GREEN "Successfully routed all %u input rules\n" NORMAL,route->numberOfRoutingRules); + } else + { + fprintf(stderr,RED "Failed routing %u out of %u input rules\n" NORMAL, routingFailures,route->numberOfRoutingRules); + } + + route->resolved = (routingFailures==0); + + return (routingFailures==0); +} + + + +static int routeInput( + float * preexistingOutput2DJoints, + int * output2DJointsLength, + struct ModelConfigurationData * config, + struct inputRouting * route, + float * raw2DPoints, + int raw2DPointsLength + ) +{ + if (route->resolved) + { + //if (raw2DPointsLength==route->numberOfRoutingRules) + { + float * output = preexistingOutput2DJoints; + + int val = 0; + for (int i=0; inumberOfRoutingRules; i++) + { + fprintf(stderr,"%s ",config->hierarchyElements[i].joint); + //Each rule has 3 values 2DX, 2DY, 2DVisibility + //-------------------------------------------------------------------------- + output[val]=raw2DPoints[(route->routingRules[i]*3) + 0]; + fprintf(stderr,"2DX=%0.2f ",output[val]); + val+=1; + //-------------------------------------------------------------------------- + output[val]=raw2DPoints[(route->routingRules[i]*3) + 1]; + fprintf(stderr,"2DY=%0.2f ",output[val]); + val+=1; + //-------------------------------------------------------------------------- + output[val]=raw2DPoints[(route->routingRules[i]*3) + 2]; + fprintf(stderr,"2DVisibility=%0.2f \n",output[val]); + val+=1; + //-------------------------------------------------------------------------- + } + return 1; + } + } + return 0; +} + + + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/src/MocapNET4/MocapNETLib4/JSON/nxjson.c b/src/MocapNET4/MocapNETLib4/JSON/nxjson.c new file mode 100644 index 0000000..84a0c3c --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/JSON/nxjson.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2013 Yaroslav Stavnichiy + * + * This file is part of NXJSON. + * + * NXJSON is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * NXJSON is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with NXJSON. If not, see . + */ + +// this file can be #included in your code +#ifndef NXJSON_C +#define NXJSON_C + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include +#include + +#include "nxjson.h" + +// redefine NX_JSON_CALLOC & NX_JSON_FREE to use custom allocator +#ifndef NX_JSON_CALLOC +#define NX_JSON_CALLOC() calloc(1, sizeof(nx_json)) +#define NX_JSON_FREE(json) free((void*)(json)) +#endif + +// redefine NX_JSON_REPORT_ERROR to use custom error reporting +#ifndef NX_JSON_REPORT_ERROR +#define NX_JSON_REPORT_ERROR(msg, p) fprintf(stderr, "NXJSON PARSE ERROR (%d): " msg " at %s\n", __LINE__, p) +#endif + +#define IS_WHITESPACE(c) ((unsigned char)(c)<=(unsigned char)' ') + +static const nx_json dummy={ NX_JSON_NULL }; + +static nx_json* create_json(nx_json_type type, const char* key, nx_json* parent) { + nx_json* js=NX_JSON_CALLOC(); + assert(js); + js->type=type; + js->key=key; + if (!parent->last_child) { + parent->child=parent->last_child=js; + } + else { + parent->last_child->next=js; + parent->last_child=js; + } + parent->length++; + return js; +} + +void nx_json_free(const nx_json* js) { + nx_json* p=js->child; + nx_json* p1; + while (p) { + p1=p->next; + nx_json_free(p); + p=p1; + } + NX_JSON_FREE(js); +} + +static int unicode_to_utf8(unsigned int codepoint, char* p, char** endp) { + // code from http://stackoverflow.com/a/4609989/697313 + if (codepoint<0x80) *p++=codepoint; + else if (codepoint<0x800) *p++=192+codepoint/64, *p++=128+codepoint%64; + else if (codepoint-0xd800u<0x800) return 0; // surrogate must have been treated earlier + else if (codepoint<0x10000) *p++=224+codepoint/4096, *p++=128+codepoint/64%64, *p++=128+codepoint%64; + else if (codepoint<0x110000) *p++=240+codepoint/262144, *p++=128+codepoint/4096%64, *p++=128+codepoint/64%64, *p++=128+codepoint%64; + else return 0; // error + *endp=p; + return 1; +} + +nx_json_unicode_encoder nx_json_unicode_to_utf8=unicode_to_utf8; + +static inline int hex_val(char c) { + if (c>='0' && c<='9') return c-'0'; + if (c>='a' && c<='f') return c-'a'+10; + if (c>='A' && c<='F') return c-'A'+10; + return -1; +} + +static char* unescape_string(char* s, char** end, nx_json_unicode_encoder encoder) { + char* p=s; + char* d=s; + char c; + while ((c=*p++)) { + if (c=='"') { + *d='\0'; + *end=p; + return s; + } + else if (c=='\\') { + switch (*p) { + case '\\': + case '/': + case '"': + *d++=*p++; + break; + case 'b': + *d++='\b'; p++; + break; + case 'f': + *d++='\f'; p++; + break; + case 'n': + *d++='\n'; p++; + break; + case 'r': + *d++='\r'; p++; + break; + case 't': + *d++='\t'; p++; + break; + case 'u': // unicode + if (!encoder) { + // leave untouched + *d++=c; + break; + } + char* ps=p-1; + int h1, h2, h3, h4; + if ((h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) { + NX_JSON_REPORT_ERROR("invalid unicode escape", p-1); + return 0; + } + unsigned int codepoint=h1<<12|h2<<8|h3<<4|h4; + if ((codepoint & 0xfc00)==0xd800) { // high surrogate; need one more unicode to succeed + p+=6; + if (p[-1]!='\\' || *p!='u' || (h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) { + NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); + return 0; + } + unsigned int codepoint2=h1<<12|h2<<8|h3<<4|h4; + if ((codepoint2 & 0xfc00)!=0xdc00) { + NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); + return 0; + } + codepoint=0x10000+((codepoint-0xd800)<<10)+(codepoint2-0xdc00); + } + if (!encoder(codepoint, d, &d)) { + NX_JSON_REPORT_ERROR("invalid codepoint", ps); + return 0; + } + p+=5; + break; + default: + // leave untouched + *d++=c; + break; + } + } + else { + *d++=c; + } + } + NX_JSON_REPORT_ERROR("no closing quote for string", s); + return 0; +} + +static char* skip_block_comment(char* p) { + // assume p[-2]=='/' && p[-1]=='*' + char* ps=p-2; + if (!*p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; + } + REPEAT: + p=strchr(p+1, '/'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; + } + if (p[-1]!='*') goto REPEAT; + return p+1; +} + +static char* parse_key(const char** key, char* p, nx_json_unicode_encoder encoder) { + // on '}' return with *p=='}' + char c; + while ((c=*p++)) { + if (c=='"') { + *key=unescape_string(p, &p, encoder); + if (!*key) return 0; // propagate error + while (*p && IS_WHITESPACE(*p)) p++; + if (*p==':') return p+1; + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; + } + else if (IS_WHITESPACE(c) || c==',') { + // continue + } + else if (c=='}') { + return p-1; + } + else if (c=='/') { + if (*p=='/') { // line comment + char* ps=p-1; + p=strchr(p+1, '\n'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; // error + } + p++; + } + else if (*p=='*') { // block comment + p=skip_block_comment(p+1); + if (!p) return 0; + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error + } + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error + } + } + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error +} + +static char* parse_value(nx_json* parent, const char* key, char* p, nx_json_unicode_encoder encoder) { + nx_json* js; + while (1) { + switch (*p) { + case '\0': + NX_JSON_REPORT_ERROR("unexpected end of text", p); + return 0; // error + case ' ': case '\t': case '\n': case '\r': + case ',': + // skip + p++; + break; + case '{': + js=create_json(NX_JSON_OBJECT, key, parent); + p++; + while (1) { + const char* new_key; + p=parse_key(&new_key, p, encoder); + if (!p) return 0; // error + if (*p=='}') return p+1; // end of object + p=parse_value(js, new_key, p, encoder); + if (!p) return 0; // error + } + case '[': + js=create_json(NX_JSON_ARRAY, key, parent); + p++; + while (1) { + p=parse_value(js, 0, p, encoder); + if (!p) return 0; // error + if (*p==']') return p+1; // end of array + } + case ']': + return p; + case '"': + p++; + js=create_json(NX_JSON_STRING, key, parent); + js->text_value=unescape_string(p, &p, encoder); + if (!js->text_value) return 0; // propagate error + return p; + case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + { + js=create_json(NX_JSON_INTEGER, key, parent); + char* pe; + errno = 0; + js->int_value=strtoll(p, &pe, 0); + if (pe==p || errno==ERANGE) { + NX_JSON_REPORT_ERROR("invalid number", p); + return 0; // error + } + if (*pe=='.' || *pe=='e' || *pe=='E') { // double value + js->type=NX_JSON_DOUBLE; + errno = 0; + js->dbl_value=strtod(p, &pe); + if (pe==p || errno==ERANGE) { + NX_JSON_REPORT_ERROR("invalid number", p); + return 0; // error + } + } + else { + js->dbl_value=js->int_value; + } + return pe; + } + case 't': + if (!strncmp(p, "true", 4)) { + js=create_json(NX_JSON_BOOL, key, parent); + js->int_value=1; + return p+4; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case 'f': + if (!strncmp(p, "false", 5)) { + js=create_json(NX_JSON_BOOL, key, parent); + js->int_value=0; + return p+5; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case 'n': + if (!strncmp(p, "null", 4)) { + create_json(NX_JSON_NULL, key, parent); + return p+4; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case '/': // comment + if (p[1]=='/') { // line comment + char* ps=p; + p=strchr(p+2, '\n'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; // error + } + p++; + } + else if (p[1]=='*') { // block comment + p=skip_block_comment(p+2); + if (!p) return 0; + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + } + break; + default: + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + } + } +} + +const nx_json* nx_json_parse_utf8(char* text) { + return nx_json_parse(text, unicode_to_utf8); +} + +const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder) { + nx_json js={0}; + if (!parse_value(&js, 0, text, encoder)) { + if (js.child) nx_json_free(js.child); + return 0; + } + return js.child; +} + +const nx_json* nx_json_get(const nx_json* json, const char* key) { + if (!json || !key) return &dummy; // never return null + nx_json* js; + for (js=json->child; js; js=js->next) { + if (js->key && !strcmp(js->key, key)) return js; + } + return &dummy; // never return null +} + +const nx_json* nx_json_item(const nx_json* json, int idx) { + if (!json) return &dummy; // never return null + nx_json* js; + for (js=json->child; js; js=js->next) { + if (!idx--) return js; + } + return &dummy; // never return null +} + + +#ifdef __cplusplus +} +#endif + +#endif /* NXJSON_C */ diff --git a/src/MocapNET4/MocapNETLib4/JSON/nxjson.h b/src/MocapNET4/MocapNETLib4/JSON/nxjson.h new file mode 100644 index 0000000..f85bba2 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/JSON/nxjson.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Yaroslav Stavnichiy + * + * This file is part of NXJSON. + * + * NXJSON is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * NXJSON is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with NXJSON. If not, see . + */ + +#ifndef NXJSON_H +#define NXJSON_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum nx_json_type { + NX_JSON_NULL, // this is null value + NX_JSON_OBJECT, // this is an object; properties can be found in child nodes + NX_JSON_ARRAY, // this is an array; items can be found in child nodes + NX_JSON_STRING, // this is a string; value can be found in text_value field + NX_JSON_INTEGER, // this is an integer; value can be found in int_value field + NX_JSON_DOUBLE, // this is a double; value can be found in dbl_value field + NX_JSON_BOOL // this is a boolean; value can be found in int_value field +} nx_json_type; + +typedef struct nx_json { + nx_json_type type; // type of json node, see above + const char* key; // key of the property; for object's children only + const char* text_value; // text value of STRING node + long long int_value; // the value of INTEGER or BOOL node + double dbl_value; // the value of DOUBLE node + int length; // number of children of OBJECT or ARRAY + struct nx_json* child; // points to first child + struct nx_json* next; // points to next child + struct nx_json* last_child; +} nx_json; + +typedef int (*nx_json_unicode_encoder)(unsigned int codepoint, char* p, char** endp); + +extern nx_json_unicode_encoder nx_json_unicode_to_utf8; + +const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder); +const nx_json* nx_json_parse_utf8(char* text); +void nx_json_free(const nx_json* js); +const nx_json* nx_json_get(const nx_json* json, const char* key); // get object's property by key +const nx_json* nx_json_item(const nx_json* json, int idx); // get array element by index + + +#ifdef __cplusplus +} +#endif + +#endif /* NXJSON_H */ diff --git a/src/MocapNET4/MocapNETLib4/JSON/readListFile.h b/src/MocapNET4/MocapNETLib4/JSON/readListFile.h new file mode 100644 index 0000000..e90c3d1 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/JSON/readListFile.h @@ -0,0 +1,173 @@ +/** @file readListFile.h + * @brief An implementation of reading a list from a text file + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef READ_LIST_FILE_H_INCLUDED +#define READ_LIST_FILE_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include + +struct listFileEntry +{ + int strLength; + char * str; +}; + +struct listFileData +{ + unsigned int numberOfEntries; + struct listFileEntry * entry; +}; + + +static int slowLineCounter(const char * filename) +{ + char ch; + int linesCount=0; + //open file in read more + FILE * fp=fopen(filename,"r"); + if(fp==NULL) { + printf("File \"%s\" does not exist!!!\n",filename); + return -1; + } + //read character by character and check for new line + while((ch=fgetc(fp))!=EOF) + { + if(ch=='\n') + linesCount++; + } + //close the file + fclose(fp); + + return linesCount; +} + +static int destroyListFile(struct listFileData * listOutput) +{ + fprintf(stderr,"destroying List File..! \n"); + if (listOutput!=0) + { + if (listOutput->entry!=0) + { + for (int i=0; inumberOfEntries; i++) + { + if (listOutput->entry[i].str!=0) + { + free(listOutput->entry[i].str); + listOutput->entry[i].strLength = 0; + } + } + //------------------------------------------------ + free(listOutput->entry); + listOutput->entry=0; + } + } + fprintf(stderr,"destroyed List File..! \n"); + return 1; +} + + + +static int printListFile(struct listFileData * listOutput,const char * label) +{ + if (listOutput==0) { return 0; } + if (listOutput->entry!=0) + { + printf("Listing %s\n",label); + printf("_______________________\n"); + for (int i=0; inumberOfEntries; i++) + { + printf("Line %u === `%s`\n",i,listOutput->entry[i].str); + } + printf("\n\n\n"); + return 1; + } + + + return 0; +} + +static int readListFile(struct listFileData * listOutput,const char * filename) +{ + if (listOutput==0) { return 0; } + + //We now know the number of entries + listOutput->numberOfEntries = slowLineCounter(filename); + + if (listOutput!=0) + { + //Clean up everything! + destroyListFile(listOutput); + } + + + listOutput->entry = (struct listFileEntry *) malloc(sizeof(struct listFileEntry) * listOutput->numberOfEntries ); + if (listOutput->entry==0) { return 0; } + + + char * line = NULL; + size_t len = 0; + ssize_t read = 0; + + FILE * fp = fopen(filename, "r"); + if (fp == NULL) + { return 0; } + + unsigned int entryNumber = 0; + int i=0; + while ((read=getline(&line, &len, fp)) != -1) + { + int stringLength = strlen(line); + + int stringLengthWithoutNull = stringLength-1; + while ( (stringLengthWithoutNull>0) && ( (line[stringLengthWithoutNull]==10) || (line[stringLengthWithoutNull]==13) ) ) + { + line[stringLengthWithoutNull]=0; + stringLengthWithoutNull-=1; + stringLength-=1; + } + + + //fprintf(stderr,"reading line %s (%u)..! \n",line,stringLength); + if (entryNumbernumberOfEntries) + { + listOutput->entry[i].str = (char *) malloc(sizeof(char) * (stringLength+2)); + if (listOutput->entry[i].str!=0) + { + listOutput->entry[i].strLength = stringLength; + strncpy(listOutput->entry[i].str,line,stringLength); + listOutput->entry[i].str[stringLength]=0; //Null termination + //snprintf(listOutput->entry[i].str,stringLength+1,"%s",line); + } + + ++entryNumber; + } + + + i+=1; + } + + fclose(fp); + if (line) + { free(line); } + return 1; +} + + +#ifdef __cplusplus +} +#endif + + + + +#endif + diff --git a/src/MocapNET4/MocapNETLib4/JSON/readModelConfiguration.h b/src/MocapNET4/MocapNETLib4/JSON/readModelConfiguration.h new file mode 100644 index 0000000..84b40b3 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/JSON/readModelConfiguration.h @@ -0,0 +1,753 @@ +/** @file readModelConfiguration.h + * @brief Parsing the JSON files accompanying models..! + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef READ_JSON_CONFIGURATION_H_INCLUDED +#define READ_JSON_CONFIGURATION_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include +#include +#include "nxjson.h" +#include "../tools.h" + +#define SMALL_STR 32 +#define BIG_STR 128 +#define MAX_JOINT_NAME 32 +#define MAX_DESCRIPTOR_ELEMENTS 64 +#define MAX_HIERARCHY_ELEMENTS 64 +#define MAX_BANNED_ELEMENTS 16 + +//-------------------------------------- +struct jointName +{ + char joint[MAX_JOINT_NAME]; +}; +//-------------------------------------- +struct jointPair +{ + char jointStart[MAX_JOINT_NAME]; + unsigned int jointStartID; + char jointEnd[MAX_JOINT_NAME]; + unsigned int jointEndID; +}; +//-------------------------------------- +struct jointDescriptorItem +{ + char joint[MAX_JOINT_NAME]; + unsigned int jointID; + char isVirtual; + float xOffset; + float yOffset; + char halfwayFromJointAndThis[MAX_JOINT_NAME]; + unsigned int secondTargetJointID; +}; +//-------------------------------------- +struct jointHierarchyItem +{ + char joint[MAX_JOINT_NAME]; + char inheritNetwork[MAX_JOINT_NAME]; + char parent[MAX_JOINT_NAME]; + unsigned int parentID; + unsigned int importance; + char immuneToSelfOcclusions; +}; +//-------------------------------------- + +struct ModelConfigurationData +{ + float version; + //-------------------------------------- + char backend[SMALL_STR]; + char precision[SMALL_STR]; + char BVH[BIG_STR]; + char outputDirectory[BIG_STR]; + unsigned int veryHighNumberOfEpochs; + unsigned int highNumberOfEpochs; + unsigned int defaultNumberOfEpochs; + unsigned int defaultBatchSize; + float learningRate; + float minEarlyStoppingDelta; + char activationFunction[SMALL_STR]; + float dropoutRate; + float lamda; + char useQuadLoss; + char useSquaredLoss; + unsigned int earlyStoppingPatience; + char rememberWeights; + char rememberConsecutiveWeights; + char useOnlineHardExampleMining; + unsigned int hardMiningEpochs; + unsigned int normalMiningEpochs; + unsigned int groupOutputs; + char ignoreOcclusions; + char NSDMNormalizationMasterSwitch; + char NSDMAlsoUseAlignmentAngles; + unsigned int neuralNetworkDepth; + char EDM; + char eNSRM; + char doPCA[BIG_STR]; + unsigned int PCADimensionsKept; + unsigned int padEnsembleInput; + //-------------------------------------- + struct jointPair normalizedBasedOn[3]; + unsigned int numberOfNormalizedBasedOnRules; + //-------------------------------------- + struct jointPair alignment[3]; + unsigned int numberOfAlignmentRules; + //-------------------------------------- + struct jointDescriptorItem descriptorElements[MAX_DESCRIPTOR_ELEMENTS]; + unsigned int numberOfDescriptorElements; + //-------------------------------------- + struct jointHierarchyItem hierarchyElements[MAX_HIERARCHY_ELEMENTS]; + unsigned int numberOfHierarchyElements; + //-------------------------------------- + struct jointName bannedJoints[MAX_BANNED_ELEMENTS]; + unsigned int numberOfBannedJoints; +}; + + +static void printModelConfigurationData(struct ModelConfigurationData* out) +{ + fprintf(stderr,"Version : %0.2f\n", out->version); + fprintf(stderr,"Backend : %s\n", out->backend); + fprintf(stderr,"Precision : %s\n", out->precision); + fprintf(stderr,"BVH : %s\n", out->BVH); + fprintf(stderr,"outputDirectory : %s\n", out->outputDirectory); + fprintf(stderr,"veryHighNumberOfEpochs : %u\n", out->veryHighNumberOfEpochs); + fprintf(stderr,"highNumberOfEpochs : %u\n", out->highNumberOfEpochs); + fprintf(stderr,"defaultNumberOfEpochs : %u\n", out->defaultNumberOfEpochs); + fprintf(stderr,"defaultBatchSize : %u\n", out->defaultBatchSize); + fprintf(stderr,"learningRate : %f\n", out->learningRate); + fprintf(stderr,"minEarlyStoppingDelta : %f\n", out->minEarlyStoppingDelta); + fprintf(stderr,"activationFunction : %s\n", out->activationFunction); + fprintf(stderr,"dropoutRate : %f\n", out->dropoutRate); + fprintf(stderr,"lamda : %f\n", out->lamda); + fprintf(stderr,"useQuadLoss : %u\n", out->useQuadLoss); + fprintf(stderr,"useSquaredLoss : %u\n", out->useSquaredLoss); + fprintf(stderr,"earlyStoppingPatience : %u\n", out->earlyStoppingPatience); + fprintf(stderr,"rememberWeights : %u\n", out->rememberWeights); + fprintf(stderr,"rememberConsecutiveWeights : %u\n", out->rememberConsecutiveWeights); + fprintf(stderr,"useOnlineHardExampleMining : %u\n", out->useOnlineHardExampleMining); + fprintf(stderr,"hardMiningEpochs : %u\n", out->hardMiningEpochs); + fprintf(stderr,"normalMiningEpochs : %u\n", out->normalMiningEpochs); + fprintf(stderr,"groupOutputs : %u\n", out->groupOutputs); + fprintf(stderr,"ignoreOcclusions : %u\n", out->ignoreOcclusions); + fprintf(stderr,"NSDMNormalizationMasterSwitch : %u\n",out->NSDMNormalizationMasterSwitch); + fprintf(stderr,"NSDMAlsoUseAlignmentAngles : %u\n", out->NSDMAlsoUseAlignmentAngles); + fprintf(stderr,"neuralNetworkDepth : %u\n", out->neuralNetworkDepth); + fprintf(stderr,"EDM : %u\n", out->EDM); + fprintf(stderr,"eNSRM : %u\n", out->eNSRM); + fprintf(stderr,"BVH : %s\n", out->doPCA); + fprintf(stderr,"PCADimensionsKept : %u\n", out->PCADimensionsKept); + fprintf(stderr,"padEnsembleInput : %u\n", out->padEnsembleInput); + + fprintf(stderr,"Normalization Rules : %u\n",out->numberOfNormalizedBasedOnRules); + for (int i=0; inumberOfNormalizedBasedOnRules; i++) + { + fprintf(stderr,"Rule %u : \n",i); + fprintf(stderr," jointStart:%s\n" ,out->normalizedBasedOn[i].jointStart); + fprintf(stderr," jointStartID:%u\n",out->normalizedBasedOn[i].jointStartID); + fprintf(stderr," jointEnd:%s\n" ,out->normalizedBasedOn[i].jointEnd); + fprintf(stderr," jointEndID:%u\n" ,out->normalizedBasedOn[i].jointEndID); + } + + fprintf(stderr,"Alignment Rules : %u\n",out->numberOfAlignmentRules); + for (int i=0; inumberOfAlignmentRules; i++) + { + fprintf(stderr,"Rule %u : \n",i); + fprintf(stderr," jointStart:%s\n" ,out->alignment[i].jointStart); + fprintf(stderr," jointStartID:%u\n",out->alignment[i].jointStartID); + fprintf(stderr," jointEnd:%s\n" ,out->alignment[i].jointEnd); + fprintf(stderr," jointEndID:%u\n" ,out->alignment[i].jointEndID); + } + + fprintf(stderr,"Descriptor Rules : %u\n",out->numberOfDescriptorElements); + for (int i=0; inumberOfDescriptorElements; i++) + { + fprintf(stderr,"Rule %u : \n",i); + fprintf(stderr," joint:%s\n" ,out->descriptorElements[i].joint); + fprintf(stderr," jointID:%u\n" ,out->descriptorElements[i].jointID); + fprintf(stderr," isVirtual:%u\n" ,out->descriptorElements[i].isVirtual); + fprintf(stderr," xOffset:%f\n" ,out->descriptorElements[i].xOffset); + fprintf(stderr," yOffset:%f\n" ,out->descriptorElements[i].yOffset); + fprintf(stderr," halfwayFromJointAndThis:%s\n",out->descriptorElements[i].halfwayFromJointAndThis); + fprintf(stderr," secondTargetJointID:%u\n" ,out->descriptorElements[i].secondTargetJointID); + } + + //TODO POPULATE JOINT ID! + + fprintf(stderr,"Hierarchy Rules : %u\n",out->numberOfHierarchyElements); + for (int i=0; inumberOfHierarchyElements; i++) + { + fprintf(stderr,"Rule %u : \n",i); + fprintf(stderr," joint:%s\n" ,out->hierarchyElements[i].joint); + fprintf(stderr," inheritNetwork:%s\n" ,out->hierarchyElements[i].inheritNetwork); + fprintf(stderr," parent:%s\n" ,out->hierarchyElements[i].parent); + fprintf(stderr," parentID:%u\n" ,out->hierarchyElements[i].parentID); + fprintf(stderr," importance:%u\n" ,out->hierarchyElements[i].importance); + fprintf(stderr," immuneToSelfOcclusions:%u\n" ,out->hierarchyElements[i].immuneToSelfOcclusions); + } + + + fprintf(stderr,"Banned Encoders Rules : %u\n",out->numberOfBannedJoints); + for (int i=0; inumberOfBannedJoints; i++) + { + fprintf(stderr,"Rule %u : \n",i); + fprintf(stderr," joint:%s\n" ,out->bannedJoints[i].joint); + } +} + + + + +static int resolveConfigurationData(struct ModelConfigurationData* config) +{ + if (config!=0) + { //Found a configuration to resolve.. + + printModelConfigurationData(config); + + fprintf(stderr,"Resolving %u NSxM matrix elements..\n",config->numberOfDescriptorElements); + fprintf(stderr,"Using %u hierarchy elements..\n",config->numberOfHierarchyElements); + + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + + for (unsigned int descID=0; descIDnumberOfDescriptorElements; descID++) + { + //Resolve NSxM to Joint ID + for (unsigned int jointID=0; jointIDnumberOfHierarchyElements; jointID++) + { + fprintf(stderr,"Trying to match %s(%u/%u) with %s(%u/%u)\n",config->descriptorElements[descID].joint,descID,config->numberOfDescriptorElements,config->hierarchyElements[jointID].joint,jointID,config->numberOfHierarchyElements); + + if (strcmp(config->descriptorElements[descID].joint,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->descriptorElements[descID].jointID = jointID; + break; + } + } + + //Resolve NSxM to Joint ID for halfway + for (int jointID=0; jointIDnumberOfHierarchyElements; jointID++) + { + if (config->descriptorElements[descID].isVirtual==2) + { + fprintf(stderr,"Trying to match %s(%u) with %s(%u)\n",config->descriptorElements[descID].halfwayFromJointAndThis,descID,config->hierarchyElements[jointID].joint,jointID); + + if (strcmp(config->descriptorElements[descID].halfwayFromJointAndThis,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->descriptorElements[descID].secondTargetJointID = jointID; + break; + } + } + } + } + + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + //--------------------------------------------------------------------------------------------------------------------------- + + + + + const int INVALID_VALUE=666; + + //Resolve normalized based on order.. + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfNormalizedBasedOnRules; descID++) + { + config->normalizedBasedOn[descID].jointStartID=INVALID_VALUE; + config->normalizedBasedOn[descID].jointEndID =INVALID_VALUE; + } + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfDescriptorElements; descID++) + { + for (int jointID=0; jointIDnumberOfHierarchyElements; jointID++) + { + fprintf(stderr,"Trying to match norm rule %s(%u/%u) with %s(%u/%u)\n",config->descriptorElements[descID].joint,descID,config->numberOfNormalizedBasedOnRules,config->hierarchyElements[jointID].joint,jointID,config->numberOfHierarchyElements); + + if (strcmp(config->normalizedBasedOn[descID].jointStart,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->normalizedBasedOn[descID].jointStartID = jointID; + } + if (strcmp(config->normalizedBasedOn[descID].jointEnd,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->normalizedBasedOn[descID].jointEndID = jointID; + } + } + } + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfNormalizedBasedOnRules; descID++) + { + if ( config->normalizedBasedOn[descID].jointStartID == INVALID_VALUE ) + { + fprintf(stderr,"Normalization Rule %u/%u for start of joint is unresolved, stopping..\n",descID,config->numberOfNormalizedBasedOnRules); + return 0; + } + if ( config->normalizedBasedOn[descID].jointEndID == INVALID_VALUE ) + { + fprintf(stderr,"Normalization Rule %u/%u for end of joint is unresolved, stopping..\n",descID,config->numberOfNormalizedBasedOnRules); + return 0; + } + } + //--------------------------------------------------------------------------------------------------------------------------- + + + // Resolve joint alignment.. + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfAlignmentRules; descID++) + { + config->alignment[descID].jointStartID=INVALID_VALUE; + config->alignment[descID].jointEndID =INVALID_VALUE; + } + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfAlignmentRules; descID++) + { + for (int jointID=0; jointIDnumberOfHierarchyElements; jointID++) + { + fprintf(stderr,"Trying to match norm rule %s(%u/%u) with %s(%u/%u)\n",config->descriptorElements[descID].joint,descID,config->numberOfNormalizedBasedOnRules,config->hierarchyElements[jointID].joint,jointID,config->numberOfHierarchyElements); + + if (strcmp(config->alignment[descID].jointStart,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->alignment[descID].jointStartID = jointID; + } + if (strcmp(config->alignment[descID].jointEnd,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->alignment[descID].jointEndID = jointID; + } + } + } + //--------------------------------------------------------------------------------------------------------------------------- + for (unsigned int descID=0; descIDnumberOfAlignmentRules; descID++) + { + if ( config->alignment[descID].jointStartID == INVALID_VALUE ) + { + fprintf(stderr,"Normalization Rule %u/%u for start of joint is unresolved, stopping..\n",descID,config->numberOfNormalizedBasedOnRules); + return 0; + } + if ( config->alignment[descID].jointEndID == INVALID_VALUE ) + { + fprintf(stderr,"Normalization Rule %u/%u for end of joint is unresolved, stopping..\n",descID,config->numberOfNormalizedBasedOnRules); + return 0; + } + } + //--------------------------------------------------------------------------------------------------------------------------- + + + + //Resolve parent order for OpenCV drawing.. + for (unsigned int jointID=0; jointIDnumberOfHierarchyElements; jointID++) + { + //Resolve NSxM to Joint ID + for (unsigned int parentID=0; parentIDnumberOfHierarchyElements; parentID++) + { + if (strcmp(config->hierarchyElements[parentID].joint,config->hierarchyElements[jointID].joint)==0) + { + fprintf(stderr,"Found\n"); + config->hierarchyElements[jointID].parentID = jointID; + break; + } + } + } + + + return 1; + } + return 0; +} + + + + + +static int loadModelConfigurationData(struct ModelConfigurationData* out,const char * jsonFilename) +{ + fprintf(stderr,"Loading Configuration file %s ...\n",jsonFilename); + unsigned int inputLength=0; + char* input = readFileToMemory(jsonFilename,&inputLength); + if (input!=0) + { + fprintf(stderr,"Parsing %s ...\n",jsonFilename); + const nx_json* json=nx_json_parse_utf8(input); + const nx_json* rule=0; + const nx_json* item=0; + + //------------------------------------------------------------------- + const nx_json* j = nx_json_get(json,"version"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { out->version = atof(j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"backend"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->backend,SMALL_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"precision"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->precision,SMALL_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"BVH"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->BVH,BIG_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"OutputDirectory"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->outputDirectory,BIG_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"veryHighNumberOfEpochs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->veryHighNumberOfEpochs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"highNumberOfEpochs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->highNumberOfEpochs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"defaultNumberOfEpochs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->defaultNumberOfEpochs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"defaultBatchSize"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->defaultBatchSize = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"learningRate"); + if ((j!=0) && (j->type==NX_JSON_DOUBLE)) { out->learningRate = (float) j->dbl_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"minEarlyStoppingDelta"); + if ((j!=0) && (j->type==NX_JSON_DOUBLE)) { out->minEarlyStoppingDelta = (float) j->dbl_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"activationFunction"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->activationFunction,SMALL_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"dropoutRate"); + if ((j!=0) && (j->type==NX_JSON_DOUBLE)) { out->dropoutRate = (float) j->dbl_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"lamda"); + if ((j!=0) && (j->type==NX_JSON_DOUBLE)) { out->lamda = (float) j->dbl_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"useQuadLoss"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->useQuadLoss = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"useSquaredLoss"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->useSquaredLoss = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"earlyStoppingPatience"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->earlyStoppingPatience = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"rememberWeights"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->rememberWeights = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"rememberConsecutiveWeights"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->rememberConsecutiveWeights = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"useOnlineHardExampleMining"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->useOnlineHardExampleMining = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"hardMiningEpochs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->hardMiningEpochs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"normalMiningEpochs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->normalMiningEpochs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"groupOutputs"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->groupOutputs = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"ignoreOcclusions"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->ignoreOcclusions = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"NSDMNormalizationMasterSwitch"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->NSDMNormalizationMasterSwitch = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"NSDMAlsoUseAlignmentAngles"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->NSDMAlsoUseAlignmentAngles = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"neuralNetworkDepth"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->neuralNetworkDepth = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"EDM"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->EDM = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"eNSRM"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->eNSRM = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"doPCA"); + if ((j!=0) && (j->type==NX_JSON_STRING)) { snprintf(out->doPCA,BIG_STR,"%s",j->text_value); } + //------------------------------------------------------------------- + j = nx_json_get(json,"PCADimensionsKept"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->PCADimensionsKept = j->int_value; } + //------------------------------------------------------------------- + j = nx_json_get(json,"padEnsembleInput"); + if ((j!=0) && (j->type==NX_JSON_INTEGER)) { out->padEnsembleInput = j->int_value; } + //------------------------------------------------------------------- + //------------------------------------------------------------------- + //------------------------------------------------------------------- + fprintf(stderr,"Parsed Initial Variables...\n"); + + //Now to parse Normalization elements.. + //------------------------------------------------------------------- + j = nx_json_get(json,"NormalizeNSDMBasedOn"); + if (j!=0) { + out->numberOfNormalizedBasedOnRules = j->length; + if (out->numberOfNormalizedBasedOnRules>3) + { + fprintf(stderr,"Maximum Accepted Normalization rules are 3!"); + out->numberOfNormalizedBasedOnRules = 3; + } + + for (int i=0; inumberOfNormalizedBasedOnRules; i++) + { + rule = nx_json_item(j,i); + //--------------------------------------------------------------------------------------------------- + item = nx_json_get(rule,"jointStart"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->normalizedBasedOn[i].jointStart,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"jointStartID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->normalizedBasedOn[i].jointStartID = item->int_value; } + item = nx_json_get(rule,"jointEnd"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->normalizedBasedOn[i].jointEnd,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"jointEndID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->normalizedBasedOn[i].jointEndID = item->int_value; } + } + } + //------------------------------------------------------------------- + + + //Now to parse Alignment elements.. + //------------------------------------------------------------------- + j = nx_json_get(json,"Alignment"); + if (j!=0) { + out->numberOfAlignmentRules = j->length; + if (out->numberOfAlignmentRules>3) + { + fprintf(stderr,"Maximum Accepted Alignment rules are 3!"); + out->numberOfAlignmentRules = 3; + } + + for (int i=0; inumberOfAlignmentRules; i++) + { + rule = nx_json_item(j,i); + //--------------------------------------------------------------------------------------------------- + item = nx_json_get(rule,"jointStart"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->alignment[i].jointStart,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"jointStartID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->alignment[i].jointStartID = item->int_value; } + item = nx_json_get(rule,"jointEnd"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->alignment[i].jointEnd,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"jointEndID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->alignment[i].jointEndID = item->int_value; } + } + } + //------------------------------------------------------------------- + + + //Now to parse NSxM elements.. + //------------------------------------------------------------------- + j = nx_json_get(json,"NSDM"); + if (j!=0) { + out->numberOfDescriptorElements = j->length; + if (out->numberOfDescriptorElements>MAX_DESCRIPTOR_ELEMENTS) + { + fprintf(stderr,"Maximum Accepted NSxM rules are %u!",MAX_DESCRIPTOR_ELEMENTS); + out->numberOfDescriptorElements = MAX_DESCRIPTOR_ELEMENTS; + } + + for (int i=0; inumberOfDescriptorElements; i++) + { + rule = nx_json_item(j,i); + //--------------------------------------------------------------------------------------------------- + item = nx_json_get(rule,"joint"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->descriptorElements[i].joint,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"jointID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->descriptorElements[i].jointID = item->int_value; } + item = nx_json_get(rule,"isVirtual"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->descriptorElements[i].isVirtual = item->int_value; } + item = nx_json_get(rule,"xOffset"); + if ((item!=0) && (item->type==NX_JSON_DOUBLE)) { out->descriptorElements[i].xOffset = (float) item->dbl_value; } + item = nx_json_get(rule,"yOffset"); + if ((item!=0) && (item->type==NX_JSON_DOUBLE)) { out->descriptorElements[i].yOffset = (float) item->dbl_value; } + item = nx_json_get(rule,"halfWayFromThisAnd"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->descriptorElements[i].halfwayFromJointAndThis,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"secondTargetJointID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->descriptorElements[i].secondTargetJointID = item->int_value; } + } + } + //------------------------------------------------------------------- + + + + + //Now to parse Hierarchy elements.. + //------------------------------------------------------------------- + j = nx_json_get(json,"hierarchy"); + if (j!=0) { + out->numberOfHierarchyElements = j->length; + if (out->numberOfHierarchyElements>MAX_HIERARCHY_ELEMENTS) + { + fprintf(stderr,"Maximum Accepted Hierarchy rules are %u!",MAX_HIERARCHY_ELEMENTS); + out->numberOfHierarchyElements = MAX_HIERARCHY_ELEMENTS; + } + + for (int i=0; inumberOfHierarchyElements; i++) + { + rule = nx_json_item(j,i); + //--------------------------------------------------------------------------------------------------- + item = nx_json_get(rule,"joint"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->hierarchyElements[i].joint,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"inheritNetwork"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->hierarchyElements[i].inheritNetwork,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"parent"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->hierarchyElements[i].parent,MAX_JOINT_NAME,"%s",item->text_value); } + item = nx_json_get(rule,"parentID"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->hierarchyElements[i].parentID = item->int_value; } + item = nx_json_get(rule,"importance"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->hierarchyElements[i].importance = item->int_value; } + item = nx_json_get(rule,"immuneToSelfOcclusions"); + if ((item!=0) && (item->type==NX_JSON_INTEGER)) { out->hierarchyElements[i].immuneToSelfOcclusions = item->int_value; } + } + } + //------------------------------------------------------------------- + + + + + //Now to parse Hierarchy elements.. + //------------------------------------------------------------------- + j = nx_json_get(json,"banned"); + if (j!=0) { + out->numberOfBannedJoints = j->length; + if (out->numberOfBannedJoints>MAX_BANNED_ELEMENTS) + { + fprintf(stderr,"Maximum Accepted Banned rules are %u!",MAX_BANNED_ELEMENTS); + out->numberOfBannedJoints = MAX_BANNED_ELEMENTS; + } + + for (int i=0; inumberOfBannedJoints; i++) + { + rule = nx_json_item(j,i); + //--------------------------------------------------------------------------------------------------- + item = nx_json_get(rule,"output"); + if ((item!=0) && (item->type==NX_JSON_STRING)) { snprintf(out->bannedJoints[i].joint,MAX_JOINT_NAME,"%s",item->text_value); } + } + } + //------------------------------------------------------------------- + + return resolveConfigurationData(out); + } + return 0; +} + + + + + + + +static int getCompositePoint( + float * iXOut, + float * iYOut, + float * iVisibilityOut, + int * invalidPointOut, + struct ModelConfigurationData* rules, + int i, + float * points2D, + unsigned int points2DLength + ) +{ + if (i > rules->numberOfDescriptorElements) + { + fprintf(stderr,"Point %u out of bounds for descriptor elements\n",i); + return 0; + } + + int invalidPoint = 0; + int numberOfNSDMRules = rules->numberOfDescriptorElements; + int iJointID = rules->descriptorElements[i].jointID; + + if (iJointID*3 > points2DLength) + { + fprintf(stderr,"Unable to get composite point %u\n",i); + return 0; + } + + float iX = points2D[iJointID*3+0]; + float iY = points2D[iJointID*3+1]; + float iVisibility = points2D[iJointID*3+2]; + //--------------------------------------------------------------------------- + //In case we fall through.. + *iXOut = iX; + *iYOut = iY; + *iVisibilityOut = iVisibility; + *invalidPointOut = invalidPoint; + //--------------------------------------------------------------------------- + + + if ((iX!=0) || (iY!=0)) + { + //--------------------------------------------------------------------------- + // Synthetic Points + //--------------------------------------------------------------------------- + + if (rules->descriptorElements[i].isVirtual==1) + { + + iX=iX+rules->descriptorElements[i].xOffset; + iY=iY+rules->descriptorElements[i].yOffset; + } + else + if (rules->descriptorElements[i].isVirtual==2) + { + int secondTargetJointID = rules->descriptorElements[i].secondTargetJointID;// rules['NSDM'][i]['secondTargetJointID'] + float secondTargetX = points2D[secondTargetJointID*3+0]; + float secondTargetY = points2D[secondTargetJointID*3+1]; + if ((secondTargetX==0) && (secondTargetY==0)) + { + invalidPoint=1; + iX=0; + iY=0; + } + else + { + iX=(float) (iX+secondTargetX)/2; + iY=(float) (iY+secondTargetY)/2; + } + } + //--------------------------------------------------------------------------- + + //Return values.. + *iXOut = iX; + *iYOut = iY; + *iVisibilityOut = iVisibility; + *invalidPointOut = invalidPoint; + return 1; + } + return 0; +} + + + + + + + + + + + + + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/src/MocapNET4/MocapNETLib4/NSxM/EDM.h b/src/MocapNET4/MocapNETLib4/NSxM/EDM.h new file mode 100644 index 0000000..65e8e84 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/NSxM/EDM.h @@ -0,0 +1,103 @@ +/** @file EDM.h + * @brief An implementation of an EDM descriptor + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef EDM_H_INCLUDED +#define EDM_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../JSON/readModelConfiguration.h" +#include "NSxM.h" +#include + + +static int countEDMElements(int numberOfJointRules) +{ + int count = 0; + for (int i=0; ij) + { + count+=1; + } + } + } + return count; +} + +/** @brief This function returns the euclidean distance between two input 2D joints and zero if either of them is invalid*/ +static float getJoint2DDistanceEDM(float aX,float aY,float bX,float bY) +{ + if ( ((aX==0) && (aY==0)) || ((bX==0) && (bY==0)) ) + { + return 0.0; + } + //------------------------- + float xDistance=(float) bX-aX; + float yDistance=(float) bY-aY; + float result = sqrt( (xDistance*xDistance) + (yDistance*yDistance) ); + + if (result!=result) + { + fprintf(stderr,"getJoint2DDistanceEDM yielded NaN..\n"); + return 0.0; + } + + return result; +} + +static int appendEDMElements( + float * input2DJoints, + unsigned int input2DJointsLength, + float * output, + struct ModelConfigurationData* rules + ) +{ + int numberOfJointRules = rules->numberOfDescriptorElements; + //---------------------- + float iX=0.0,iY=0.0,iVisibility=0.0; + int iInvalidPoint=0; + //---------------------- + float jX=0.0,jY=0.0,jVisibility=0.0; + int jInvalidPoint=0; + //---------------------- + int count = 0; + for (int i=0; ij) + { + getCompositePoint(&jX,&jY,&jVisibility,&jInvalidPoint,rules,j,input2DJoints,input2DJointsLength); + if ( (iInvalidPoint) && (jInvalidPoint) ) //<- Why is this AND and not OR ? also in EDM.py code + { + output[count] = 0.0; + } else + { + output[count] = getJoint2DDistanceEDM(iX,iY,jX,jY); + } + count+=1; + } + } + } + + return count; +} + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/src/MocapNET4/MocapNETLib4/NSxM/NSDM.h b/src/MocapNET4/MocapNETLib4/NSxM/NSDM.h new file mode 100644 index 0000000..7d044b7 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/NSxM/NSDM.h @@ -0,0 +1,45 @@ +/** @file NSDM.h + * @brief An implementation of an NSRM descriptor + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef NSDM_H_INCLUDED +#define NSDM_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include "../JSON/readModelConfiguration.h" +#include "NSxM.h" +#include + +static int countNSDMElements(int numberOfJointRules) +{ + return 2*numberOfJointRules*numberOfJointRules; +} + + + +static int appendNSDMElements( + float * input2DJoints, + struct descriptor * output, + int numberOfJointRules + ) +{ + return 0; +} + + + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/src/MocapNET4/MocapNETLib4/NSxM/NSRM.h b/src/MocapNET4/MocapNETLib4/NSxM/NSRM.h new file mode 100644 index 0000000..5f74405 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/NSxM/NSRM.h @@ -0,0 +1,343 @@ +/** @file NSRM.h + * @brief An implementation of an NSRM descriptor + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef NSRM_H_INCLUDED +#define NSRM_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include + + +const float goFromRadToDegrees=(float) 180.0 / M_PI; +const float goFromDegreesToRad=(float) M_PI / 180.0; + +/** @brief This function returns the euclidean distance between two input 2D joints and zero if either of them is invalid*/ +static float getJoint2DDistance_NSRM(float aX,float aY,float bX,float bY) +{ + float xDistance=(float) bX-aX; + float yDistance=(float) bY-aY; + return (float) sqrt( (xDistance*xDistance) + (yDistance*yDistance) ); +} + + +static float getAngleToAlignToZero_NSRM(float aX,float aY,float bX,float bY) +{ + if ( (aX==bX) && (aY==bY) ) { return 0; } + + + //Bigger magnitudes.. + aX=100*aX; + aY=100*aY; + bX=100*bX; + bY=100*bY; + + //We have points a, b and c and we want to calculate angle b + float lengthBetweenAAndB = getJoint2DDistance_NSRM(aX,aY,bX,bY); + + + //We align vertically.. , Point C is B offset in Y direction + float cX = bX; + float cY = bY - lengthBetweenAAndB; + + //fprintf(stderr,"We want to align A(%0.2f,%0.2f) to C(%0.2f,%0.2f) with pivot B(%0.2f,%0.2f)\n",aX,aY,cX,cY,bX,bY); + //fprintf(stderr,"length AB = %0.2f\n",lengthBetweenAAndB); + //fprintf(stderr,"bY = %0.2f\n",bY); + //fprintf(stderr,"cY = %0.2f = %0.2f - %0.2f\n",cY,bY,lengthBetweenAAndB); + + + //Calulate vector a->b + float abX = bX - aX; + float abY = bY - aY; + + //calculate vector c->b + float cbX = bX - cX; + float cbY = bY - cY; + + + float dot = ((abX * cbX) + (abY * cbY)); // dot product + float cross = ((abX * cbY) - (abY * cbX)); // cross product + + float alpha = atan2(cross,dot); + + //fprintf(stderr,"Angle is %0.2f rad or %0.2f degrees \n",alpha,alpha*goFromRadToDegrees); + return (float) alpha;// * goFromRadToDegrees ; +} + + + +static float getAngleToAlignToZeroJoints_NSRM(float * positions,unsigned int centerJoint,unsigned int referenceJoint) +{ + //We have points a, b and c and we want to calculate angle b + float aX= positions[referenceJoint*3+0]; + float aY= positions[referenceJoint*3+1]; + + float bX= positions[centerJoint*3+0]; + float bY= positions[centerJoint*3+1]; + + return getAngleToAlignToZero_NSRM(aX,aY,bX,bY); +} + + +/* +static int rotate2DPointsBasedOnJointAsCenter_NSRM(float * positions,int positionsSize,float angle,unsigned int centerJoint) +{ + if (positionsSize%3!=0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: incorrect positions.. \n" NORMAL); + return 0; + } + + if (positionsSize<=centerJoint*3) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: centerJoint out of bounds.. \n" NORMAL); + return 0; + } + + float s = sin((float) angle * goFromDegreesToRad); + float c = cos((float) angle * goFromDegreesToRad); + //================================================= + float cx = positions[centerJoint*3+0]; + float cy = positions[centerJoint*3+1]; + float cVisibility = positions[centerJoint*3+2]; + //================================================= + + if (cVisibility==0.0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: cannot work without pivot joint.. \n" NORMAL); + return 0; + } + + for (unsigned int jID=0; jID ",jX,jY,cx,cy,angle); + + //Translate point back to origin: + jX -= cx; + jY -= cy; + + //Rotate point + float xnew = jX * c - jY * s; + float ynew = jX * s + jY * c; + + //Translate point back: + positions[jID*3+0] = xnew + cx; + positions[jID*3+1] = ynew + cy; + + //fprintf(stderr,"%0.2f,%0.2f\n",positions[jID*3+0],positions[jID*3+1]); + } + + + return 1; +}*/ + + + +static int countNSRMElements(int numberOfJointRules) +{ + return numberOfJointRules*numberOfJointRules; +} + + + + +static int rotate2DPointsBasedOnJointAsCenter_NSRM(float * positions,unsigned int positionsLength,float angle,unsigned int centerJoint) +{ + if (positionsLength%3!=0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: incorrect positions.. \n" NORMAL); + return 0; + } + + if (positionsLength<=centerJoint*3) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: centerJoint out of bounds.. \n" NORMAL); + return 0; + } + + float s = sin((float) angle * goFromDegreesToRad ); + float c = cos((float) angle * goFromDegreesToRad ); + + float cx=positions[centerJoint*3+0]; + float cy=positions[centerJoint*3+1]; + float cVisibility=positions[centerJoint*3+2]; + + if (cVisibility==0.0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: cannot work with invisible pivot joint.. \n" NORMAL); + return 0; + } + + for (unsigned int jID=0; jID ",jX,jY,cx,cy,angle); + + //Translate point back to origin: + jX -= cx; + jY -= cy; + + //Rotate point + float xnew = jX * c - jY * s; + float ynew = jX * s + jY * c; + + //Translate point back: + positions[jID*3+0] = xnew + cx; + positions[jID*3+1] = ynew + cy; + + //fprintf(stderr,"%0.2f,%0.2f\n",positions[jID*3+0],positions[jID*3+1]); + } + + + return 1; +} + + + +static float performNSRMAlignment(float * input2DJoints, + unsigned int input2DJointsLength, + struct ModelConfigurationData* rules) +{ + //Enforce alignment rule.. + //------------------------------------------------------------ + int pivotPoint = rules->alignment[0].jointStartID;// rules['Alignment'][0]['jointStartID'] + int referencePoint = rules->alignment[0].jointEndID; // rules['Alignment'][0]['jointEndID'] + //------------------------------------------------------------ + float pivotX = input2DJoints[pivotPoint*3+0]; + float pivotY = input2DJoints[pivotPoint*3+1]; + float pivotVisibility = input2DJoints[pivotPoint*3+2]; + + float referenceX = input2DJoints[referencePoint*3+0]; + float referenceY = input2DJoints[referencePoint*3+1]; + float referenceVisibility = input2DJoints[referencePoint*3+2]; + //------------------------------------------------------------ + + float angleToRotate = 0.0; + if ((pivotVisibility!=0) && (referenceVisibility!=0)) + { + angleToRotate = getAngleToAlignToZero_NSRM(pivotX,pivotY,referenceX,referenceY); + rotate2DPointsBasedOnJointAsCenter_NSRM(input2DJoints,input2DJointsLength,angleToRotate,pivotPoint); + } + + return angleToRotate; +} + + +static int appendNSRMElements( + float * input2DJoints, + unsigned int input2DJointsLength, + float * output, + struct ModelConfigurationData* rules, + float angleUsedToRotateInput + ) +{ + int numberOfJointRules = rules->numberOfDescriptorElements; + //---------------------- + float iX=0.0,iY=0.0,iVisibility=0.0; + int iInvalidPoint=0; + //---------------------- + float jX=0.0,jY=0.0,jVisibility=0.0; + int jInvalidPoint=0; + //---------------------- + + //----------------------------------------------------------------------------------------------------- + // ..Main NSRM parameters .. + //----------------------------------------------------------------------------------------------------- + int count = 0; + for (int i=0; ieNSRM) + { + count=0; + int iJointID = rules->descriptorElements[0].jointID; + iX = input2DJoints[iJointID*3+0]; + iY = input2DJoints[iJointID*3+1]; + //getCompositePoint(&iX,&iY,&iVisibility,&iInvalidPoint,rules,i,input2DJoints,input2DJointsLength); + for (int i=0; i0) + { + int jJointID = rules->descriptorElements[j].jointID; + jX = input2DJoints[jJointID*3+0]; + jY = input2DJoints[jJointID*3+1]; + //getCompositePoint(&jX,&jY,&jVisibility,&jInvalidPoint,rules,j,input2DJoints,input2DJointsLength); + output[count] = getJoint2DDistance_NSRM(iX,iY,jX,jY); + } + } + count+=1; + } + } + } + + + //NSDM Normalization rule does not apply on NSRM + //SKIPPED + //----------------------------------------------------------------------------------------------------- + + //Enforce alignment rule.. + //------------------------------------------------------------ + int iJointID = rules->alignment[0].jointStartID;// rules['Alignment'][0]['jointStartID'] + int jJointID = rules->alignment[0].jointEndID; // rules['Alignment'][0]['jointEndID'] + //------------------------------------------------------------ + float aX = input2DJoints[iJointID*3+0]; + float aY = input2DJoints[iJointID*3+1]; + float bX = input2DJoints[jJointID*3+0]; + float bY = input2DJoints[jJointID*3+1]; + //------------------------------------------------------------ + float alignmentAngle = getAngleToAlignToZero_NSRM(aX,aY,bX,bY); + //fprintf(stderr,"ALIGNMENT %u %u %f",iJointID,jJointID,alignmentAngle); + for (int i=0; i +#include +#include +#include + +#include "../JSON/readListFile.h" +#include "../JSON/readModelConfiguration.h" + +#include "../PCA/PCA.h" + +#include "../IO/inputRouting.h" + +#include "EDM.h" +#include "NSDM.h" +#include "NSRM.h" + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + +struct label +{ + char * str; + unsigned int length; +}; + +struct descriptor +{ + float * routedInput; + int routedInputLength; + unsigned int numberOfElements; + unsigned int maxNumberOfElements; + struct label * labels; + float * values; +}; + + +static int destroyDescriptor( + struct descriptor * output + ) +{ + //----------------------------------------------- + if (output->values!=0) + { free(output->values); } + //----------------------------------------------- + for (int i=0; imaxNumberOfElements; i++) + { + if (output->labels[i].str!=0) + { + free(output->labels[i].str); + output->labels[i].str = 0; + output->labels[i].length = 0; + } + } + //----------------------------------------------- + return 1; +} + + + + +static int createDescriptor( + struct descriptor * output, + float * data2DRaw, + unsigned int data2DRawLength, + struct inputRouting * route, + struct ModelConfigurationData * config, + struct PCAData * pca, + struct listFileData * listInputJoints, + struct listFileData * listOutput + ) +{ + fprintf(stderr,GREEN "createDescriptor..\n" NORMAL); + if (output==0) { return 0; } + //--------------------------------------------------------- + int useEDM = config->EDM; + int useNSRM = config->eNSRM; + int alignPoints = config->NSDMAlsoUseAlignmentAngles; + int usePCADimensions = config->PCADimensionsKept; + int numberOfJointRules = config->numberOfDescriptorElements; + //--------------------------------------------------------- + + + if (output->routedInput==0) + { + output->routedInput = (float *) malloc(sizeof(float) * route->numberOfRoutingRules * 3); + output->routedInputLength = route->numberOfRoutingRules * 3; + } + + //-------------------------------------- + if ( + !routeInput( + output->routedInput, + &output->routedInputLength, + config, + route, + data2DRaw, + data2DRawLength + ) + ) + { + fprintf(stderr,RED "Unable to route 2D input..\n" NORMAL); + return 0; + } + //-------------------------------------- + float * data2D = output->routedInput; + unsigned int data2DLength = output->routedInputLength; + //-------------------------------------- + + if (listInputJoints->numberOfEntries!=data2DLength) + { + fprintf(stderr,RED "Mismatch of Input 2D Points Vs Neural Network 2D Joints..\n" NORMAL); + } + + for (int i=0; ioutput->maxNumberOfElements) + { + //Space already allocated but not enough, + //Destroy anything previously allocated + destroyDescriptor(output); + } + //--------------------------------------------------------- + if (output->maxNumberOfElements==0) + { //If operating on a newly allocated descriptor + output->maxNumberOfElements = neededSpace; + //--------------------------------------------------------- + output->values = (float*) malloc( sizeof(float) * output->maxNumberOfElements ); + if (output->values!=0) + { memset(output->values,0,sizeof(float) * output->maxNumberOfElements); } + //--------------------------------------------------------- + output->labels = (struct label *) malloc( sizeof(struct label) * output->maxNumberOfElements ); + if (output->labels!=0) + { memset(output->labels,0,sizeof(struct label) * output->maxNumberOfElements); } + //We have a clean output descriptor + } + //--------------------------------------------------------- + + + float * outputInPosition = output->values; + + //Copy all 2D Data this must happen before the alignment!.. + fprintf(stderr,"Copying %u 2D coordinates \n",data2DLength); + for (int i=0; ieNSRM)//alignPoints) + { + fprintf(stderr,"Aligning Points\n"); + //Do point alignment here.. + angleToRotate = performNSRMAlignment(data2D,data2DLength,config); + fprintf(stderr,YELLOW "Correcting skeleton by rotating it %0.2f degrees\n" NORMAL,angleToRotate); + } + + /* + fprintf(stderr," 2d = list()\n"); + for (int i=0; ivalues[i],i); + }*/ + + + if (useEDM) + { + fprintf(stderr,"Using EDM\n"); + //--------------------------------------------------------- + int EDMElements = appendEDMElements( + data2D, + data2DLength, + outputInPosition, + config + ); + outputInPosition += EDMElements; + //--------------------------------------------------------- + /* + fprintf(stderr," EDM = list()\n"); + for (int i=0; ivalues[i],i); + }*/ + } + + + if (useNSRM) + { + fprintf(stderr,"Using NSRM\n"); + //--------------------------------------------------------- + int NSRMElements = appendNSRMElements( + data2D, + data2DLength, + outputInPosition, + config, + angleToRotate + ); + outputInPosition += NSRMElements; + //--------------------------------------------------------- + /* + fprintf(stderr," NSRM = list()\n"); + for (int i=0; ivalues[i],i); + }*/ + } + output->values[169] += angleToRotate; + + fprintf(stderr,"Descriptor yielded %u elements : ",neededSpace); + output->numberOfElements = neededSpace; + for (int i=0; ivalues[i]>10.0) { fprintf(stderr,RED); } + fprintf(stderr,"%0.2f(#%u) ",output->values[i],i); + if (output->values[i]>10.0) { fprintf(stderr,NORMAL); } + } + fprintf(stderr,"\n"); + + if (config->doPCA) + { + /* + fprintf(stderr," PCA = list()\n"); + for (int i=0; ivalues[i],i); + }*/ + int finalOutputSize = config->PCADimensionsKept; + doPCATransform( + output->values, + &finalOutputSize, + pca, + output->values, + neededSpace, + config->PCADimensionsKept + ); + + fprintf(stderr,"PCA (mean %0.2f/std %0.2f) packed descriptor yielded %u elements : \n",pca->mean,pca->std,finalOutputSize); + for (int i=0; ivalues[i]); + } + output->numberOfElements = finalOutputSize; + } + + return 1; +} + +/** @brief This function returns the euclidean distance between two input 2D joints and zero if either of them is invalid*/ +static float getJoint2DDistanceNSxM(float* in,int jointA,int jointB) +{ + float aX=in[jointA*3+0]; + float aY=in[jointA*3+1]; + float bX=in[jointB*3+0]; + float bY=in[jointB*3+1]; + if ( ((aX==0) && (aY==0)) || ((bX==0) && (bY==0)) ) { + return 0.0; + } + + + float xDistance=(float) bX-aX; + float yDistance=(float) bY-aY; + return sqrt( (xDistance*xDistance) + (yDistance*yDistance) ); +} + + + + +/** @brief This is an array of names for all uncompressed 2D inputs expected. */ +static const unsigned int mocapNET_InputLength_WithoutNSDM_upperbody = 33; + +/** @brief Use rich diagonal, part of networks after 31-01-2021 */ +static const unsigned int richDiagonal_upperbody = 1; + +/** @brief An array of strings that contains the label for each expected input. */ +static const char * mocapNET_upperbody[] = +{ + "2DX_hip", //0 + "2DY_hip", //1 + "visible_hip", //2 + "2DX_neck", //3 + "2DY_neck", //4 + "visible_neck", //5 + "2DX_head", //6 + "2DY_head", //7 + "visible_head", //8 + "2DX_EndSite_eye.l", //9 + "2DY_EndSite_eye.l", //10 + "visible_EndSite_eye.l", //11 + "2DX_EndSite_eye.r", //12 + "2DY_EndSite_eye.r", //13 + "visible_EndSite_eye.r", //14 + "2DX_rshoulder", //15 + "2DY_rshoulder", //16 + "visible_rshoulder", //17 + "2DX_relbow", //18 + "2DY_relbow", //19 + "visible_relbow", //20 + "2DX_rhand", //21 + "2DY_rhand", //22 + "visible_rhand", //23 + "2DX_lshoulder", //24 + "2DY_lshoulder", //25 + "visible_lshoulder", //26 + "2DX_lelbow", //27 + "2DY_lelbow", //28 + "visible_lelbow", //29 + "2DX_lhand", //30 + "2DY_lhand", //31 + "visible_lhand", //32 +//This is where regular input ends and the NSDM data kicks in.. + "angleUsedFor2DRotation_0", //33 + "hipY-EndSite_eye.rY-Angle", //34 + "hipY-EndSite_eye.lY-Angle", //35 + "hipY-neckY-Angle", //36 + "hipY-rshoulderY-Angle", //37 + "hipY-halfway_rshoulder_and_relbowY-Angle", //38 + "hipY-relbowY-Angle", //39 + "hipY-halfway_relbow_and_rhandY-Angle", //40 + "hipY-rhandY-Angle", //41 + "hipY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //42 + "hipY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //43 + "hipY-lshoulderY-Angle", //44 + "hipY-halfway_lshoulder_and_lelbowY-Angle", //45 + "hipY-lelbowY-Angle", //46 + "hipY-halfway_lelbow_and_lhandY-Angle", //47 + "hipY-lhandY-Angle", //48 + "hipY-halfway_neck_and_hipY-Angle", //49 + "EndSite_eye.rY-hipY-Angle", //50 + "angleUsedFor2DRotation_1", //51 + "EndSite_eye.rY-EndSite_eye.lY-Angle", //52 + "EndSite_eye.rY-neckY-Angle", //53 + "EndSite_eye.rY-rshoulderY-Angle", //54 + "EndSite_eye.rY-halfway_rshoulder_and_relbowY-Angle", //55 + "EndSite_eye.rY-relbowY-Angle", //56 + "EndSite_eye.rY-halfway_relbow_and_rhandY-Angle", //57 + "EndSite_eye.rY-rhandY-Angle", //58 + "EndSite_eye.rY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //59 + "EndSite_eye.rY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //60 + "EndSite_eye.rY-lshoulderY-Angle", //61 + "EndSite_eye.rY-halfway_lshoulder_and_lelbowY-Angle", //62 + "EndSite_eye.rY-lelbowY-Angle", //63 + "EndSite_eye.rY-halfway_lelbow_and_lhandY-Angle", //64 + "EndSite_eye.rY-lhandY-Angle", //65 + "EndSite_eye.rY-halfway_neck_and_hipY-Angle", //66 + "EndSite_eye.lY-hipY-Angle", //67 + "EndSite_eye.lY-EndSite_eye.rY-Angle", //68 + "angleUsedFor2DRotation_2", //69 + "EndSite_eye.lY-neckY-Angle", //70 + "EndSite_eye.lY-rshoulderY-Angle", //71 + "EndSite_eye.lY-halfway_rshoulder_and_relbowY-Angle", //72 + "EndSite_eye.lY-relbowY-Angle", //73 + "EndSite_eye.lY-halfway_relbow_and_rhandY-Angle", //74 + "EndSite_eye.lY-rhandY-Angle", //75 + "EndSite_eye.lY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //76 + "EndSite_eye.lY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //77 + "EndSite_eye.lY-lshoulderY-Angle", //78 + "EndSite_eye.lY-halfway_lshoulder_and_lelbowY-Angle", //79 + "EndSite_eye.lY-lelbowY-Angle", //80 + "EndSite_eye.lY-halfway_lelbow_and_lhandY-Angle", //81 + "EndSite_eye.lY-lhandY-Angle", //82 + "EndSite_eye.lY-halfway_neck_and_hipY-Angle", //83 + "neckY-hipY-Angle", //84 + "neckY-EndSite_eye.rY-Angle", //85 + "neckY-EndSite_eye.lY-Angle", //86 + "angleUsedFor2DRotation_3", //87 + "neckY-rshoulderY-Angle", //88 + "neckY-halfway_rshoulder_and_relbowY-Angle", //89 + "neckY-relbowY-Angle", //90 + "neckY-halfway_relbow_and_rhandY-Angle", //91 + "neckY-rhandY-Angle", //92 + "neckY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //93 + "neckY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //94 + "neckY-lshoulderY-Angle", //95 + "neckY-halfway_lshoulder_and_lelbowY-Angle", //96 + "neckY-lelbowY-Angle", //97 + "neckY-halfway_lelbow_and_lhandY-Angle", //98 + "neckY-lhandY-Angle", //99 + "neckY-halfway_neck_and_hipY-Angle", //100 + "rshoulderY-hipY-Angle", //101 + "rshoulderY-EndSite_eye.rY-Angle", //102 + "rshoulderY-EndSite_eye.lY-Angle", //103 + "rshoulderY-neckY-Angle", //104 + "angleUsedFor2DRotation_4", //105 + "rshoulderY-halfway_rshoulder_and_relbowY-Angle", //106 + "rshoulderY-relbowY-Angle", //107 + "rshoulderY-halfway_relbow_and_rhandY-Angle", //108 + "rshoulderY-rhandY-Angle", //109 + "rshoulderY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //110 + "rshoulderY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //111 + "rshoulderY-lshoulderY-Angle", //112 + "rshoulderY-halfway_lshoulder_and_lelbowY-Angle", //113 + "rshoulderY-lelbowY-Angle", //114 + "rshoulderY-halfway_lelbow_and_lhandY-Angle", //115 + "rshoulderY-lhandY-Angle", //116 + "rshoulderY-halfway_neck_and_hipY-Angle", //117 + "halfway_rshoulder_and_relbowY-hipY-Angle", //118 + "halfway_rshoulder_and_relbowY-EndSite_eye.rY-Angle", //119 + "halfway_rshoulder_and_relbowY-EndSite_eye.lY-Angle", //120 + "halfway_rshoulder_and_relbowY-neckY-Angle", //121 + "halfway_rshoulder_and_relbowY-rshoulderY-Angle", //122 + "angleUsedFor2DRotation_5", //123 + "halfway_rshoulder_and_relbowY-relbowY-Angle", //124 + "halfway_rshoulder_and_relbowY-halfway_relbow_and_rhandY-Angle", //125 + "halfway_rshoulder_and_relbowY-rhandY-Angle", //126 + "halfway_rshoulder_and_relbowY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //127 + "halfway_rshoulder_and_relbowY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //128 + "halfway_rshoulder_and_relbowY-lshoulderY-Angle", //129 + "halfway_rshoulder_and_relbowY-halfway_lshoulder_and_lelbowY-Angle", //130 + "halfway_rshoulder_and_relbowY-lelbowY-Angle", //131 + "halfway_rshoulder_and_relbowY-halfway_lelbow_and_lhandY-Angle", //132 + "halfway_rshoulder_and_relbowY-lhandY-Angle", //133 + "halfway_rshoulder_and_relbowY-halfway_neck_and_hipY-Angle", //134 + "relbowY-hipY-Angle", //135 + "relbowY-EndSite_eye.rY-Angle", //136 + "relbowY-EndSite_eye.lY-Angle", //137 + "relbowY-neckY-Angle", //138 + "relbowY-rshoulderY-Angle", //139 + "relbowY-halfway_rshoulder_and_relbowY-Angle", //140 + "angleUsedFor2DRotation_6", //141 + "relbowY-halfway_relbow_and_rhandY-Angle", //142 + "relbowY-rhandY-Angle", //143 + "relbowY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //144 + "relbowY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //145 + "relbowY-lshoulderY-Angle", //146 + "relbowY-halfway_lshoulder_and_lelbowY-Angle", //147 + "relbowY-lelbowY-Angle", //148 + "relbowY-halfway_lelbow_and_lhandY-Angle", //149 + "relbowY-lhandY-Angle", //150 + "relbowY-halfway_neck_and_hipY-Angle", //151 + "halfway_relbow_and_rhandY-hipY-Angle", //152 + "halfway_relbow_and_rhandY-EndSite_eye.rY-Angle", //153 + "halfway_relbow_and_rhandY-EndSite_eye.lY-Angle", //154 + "halfway_relbow_and_rhandY-neckY-Angle", //155 + "halfway_relbow_and_rhandY-rshoulderY-Angle", //156 + "halfway_relbow_and_rhandY-halfway_rshoulder_and_relbowY-Angle", //157 + "halfway_relbow_and_rhandY-relbowY-Angle", //158 + "angleUsedFor2DRotation_7", //159 + "halfway_relbow_and_rhandY-rhandY-Angle", //160 + "halfway_relbow_and_rhandY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //161 + "halfway_relbow_and_rhandY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //162 + "halfway_relbow_and_rhandY-lshoulderY-Angle", //163 + "halfway_relbow_and_rhandY-halfway_lshoulder_and_lelbowY-Angle", //164 + "halfway_relbow_and_rhandY-lelbowY-Angle", //165 + "halfway_relbow_and_rhandY-halfway_lelbow_and_lhandY-Angle", //166 + "halfway_relbow_and_rhandY-lhandY-Angle", //167 + "halfway_relbow_and_rhandY-halfway_neck_and_hipY-Angle", //168 + "rhandY-hipY-Angle", //169 + "rhandY-EndSite_eye.rY-Angle", //170 + "rhandY-EndSite_eye.lY-Angle", //171 + "rhandY-neckY-Angle", //172 + "rhandY-rshoulderY-Angle", //173 + "rhandY-halfway_rshoulder_and_relbowY-Angle", //174 + "rhandY-relbowY-Angle", //175 + "rhandY-halfway_relbow_and_rhandY-Angle", //176 + "angleUsedFor2DRotation_8", //177 + "rhandY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //178 + "rhandY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //179 + "rhandY-lshoulderY-Angle", //180 + "rhandY-halfway_lshoulder_and_lelbowY-Angle", //181 + "rhandY-lelbowY-Angle", //182 + "rhandY-halfway_lelbow_and_lhandY-Angle", //183 + "rhandY-lhandY-Angle", //184 + "rhandY-halfway_neck_and_hipY-Angle", //185 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-hipY-Angle", //186 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-EndSite_eye.rY-Angle", //187 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-EndSite_eye.lY-Angle", //188 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-neckY-Angle", //189 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-rshoulderY-Angle", //190 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-halfway_rshoulder_and_relbowY-Angle", //191 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-relbowY-Angle", //192 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-halfway_relbow_and_rhandY-Angle", //193 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-rhandY-Angle", //194 + "angleUsedFor2DRotation_9", //195 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //196 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-lshoulderY-Angle", //197 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-halfway_lshoulder_and_lelbowY-Angle", //198 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-lelbowY-Angle", //199 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-halfway_lelbow_and_lhandY-Angle", //200 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-lhandY-Angle", //201 + "virtual_hip_x_minus_0_15_y_minus_0_15Y-halfway_neck_and_hipY-Angle", //202 + "virtual_hip_x_plus0_15_y_minus_0_15Y-hipY-Angle", //203 + "virtual_hip_x_plus0_15_y_minus_0_15Y-EndSite_eye.rY-Angle", //204 + "virtual_hip_x_plus0_15_y_minus_0_15Y-EndSite_eye.lY-Angle", //205 + "virtual_hip_x_plus0_15_y_minus_0_15Y-neckY-Angle", //206 + "virtual_hip_x_plus0_15_y_minus_0_15Y-rshoulderY-Angle", //207 + "virtual_hip_x_plus0_15_y_minus_0_15Y-halfway_rshoulder_and_relbowY-Angle", //208 + "virtual_hip_x_plus0_15_y_minus_0_15Y-relbowY-Angle", //209 + "virtual_hip_x_plus0_15_y_minus_0_15Y-halfway_relbow_and_rhandY-Angle", //210 + "virtual_hip_x_plus0_15_y_minus_0_15Y-rhandY-Angle", //211 + "virtual_hip_x_plus0_15_y_minus_0_15Y-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //212 + "angleUsedFor2DRotation_10", //213 + "virtual_hip_x_plus0_15_y_minus_0_15Y-lshoulderY-Angle", //214 + "virtual_hip_x_plus0_15_y_minus_0_15Y-halfway_lshoulder_and_lelbowY-Angle", //215 + "virtual_hip_x_plus0_15_y_minus_0_15Y-lelbowY-Angle", //216 + "virtual_hip_x_plus0_15_y_minus_0_15Y-halfway_lelbow_and_lhandY-Angle", //217 + "virtual_hip_x_plus0_15_y_minus_0_15Y-lhandY-Angle", //218 + "virtual_hip_x_plus0_15_y_minus_0_15Y-halfway_neck_and_hipY-Angle", //219 + "lshoulderY-hipY-Angle", //220 + "lshoulderY-EndSite_eye.rY-Angle", //221 + "lshoulderY-EndSite_eye.lY-Angle", //222 + "lshoulderY-neckY-Angle", //223 + "lshoulderY-rshoulderY-Angle", //224 + "lshoulderY-halfway_rshoulder_and_relbowY-Angle", //225 + "lshoulderY-relbowY-Angle", //226 + "lshoulderY-halfway_relbow_and_rhandY-Angle", //227 + "lshoulderY-rhandY-Angle", //228 + "lshoulderY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //229 + "lshoulderY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //230 + "angleUsedFor2DRotation_11", //231 + "lshoulderY-halfway_lshoulder_and_lelbowY-Angle", //232 + "lshoulderY-lelbowY-Angle", //233 + "lshoulderY-halfway_lelbow_and_lhandY-Angle", //234 + "lshoulderY-lhandY-Angle", //235 + "lshoulderY-halfway_neck_and_hipY-Angle", //236 + "halfway_lshoulder_and_lelbowY-hipY-Angle", //237 + "halfway_lshoulder_and_lelbowY-EndSite_eye.rY-Angle", //238 + "halfway_lshoulder_and_lelbowY-EndSite_eye.lY-Angle", //239 + "halfway_lshoulder_and_lelbowY-neckY-Angle", //240 + "halfway_lshoulder_and_lelbowY-rshoulderY-Angle", //241 + "halfway_lshoulder_and_lelbowY-halfway_rshoulder_and_relbowY-Angle", //242 + "halfway_lshoulder_and_lelbowY-relbowY-Angle", //243 + "halfway_lshoulder_and_lelbowY-halfway_relbow_and_rhandY-Angle", //244 + "halfway_lshoulder_and_lelbowY-rhandY-Angle", //245 + "halfway_lshoulder_and_lelbowY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //246 + "halfway_lshoulder_and_lelbowY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //247 + "halfway_lshoulder_and_lelbowY-lshoulderY-Angle", //248 + "angleUsedFor2DRotation_12", //249 + "halfway_lshoulder_and_lelbowY-lelbowY-Angle", //250 + "halfway_lshoulder_and_lelbowY-halfway_lelbow_and_lhandY-Angle", //251 + "halfway_lshoulder_and_lelbowY-lhandY-Angle", //252 + "halfway_lshoulder_and_lelbowY-halfway_neck_and_hipY-Angle", //253 + "lelbowY-hipY-Angle", //254 + "lelbowY-EndSite_eye.rY-Angle", //255 + "lelbowY-EndSite_eye.lY-Angle", //256 + "lelbowY-neckY-Angle", //257 + "lelbowY-rshoulderY-Angle", //258 + "lelbowY-halfway_rshoulder_and_relbowY-Angle", //259 + "lelbowY-relbowY-Angle", //260 + "lelbowY-halfway_relbow_and_rhandY-Angle", //261 + "lelbowY-rhandY-Angle", //262 + "lelbowY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //263 + "lelbowY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //264 + "lelbowY-lshoulderY-Angle", //265 + "lelbowY-halfway_lshoulder_and_lelbowY-Angle", //266 + "angleUsedFor2DRotation_13", //267 + "lelbowY-halfway_lelbow_and_lhandY-Angle", //268 + "lelbowY-lhandY-Angle", //269 + "lelbowY-halfway_neck_and_hipY-Angle", //270 + "halfway_lelbow_and_lhandY-hipY-Angle", //271 + "halfway_lelbow_and_lhandY-EndSite_eye.rY-Angle", //272 + "halfway_lelbow_and_lhandY-EndSite_eye.lY-Angle", //273 + "halfway_lelbow_and_lhandY-neckY-Angle", //274 + "halfway_lelbow_and_lhandY-rshoulderY-Angle", //275 + "halfway_lelbow_and_lhandY-halfway_rshoulder_and_relbowY-Angle", //276 + "halfway_lelbow_and_lhandY-relbowY-Angle", //277 + "halfway_lelbow_and_lhandY-halfway_relbow_and_rhandY-Angle", //278 + "halfway_lelbow_and_lhandY-rhandY-Angle", //279 + "halfway_lelbow_and_lhandY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //280 + "halfway_lelbow_and_lhandY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //281 + "halfway_lelbow_and_lhandY-lshoulderY-Angle", //282 + "halfway_lelbow_and_lhandY-halfway_lshoulder_and_lelbowY-Angle", //283 + "halfway_lelbow_and_lhandY-lelbowY-Angle", //284 + "angleUsedFor2DRotation_14", //285 + "halfway_lelbow_and_lhandY-lhandY-Angle", //286 + "halfway_lelbow_and_lhandY-halfway_neck_and_hipY-Angle", //287 + "lhandY-hipY-Angle", //288 + "lhandY-EndSite_eye.rY-Angle", //289 + "lhandY-EndSite_eye.lY-Angle", //290 + "lhandY-neckY-Angle", //291 + "lhandY-rshoulderY-Angle", //292 + "lhandY-halfway_rshoulder_and_relbowY-Angle", //293 + "lhandY-relbowY-Angle", //294 + "lhandY-halfway_relbow_and_rhandY-Angle", //295 + "lhandY-rhandY-Angle", //296 + "lhandY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //297 + "lhandY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //298 + "lhandY-lshoulderY-Angle", //299 + "lhandY-halfway_lshoulder_and_lelbowY-Angle", //300 + "lhandY-lelbowY-Angle", //301 + "lhandY-halfway_lelbow_and_lhandY-Angle", //302 + "angleUsedFor2DRotation_15", //303 + "lhandY-halfway_neck_and_hipY-Angle", //304 + "halfway_neck_and_hipY-hipY-Angle", //305 + "halfway_neck_and_hipY-EndSite_eye.rY-Angle", //306 + "halfway_neck_and_hipY-EndSite_eye.lY-Angle", //307 + "halfway_neck_and_hipY-neckY-Angle", //308 + "halfway_neck_and_hipY-rshoulderY-Angle", //309 + "halfway_neck_and_hipY-halfway_rshoulder_and_relbowY-Angle", //310 + "halfway_neck_and_hipY-relbowY-Angle", //311 + "halfway_neck_and_hipY-halfway_relbow_and_rhandY-Angle", //312 + "halfway_neck_and_hipY-rhandY-Angle", //313 + "halfway_neck_and_hipY-virtual_hip_x_minus_0_15_y_minus_0_15Y-Angle", //314 + "halfway_neck_and_hipY-virtual_hip_x_plus0_15_y_minus_0_15Y-Angle", //315 + "halfway_neck_and_hipY-lshoulderY-Angle", //316 + "halfway_neck_and_hipY-halfway_lshoulder_and_lelbowY-Angle", //317 + "halfway_neck_and_hipY-lelbowY-Angle", //318 + "halfway_neck_and_hipY-halfway_lelbow_and_lhandY-Angle", //319 + "halfway_neck_and_hipY-lhandY-Angle", //320 + "angleUsedFor2DRotation_16", //321 + "end" +}; +/** @brief Programmer friendly enumerator of expected inputs*/ +enum mocapNET_upperbody_enum +{ + MNET_UPPERBODY_IN_2DX_HIP = 0, //0 + MNET_UPPERBODY_IN_2DY_HIP, //1 + MNET_UPPERBODY_IN_VISIBLE_HIP, //2 + MNET_UPPERBODY_IN_2DX_NECK, //3 + MNET_UPPERBODY_IN_2DY_NECK, //4 + MNET_UPPERBODY_IN_VISIBLE_NECK, //5 + MNET_UPPERBODY_IN_2DX_HEAD, //6 + MNET_UPPERBODY_IN_2DY_HEAD, //7 + MNET_UPPERBODY_IN_VISIBLE_HEAD, //8 + MNET_UPPERBODY_IN_2DX_ENDSITE_EYE_L, //9 + MNET_UPPERBODY_IN_2DY_ENDSITE_EYE_L, //10 + MNET_UPPERBODY_IN_VISIBLE_ENDSITE_EYE_L, //11 + MNET_UPPERBODY_IN_2DX_ENDSITE_EYE_R, //12 + MNET_UPPERBODY_IN_2DY_ENDSITE_EYE_R, //13 + MNET_UPPERBODY_IN_VISIBLE_ENDSITE_EYE_R, //14 + MNET_UPPERBODY_IN_2DX_RSHOULDER, //15 + MNET_UPPERBODY_IN_2DY_RSHOULDER, //16 + MNET_UPPERBODY_IN_VISIBLE_RSHOULDER, //17 + MNET_UPPERBODY_IN_2DX_RELBOW, //18 + MNET_UPPERBODY_IN_2DY_RELBOW, //19 + MNET_UPPERBODY_IN_VISIBLE_RELBOW, //20 + MNET_UPPERBODY_IN_2DX_RHAND, //21 + MNET_UPPERBODY_IN_2DY_RHAND, //22 + MNET_UPPERBODY_IN_VISIBLE_RHAND, //23 + MNET_UPPERBODY_IN_2DX_LSHOULDER, //24 + MNET_UPPERBODY_IN_2DY_LSHOULDER, //25 + MNET_UPPERBODY_IN_VISIBLE_LSHOULDER, //26 + MNET_UPPERBODY_IN_2DX_LELBOW, //27 + MNET_UPPERBODY_IN_2DY_LELBOW, //28 + MNET_UPPERBODY_IN_VISIBLE_LELBOW, //29 + MNET_UPPERBODY_IN_2DX_LHAND, //30 + MNET_UPPERBODY_IN_2DY_LHAND, //31 + MNET_UPPERBODY_IN_VISIBLE_LHAND, //32 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_0, //33 + MNET_UPPERBODY_IN_HIPY_ENDSITE_EYE_RY_ANGLE, //34 + MNET_UPPERBODY_IN_HIPY_ENDSITE_EYE_LY_ANGLE, //35 + MNET_UPPERBODY_IN_HIPY_NECKY_ANGLE, //36 + MNET_UPPERBODY_IN_HIPY_RSHOULDERY_ANGLE, //37 + MNET_UPPERBODY_IN_HIPY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //38 + MNET_UPPERBODY_IN_HIPY_RELBOWY_ANGLE, //39 + MNET_UPPERBODY_IN_HIPY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //40 + MNET_UPPERBODY_IN_HIPY_RHANDY_ANGLE, //41 + MNET_UPPERBODY_IN_HIPY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //42 + MNET_UPPERBODY_IN_HIPY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //43 + MNET_UPPERBODY_IN_HIPY_LSHOULDERY_ANGLE, //44 + MNET_UPPERBODY_IN_HIPY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //45 + MNET_UPPERBODY_IN_HIPY_LELBOWY_ANGLE, //46 + MNET_UPPERBODY_IN_HIPY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //47 + MNET_UPPERBODY_IN_HIPY_LHANDY_ANGLE, //48 + MNET_UPPERBODY_IN_HIPY_HALFWAY_NECK_AND_HIPY_ANGLE, //49 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HIPY_ANGLE, //50 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_1, //51 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_ENDSITE_EYE_LY_ANGLE, //52 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_NECKY_ANGLE, //53 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_RSHOULDERY_ANGLE, //54 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //55 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_RELBOWY_ANGLE, //56 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //57 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_RHANDY_ANGLE, //58 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //59 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //60 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_LSHOULDERY_ANGLE, //61 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //62 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_LELBOWY_ANGLE, //63 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //64 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_LHANDY_ANGLE, //65 + MNET_UPPERBODY_IN_ENDSITE_EYE_RY_HALFWAY_NECK_AND_HIPY_ANGLE, //66 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HIPY_ANGLE, //67 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_ENDSITE_EYE_RY_ANGLE, //68 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_2, //69 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_NECKY_ANGLE, //70 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_RSHOULDERY_ANGLE, //71 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //72 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_RELBOWY_ANGLE, //73 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //74 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_RHANDY_ANGLE, //75 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //76 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //77 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_LSHOULDERY_ANGLE, //78 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //79 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_LELBOWY_ANGLE, //80 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //81 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_LHANDY_ANGLE, //82 + MNET_UPPERBODY_IN_ENDSITE_EYE_LY_HALFWAY_NECK_AND_HIPY_ANGLE, //83 + MNET_UPPERBODY_IN_NECKY_HIPY_ANGLE, //84 + MNET_UPPERBODY_IN_NECKY_ENDSITE_EYE_RY_ANGLE, //85 + MNET_UPPERBODY_IN_NECKY_ENDSITE_EYE_LY_ANGLE, //86 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_3, //87 + MNET_UPPERBODY_IN_NECKY_RSHOULDERY_ANGLE, //88 + MNET_UPPERBODY_IN_NECKY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //89 + MNET_UPPERBODY_IN_NECKY_RELBOWY_ANGLE, //90 + MNET_UPPERBODY_IN_NECKY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //91 + MNET_UPPERBODY_IN_NECKY_RHANDY_ANGLE, //92 + MNET_UPPERBODY_IN_NECKY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //93 + MNET_UPPERBODY_IN_NECKY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //94 + MNET_UPPERBODY_IN_NECKY_LSHOULDERY_ANGLE, //95 + MNET_UPPERBODY_IN_NECKY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //96 + MNET_UPPERBODY_IN_NECKY_LELBOWY_ANGLE, //97 + MNET_UPPERBODY_IN_NECKY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //98 + MNET_UPPERBODY_IN_NECKY_LHANDY_ANGLE, //99 + MNET_UPPERBODY_IN_NECKY_HALFWAY_NECK_AND_HIPY_ANGLE, //100 + MNET_UPPERBODY_IN_RSHOULDERY_HIPY_ANGLE, //101 + MNET_UPPERBODY_IN_RSHOULDERY_ENDSITE_EYE_RY_ANGLE, //102 + MNET_UPPERBODY_IN_RSHOULDERY_ENDSITE_EYE_LY_ANGLE, //103 + MNET_UPPERBODY_IN_RSHOULDERY_NECKY_ANGLE, //104 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_4, //105 + MNET_UPPERBODY_IN_RSHOULDERY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //106 + MNET_UPPERBODY_IN_RSHOULDERY_RELBOWY_ANGLE, //107 + MNET_UPPERBODY_IN_RSHOULDERY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //108 + MNET_UPPERBODY_IN_RSHOULDERY_RHANDY_ANGLE, //109 + MNET_UPPERBODY_IN_RSHOULDERY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //110 + MNET_UPPERBODY_IN_RSHOULDERY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //111 + MNET_UPPERBODY_IN_RSHOULDERY_LSHOULDERY_ANGLE, //112 + MNET_UPPERBODY_IN_RSHOULDERY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //113 + MNET_UPPERBODY_IN_RSHOULDERY_LELBOWY_ANGLE, //114 + MNET_UPPERBODY_IN_RSHOULDERY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //115 + MNET_UPPERBODY_IN_RSHOULDERY_LHANDY_ANGLE, //116 + MNET_UPPERBODY_IN_RSHOULDERY_HALFWAY_NECK_AND_HIPY_ANGLE, //117 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_HIPY_ANGLE, //118 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_ENDSITE_EYE_RY_ANGLE, //119 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_ENDSITE_EYE_LY_ANGLE, //120 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_NECKY_ANGLE, //121 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_RSHOULDERY_ANGLE, //122 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_5, //123 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_RELBOWY_ANGLE, //124 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //125 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_RHANDY_ANGLE, //126 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //127 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //128 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_LSHOULDERY_ANGLE, //129 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //130 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_LELBOWY_ANGLE, //131 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //132 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_LHANDY_ANGLE, //133 + MNET_UPPERBODY_IN_HALFWAY_RSHOULDER_AND_RELBOWY_HALFWAY_NECK_AND_HIPY_ANGLE, //134 + MNET_UPPERBODY_IN_RELBOWY_HIPY_ANGLE, //135 + MNET_UPPERBODY_IN_RELBOWY_ENDSITE_EYE_RY_ANGLE, //136 + MNET_UPPERBODY_IN_RELBOWY_ENDSITE_EYE_LY_ANGLE, //137 + MNET_UPPERBODY_IN_RELBOWY_NECKY_ANGLE, //138 + MNET_UPPERBODY_IN_RELBOWY_RSHOULDERY_ANGLE, //139 + MNET_UPPERBODY_IN_RELBOWY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //140 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_6, //141 + MNET_UPPERBODY_IN_RELBOWY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //142 + MNET_UPPERBODY_IN_RELBOWY_RHANDY_ANGLE, //143 + MNET_UPPERBODY_IN_RELBOWY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //144 + MNET_UPPERBODY_IN_RELBOWY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //145 + MNET_UPPERBODY_IN_RELBOWY_LSHOULDERY_ANGLE, //146 + MNET_UPPERBODY_IN_RELBOWY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //147 + MNET_UPPERBODY_IN_RELBOWY_LELBOWY_ANGLE, //148 + MNET_UPPERBODY_IN_RELBOWY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //149 + MNET_UPPERBODY_IN_RELBOWY_LHANDY_ANGLE, //150 + MNET_UPPERBODY_IN_RELBOWY_HALFWAY_NECK_AND_HIPY_ANGLE, //151 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_HIPY_ANGLE, //152 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_ENDSITE_EYE_RY_ANGLE, //153 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_ENDSITE_EYE_LY_ANGLE, //154 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_NECKY_ANGLE, //155 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_RSHOULDERY_ANGLE, //156 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //157 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_RELBOWY_ANGLE, //158 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_7, //159 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_RHANDY_ANGLE, //160 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //161 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //162 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_LSHOULDERY_ANGLE, //163 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //164 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_LELBOWY_ANGLE, //165 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //166 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_LHANDY_ANGLE, //167 + MNET_UPPERBODY_IN_HALFWAY_RELBOW_AND_RHANDY_HALFWAY_NECK_AND_HIPY_ANGLE, //168 + MNET_UPPERBODY_IN_RHANDY_HIPY_ANGLE, //169 + MNET_UPPERBODY_IN_RHANDY_ENDSITE_EYE_RY_ANGLE, //170 + MNET_UPPERBODY_IN_RHANDY_ENDSITE_EYE_LY_ANGLE, //171 + MNET_UPPERBODY_IN_RHANDY_NECKY_ANGLE, //172 + MNET_UPPERBODY_IN_RHANDY_RSHOULDERY_ANGLE, //173 + MNET_UPPERBODY_IN_RHANDY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //174 + MNET_UPPERBODY_IN_RHANDY_RELBOWY_ANGLE, //175 + MNET_UPPERBODY_IN_RHANDY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //176 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_8, //177 + MNET_UPPERBODY_IN_RHANDY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //178 + MNET_UPPERBODY_IN_RHANDY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //179 + MNET_UPPERBODY_IN_RHANDY_LSHOULDERY_ANGLE, //180 + MNET_UPPERBODY_IN_RHANDY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //181 + MNET_UPPERBODY_IN_RHANDY_LELBOWY_ANGLE, //182 + MNET_UPPERBODY_IN_RHANDY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //183 + MNET_UPPERBODY_IN_RHANDY_LHANDY_ANGLE, //184 + MNET_UPPERBODY_IN_RHANDY_HALFWAY_NECK_AND_HIPY_ANGLE, //185 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HIPY_ANGLE, //186 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ENDSITE_EYE_RY_ANGLE, //187 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ENDSITE_EYE_LY_ANGLE, //188 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_NECKY_ANGLE, //189 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_RSHOULDERY_ANGLE, //190 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //191 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_RELBOWY_ANGLE, //192 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //193 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_RHANDY_ANGLE, //194 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_9, //195 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //196 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_LSHOULDERY_ANGLE, //197 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //198 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_LELBOWY_ANGLE, //199 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //200 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_LHANDY_ANGLE, //201 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_HALFWAY_NECK_AND_HIPY_ANGLE, //202 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HIPY_ANGLE, //203 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ENDSITE_EYE_RY_ANGLE, //204 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ENDSITE_EYE_LY_ANGLE, //205 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_NECKY_ANGLE, //206 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_RSHOULDERY_ANGLE, //207 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //208 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_RELBOWY_ANGLE, //209 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //210 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_RHANDY_ANGLE, //211 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //212 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_10, //213 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_LSHOULDERY_ANGLE, //214 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //215 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_LELBOWY_ANGLE, //216 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //217 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_LHANDY_ANGLE, //218 + MNET_UPPERBODY_IN_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_HALFWAY_NECK_AND_HIPY_ANGLE, //219 + MNET_UPPERBODY_IN_LSHOULDERY_HIPY_ANGLE, //220 + MNET_UPPERBODY_IN_LSHOULDERY_ENDSITE_EYE_RY_ANGLE, //221 + MNET_UPPERBODY_IN_LSHOULDERY_ENDSITE_EYE_LY_ANGLE, //222 + MNET_UPPERBODY_IN_LSHOULDERY_NECKY_ANGLE, //223 + MNET_UPPERBODY_IN_LSHOULDERY_RSHOULDERY_ANGLE, //224 + MNET_UPPERBODY_IN_LSHOULDERY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //225 + MNET_UPPERBODY_IN_LSHOULDERY_RELBOWY_ANGLE, //226 + MNET_UPPERBODY_IN_LSHOULDERY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //227 + MNET_UPPERBODY_IN_LSHOULDERY_RHANDY_ANGLE, //228 + MNET_UPPERBODY_IN_LSHOULDERY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //229 + MNET_UPPERBODY_IN_LSHOULDERY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //230 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_11, //231 + MNET_UPPERBODY_IN_LSHOULDERY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //232 + MNET_UPPERBODY_IN_LSHOULDERY_LELBOWY_ANGLE, //233 + MNET_UPPERBODY_IN_LSHOULDERY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //234 + MNET_UPPERBODY_IN_LSHOULDERY_LHANDY_ANGLE, //235 + MNET_UPPERBODY_IN_LSHOULDERY_HALFWAY_NECK_AND_HIPY_ANGLE, //236 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_HIPY_ANGLE, //237 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_ENDSITE_EYE_RY_ANGLE, //238 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_ENDSITE_EYE_LY_ANGLE, //239 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_NECKY_ANGLE, //240 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_RSHOULDERY_ANGLE, //241 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //242 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_RELBOWY_ANGLE, //243 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //244 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_RHANDY_ANGLE, //245 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //246 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //247 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_LSHOULDERY_ANGLE, //248 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_12, //249 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_LELBOWY_ANGLE, //250 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //251 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_LHANDY_ANGLE, //252 + MNET_UPPERBODY_IN_HALFWAY_LSHOULDER_AND_LELBOWY_HALFWAY_NECK_AND_HIPY_ANGLE, //253 + MNET_UPPERBODY_IN_LELBOWY_HIPY_ANGLE, //254 + MNET_UPPERBODY_IN_LELBOWY_ENDSITE_EYE_RY_ANGLE, //255 + MNET_UPPERBODY_IN_LELBOWY_ENDSITE_EYE_LY_ANGLE, //256 + MNET_UPPERBODY_IN_LELBOWY_NECKY_ANGLE, //257 + MNET_UPPERBODY_IN_LELBOWY_RSHOULDERY_ANGLE, //258 + MNET_UPPERBODY_IN_LELBOWY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //259 + MNET_UPPERBODY_IN_LELBOWY_RELBOWY_ANGLE, //260 + MNET_UPPERBODY_IN_LELBOWY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //261 + MNET_UPPERBODY_IN_LELBOWY_RHANDY_ANGLE, //262 + MNET_UPPERBODY_IN_LELBOWY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //263 + MNET_UPPERBODY_IN_LELBOWY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //264 + MNET_UPPERBODY_IN_LELBOWY_LSHOULDERY_ANGLE, //265 + MNET_UPPERBODY_IN_LELBOWY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //266 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_13, //267 + MNET_UPPERBODY_IN_LELBOWY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //268 + MNET_UPPERBODY_IN_LELBOWY_LHANDY_ANGLE, //269 + MNET_UPPERBODY_IN_LELBOWY_HALFWAY_NECK_AND_HIPY_ANGLE, //270 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_HIPY_ANGLE, //271 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_ENDSITE_EYE_RY_ANGLE, //272 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_ENDSITE_EYE_LY_ANGLE, //273 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_NECKY_ANGLE, //274 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_RSHOULDERY_ANGLE, //275 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //276 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_RELBOWY_ANGLE, //277 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //278 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_RHANDY_ANGLE, //279 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //280 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //281 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_LSHOULDERY_ANGLE, //282 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //283 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_LELBOWY_ANGLE, //284 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_14, //285 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_LHANDY_ANGLE, //286 + MNET_UPPERBODY_IN_HALFWAY_LELBOW_AND_LHANDY_HALFWAY_NECK_AND_HIPY_ANGLE, //287 + MNET_UPPERBODY_IN_LHANDY_HIPY_ANGLE, //288 + MNET_UPPERBODY_IN_LHANDY_ENDSITE_EYE_RY_ANGLE, //289 + MNET_UPPERBODY_IN_LHANDY_ENDSITE_EYE_LY_ANGLE, //290 + MNET_UPPERBODY_IN_LHANDY_NECKY_ANGLE, //291 + MNET_UPPERBODY_IN_LHANDY_RSHOULDERY_ANGLE, //292 + MNET_UPPERBODY_IN_LHANDY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //293 + MNET_UPPERBODY_IN_LHANDY_RELBOWY_ANGLE, //294 + MNET_UPPERBODY_IN_LHANDY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //295 + MNET_UPPERBODY_IN_LHANDY_RHANDY_ANGLE, //296 + MNET_UPPERBODY_IN_LHANDY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //297 + MNET_UPPERBODY_IN_LHANDY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //298 + MNET_UPPERBODY_IN_LHANDY_LSHOULDERY_ANGLE, //299 + MNET_UPPERBODY_IN_LHANDY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //300 + MNET_UPPERBODY_IN_LHANDY_LELBOWY_ANGLE, //301 + MNET_UPPERBODY_IN_LHANDY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //302 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_15, //303 + MNET_UPPERBODY_IN_LHANDY_HALFWAY_NECK_AND_HIPY_ANGLE, //304 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_HIPY_ANGLE, //305 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_ENDSITE_EYE_RY_ANGLE, //306 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_ENDSITE_EYE_LY_ANGLE, //307 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_NECKY_ANGLE, //308 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_RSHOULDERY_ANGLE, //309 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_HALFWAY_RSHOULDER_AND_RELBOWY_ANGLE, //310 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_RELBOWY_ANGLE, //311 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_HALFWAY_RELBOW_AND_RHANDY_ANGLE, //312 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_RHANDY_ANGLE, //313 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15Y_ANGLE, //314 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15Y_ANGLE, //315 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_LSHOULDERY_ANGLE, //316 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_HALFWAY_LSHOULDER_AND_LELBOWY_ANGLE, //317 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_LELBOWY_ANGLE, //318 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_HALFWAY_LELBOW_AND_LHANDY_ANGLE, //319 + MNET_UPPERBODY_IN_HALFWAY_NECK_AND_HIPY_LHANDY_ANGLE, //320 + MNET_UPPERBODY_IN_ANGLEUSEDFOR2DROTATION_16, //321 + MNET_UPPERBODY_IN_NUMBER +}; + +/** @brief Programmer friendly enumerator of expected outputs + TODO: CAREFULL!*/ +enum mocapNET_Output_upperbody_enum +{ + MOCAPNET_UPPERBODY_OUTPUT_HIP_XPOSITION = 0, //0 + MOCAPNET_UPPERBODY_OUTPUT_HIP_YPOSITION, //1 + MOCAPNET_UPPERBODY_OUTPUT_HIP_ZPOSITION, //2 + MOCAPNET_UPPERBODY_OUTPUT_HIP_ZROTATION, //3 + MOCAPNET_UPPERBODY_OUTPUT_HIP_YROTATION, //4 + MOCAPNET_UPPERBODY_OUTPUT_HIP_XROTATION, //5 + MOCAPNET_UPPERBODY_OUTPUT_ABDOMEN_ZROTATION, //6 + MOCAPNET_UPPERBODY_OUTPUT_ABDOMEN_XROTATION, //7 + MOCAPNET_UPPERBODY_OUTPUT_ABDOMEN_YROTATION, //8 + MOCAPNET_UPPERBODY_OUTPUT_CHEST_ZROTATION, //9 + MOCAPNET_UPPERBODY_OUTPUT_CHEST_XROTATION, //10 + MOCAPNET_UPPERBODY_OUTPUT_CHEST_YROTATION, //11 + MOCAPNET_UPPERBODY_OUTPUT_NECK_ZROTATION, //12 + MOCAPNET_UPPERBODY_OUTPUT_NECK_XROTATION, //13 + MOCAPNET_UPPERBODY_OUTPUT_NECK_YROTATION, //14 + MOCAPNET_UPPERBODY_OUTPUT_HEAD_ZROTATION, //15 + MOCAPNET_UPPERBODY_OUTPUT_HEAD_XROTATION, //16 + MOCAPNET_UPPERBODY_OUTPUT_HEAD_YROTATION, //17 + MOCAPNET_UPPERBODY_OUTPUT_EYE_L_ZROTATION, //18 + MOCAPNET_UPPERBODY_OUTPUT_EYE_L_XROTATION, //19 + MOCAPNET_UPPERBODY_OUTPUT_EYE_L_YROTATION, //20 + MOCAPNET_UPPERBODY_OUTPUT_EYE_R_ZROTATION, //21 + MOCAPNET_UPPERBODY_OUTPUT_EYE_R_XROTATION, //22 + MOCAPNET_UPPERBODY_OUTPUT_EYE_R_YROTATION, //23 + MOCAPNET_UPPERBODY_OUTPUT_RSHOULDER_ZROTATION, //24 + MOCAPNET_UPPERBODY_OUTPUT_RSHOULDER_XROTATION, //25 + MOCAPNET_UPPERBODY_OUTPUT_RSHOULDER_YROTATION, //26 + MOCAPNET_UPPERBODY_OUTPUT_RELBOW_ZROTATION, //27 + MOCAPNET_UPPERBODY_OUTPUT_RELBOW_XROTATION, //28 + MOCAPNET_UPPERBODY_OUTPUT_RELBOW_YROTATION, //29 + MOCAPNET_UPPERBODY_OUTPUT_RHAND_ZROTATION, //30 + MOCAPNET_UPPERBODY_OUTPUT_RHAND_XROTATION, //31 + MOCAPNET_UPPERBODY_OUTPUT_RHAND_YROTATION, //32 + MOCAPNET_UPPERBODY_OUTPUT_LSHOULDER_ZROTATION, //33 + MOCAPNET_UPPERBODY_OUTPUT_LSHOULDER_XROTATION, //34 + MOCAPNET_UPPERBODY_OUTPUT_LSHOULDER_YROTATION, //35 + MOCAPNET_UPPERBODY_OUTPUT_LELBOW_ZROTATION, //36 + MOCAPNET_UPPERBODY_OUTPUT_LELBOW_XROTATION, //37 + MOCAPNET_UPPERBODY_OUTPUT_LELBOW_YROTATION, //38 + MOCAPNET_UPPERBODY_OUTPUT_LHAND_ZROTATION, //39 + MOCAPNET_UPPERBODY_OUTPUT_LHAND_XROTATION, //40 + MOCAPNET_UPPERBODY_OUTPUT_LHAND_YROTATION, //41 + MOCAPNET_UPPERBODY_OUTPUT_NUMBER +}; + +/** @brief Programmer friendly enumerator of NSDM elments*/ +enum mocapNET_NSDM_upperbody_enum +{ + MNET_NSDM_UPPERBODY_HIP = 0, //0 + MNET_NSDM_UPPERBODY_ENDSITE_EYE_R, //1 + MNET_NSDM_UPPERBODY_ENDSITE_EYE_L, //2 + MNET_NSDM_UPPERBODY_NECK, //3 + MNET_NSDM_UPPERBODY_RSHOULDER, //4 + MNET_NSDM_UPPERBODY_VIRTUAL_HALFWAY_BETWEEN_RSHOULDER_AND_RELBOW, //5 + MNET_NSDM_UPPERBODY_RELBOW, //6 + MNET_NSDM_UPPERBODY_VIRTUAL_HALFWAY_BETWEEN_RELBOW_AND_RHAND, //7 + MNET_NSDM_UPPERBODY_RHAND, //8 + MNET_NSDM_UPPERBODY_VIRTUAL_HIP_X_MINUS_0_15_Y_MINUS_0_15, //9 + MNET_NSDM_UPPERBODY_VIRTUAL_HIP_X_PLUS0_15_Y_MINUS_0_15, //10 + MNET_NSDM_UPPERBODY_LSHOULDER, //11 + MNET_NSDM_UPPERBODY_VIRTUAL_HALFWAY_BETWEEN_LSHOULDER_AND_LELBOW, //12 + MNET_NSDM_UPPERBODY_LELBOW, //13 + MNET_NSDM_UPPERBODY_VIRTUAL_HALFWAY_BETWEEN_LELBOW_AND_LHAND, //14 + MNET_NSDM_UPPERBODY_LHAND, //15 + MNET_NSDM_UPPERBODY_VIRTUAL_HALFWAY_BETWEEN_NECK_AND_HIP, //16 + MNET_NSDM_UPPERBODY_NUMBER +}; + +/** @brief This is a lookup table to immediately resolve referred Joints*/ +static const int mocapNET_ResolveJoint_upperbody[] = +{ + 0, //0 + 4, //1 + 3, //2 + 1, //3 + 5, //4 + 5, //5 + 6, //6 + 6, //7 + 7, //8 + 0, //9 + 0, //10 + 8, //11 + 8, //12 + 9, //13 + 9, //14 + 10, //15 + 1, //16 + 0//end of array +}; + +/** @brief This is a lookup table to immediately resolve referred Joints of second targets*/ +static const int mocapNET_ResolveSecondTargetJoint_upperbody[] = +{ + 0, //0 + 0, //1 + 0, //2 + 0, //3 + 0, //4 + 6, //5 + 0, //6 + 7, //7 + 0, //8 + 0, //9 + 0, //10 + 0, //11 + 9, //12 + 0, //13 + 10, //14 + 0, //15 + 0, //16 + 0//end of array +}; + +/** @brief This is the configuration of NSDM elements : + * A value of 0 is a normal 2D point + * A value of 1 is a 2D point plus some offset + * A value of 2 is a virtual point between two 2D points */ +static const int mocapNET_ArtificialJoint_upperbody[] = +{ + 0, //0 + 0, //1 + 0, //2 + 0, //3 + 0, //4 + 2, //5 + 0, //6 + 2, //7 + 0, //8 + 1, //9 + 1, //10 + 0, //11 + 2, //12 + 0, //13 + 2, //14 + 0, //15 + 2, //16 + 0//end of array +}; + +/** @brief These are X offsets for artificial joints of type 1 ( see mocapNET_ArtificialJoint_upperbody )*/ +static const float mocapNET_ArtificialJointXOffset_upperbody[] = +{ + 0, //0 + 0, //1 + 0, //2 + 0, //3 + 0, //4 + 0, //5 + 0, //6 + 0, //7 + 0, //8 + -0.15, //9 + 0.15, //10 + 0, //11 + 0, //12 + 0, //13 + 0, //14 + 0, //15 + 0, //16 + 0//end of array +}; + +/** @brief These are Y offsets for artificial joints of type 1 ( see mocapNET_ArtificialJoint_upperbody )*/ +static const float mocapNET_ArtificialJointYOffset_upperbody[] = +{ + 0, //0 + 0, //1 + 0, //2 + 0, //3 + 0, //4 + 0, //5 + 0, //6 + 0, //7 + 0, //8 + -0.15, //9 + -0.15, //10 + 0, //11 + 0, //12 + 0, //13 + 0, //14 + 0, //15 + 0, //16 + 0//end of array +}; + +/** @brief These are 2D Joints that are used as starting points for scaling vectors*/ +static const int mocapNET_ScalingStart_upperbody[] = +{ + 0, //0 + 0, //1 + 0//end of array +}; + +/** @brief These are 2D Joints that are used as ending points for scaling vectors*/ +static const int mocapNET_ScalingEnd_upperbody[] = +{ + 5, //0 + 8, //1 + 0//end of array +}; + +/** @brief These is a 2D Joints that is used as alignment for the skeleton*/ +static const int mocapNET_AlignmentStart_upperbody[] = +{ + 0, //0 + 0//end of array +}; + +/** @brief These is a 2D Joints that is used as alignment for the skeleton*/ +static const int mocapNET_AlignmentEnd_upperbody[] = +{ + 1, //0 + 0//end of array +}; + +/** @brief This function can be used to debug NSDM input and find in a user friendly what is missing..!*/ +static int upperbodyCountMissingNSDMElements(std::vector mocapNETInput,int verbose) +{ + unsigned int numberOfZeros=0; + for (int i=0; i skeletonSerialized %s\n ",mocapNET_upperbody[i],labels[i]); + } +} + +/** @brief This function returns the euclidean distance between two input 2D joints and zero if either of them is invalid*/ +static float getJoint2DDistance_UPPERBODY(std::vector in,int jointA,int jointB) +{ + float aX=in[jointA*3+0]; + float aY=in[jointA*3+1]; + float bX=in[jointB*3+0]; + float bY=in[jointB*3+1]; + if ( ((aX==0) && (aY==0)) || ((bX==0) && (bY==0)) ) { + return 0.0; + } + + + float xDistance=(float) bX-aX; + float yDistance=(float) bY-aY; + return sqrt( (xDistance*xDistance) + (yDistance*yDistance) ); +} +/* +static std::vector upperbodyCreateNDSM(std::vector in,float alignmentAngle2D,int havePositionalElements,int haveAngularElements,int doNormalization) +{ + std::vector result; + int secondTargetJointID; + float sIX,sIY,sJX,sJY; + for (int i=0; i0) + { + unsigned int numberOfDistanceSamples=0; + float sumOfDistanceSamples=0.0; + for ( int i=0; i0.0) + { + numberOfDistanceSamples=numberOfDistanceSamples+1; + sumOfDistanceSamples=sumOfDistanceSamples+distance; + } + } +//------------------------------------------------------------------------------------------------- + float scaleDistance=1.0; +//------------------------------------------------------------------------------------------------- + if (numberOfDistanceSamples>0) + { + scaleDistance=(float) sumOfDistanceSamples/numberOfDistanceSamples; + } +//------------------------------------------------------------------------------------------------- + if (scaleDistance!=1.0) + { + for (int i=0; imaxValue) { + maxValue=result[i]; + } + } + fprintf(stderr,"Original Min Value %0.2f, Max Value %0.2f \n",minValue,maxValue); + + + unsigned int iJointID=mocapNET_AlignmentStart_upperbody[0]; + unsigned int jJointID=mocapNET_AlignmentEnd_upperbody[0]; + float aX=in[iJointID*3+0]; + float aY=in[iJointID*3+1]; + float bX=in[jJointID*3+0]; + float bY=in[jJointID*3+1]; + float alignmentAngle=getAngleToAlignToZero_tools(aX,aY,bX,bY); + for (int i=0; imaxValue) { + maxValue=result[i]; + } + } + fprintf(stderr,"Aligned Min Value %0.2f, Max Value %0.2f \n",minValue,maxValue); + + + } +//------------------------------------------------------------------------------------------------- + + + } //If normalization is enabled.. + + +//New normalization code that overrides diagonal of Matrix + unsigned int elementID=0; + unsigned int firstJointID=mocapNET_ResolveJoint_upperbody[0]; + for (unsigned int i=0; i0) && (richDiagonal_upperbody) ) + { + unsigned int jJointID=mocapNET_ResolveJoint_upperbody[j]; + result[elementID]=getJoint2DDistance_UPPERBODY(in,firstJointID,jJointID); + } + } + elementID+=1; + } + } + return result; +} + +*/ \ No newline at end of file diff --git a/src/MocapNET4/MocapNETLib4/NSxM/calculations.c b/src/MocapNET4/MocapNETLib4/NSxM/calculations.c new file mode 100644 index 0000000..5069948 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/NSxM/calculations.c @@ -0,0 +1,137 @@ +#include "calculations.h" + +#include +#include + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + + +const float goFromRadToDegrees=(float) 180.0 / M_PI; +const float goFromDegreesToRad=(float) M_PI / 180.0; + + +/** @brief This function returns the euclidean distance between two input 2D joints and zero if either of them is invalid*/ +float getJoint2DDistance_tools(float aX,float aY,float bX,float bY) +{ + float xDistance=(float) bX-aX; + float yDistance=(float) bY-aY; + return (float) sqrt( (xDistance*xDistance) + (yDistance*yDistance) ); +} + + +float getAngleToAlignToZero_tools(float aX,float aY,float bX,float bY) +{ + if ( (aX==bX) && (aY==bY) ) { return 0; } + + + //Bigger magnitudes.. + aX=100*aX; + aY=100*aY; + bX=100*bX; + bY=100*bY; + + //We have points a, b and c and we want to calculate angle b + float lengthBetweenAAndB = getJoint2DDistance_tools(aX,aY,bX,bY); + + + //We align vertically.. , Point C is B offset in Y direction + float cX = bX; + float cY = bY - lengthBetweenAAndB; + + //fprintf(stderr,"We want to align A(%0.2f,%0.2f) to C(%0.2f,%0.2f) with pivot B(%0.2f,%0.2f)\n",aX,aY,cX,cY,bX,bY); + //fprintf(stderr,"length AB = %0.2f\n",lengthBetweenAAndB); + //fprintf(stderr,"bY = %0.2f\n",bY); + //fprintf(stderr,"cY = %0.2f = %0.2f - %0.2f\n",cY,bY,lengthBetweenAAndB); + + + //Calulate vector a->b + float abX = bX - aX; + float abY = bY - aY; + + //calculate vector c->b + float cbX = bX - cX; + float cbY = bY - cY; + + + float dot = (abX * cbX + abY * cbY); // dot product + float cross = (abX * cbY - abY * cbX); // cross product + + float alpha = atan2(cross, dot); + + //fprintf(stderr,"Angle is %0.2f rad or %0.2f degrees \n",alpha,alpha*goFromRadToDegrees); + return (float) alpha;// * goFromRadToDegrees ; +} + + + +float getAngleToAlignToZero(float *positions,unsigned int centerJoint,unsigned int referenceJoint) +{ + //We have points a, b and c and we want to calculate angle b + float aX= positions[referenceJoint*3+0]; + float aY= positions[referenceJoint*3+1]; + + float bX= positions[centerJoint*3+0]; + float bY= positions[centerJoint*3+1]; + + return getAngleToAlignToZero_tools(aX,aY,bX,bY); +} + + + +int rotate2DPointsBasedOnJointAsCenter(float * positions,unsigned int positionsLength,float angle,unsigned int centerJoint) +{ + if (positionsLength%3!=0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: incorrect positions.. \n" NORMAL); + return 0; + } + + if (positionsLength<=centerJoint*3) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: centerJoint out of bounds.. \n" NORMAL); + return 0; + } + + float s = sin((float) angle * goFromDegreesToRad ); + float c = cos((float) angle * goFromDegreesToRad ); + + float cx=positions[centerJoint*3+0]; + float cy=positions[centerJoint*3+1]; + float cVisibility=positions[centerJoint*3+2]; + + if (cVisibility==0.0) + { + fprintf(stderr,RED "rotate2DPointsBasedOnJointAsCenter: cannot work without pivot joint.. \n" NORMAL); + return 0; + } + + for (unsigned int jID=0; jID ",jX,jY,cx,cy,angle); + + //Translate point back to origin: + jX -= cx; + jY -= cy; + + //Rotate point + float xnew = jX * c - jY * s; + float ynew = jX * s + jY * c; + + //Translate point back: + positions[jID*3+0] = xnew + cx; + positions[jID*3+1] = ynew + cy; + + //fprintf(stderr,"%0.2f,%0.2f\n",positions[jID*3+0],positions[jID*3+1]); + } + + + return 1; +} + diff --git a/src/MocapNET4/MocapNETLib4/NSxM/calculations.h b/src/MocapNET4/MocapNETLib4/NSxM/calculations.h new file mode 100644 index 0000000..5ee5469 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/NSxM/calculations.h @@ -0,0 +1,27 @@ +/** @file calculations.h + * @brief calculations used for descriptors + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef CALCULATIONS_H_INCLUDED +#define CALCULATIONS_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#include + +float getJoint2DDistance_tools(float aX,float aY,float bX,float bY); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/src/MocapNET4/MocapNETLib4/PCA/PCA.h b/src/MocapNET4/MocapNETLib4/PCA/PCA.h new file mode 100644 index 0000000..b0d89a2 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/PCA/PCA.h @@ -0,0 +1,446 @@ +/** @file PCA.h + * @brief An implementation of a PCA data loader + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef PCA_H_INCLUDED +#define PCA_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include +#include "../JSON/nxjson.h" +#include "../tools.h" + + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + +struct eigenVectorOpt +{ + float __attribute__((aligned(16))) * value; +}; + +struct complexNumber +{ + float realPart; + float imaginaryPart; +}; + +struct eigenVector +{ + struct complexNumber * value; +}; + +struct PCAData +{ + unsigned int numberOfSamplesUsedToCreatePCA; + unsigned int numberOfEigenValues; + float mean; + float std; + struct complexNumber * eigenValues; + struct eigenVector * eigenVectors; + float * screeProportion; + float * screeCumulative; +}; + +//Complex Numbers +//http://ebooks.edu.gr/ebooks/v/html/8547/2754/Mathimatika-B-Lykeiou-ThSp_html-apli/index5_2.html +static struct complexNumber addComplexNumbers(struct complexNumber a,struct complexNumber b) +{ + struct complexNumber result={0}; + result.realPart = a.realPart + b.realPart; + result.imaginaryPart = a.imaginaryPart + b.imaginaryPart; + return result; +} + +static struct complexNumber multiplyComplexNumbers(struct complexNumber a,struct complexNumber b) +{ + struct complexNumber result={0}; + // (A + B i) * (C + D i) = (AC-BD) + (AD+BC) i + // (a.realPart + a.imaginaryPart i) * (b.realPart + b.imaginaryPart i) = (a.realPart b.realPart - a.imaginaryPart b.imaginaryPart) + (a.realPart b.imaginaryPart + a.imaginaryPart b.realPart) i + result.realPart = (a.realPart*b.realPart) - (a.imaginaryPart*b.imaginaryPart); + result.imaginaryPart = (a.realPart*b.imaginaryPart) + (a.imaginaryPart*b.realPart); + return result; +} + +static char * recognizeComplexNumberStart(char * complexNumber) +{ + int l = strlen(complexNumber); + char * res = complexNumber; + //All Complex Numbers end with j + //------------------------------- + //We expect something like : + // -4.1106844e-12-4.560428e-12j + // or + // -4.1106844e-12-4.560428e+12j + // or + // -4.1106844e-12-4.560428j + // or + // -4.1106844e-12+48j + //------------------------------- + if (l>2) + { + if (complexNumber[l-1]=='j') + { + l=l-2; + while (l>0) + { + if ( + (complexNumber[l]=='+') || + (complexNumber[l]=='-') + ) + { + //We have reache a +/- symbol there are three cases : + //Case A ) if the previous character is an e then it is a scientific notation complex number! + //Case B ) if not then we found the complex number start! + //Case C ) if l is 0 then we reached the start of the string! + if (l>0) + { + if (complexNumber[l-1]!='e') + { + //This is not a scientific notation so Case B we found our result! + res = complexNumber+l; + break; + } + } + } + l=l-1; + } + } + } + return res; +} + + + + + + + +static struct complexNumber parseComplexNumber(const char * complexNumberString) +{ + struct complexNumber result={0}; + //--------------------------------------------------------------------------------------------- + if ( complexNumberString == 0 ) { return result; } + if (complexNumberString[0]=='(') { complexNumberString++; } //Point to second element to skip ( + + char localBuffer[128]; + snprintf(localBuffer,128,"%s",complexNumberString); + + + float imaginaryPart = 0.0; + char * seperator = strchr(localBuffer,')'); + if (seperator!=0) { *seperator = 0; } //Trim last ) + + + seperator = strstr(localBuffer,"+0j"); + if (seperator!=0) + { + //This is the easy case which means that the value is a clear float value and the +0j can be easily discarded! + *seperator = 0; + } else + { + unsigned int l = strlen(localBuffer); + if (localBuffer[l-1]=='j') + { + fprintf(stderr,"Parsing complex number `%s`\n",localBuffer); + seperator = recognizeComplexNumberStart(localBuffer); + if (seperator!=0) + { + *seperator = 0; + fprintf(stderr,"Imaginary part `%s` / keeping real part `%s` \n",seperator+1,localBuffer); + char * imaginaryPartString = seperator+1; + int lastCharacter = strlen(imaginaryPartString); + if (imaginaryPartString[lastCharacter-1]=='j') + { imaginaryPartString[lastCharacter-1]=0; } + imaginaryPart = strtof(imaginaryPartString,NULL); + } + } + } + + float cleanedFloat = strtof(localBuffer,NULL); + //fprintf(stderr,"Cleaned float is %f \n",cleanedFloat); + if (cleanedFloat!=cleanedFloat) + { + //If we got a NaN value then clean it.. + cleanedFloat = 0.0; + } + + + result.realPart = cleanedFloat; + result.imaginaryPart = imaginaryPart; + + return result; +} + + +static int unloadPCAData(struct PCAData* pca) +{ + if (pca!=0) + { + if (pca->screeProportion!=0) { free(pca->screeProportion); pca->screeProportion=0; } + if (pca->screeCumulative!=0) { free(pca->screeCumulative); pca->screeCumulative=0; } + if (pca->eigenValues!=0) { free(pca->eigenValues); pca->eigenValues=0; } + if (pca->eigenVectors!=0) + { + for (int i=0; inumberOfEigenValues; i++) + { + free(pca->eigenVectors[i].value); + } + free(pca->eigenVectors); + } + return 1; + } + return 0; +} + +static int loadPCADataFromJSON(struct PCAData* output, const char * jsonFilename) +{ + fprintf(stderr,"Loading PCA file %s ...\n",jsonFilename); + unsigned int inputLength=0; + char* input = readFileToMemory(jsonFilename,&inputLength); + if (input!=0) + { + //fprintf(stderr,"JSON DATA %s ...\n",input); + fprintf(stderr,"Parsing %s ...\n",jsonFilename); + const nx_json* json=nx_json_parse_utf8(input); + + //------------------------------------------------------------------- + const nx_json* j = nx_json_get(json,"numberOfSamplesFittedOn"); + fprintf(stderr,"key(%s)/type(%u)\n",j->key,j->type); + output->numberOfSamplesUsedToCreatePCA = (unsigned int) atoi(j->text_value); + //------------------------------------------------------------------- + j = nx_json_get(json,"expectedInputs"); + fprintf(stderr,"key(%s)/type(%u)\n",j->key,j->type); + output->numberOfEigenValues = (unsigned int) atoi(j->text_value); + //------------------------------------------------------------------- + j = nx_json_get(json,"std"); + fprintf(stderr,"key(%s)/type(%u)\n",j->key,j->type); + output->std = (float) atof(j->text_value); + j = nx_json_get(json,"mean"); + fprintf(stderr,"key(%s)/type(%u)\n",j->key,j->type); + output->mean = (float) atof(j->text_value); + //------------------------------------------------------------------- + + //Our Summary..! + //---------------------------------------------------------------------------------------------------------------------------- + fprintf(stderr,"Number Of Samples %u\n",output->numberOfSamplesUsedToCreatePCA); + fprintf(stderr,"Number Of Eigen Values %u\n",output->numberOfEigenValues); + fprintf(stderr,"Mean %0.2f / Std %0.2f\n",output->mean,output->std ); + + //We have now allocated enough space and are ready to parse all incoming values.. + //---------------------------------------------------------------------------------------------------------------------------- + output->eigenValues = (struct complexNumber*) malloc(sizeof(struct complexNumber) * output->numberOfEigenValues); + output->eigenVectors = (struct eigenVector*) malloc(sizeof(struct eigenVector) * output->numberOfEigenValues); + if (output->eigenVectors) + { + for (int i=0; inumberOfEigenValues; i++) + { + output->eigenVectors[i].value = (struct complexNumber*) malloc(sizeof(struct complexNumber) * output->numberOfEigenValues); + } + } + output->screeProportion = (float*) malloc(sizeof(float) * output->numberOfEigenValues); + output->screeCumulative = (float*) malloc(sizeof(float) * output->numberOfEigenValues); + //---------------------------------------------------------------------------------------------------------------------------- + + + j = nx_json_get(json,"eigenvalues"); + fprintf(stderr,"We encountered %u eigenvalues (header says %u) \n",j->length,output->numberOfEigenValues); + if (j->length == output->numberOfEigenValues) + { + //We have a correct number of eigenvalues so let's read them! + for (int idx=0; idxnumberOfEigenValues; idx++) + { + const nx_json* item = nx_json_item(j,idx); + //fprintf(stderr,"key(%s)/index(%u)/type(%u)\n",j->key,idx,item->type); + output->eigenValues[idx] = parseComplexNumber(item->text_value); + } + } + + + const nx_json* jY = nx_json_get(json,"eigenvectors"); + if (jY->length == output->numberOfEigenValues) + { + fprintf(stderr,"We encountered %u eigenvectors (header says %u) \n",jY->length,output->numberOfEigenValues); + //We have a correct number of eigenvalues so let's read them! + for (int idy=0; idynumberOfEigenValues; idy++) + { + const nx_json* itemY = nx_json_item(jY,idy); + if (itemY->length == output->numberOfEigenValues) + { + for (int idx=0; idxnumberOfEigenValues; idx++) + { + const nx_json* itemX = nx_json_item(itemY,idx); + //fprintf(stderr,"key(%s)/index(%u)/type(%u)\n",j->key,idx,item->type); + output->eigenVectors[idx].value[idy] = parseComplexNumber(itemX->text_value); + } + } else + { + fprintf(stderr,"Eigen Vector %d has an incorrect number of values (%u)\n",idy,itemY->length); + } + } + } + + + j = nx_json_get(json,"scree_proportion"); + fprintf(stderr,"We encountered %u scree_proportions (header says %u) \n",j->length,output->numberOfEigenValues); + if (j->length == output->numberOfEigenValues) + { + //We have a correct number of eigenvalues so let's read them! + for (int idx=0; idxnumberOfEigenValues; idx++) + { + const nx_json* item = nx_json_item(j,idx); + //fprintf(stderr,"key(%s)/index(%u)/type(%u)\n",j->key,idx,item->type); + output->screeProportion[idx] = strtof(item->text_value,NULL); + } + } + + + j = nx_json_get(json,"scree_cumulative"); + fprintf(stderr,"We encountered %u scree_cumulatives (header says %u) \n",j->length,output->numberOfEigenValues); + if (j->length == output->numberOfEigenValues) + { + //We have a correct number of eigenvalues so let's read them! + for (int idx=0; idxnumberOfEigenValues; idx++) + { + const nx_json* item = nx_json_item(j,idx); + //fprintf(stderr,"key(%s)/index(%u)/type(%u)\n",j->key,idx,item->type); + output->screeCumulative[idx] = strtof(item->text_value,NULL); + } + } + + nx_json_free(json); + free(input); + + return 1; + } + return 0; +} + + +float dotProduct(float * vect_A, float * vect_B, int n) +{ + float product = 0.0; + + for (int i = 0; i < n; i++) + { product += vect_A[i] * vect_B[i]; } + + return product; +} + + + +static int doPCATransform(float * output,int * outputSize,struct PCAData* pca,float * inputRaw,int inputSize,int selectedPCADimensions) +{ + if (pca==0) { return 0; } + if (pca->numberOfEigenValues!=inputSize) { fprintf(stderr, RED "PCA: Shape given as input (%d,) is not aligned with PCA loaded (%u,%d) \n" NORMAL,inputSize,pca->numberOfEigenValues,*outputSize); return 0; } + + float mean = pca->mean; + float std = pca->std; + if (std == 0.0 ) { std=1.0; } //Don't ever divide by zero + + fprintf(stderr," sample = list()\n"); + for (int i=0; iinputSize) + { + selectedPCADimensions = inputSize; + } + + fprintf(stderr,"We want dot product of %u dimensions : \n",selectedPCADimensions); + fprintf(stderr,"Input of size 0->%u\n",inputSize); + fprintf(stderr,"EigenVector of size 0->%u\n",pca->numberOfEigenValues); + + /* + * input is 1 x 458 + eigenvectors is 458 x 458 + result is 1 x 210 + + data is 1 x 461 + eigenvectors is 461 x 461 + result is 1 x 210 + a = [[0, 1 , 2]] +b = [[4, 1], [3, 2], [10,10] ] +#[[23 22]] +print(np.dot(a,b)) + * ./MocapNET4TestD + + * python3 DNN_Tensorflow2/principleComponentAnalysis.py + */ + + for (int i=0; inumberOfEigenValues; j++) + { + //fprintf(stderr,"%0.2f * %0.2f \n",input[j],pca->eigenVectors[i].value[j]); + //output[i] += input[j] * pca->eigenVectors[i].value[j]; + //output[i] += input[j] * pca->eigenVectors[i].value[j].realPart; + struct complexNumber thisOutputComplex = multiplyComplexNumbers(inputComplex[j],pca->eigenVectors[i].value[j]); + outputComplex[i].realPart += thisOutputComplex.realPart; + outputComplex[i].imaginaryPart += thisOutputComplex.imaginaryPart; + } + //--------------------------------- + } + + //We have gone through all of the complex arithmetic, now we will keep only the real part + for (int i=0; i Median is ",median,"Mean is ",mean," Std is ",std," Var is ",var) + + sys.exit(0) + +class PCA(): + def __init__(self, + inputData:np.array=np.array([]), + savedFile:str="" + ): + self.mean = 0.0 + self.std = 1.0 + self.eigenvalues = np.array([]) + self.eigenvectors = np.array([]) + self.proportional = list() + self.cumulative = list() + self.numberOfSamplesFittedOn = 0 + self.expectedInputs = 0 + + if (savedFile!=""): + self.load(savedFile) + elif inputData.size != 0: + self.fit(inputData) + else: + print("No PCA input given..!") + + def ok(self): + return self.numberOfSamplesFittedOn!=0 + + def getNumberOfExpectedSamples(self): + #return len(self.eigenvalues) + return np.size(self.eigenvalues, axis = 0) + + def fit(self,data): + #doPCAUsingSKLearn(data,"Test") + #getStatsPerColumn(data) + #print(data) + + self.numberOfSamplesFittedOn = data.shape[0] + + print("Doing PCA fit on ",self.numberOfSamplesFittedOn) + print(" please wait .. ") + + #Standardize data + #------------------------------------------------------------------------------------------------- + self.mean = data.mean() + data = data - self.mean + # Normalize + self.std = data.std() + if (self.std!=0.0): + data = data / self.std + #print('Data Mean : ',self.mean,'STD: ',self.std) + #------------------------------------------------------------------------------------------------- + + #Take the matrix, transpose it, and multiply the transposed matrix. This is the covariance matrix. + covarianceMatrix = np.dot(data.T,data) + + #Get an array of computed eigenvalues and a matrix whose columns are the normalized eigenvectors corresponding to the eigenvalues in that order. + #In this step it is important to make sure that the eigenvalues and its eigenvectors are sorted in descending order (from largest to smallest). Sort the eigenvalues and then the eigenvectors, accordingly. + self.eigenvalues, self.eigenvectors = np.linalg.eig(covarianceMatrix) + + #Sort eigenvectors according to eigenvalues + idx = self.eigenvalues.argsort()[::-1] + self.eigenvalues = self.eigenvalues[idx] + self.eigenvectors = self.eigenvectors[:,idx] + + #Assign P to the matrix of eigenvectors and D to the diagonal matrix with eigenvalues on the diagonal and values of zero everywhere else. + #The eigenvalues on the diagonal of D will be associated with the corresponding column in P. + D = np.diag(self.eigenvalues) + P = self.eigenvectors + + self.expectedInputs = len(self.eigenvalues) + + #1. Calculate the proportion of variance explained by each feature + sumOfEigenvalues = np.sum(self.eigenvalues) + self.proportional = [i/sumOfEigenvalues for i in self.eigenvalues] + #2. Calculate the cumulative variance + self.cumulative = [np.sum(self.proportional[:i+1]) for i in range(len(self.proportional))] + + def transform(self,data,selectedPCADimensions=0): + if (self.numberOfSamplesFittedOn==0): + print("Can't transform input with no PCA loaded ..") + return data + #Normalize input data + data = data - self.mean + if (self.std!=0.0): + data = data / self.std + #Do transform.. + if (selectedPCADimensions==0): + #Transform using all PCA components + return np.dot(data,self.eigenvectors) + else: + #print("eigenvectors is ",eigenvectors.shape[0]," x ",eigenvectors.shape[1]) + return data.dot(self.eigenvectors[:,:selectedPCADimensions]) + + def save(self,filename): + print("Saving PCA to ",filename) + outputDict = dict() + #------------------------------------------ + outputDict["numberOfSamplesFittedOn"]= str(self.numberOfSamplesFittedOn) + outputDict["expectedInputs"] = str(self.expectedInputs) + outputDict["mean"] = str(self.mean) + outputDict["std"] = str(self.std) + outputDict["eigenvalues"] = list() + outputDict["eigenvectors"] = list() + outputDict["scree_proportion"] = list() + outputDict["scree_cumulative"] = list() + #------------------------------------------ + for v in range(0,len(self.proportional)): + outputDict["scree_proportion"].append(str(self.proportional[v])) + outputDict["scree_cumulative"].append(str(self.cumulative[v])) + #------------------------------------------ + print("eigenvalues ",self.eigenvalues.shape[0]) + for v in range(0,self.eigenvalues.shape[0]): + outputDict["eigenvalues"].append(str(self.eigenvalues[v])) + #------------------------------------------ + print("eigenvectors ",self.eigenvectors.shape[0]," x ",self.eigenvectors.shape[1]) + for r in range(0,self.eigenvectors.shape[0]): + thisRow = list() + for c in range(0,self.eigenvectors.shape[1]): + thisRow.append(str(self.eigenvectors[r,c])) + outputDict["eigenvectors"].append(thisRow) + #------------------------------------------ + import json + json_obj = json.dumps(outputDict) + file = open(filename,'w',encoding="utf-8") + file.write(json_obj) + file.close() + + def load(self,filename): + print("Loading PCA from ",filename) + import json + file = open(filename,'r',encoding="utf-8") + data = json.load(file) + #----------------------------------------------------- + self.numberOfSamplesFittedOn = int(data["numberOfSamplesFittedOn"]) + self.expectedInputs = int(data["expectedInputs"]) + self.mean = float(data["mean"]) + self.std = float(data["std"]) + #----------------------------------------------------- + numberOfEigenValues = len(data["eigenvalues"]) + print("Eigen values = ",numberOfEigenValues) + self.eigenvalues = np.full([numberOfEigenValues],fill_value=0,dtype=np.complex_,order='C') + for i in range(0,numberOfEigenValues): + self.eigenvalues[i] = complex(data["eigenvalues"][i]) + #----------------------------------------------------- + numberOfEigenVectors = len(data["eigenvectors"]) + print("Eigen vectors = ",numberOfEigenVectors) + self.eigenvectors = np.full([numberOfEigenVectors,numberOfEigenVectors],fill_value=0,dtype=np.complex_,order='C') + for r in range(0,numberOfEigenVectors): + for c in range(0,numberOfEigenVectors): + self.eigenvectors[r,c] = complex(data["eigenvectors"][r][c]) + #----------------------------------------------------- + file.close() + return self.mean,self.std,self.eigenvalues,self.eigenvectors + + + def visualize(self,data,saveToFile="",onlyScreePlotNDimensions=0,label="PCA",colors=list(),colorLabel="Highlighting PC-4",viewAzimuth=45,viewElevation=45,showScree=1): + import matplotlib.pyplot as plt + + font = {'family' : 'normal', + 'weight' : 'bold', + 'size' : 28} + + plt.rc('font', **font) + plt.rc('xtick', labelsize=15) + plt.rc('ytick', labelsize=15) + # === Plot ========================================================================= + fig = plt.figure() + fig.set_size_inches(19.2, 10.8, forward=True) + + if (showScree==1): + ax2 = fig.add_subplot(1, 2, 1) + ax1 = fig.add_subplot(1, 2, 2,projection='3d') + else: + ax1 = fig.add_subplot(1, 1, 1,projection='3d') + #=================================================================================== + + #Number of PCA components to plot on first plot (our plot is 3D so max is 4 if we dont have a color ..! ) + keepNDimensions = 3 + if (len(colors)==0): + keepNDimensions = 4 + + #Do transform of our input using the PCA dimensions as new basis + #=================================================================================== + transformedData = self.transform(data,selectedPCADimensions=keepNDimensions).real + #=================================================================================== + + if (len(colors)==0): + colors = transformedData[:,3] + colorLabel = "highlighting PC-4" + else: + print("Using provided set of colorValues") + keepNDimensions = 3 + + #If there is no limit on Scree plot dimensions then plot all + if (onlyScreePlotNDimensions==0): + onlyScreePlotNDimensions = len(eigenvalues) + #=================================================================================== + plottedEigenValues = self.eigenvalues + plottedEigenValues=list() + for i in range(0,onlyScreePlotNDimensions): + plottedEigenValues.append(self.eigenvalues[i]) + #=================================================================================== + #1. Calculate the proportion of variance explained by each feature + sum_eigenvalues = np.sum(plottedEigenValues) + prop_var = [i/sum_eigenvalues for i in plottedEigenValues] + #2. Calculate the cumulative variance + cum_var = [np.sum(prop_var[:i+1]) for i in range(len(prop_var))] + #=================================================================================== + + ax1.view_init(viewAzimuth,viewElevation) + #=================================================================================== + ax1.scatter(transformedData[:,0],transformedData[:,1],transformedData[:,2],c=colors) + #=================================================================================== + + # Adding title, xlabel and ylabel + ax1.set_title('PCA %s %s '%(label,colorLabel)) # Title of the plot + ax1.set_xlabel('PC-1 (%0.2f %%) '% (100.0*float(prop_var[0])),labelpad=30) # X-Label + ax1.set_ylabel('PC-2 (%0.2f %%) '% (100.0*float(prop_var[1])),labelpad=30) # Y-Label + ax1.set_zlabel('PC-3 (%0.2f %%) '% (100.0*float(prop_var[2])),labelpad=30) # Z-Label + #ax1.tick_params(axis='x', pad=5) #fine tune numbers of plot + #=================================================================================== + #=================================================================================== + #=================================================================================== + if (showScree==1): + # Plot scree plot from PCA + x_labels = ['PC{}'.format(i+1) for i in range(len(prop_var))] + ax2.plot(x_labels, prop_var, marker='o', markersize=6, color='skyblue', linewidth=2, label='Proportion of variance') + ax2.plot(x_labels, cum_var, marker='o', color='orange', linewidth=2, label="Cumulative variance") + ax2.legend() + ax2.set_title('Scree plot %s '%label) + ax2.set_xlabel('Principal components') + ax2.set_ylabel('Proportion of variance') + #=================================================================================== + #=================================================================================== + #=================================================================================== + + plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.08) + + if (saveToFile!=""): + fig.savefig(saveToFile) + else: + plt.show() + + +if __name__ == '__main__': + pca = PCA(savedFile="../../../../dataset/combinedModel/mocapnet4/mode1/1.0/step1_upperbody_all/upperbody_all.pca") + inptR = [1.0] * 458 + inpt =np.asarray(inptR,dtype=np.float32) + outLength = 210 + out = pca.transform(inpt,selectedPCADimensions=outLength) + for i in range(0,outLength): + print("%u = %0.6f" % (i,out[i])) + diff --git a/src/MocapNET4/MocapNETLib4/config.h b/src/MocapNET4/MocapNETLib4/config.h new file mode 100644 index 0000000..9fe3f2e --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/config.h @@ -0,0 +1,46 @@ +#ifndef MOCAPNET_CONFIGURATION_H_INCLUDED +#define MOCAPNET_CONFIGURATION_H_INCLUDED + +#ifdef __cplusplus +extern "C" +{ +#endif + +//Neural network orientations centered around 0 +#define NN_ORIENTATIONS_TRAINED_AROUND_ZERO_AND_REQUIRE_TRICK 0 + +//Also swap bvh rotations before IK step +#define APPLY_BVH_FIX_TO_IK_INPUT 0 + +//Test swapped +#define SWAP_LEFT_RIGHT_ENSEMBLES 0 + +//Hands mode ( 1 / 3 (deprecated) / 5 ) +#define HANDS_MODE 1 + +//Use flip for RHand Regression..! +#define RHAND_FLIP 1 + + +//Limits synced to scripts/createRandomizedDatset.sh +const float FRONT_MIN_ORIENTATION = -45.0; +const float FRONT_MAX_ORIENTATION = 45.0; +//-------------------------------- +const float BACK_MIN_ORIENTATION = 135.0; +const float BACK_MAX_ORIENTATION = 225.0; +const float BACK_ALT_MIN_ORIENTATION = -225; +const float BACK_ALT_MAX_ORIENTATION = -135; +//-------------------------------- +const float LEFT_MIN_ORIENTATION = -135.0; +const float LEFT_MAX_ORIENTATION = -45.0; +//-------------------------------- +const float RIGHT_MIN_ORIENTATION = 45.0; +const float RIGHT_MAX_ORIENTATION = 135.0; +//-------------------------------- + + +#ifdef __cplusplus +} +#endif + +#endif // MOCAPNET_CONFIGURATION_H_INCLUDED diff --git a/src/MocapNET4/MocapNETLib4/mocapnet4.cpp b/src/MocapNET4/MocapNETLib4/mocapnet4.cpp new file mode 100644 index 0000000..1917d2a --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/mocapnet4.cpp @@ -0,0 +1,61 @@ +//MOCAPNET2 ------------------------------------ +#include "../MocapNETLib4/mocapnet4.h" +//---------------------------------------------- +#include "../MocapNETLib4/config.h" +#include "../MocapNETLib4/JSON/readModelConfiguration.h" +//---------------------------------------------- + +#include "tools.h" +#include "../../../dependencies/nxjson/nxjson.h" + +#include +#include + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + + + + +int loadMocapNET4( + struct MocapNET4 * mnet, + const char * description + ) +{ + + unsigned int length = 0; + char * data = readFileToMemory("dataset/combinedModel/mocapnet4/mode1/1.0/step1_upperbody_all",&length); + + struct ModelConfigurationData modelConfiguration={0}; + loadModelConfigurationData(&modelConfiguration,"dataset/combinedModel/mocapnet4/mode1/1.0/step1_upperbody_all/upperbody_configuration.json"); + + return 0; +} + + + +std::vector runMocapNET4( + struct MocapNET4 * mnet, + struct skeletonSerialized * input, + int doLowerbody, + int doHands, + int doFace, + int doGestureDetection, + unsigned int useInverseKinematics, + int doOutputFiltering + ) +{ + std::vector emptyResult; + return emptyResult; +} + + + + +int unloadMocapNET4(struct MocapNET4 * mnet) +{ + return 0; +} diff --git a/src/MocapNET4/MocapNETLib4/mocapnet4.h b/src/MocapNET4/MocapNETLib4/mocapnet4.h new file mode 100644 index 0000000..9cca20e --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/mocapnet4.h @@ -0,0 +1,3464 @@ +#pragma once +/** @file mocapnet4.hpp + * @brief The MocapNET C library + * As seen in https://www.youtube.com/watch?v=fH5e-KMBvM0 , the MocapNET network requires two types of input. + * The first is an uncompressed list of (x,y,v) joints and the second an NSDM array. To add to those the output consists of BVH + * frames that must be accompanied by a header. This library internally handles all of these details. + * @author Ammar Qammaz (AmmarkoV) + */ + + +#include +#include + +/** + * @brief MocapNET version + */ +static const char MocapNETVersion[] = { "4.0" }; + +/** + * @brief MocapNET has been trained on 1920x1080 frames, so all the received coordinates are normalized in the +* 0..1 range based on that. This means that the NN learns the X and Y variations. If a joint lies at pixel 500,500 +* it will be represented as 500/1920 , 500/1080. +* Now if a user uses another configuration, let's say a vertical (portrait) feed where the resolution is 1080x1920 +* the 2D points will get normalized at 500/1080 , 500/1920 and the resulting 2D joint cloud won't work as well +* This is why it is better to change the aspect ratio while normalizing + */ +static const unsigned int MocapNETTrainingWidth=1920, MocapNETTrainingHeight=1080; + + + +/** + * @brief MocapNET output joint names that correspond to the BVH file + * These should correspond to `cat dataset/headerWithHeadAndOneMotion.bvh | grep JOINT` +*/ +static const char * MocapNETOutputJointNames[] = +{ +"hip", +"abdomen", +"chest", +"neck", +"neck1", +"head", +"__jaw", +"jaw", +"special04", +"oris02", +"oris01", +"oris06.l", +"oris07.l", +"oris06.r", +"oris07.r", +"tongue00", +"tongue01", +"tongue02", +"tongue03", +"__tongue04", +"tongue04", +"tongue07.l", +"tongue07.r", +"tongue06.l", +"tongue06.r", +"tongue05.l", +"tongue05.r", +"__levator02.l", +"levator02.l", +"levator03.l", +"levator04.l", +"levator05.l", +"__levator02.r", +"levator02.r", +"levator03.r", +"levator04.r", +"levator05.r", +"__special01", +"special01", +"oris04.l", +"oris03.l", +"oris04.r", +"oris03.r", +"oris06", +"oris05", +"__special03", +"special03", +"__levator06.l", +"levator06.l", +"__levator06.r", +"levator06.r", +"special06.l", +"special05.l", +"eye.l", +"orbicularis03.l", +"orbicularis04.l", +"special06.r", +"special05.r", +"eye.r", +"orbicularis03.r", +"orbicularis04.r", +"__temporalis01.l", +"temporalis01.l", +"oculi02.l", +"oculi01.l", +"__temporalis01.r", +"temporalis01.r", +"oculi02.r", +"oculi01.r", +"__temporalis02.l", +"temporalis02.l", +"risorius02.l", +"risorius03.l", +"__temporalis02.r", +"temporalis02.r", +"risorius02.r", +"risorius03.r", +"rCollar", +"rShldr", +"rForeArm", +"rHand", +"metacarpal1.r", +"finger2-1.r", +"finger2-2.r", +"finger2-3.r", +"metacarpal2.r", +"finger3-1.r", +"finger3-2.r", +"finger3-3.r", +"__metacarpal3.r", +"metacarpal3.r", +"finger4-1.r", +"finger4-2.r", +"finger4-3.r", +"__metacarpal4.r", +"metacarpal4.r", +"finger5-1.r", +"finger5-2.r", +"finger5-3.r", +"rthumbBase", +"rthumb", +"finger1-2.r", +"finger1-3.r", +"lCollar", +"lShldr", +"lForeArm", +"lHand", +"metacarpal1.l", +"finger2-1.l", +"finger2-2.l", +"finger2-3.l", +"metacarpal2.l", +"finger3-1.l", +"finger3-2.l", +"finger3-3.l", +"__metacarpal3.l", +"metacarpal3.l", +"finger4-1.l", +"finger4-2.l", +"finger4-3.l", +"__metacarpal4.l", +"metacarpal4.l", +"finger5-1.l", +"finger5-2.l", +"finger5-3.l", +"lthumbBase", +"lthumb", +"finger1-2.l", +"finger1-3.l", +"rButtock", +"rThigh", +"rShin", +"rFoot", +"toe1-1.R", +"toe1-2.R", +"toe2-1.R", +"toe2-2.R", +"toe2-3.R", +"toe3-1.R", +"toe3-2.R", +"toe3-3.R", +"toe4-1.R", +"toe4-2.R", +"toe4-3.R", +"toe5-1.R", +"toe5-2.R", +"toe5-3.R", +"lButtock", +"lThigh", +"lShin", +"lFoot", +"toe1-1.L", +"toe1-2.L", +"toe2-1.L", +"toe2-2.L", +"toe2-3.L", +"toe3-1.L", +"toe3-2.L", +"toe3-3.L", +"toe4-1.L", +"toe4-2.L", +"toe4-3.L", +"toe5-1.L", +"toe5-2.L", +"toe5-3.L" +}; + + + + + + +/** + * @brief This is a programmer friendly enumerator of joint output extracted from MocapNET. + * Use ./GroundTruthDumper --from dataset/headerWithHeadAndOneMotion.bvh --printc + * to extract this automatically + */ +enum MOCAPNET_Output_Joint_Name_ENUM +{ +MOCAPNET_OUTPUT_JOINT_HIP, +MOCAPNET_OUTPUT_JOINT_ABDOMEN, +MOCAPNET_OUTPUT_JOINT_CHEST, +MOCAPNET_OUTPUT_JOINT_NECK, +MOCAPNET_OUTPUT_JOINT_NECK1, +MOCAPNET_OUTPUT_JOINT_HEAD, +MOCAPNET_OUTPUT_JOINT___JAW, +MOCAPNET_OUTPUT_JOINT_JAW, +MOCAPNET_OUTPUT_JOINT_SPECIAL04, +MOCAPNET_OUTPUT_JOINT_ORIS02, +MOCAPNET_OUTPUT_JOINT_ORIS01, +MOCAPNET_OUTPUT_JOINT_ORIS06_L, +MOCAPNET_OUTPUT_JOINT_ORIS07_L, +MOCAPNET_OUTPUT_JOINT_ORIS06_R, +MOCAPNET_OUTPUT_JOINT_ORIS07_R, +MOCAPNET_OUTPUT_JOINT_TONGUE00, +MOCAPNET_OUTPUT_JOINT_TONGUE01, +MOCAPNET_OUTPUT_JOINT_TONGUE02, +MOCAPNET_OUTPUT_JOINT_TONGUE03, +MOCAPNET_OUTPUT_JOINT___TONGUE04, +MOCAPNET_OUTPUT_JOINT_TONGUE04, +MOCAPNET_OUTPUT_JOINT_TONGUE07_L, +MOCAPNET_OUTPUT_JOINT_TONGUE07_R, +MOCAPNET_OUTPUT_JOINT_TONGUE06_L, +MOCAPNET_OUTPUT_JOINT_TONGUE06_R, +MOCAPNET_OUTPUT_JOINT_TONGUE05_L, +MOCAPNET_OUTPUT_JOINT_TONGUE05_R, +MOCAPNET_OUTPUT_JOINT___LEVATOR02_L, +MOCAPNET_OUTPUT_JOINT_LEVATOR02_L, +MOCAPNET_OUTPUT_JOINT_LEVATOR03_L, +MOCAPNET_OUTPUT_JOINT_LEVATOR04_L, +MOCAPNET_OUTPUT_JOINT_LEVATOR05_L, +MOCAPNET_OUTPUT_JOINT___LEVATOR02_R, +MOCAPNET_OUTPUT_JOINT_LEVATOR02_R, +MOCAPNET_OUTPUT_JOINT_LEVATOR03_R, +MOCAPNET_OUTPUT_JOINT_LEVATOR04_R, +MOCAPNET_OUTPUT_JOINT_LEVATOR05_R, +MOCAPNET_OUTPUT_JOINT___SPECIAL01, +MOCAPNET_OUTPUT_JOINT_SPECIAL01, +MOCAPNET_OUTPUT_JOINT_ORIS04_L, +MOCAPNET_OUTPUT_JOINT_ORIS03_L, +MOCAPNET_OUTPUT_JOINT_ORIS04_R, +MOCAPNET_OUTPUT_JOINT_ORIS03_R, +MOCAPNET_OUTPUT_JOINT_ORIS06, +MOCAPNET_OUTPUT_JOINT_ORIS05, +MOCAPNET_OUTPUT_JOINT___SPECIAL03, +MOCAPNET_OUTPUT_JOINT_SPECIAL03, +MOCAPNET_OUTPUT_JOINT___LEVATOR06_L, +MOCAPNET_OUTPUT_JOINT_LEVATOR06_L, +MOCAPNET_OUTPUT_JOINT___LEVATOR06_R, +MOCAPNET_OUTPUT_JOINT_LEVATOR06_R, +MOCAPNET_OUTPUT_JOINT_SPECIAL06_L, +MOCAPNET_OUTPUT_JOINT_SPECIAL05_L, +MOCAPNET_OUTPUT_JOINT_EYE_L, +MOCAPNET_OUTPUT_JOINT_ORBICULARIS03_L, +MOCAPNET_OUTPUT_JOINT_ORBICULARIS04_L, +MOCAPNET_OUTPUT_JOINT_SPECIAL06_R, +MOCAPNET_OUTPUT_JOINT_SPECIAL05_R, +MOCAPNET_OUTPUT_JOINT_EYE_R, +MOCAPNET_OUTPUT_JOINT_ORBICULARIS03_R, +MOCAPNET_OUTPUT_JOINT_ORBICULARIS04_R, +MOCAPNET_OUTPUT_JOINT___TEMPORALIS01_L, +MOCAPNET_OUTPUT_JOINT_TEMPORALIS01_L, +MOCAPNET_OUTPUT_JOINT_OCULI02_L, +MOCAPNET_OUTPUT_JOINT_OCULI01_L, +MOCAPNET_OUTPUT_JOINT___TEMPORALIS01_R, +MOCAPNET_OUTPUT_JOINT_TEMPORALIS01_R, +MOCAPNET_OUTPUT_JOINT_OCULI02_R, +MOCAPNET_OUTPUT_JOINT_OCULI01_R, +MOCAPNET_OUTPUT_JOINT___TEMPORALIS02_L, +MOCAPNET_OUTPUT_JOINT_TEMPORALIS02_L, +MOCAPNET_OUTPUT_JOINT_RISORIUS02_L, +MOCAPNET_OUTPUT_JOINT_RISORIUS03_L, +MOCAPNET_OUTPUT_JOINT___TEMPORALIS02_R, +MOCAPNET_OUTPUT_JOINT_TEMPORALIS02_R, +MOCAPNET_OUTPUT_JOINT_RISORIUS02_R, +MOCAPNET_OUTPUT_JOINT_RISORIUS03_R, +MOCAPNET_OUTPUT_JOINT_RCOLLAR, +MOCAPNET_OUTPUT_JOINT_RSHLDR, +MOCAPNET_OUTPUT_JOINT_RFOREARM, +MOCAPNET_OUTPUT_JOINT_RHAND, +MOCAPNET_OUTPUT_JOINT_METACARPAL1_R, +MOCAPNET_OUTPUT_JOINT_FINGER2_1_R, +MOCAPNET_OUTPUT_JOINT_FINGER2_2_R, +MOCAPNET_OUTPUT_JOINT_FINGER2_3_R, +MOCAPNET_OUTPUT_JOINT_METACARPAL2_R, +MOCAPNET_OUTPUT_JOINT_FINGER3_1_R, +MOCAPNET_OUTPUT_JOINT_FINGER3_2_R, +MOCAPNET_OUTPUT_JOINT_FINGER3_3_R, +MOCAPNET_OUTPUT_JOINT___METACARPAL3_R, +MOCAPNET_OUTPUT_JOINT_METACARPAL3_R, +MOCAPNET_OUTPUT_JOINT_FINGER4_1_R, +MOCAPNET_OUTPUT_JOINT_FINGER4_2_R, +MOCAPNET_OUTPUT_JOINT_FINGER4_3_R, +MOCAPNET_OUTPUT_JOINT___METACARPAL4_R, +MOCAPNET_OUTPUT_JOINT_METACARPAL4_R, +MOCAPNET_OUTPUT_JOINT_FINGER5_1_R, +MOCAPNET_OUTPUT_JOINT_FINGER5_2_R, +MOCAPNET_OUTPUT_JOINT_FINGER5_3_R, +MOCAPNET_OUTPUT_JOINT_RTHUMBBASE, +MOCAPNET_OUTPUT_JOINT_RTHUMB, +MOCAPNET_OUTPUT_JOINT_FINGER1_2_R, +MOCAPNET_OUTPUT_JOINT_FINGER1_3_R, +MOCAPNET_OUTPUT_JOINT_LCOLLAR, +MOCAPNET_OUTPUT_JOINT_LSHLDR, +MOCAPNET_OUTPUT_JOINT_LFOREARM, +MOCAPNET_OUTPUT_JOINT_LHAND, +MOCAPNET_OUTPUT_JOINT_METACARPAL1_L, +MOCAPNET_OUTPUT_JOINT_FINGER2_1_L, +MOCAPNET_OUTPUT_JOINT_FINGER2_2_L, +MOCAPNET_OUTPUT_JOINT_FINGER2_3_L, +MOCAPNET_OUTPUT_JOINT_METACARPAL2_L, +MOCAPNET_OUTPUT_JOINT_FINGER3_1_L, +MOCAPNET_OUTPUT_JOINT_FINGER3_2_L, +MOCAPNET_OUTPUT_JOINT_FINGER3_3_L, +MOCAPNET_OUTPUT_JOINT___METACARPAL3_L, +MOCAPNET_OUTPUT_JOINT_METACARPAL3_L, +MOCAPNET_OUTPUT_JOINT_FINGER4_1_L, +MOCAPNET_OUTPUT_JOINT_FINGER4_2_L, +MOCAPNET_OUTPUT_JOINT_FINGER4_3_L, +MOCAPNET_OUTPUT_JOINT___METACARPAL4_L, +MOCAPNET_OUTPUT_JOINT_METACARPAL4_L, +MOCAPNET_OUTPUT_JOINT_FINGER5_1_L, +MOCAPNET_OUTPUT_JOINT_FINGER5_2_L, +MOCAPNET_OUTPUT_JOINT_FINGER5_3_L, +MOCAPNET_OUTPUT_JOINT_LTHUMBBASE, +MOCAPNET_OUTPUT_JOINT_LTHUMB, +MOCAPNET_OUTPUT_JOINT_FINGER1_2_L, +MOCAPNET_OUTPUT_JOINT_FINGER1_3_L, +MOCAPNET_OUTPUT_JOINT_RBUTTOCK, +MOCAPNET_OUTPUT_JOINT_RTHIGH, +MOCAPNET_OUTPUT_JOINT_RSHIN, +MOCAPNET_OUTPUT_JOINT_RFOOT, +MOCAPNET_OUTPUT_JOINT_TOE1_1_R, +MOCAPNET_OUTPUT_JOINT_TOE1_2_R, +MOCAPNET_OUTPUT_JOINT_TOE2_1_R, +MOCAPNET_OUTPUT_JOINT_TOE2_2_R, +MOCAPNET_OUTPUT_JOINT_TOE2_3_R, +MOCAPNET_OUTPUT_JOINT_TOE3_1_R, +MOCAPNET_OUTPUT_JOINT_TOE3_2_R, +MOCAPNET_OUTPUT_JOINT_TOE3_3_R, +MOCAPNET_OUTPUT_JOINT_TOE4_1_R, +MOCAPNET_OUTPUT_JOINT_TOE4_2_R, +MOCAPNET_OUTPUT_JOINT_TOE4_3_R, +MOCAPNET_OUTPUT_JOINT_TOE5_1_R, +MOCAPNET_OUTPUT_JOINT_TOE5_2_R, +MOCAPNET_OUTPUT_JOINT_TOE5_3_R, +MOCAPNET_OUTPUT_JOINT_LBUTTOCK, +MOCAPNET_OUTPUT_JOINT_LTHIGH, +MOCAPNET_OUTPUT_JOINT_LSHIN, +MOCAPNET_OUTPUT_JOINT_LFOOT, +MOCAPNET_OUTPUT_JOINT_TOE1_1_L, +MOCAPNET_OUTPUT_JOINT_TOE1_2_L, +MOCAPNET_OUTPUT_JOINT_TOE2_1_L, +MOCAPNET_OUTPUT_JOINT_TOE2_2_L, +MOCAPNET_OUTPUT_JOINT_TOE2_3_L, +MOCAPNET_OUTPUT_JOINT_TOE3_1_L, +MOCAPNET_OUTPUT_JOINT_TOE3_2_L, +MOCAPNET_OUTPUT_JOINT_TOE3_3_L, +MOCAPNET_OUTPUT_JOINT_TOE4_1_L, +MOCAPNET_OUTPUT_JOINT_TOE4_2_L, +MOCAPNET_OUTPUT_JOINT_TOE4_3_L, +MOCAPNET_OUTPUT_JOINT_TOE5_1_L, +MOCAPNET_OUTPUT_JOINT_TOE5_2_L, +MOCAPNET_OUTPUT_JOINT_TOE5_3_L +}; + + + + +/** + * @brief This is a programmer friendly enumerator to access 3D output extracted from the BVH file_ + * Use _/GroundTruthDumper __from dataset/headerWithHeadAndOneMotion_bvh __printc to extract this automatically + */ +enum MOCAPNET_2D_Output_Joints +{ +MOCAPNET_2DPOINT_HIPX,//0 +MOCAPNET_2DPOINT_HIPY,//1 +MOCAPNET_2DPOINT_ABDOMENX,//2 +MOCAPNET_2DPOINT_ABDOMENY,//3 +MOCAPNET_2DPOINT_CHESTX,//4 +MOCAPNET_2DPOINT_CHESTY,//5 +MOCAPNET_2DPOINT_NECKX,//6 +MOCAPNET_2DPOINT_NECKY,//7 +MOCAPNET_2DPOINT_NECK1X,//8 +MOCAPNET_2DPOINT_NECK1Y,//9 +MOCAPNET_2DPOINT_HEADX,//10 +MOCAPNET_2DPOINT_HEADY,//11 +MOCAPNET_2DPOINT___JAWX,//12 +MOCAPNET_2DPOINT___JAWY,//13 +MOCAPNET_2DPOINT_JAWX,//14 +MOCAPNET_2DPOINT_JAWY,//15 +MOCAPNET_2DPOINT_SPECIAL04X,//16 +MOCAPNET_2DPOINT_SPECIAL04Y,//17 +MOCAPNET_2DPOINT_ORIS02X,//18 +MOCAPNET_2DPOINT_ORIS02Y,//19 +MOCAPNET_2DPOINT_ORIS01X,//20 +MOCAPNET_2DPOINT_ORIS01Y,//21 +MOCAPNET_2DPOINT_ENDSITE_ORIS01X,//22 +MOCAPNET_2DPOINT_ENDSITE_ORIS01Y,//23 +MOCAPNET_2DPOINT_ORIS06_LX,//24 +MOCAPNET_2DPOINT_ORIS06_LY,//25 +MOCAPNET_2DPOINT_ORIS07_LX,//26 +MOCAPNET_2DPOINT_ORIS07_LY,//27 +MOCAPNET_2DPOINT_ENDSITE_ORIS07_LX,//28 +MOCAPNET_2DPOINT_ENDSITE_ORIS07_LY,//29 +MOCAPNET_2DPOINT_ORIS06_RX,//30 +MOCAPNET_2DPOINT_ORIS06_RY,//31 +MOCAPNET_2DPOINT_ORIS07_RX,//32 +MOCAPNET_2DPOINT_ORIS07_RY,//33 +MOCAPNET_2DPOINT_ENDSITE_ORIS07_RX,//34 +MOCAPNET_2DPOINT_ENDSITE_ORIS07_RY,//35 +MOCAPNET_2DPOINT_TONGUE00X,//36 +MOCAPNET_2DPOINT_TONGUE00Y,//37 +MOCAPNET_2DPOINT_TONGUE01X,//38 +MOCAPNET_2DPOINT_TONGUE01Y,//39 +MOCAPNET_2DPOINT_TONGUE02X,//40 +MOCAPNET_2DPOINT_TONGUE02Y,//41 +MOCAPNET_2DPOINT_TONGUE03X,//42 +MOCAPNET_2DPOINT_TONGUE03Y,//43 +MOCAPNET_2DPOINT___TONGUE04X,//44 +MOCAPNET_2DPOINT___TONGUE04Y,//45 +MOCAPNET_2DPOINT_TONGUE04X,//46 +MOCAPNET_2DPOINT_TONGUE04Y,//47 +MOCAPNET_2DPOINT_ENDSITE_TONGUE04X,//48 +MOCAPNET_2DPOINT_ENDSITE_TONGUE04Y,//49 +MOCAPNET_2DPOINT_TONGUE07_LX,//50 +MOCAPNET_2DPOINT_TONGUE07_LY,//51 +MOCAPNET_2DPOINT_ENDSITE_TONGUE07_LX,//52 +MOCAPNET_2DPOINT_ENDSITE_TONGUE07_LY,//53 +MOCAPNET_2DPOINT_TONGUE07_RX,//54 +MOCAPNET_2DPOINT_TONGUE07_RY,//55 +MOCAPNET_2DPOINT_ENDSITE_TONGUE07_RX,//56 +MOCAPNET_2DPOINT_ENDSITE_TONGUE07_RY,//57 +MOCAPNET_2DPOINT_TONGUE06_LX,//58 +MOCAPNET_2DPOINT_TONGUE06_LY,//59 +MOCAPNET_2DPOINT_ENDSITE_TONGUE06_LX,//60 +MOCAPNET_2DPOINT_ENDSITE_TONGUE06_LY,//61 +MOCAPNET_2DPOINT_TONGUE06_RX,//62 +MOCAPNET_2DPOINT_TONGUE06_RY,//63 +MOCAPNET_2DPOINT_ENDSITE_TONGUE06_RX,//64 +MOCAPNET_2DPOINT_ENDSITE_TONGUE06_RY,//65 +MOCAPNET_2DPOINT_TONGUE05_LX,//66 +MOCAPNET_2DPOINT_TONGUE05_LY,//67 +MOCAPNET_2DPOINT_ENDSITE_TONGUE05_LX,//68 +MOCAPNET_2DPOINT_ENDSITE_TONGUE05_LY,//69 +MOCAPNET_2DPOINT_TONGUE05_RX,//70 +MOCAPNET_2DPOINT_TONGUE05_RY,//71 +MOCAPNET_2DPOINT_ENDSITE_TONGUE05_RX,//72 +MOCAPNET_2DPOINT_ENDSITE_TONGUE05_RY,//73 +MOCAPNET_2DPOINT___LEVATOR02_LX,//74 +MOCAPNET_2DPOINT___LEVATOR02_LY,//75 +MOCAPNET_2DPOINT_LEVATOR02_LX,//76 +MOCAPNET_2DPOINT_LEVATOR02_LY,//77 +MOCAPNET_2DPOINT_LEVATOR03_LX,//78 +MOCAPNET_2DPOINT_LEVATOR03_LY,//79 +MOCAPNET_2DPOINT_LEVATOR04_LX,//80 +MOCAPNET_2DPOINT_LEVATOR04_LY,//81 +MOCAPNET_2DPOINT_LEVATOR05_LX,//82 +MOCAPNET_2DPOINT_LEVATOR05_LY,//83 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR05_LX,//84 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR05_LY,//85 +MOCAPNET_2DPOINT___LEVATOR02_RX,//86 +MOCAPNET_2DPOINT___LEVATOR02_RY,//87 +MOCAPNET_2DPOINT_LEVATOR02_RX,//88 +MOCAPNET_2DPOINT_LEVATOR02_RY,//89 +MOCAPNET_2DPOINT_LEVATOR03_RX,//90 +MOCAPNET_2DPOINT_LEVATOR03_RY,//91 +MOCAPNET_2DPOINT_LEVATOR04_RX,//92 +MOCAPNET_2DPOINT_LEVATOR04_RY,//93 +MOCAPNET_2DPOINT_LEVATOR05_RX,//94 +MOCAPNET_2DPOINT_LEVATOR05_RY,//95 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR05_RX,//96 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR05_RY,//97 +MOCAPNET_2DPOINT___SPECIAL01X,//98 +MOCAPNET_2DPOINT___SPECIAL01Y,//99 +MOCAPNET_2DPOINT_SPECIAL01X,//100 +MOCAPNET_2DPOINT_SPECIAL01Y,//101 +MOCAPNET_2DPOINT_ORIS04_LX,//102 +MOCAPNET_2DPOINT_ORIS04_LY,//103 +MOCAPNET_2DPOINT_ORIS03_LX,//104 +MOCAPNET_2DPOINT_ORIS03_LY,//105 +MOCAPNET_2DPOINT_ENDSITE_ORIS03_LX,//106 +MOCAPNET_2DPOINT_ENDSITE_ORIS03_LY,//107 +MOCAPNET_2DPOINT_ORIS04_RX,//108 +MOCAPNET_2DPOINT_ORIS04_RY,//109 +MOCAPNET_2DPOINT_ORIS03_RX,//110 +MOCAPNET_2DPOINT_ORIS03_RY,//111 +MOCAPNET_2DPOINT_ENDSITE_ORIS03_RX,//112 +MOCAPNET_2DPOINT_ENDSITE_ORIS03_RY,//113 +MOCAPNET_2DPOINT_ORIS06X,//114 +MOCAPNET_2DPOINT_ORIS06Y,//115 +MOCAPNET_2DPOINT_ORIS05X,//116 +MOCAPNET_2DPOINT_ORIS05Y,//117 +MOCAPNET_2DPOINT_ENDSITE_ORIS05X,//118 +MOCAPNET_2DPOINT_ENDSITE_ORIS05Y,//119 +MOCAPNET_2DPOINT___SPECIAL03X,//120 +MOCAPNET_2DPOINT___SPECIAL03Y,//121 +MOCAPNET_2DPOINT_SPECIAL03X,//122 +MOCAPNET_2DPOINT_SPECIAL03Y,//123 +MOCAPNET_2DPOINT___LEVATOR06_LX,//124 +MOCAPNET_2DPOINT___LEVATOR06_LY,//125 +MOCAPNET_2DPOINT_LEVATOR06_LX,//126 +MOCAPNET_2DPOINT_LEVATOR06_LY,//127 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR06_LX,//128 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR06_LY,//129 +MOCAPNET_2DPOINT___LEVATOR06_RX,//130 +MOCAPNET_2DPOINT___LEVATOR06_RY,//131 +MOCAPNET_2DPOINT_LEVATOR06_RX,//132 +MOCAPNET_2DPOINT_LEVATOR06_RY,//133 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR06_RX,//134 +MOCAPNET_2DPOINT_ENDSITE_LEVATOR06_RY,//135 +MOCAPNET_2DPOINT_SPECIAL06_LX,//136 +MOCAPNET_2DPOINT_SPECIAL06_LY,//137 +MOCAPNET_2DPOINT_SPECIAL05_LX,//138 +MOCAPNET_2DPOINT_SPECIAL05_LY,//139 +MOCAPNET_2DPOINT_EYE_LX,//140 +MOCAPNET_2DPOINT_EYE_LY,//141 +MOCAPNET_2DPOINT_ENDSITE_EYE_LX,//142 +MOCAPNET_2DPOINT_ENDSITE_EYE_LY,//143 +MOCAPNET_2DPOINT_ORBICULARIS03_LX,//144 +MOCAPNET_2DPOINT_ORBICULARIS03_LY,//145 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS03_LX,//146 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS03_LY,//147 +MOCAPNET_2DPOINT_ORBICULARIS04_LX,//148 +MOCAPNET_2DPOINT_ORBICULARIS04_LY,//149 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS04_LX,//150 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS04_LY,//151 +MOCAPNET_2DPOINT_SPECIAL06_RX,//152 +MOCAPNET_2DPOINT_SPECIAL06_RY,//153 +MOCAPNET_2DPOINT_SPECIAL05_RX,//154 +MOCAPNET_2DPOINT_SPECIAL05_RY,//155 +MOCAPNET_2DPOINT_EYE_RX,//156 +MOCAPNET_2DPOINT_EYE_RY,//157 +MOCAPNET_2DPOINT_ENDSITE_EYE_RX,//158 +MOCAPNET_2DPOINT_ENDSITE_EYE_RY,//159 +MOCAPNET_2DPOINT_ORBICULARIS03_RX,//160 +MOCAPNET_2DPOINT_ORBICULARIS03_RY,//161 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS03_RX,//162 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS03_RY,//163 +MOCAPNET_2DPOINT_ORBICULARIS04_RX,//164 +MOCAPNET_2DPOINT_ORBICULARIS04_RY,//165 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS04_RX,//166 +MOCAPNET_2DPOINT_ENDSITE_ORBICULARIS04_RY,//167 +MOCAPNET_2DPOINT___TEMPORALIS01_LX,//168 +MOCAPNET_2DPOINT___TEMPORALIS01_LY,//169 +MOCAPNET_2DPOINT_TEMPORALIS01_LX,//170 +MOCAPNET_2DPOINT_TEMPORALIS01_LY,//171 +MOCAPNET_2DPOINT_OCULI02_LX,//172 +MOCAPNET_2DPOINT_OCULI02_LY,//173 +MOCAPNET_2DPOINT_OCULI01_LX,//174 +MOCAPNET_2DPOINT_OCULI01_LY,//175 +MOCAPNET_2DPOINT_ENDSITE_OCULI01_LX,//176 +MOCAPNET_2DPOINT_ENDSITE_OCULI01_LY,//177 +MOCAPNET_2DPOINT___TEMPORALIS01_RX,//178 +MOCAPNET_2DPOINT___TEMPORALIS01_RY,//179 +MOCAPNET_2DPOINT_TEMPORALIS01_RX,//180 +MOCAPNET_2DPOINT_TEMPORALIS01_RY,//181 +MOCAPNET_2DPOINT_OCULI02_RX,//182 +MOCAPNET_2DPOINT_OCULI02_RY,//183 +MOCAPNET_2DPOINT_OCULI01_RX,//184 +MOCAPNET_2DPOINT_OCULI01_RY,//185 +MOCAPNET_2DPOINT_ENDSITE_OCULI01_RX,//186 +MOCAPNET_2DPOINT_ENDSITE_OCULI01_RY,//187 +MOCAPNET_2DPOINT___TEMPORALIS02_LX,//188 +MOCAPNET_2DPOINT___TEMPORALIS02_LY,//189 +MOCAPNET_2DPOINT_TEMPORALIS02_LX,//190 +MOCAPNET_2DPOINT_TEMPORALIS02_LY,//191 +MOCAPNET_2DPOINT_RISORIUS02_LX,//192 +MOCAPNET_2DPOINT_RISORIUS02_LY,//193 +MOCAPNET_2DPOINT_RISORIUS03_LX,//194 +MOCAPNET_2DPOINT_RISORIUS03_LY,//195 +MOCAPNET_2DPOINT_ENDSITE_RISORIUS03_LX,//196 +MOCAPNET_2DPOINT_ENDSITE_RISORIUS03_LY,//197 +MOCAPNET_2DPOINT___TEMPORALIS02_RX,//198 +MOCAPNET_2DPOINT___TEMPORALIS02_RY,//199 +MOCAPNET_2DPOINT_TEMPORALIS02_RX,//200 +MOCAPNET_2DPOINT_TEMPORALIS02_RY,//201 +MOCAPNET_2DPOINT_RISORIUS02_RX,//202 +MOCAPNET_2DPOINT_RISORIUS02_RY,//203 +MOCAPNET_2DPOINT_RISORIUS03_RX,//204 +MOCAPNET_2DPOINT_RISORIUS03_RY,//205 +MOCAPNET_2DPOINT_ENDSITE_RISORIUS03_RX,//206 +MOCAPNET_2DPOINT_ENDSITE_RISORIUS03_RY,//207 +MOCAPNET_2DPOINT_RCOLLARX,//208 +MOCAPNET_2DPOINT_RCOLLARY,//209 +MOCAPNET_2DPOINT_RSHOULDERX,//210 +MOCAPNET_2DPOINT_RSHOULDERY,//211 +MOCAPNET_2DPOINT_RELBOWX,//212 +MOCAPNET_2DPOINT_RELBOWY,//213 +MOCAPNET_2DPOINT_RHANDX,//214 +MOCAPNET_2DPOINT_RHANDY,//215 +MOCAPNET_2DPOINT_METACARPAL1_RX,//216 +MOCAPNET_2DPOINT_METACARPAL1_RY,//217 +MOCAPNET_2DPOINT_FINGER2_1_RX,//218 +MOCAPNET_2DPOINT_FINGER2_1_RY,//219 +MOCAPNET_2DPOINT_FINGER2_2_RX,//220 +MOCAPNET_2DPOINT_FINGER2_2_RY,//221 +MOCAPNET_2DPOINT_FINGER2_3_RX,//222 +MOCAPNET_2DPOINT_FINGER2_3_RY,//223 +MOCAPNET_2DPOINT_ENDSITE_FINGER2_3_RX,//224 +MOCAPNET_2DPOINT_ENDSITE_FINGER2_3_RY,//225 +MOCAPNET_2DPOINT_METACARPAL2_RX,//226 +MOCAPNET_2DPOINT_METACARPAL2_RY,//227 +MOCAPNET_2DPOINT_FINGER3_1_RX,//228 +MOCAPNET_2DPOINT_FINGER3_1_RY,//229 +MOCAPNET_2DPOINT_FINGER3_2_RX,//230 +MOCAPNET_2DPOINT_FINGER3_2_RY,//231 +MOCAPNET_2DPOINT_FINGER3_3_RX,//232 +MOCAPNET_2DPOINT_FINGER3_3_RY,//233 +MOCAPNET_2DPOINT_ENDSITE_FINGER3_3_RX,//234 +MOCAPNET_2DPOINT_ENDSITE_FINGER3_3_RY,//235 +MOCAPNET_2DPOINT___METACARPAL3_RX,//236 +MOCAPNET_2DPOINT___METACARPAL3_RY,//237 +MOCAPNET_2DPOINT_METACARPAL3_RX,//238 +MOCAPNET_2DPOINT_METACARPAL3_RY,//239 +MOCAPNET_2DPOINT_FINGER4_1_RX,//240 +MOCAPNET_2DPOINT_FINGER4_1_RY,//241 +MOCAPNET_2DPOINT_FINGER4_2_RX,//242 +MOCAPNET_2DPOINT_FINGER4_2_RY,//243 +MOCAPNET_2DPOINT_FINGER4_3_RX,//244 +MOCAPNET_2DPOINT_FINGER4_3_RY,//245 +MOCAPNET_2DPOINT_ENDSITE_FINGER4_3_RX,//246 +MOCAPNET_2DPOINT_ENDSITE_FINGER4_3_RY,//247 +MOCAPNET_2DPOINT___METACARPAL4_RX,//248 +MOCAPNET_2DPOINT___METACARPAL4_RY,//249 +MOCAPNET_2DPOINT_METACARPAL4_RX,//250 +MOCAPNET_2DPOINT_METACARPAL4_RY,//251 +MOCAPNET_2DPOINT_FINGER5_1_RX,//252 +MOCAPNET_2DPOINT_FINGER5_1_RY,//253 +MOCAPNET_2DPOINT_FINGER5_2_RX,//254 +MOCAPNET_2DPOINT_FINGER5_2_RY,//255 +MOCAPNET_2DPOINT_FINGER5_3_RX,//256 +MOCAPNET_2DPOINT_FINGER5_3_RY,//257 +MOCAPNET_2DPOINT_ENDSITE_FINGER5_3_RX,//258 +MOCAPNET_2DPOINT_ENDSITE_FINGER5_3_RY,//259 +MOCAPNET_2DPOINT_RTHUMBBASEX,//260 +MOCAPNET_2DPOINT_RTHUMBBASEY,//261 +MOCAPNET_2DPOINT_RTHUMBX,//262 +MOCAPNET_2DPOINT_RTHUMBY,//263 +MOCAPNET_2DPOINT_FINGER1_2_RX,//264 +MOCAPNET_2DPOINT_FINGER1_2_RY,//265 +MOCAPNET_2DPOINT_FINGER1_3_RX,//266 +MOCAPNET_2DPOINT_FINGER1_3_RY,//267 +MOCAPNET_2DPOINT_ENDSITE_FINGER1_3_RX,//268 +MOCAPNET_2DPOINT_ENDSITE_FINGER1_3_RY,//269 +MOCAPNET_2DPOINT_LCOLLARX,//270 +MOCAPNET_2DPOINT_LCOLLARY,//271 +MOCAPNET_2DPOINT_LSHOULDERX,//272 +MOCAPNET_2DPOINT_LSHOULDERY,//273 +MOCAPNET_2DPOINT_LELBOWX,//274 +MOCAPNET_2DPOINT_LELBOWY,//275 +MOCAPNET_2DPOINT_LHANDX,//276 +MOCAPNET_2DPOINT_LHANDY,//277 +MOCAPNET_2DPOINT_METACARPAL1_LX,//278 +MOCAPNET_2DPOINT_METACARPAL1_LY,//279 +MOCAPNET_2DPOINT_FINGER2_1_LX,//280 +MOCAPNET_2DPOINT_FINGER2_1_LY,//281 +MOCAPNET_2DPOINT_FINGER2_2_LX,//282 +MOCAPNET_2DPOINT_FINGER2_2_LY,//283 +MOCAPNET_2DPOINT_FINGER2_3_LX,//284 +MOCAPNET_2DPOINT_FINGER2_3_LY,//285 +MOCAPNET_2DPOINT_ENDSITE_FINGER2_3_LX,//286 +MOCAPNET_2DPOINT_ENDSITE_FINGER2_3_LY,//287 +MOCAPNET_2DPOINT_METACARPAL2_LX,//288 +MOCAPNET_2DPOINT_METACARPAL2_LY,//289 +MOCAPNET_2DPOINT_FINGER3_1_LX,//290 +MOCAPNET_2DPOINT_FINGER3_1_LY,//291 +MOCAPNET_2DPOINT_FINGER3_2_LX,//292 +MOCAPNET_2DPOINT_FINGER3_2_LY,//293 +MOCAPNET_2DPOINT_FINGER3_3_LX,//294 +MOCAPNET_2DPOINT_FINGER3_3_LY,//295 +MOCAPNET_2DPOINT_ENDSITE_FINGER3_3_LX,//296 +MOCAPNET_2DPOINT_ENDSITE_FINGER3_3_LY,//297 +MOCAPNET_2DPOINT___METACARPAL3_LX,//298 +MOCAPNET_2DPOINT___METACARPAL3_LY,//299 +MOCAPNET_2DPOINT_METACARPAL3_LX,//300 +MOCAPNET_2DPOINT_METACARPAL3_LY,//301 +MOCAPNET_2DPOINT_FINGER4_1_LX,//302 +MOCAPNET_2DPOINT_FINGER4_1_LY,//303 +MOCAPNET_2DPOINT_FINGER4_2_LX,//304 +MOCAPNET_2DPOINT_FINGER4_2_LY,//305 +MOCAPNET_2DPOINT_FINGER4_3_LX,//306 +MOCAPNET_2DPOINT_FINGER4_3_LY,//307 +MOCAPNET_2DPOINT_ENDSITE_FINGER4_3_LX,//308 +MOCAPNET_2DPOINT_ENDSITE_FINGER4_3_LY,//309 +MOCAPNET_2DPOINT___METACARPAL4_LX,//310 +MOCAPNET_2DPOINT___METACARPAL4_LY,//311 +MOCAPNET_2DPOINT_METACARPAL4_LX,//312 +MOCAPNET_2DPOINT_METACARPAL4_LY,//313 +MOCAPNET_2DPOINT_FINGER5_1_LX,//314 +MOCAPNET_2DPOINT_FINGER5_1_LY,//315 +MOCAPNET_2DPOINT_FINGER5_2_LX,//316 +MOCAPNET_2DPOINT_FINGER5_2_LY,//317 +MOCAPNET_2DPOINT_FINGER5_3_LX,//318 +MOCAPNET_2DPOINT_FINGER5_3_LY,//319 +MOCAPNET_2DPOINT_ENDSITE_FINGER5_3_LX,//320 +MOCAPNET_2DPOINT_ENDSITE_FINGER5_3_LY,//321 +MOCAPNET_2DPOINT_LTHUMBBASEX,//322 +MOCAPNET_2DPOINT_LTHUMBBASEY,//323 +MOCAPNET_2DPOINT_LTHUMBX,//324 +MOCAPNET_2DPOINT_LTHUMBY,//325 +MOCAPNET_2DPOINT_FINGER1_2_LX,//326 +MOCAPNET_2DPOINT_FINGER1_2_LY,//327 +MOCAPNET_2DPOINT_FINGER1_3_LX,//328 +MOCAPNET_2DPOINT_FINGER1_3_LY,//329 +MOCAPNET_2DPOINT_ENDSITE_FINGER1_3_LX,//330 +MOCAPNET_2DPOINT_ENDSITE_FINGER1_3_LY,//331 +MOCAPNET_2DPOINT_RBUTTOCKX,//332 +MOCAPNET_2DPOINT_RBUTTOCKY,//333 +MOCAPNET_2DPOINT_RHIPX,//334 +MOCAPNET_2DPOINT_RHIPY,//335 +MOCAPNET_2DPOINT_RKNEEX,//336 +MOCAPNET_2DPOINT_RKNEEY,//337 +MOCAPNET_2DPOINT_RFOOTX,//338 +MOCAPNET_2DPOINT_RFOOTY,//339 +MOCAPNET_2DPOINT_TOE1_1_RX,//340 +MOCAPNET_2DPOINT_TOE1_1_RY,//341 +MOCAPNET_2DPOINT_TOE1_2_RX,//342 +MOCAPNET_2DPOINT_TOE1_2_RY,//343 +MOCAPNET_2DPOINT_ENDSITE_TOE1_2_RX,//344 +MOCAPNET_2DPOINT_ENDSITE_TOE1_2_RY,//345 +MOCAPNET_2DPOINT_TOE2_1_RX,//346 +MOCAPNET_2DPOINT_TOE2_1_RY,//347 +MOCAPNET_2DPOINT_TOE2_2_RX,//348 +MOCAPNET_2DPOINT_TOE2_2_RY,//349 +MOCAPNET_2DPOINT_TOE2_3_RX,//350 +MOCAPNET_2DPOINT_TOE2_3_RY,//351 +MOCAPNET_2DPOINT_ENDSITE_TOE2_3_RX,//352 +MOCAPNET_2DPOINT_ENDSITE_TOE2_3_RY,//353 +MOCAPNET_2DPOINT_TOE3_1_RX,//354 +MOCAPNET_2DPOINT_TOE3_1_RY,//355 +MOCAPNET_2DPOINT_TOE3_2_RX,//356 +MOCAPNET_2DPOINT_TOE3_2_RY,//357 +MOCAPNET_2DPOINT_TOE3_3_RX,//358 +MOCAPNET_2DPOINT_TOE3_3_RY,//359 +MOCAPNET_2DPOINT_ENDSITE_TOE3_3_RX,//360 +MOCAPNET_2DPOINT_ENDSITE_TOE3_3_RY,//361 +MOCAPNET_2DPOINT_TOE4_1_RX,//362 +MOCAPNET_2DPOINT_TOE4_1_RY,//363 +MOCAPNET_2DPOINT_TOE4_2_RX,//364 +MOCAPNET_2DPOINT_TOE4_2_RY,//365 +MOCAPNET_2DPOINT_TOE4_3_RX,//366 +MOCAPNET_2DPOINT_TOE4_3_RY,//367 +MOCAPNET_2DPOINT_ENDSITE_TOE4_3_RX,//368 +MOCAPNET_2DPOINT_ENDSITE_TOE4_3_RY,//369 +MOCAPNET_2DPOINT_TOE5_1_RX,//370 +MOCAPNET_2DPOINT_TOE5_1_RY,//371 +MOCAPNET_2DPOINT_TOE5_2_RX,//372 +MOCAPNET_2DPOINT_TOE5_2_RY,//373 +MOCAPNET_2DPOINT_TOE5_3_RX,//374 +MOCAPNET_2DPOINT_TOE5_3_RY,//375 +MOCAPNET_2DPOINT_ENDSITE_TOE5_3_RX,//376 +MOCAPNET_2DPOINT_ENDSITE_TOE5_3_RY,//377 +MOCAPNET_2DPOINT_LBUTTOCKX,//378 +MOCAPNET_2DPOINT_LBUTTOCKY,//379 +MOCAPNET_2DPOINT_LHIPX,//380 +MOCAPNET_2DPOINT_LHIPY,//381 +MOCAPNET_2DPOINT_LKNEEX,//382 +MOCAPNET_2DPOINT_LKNEEY,//383 +MOCAPNET_2DPOINT_LFOOTX,//384 +MOCAPNET_2DPOINT_LFOOTY,//385 +MOCAPNET_2DPOINT_TOE1_1_LX,//386 +MOCAPNET_2DPOINT_TOE1_1_LY,//387 +MOCAPNET_2DPOINT_TOE1_2_LX,//388 +MOCAPNET_2DPOINT_TOE1_2_LY,//389 +MOCAPNET_2DPOINT_ENDSITE_TOE1_2_LX,//390 +MOCAPNET_2DPOINT_ENDSITE_TOE1_2_LY,//391 +MOCAPNET_2DPOINT_TOE2_1_LX,//392 +MOCAPNET_2DPOINT_TOE2_1_LY,//393 +MOCAPNET_2DPOINT_TOE2_2_LX,//394 +MOCAPNET_2DPOINT_TOE2_2_LY,//395 +MOCAPNET_2DPOINT_TOE2_3_LX,//396 +MOCAPNET_2DPOINT_TOE2_3_LY,//397 +MOCAPNET_2DPOINT_ENDSITE_TOE2_3_LX,//398 +MOCAPNET_2DPOINT_ENDSITE_TOE2_3_LY,//399 +MOCAPNET_2DPOINT_TOE3_1_LX,//400 +MOCAPNET_2DPOINT_TOE3_1_LY,//401 +MOCAPNET_2DPOINT_TOE3_2_LX,//402 +MOCAPNET_2DPOINT_TOE3_2_LY,//403 +MOCAPNET_2DPOINT_TOE3_3_LX,//404 +MOCAPNET_2DPOINT_TOE3_3_LY,//405 +MOCAPNET_2DPOINT_ENDSITE_TOE3_3_LX,//406 +MOCAPNET_2DPOINT_ENDSITE_TOE3_3_LY,//407 +MOCAPNET_2DPOINT_TOE4_1_LX,//408 +MOCAPNET_2DPOINT_TOE4_1_LY,//409 +MOCAPNET_2DPOINT_TOE4_2_LX,//410 +MOCAPNET_2DPOINT_TOE4_2_LY,//411 +MOCAPNET_2DPOINT_TOE4_3_LX,//412 +MOCAPNET_2DPOINT_TOE4_3_LY,//413 +MOCAPNET_2DPOINT_ENDSITE_TOE4_3_LX,//414 +MOCAPNET_2DPOINT_ENDSITE_TOE4_3_LY,//415 +MOCAPNET_2DPOINT_TOE5_1_LX,//416 +MOCAPNET_2DPOINT_TOE5_1_LY,//417 +MOCAPNET_2DPOINT_TOE5_2_LX,//418 +MOCAPNET_2DPOINT_TOE5_2_LY,//419 +MOCAPNET_2DPOINT_TOE5_3_LX,//420 +MOCAPNET_2DPOINT_TOE5_3_LY,//421 +MOCAPNET_2DPOINT_ENDSITE_TOE5_3_LX,//422 +MOCAPNET_2DPOINT_ENDSITE_TOE5_3_LY//423 +}; + + + + +/** + * @brief This is a programmer friendly enumerator to access 3D output extracted from the BVH file_ + * Use _/GroundTruthDumper __from dataset/headerWithHeadAndOneMotion_bvh __printc to extract this automatically + */ +enum MOCAPNET_JointHierarchy_Joints +{ +MOCAPNET_JOINT_HIP,//0 +MOCAPNET_JOINT_ABDOMEN,//1 +MOCAPNET_JOINT_CHEST,//2 +MOCAPNET_JOINT_NECK,//3 +MOCAPNET_JOINT_NECK1,//4 +MOCAPNET_JOINT_HEAD,//5 +MOCAPNET_JOINT___JAW,//6 +MOCAPNET_JOINT_JAW,//7 +MOCAPNET_JOINT_SPECIAL04,//8 +MOCAPNET_JOINT_ORIS02,//9 +MOCAPNET_JOINT_ORIS01,//10 +MOCAPNET_JOINT_ENDSITE_ORIS01,//11 +MOCAPNET_JOINT_ORIS06_L,//12 +MOCAPNET_JOINT_ORIS07_L,//13 +MOCAPNET_JOINT_ENDSITE_ORIS07_L,//14 +MOCAPNET_JOINT_ORIS06_R,//15 +MOCAPNET_JOINT_ORIS07_R,//16 +MOCAPNET_JOINT_ENDSITE_ORIS07_R,//17 +MOCAPNET_JOINT_TONGUE00,//18 +MOCAPNET_JOINT_TONGUE01,//19 +MOCAPNET_JOINT_TONGUE02,//20 +MOCAPNET_JOINT_TONGUE03,//21 +MOCAPNET_JOINT___TONGUE04,//22 +MOCAPNET_JOINT_TONGUE04,//23 +MOCAPNET_JOINT_ENDSITE_TONGUE04,//24 +MOCAPNET_JOINT_TONGUE07_L,//25 +MOCAPNET_JOINT_ENDSITE_TONGUE07_L,//26 +MOCAPNET_JOINT_TONGUE07_R,//27 +MOCAPNET_JOINT_ENDSITE_TONGUE07_R,//28 +MOCAPNET_JOINT_TONGUE06_L,//29 +MOCAPNET_JOINT_ENDSITE_TONGUE06_L,//30 +MOCAPNET_JOINT_TONGUE06_R,//31 +MOCAPNET_JOINT_ENDSITE_TONGUE06_R,//32 +MOCAPNET_JOINT_TONGUE05_L,//33 +MOCAPNET_JOINT_ENDSITE_TONGUE05_L,//34 +MOCAPNET_JOINT_TONGUE05_R,//35 +MOCAPNET_JOINT_ENDSITE_TONGUE05_R,//36 +MOCAPNET_JOINT___LEVATOR02_L,//37 +MOCAPNET_JOINT_LEVATOR02_L,//38 +MOCAPNET_JOINT_LEVATOR03_L,//39 +MOCAPNET_JOINT_LEVATOR04_L,//40 +MOCAPNET_JOINT_LEVATOR05_L,//41 +MOCAPNET_JOINT_ENDSITE_LEVATOR05_L,//42 +MOCAPNET_JOINT___LEVATOR02_R,//43 +MOCAPNET_JOINT_LEVATOR02_R,//44 +MOCAPNET_JOINT_LEVATOR03_R,//45 +MOCAPNET_JOINT_LEVATOR04_R,//46 +MOCAPNET_JOINT_LEVATOR05_R,//47 +MOCAPNET_JOINT_ENDSITE_LEVATOR05_R,//48 +MOCAPNET_JOINT___SPECIAL01,//49 +MOCAPNET_JOINT_SPECIAL01,//50 +MOCAPNET_JOINT_ORIS04_L,//51 +MOCAPNET_JOINT_ORIS03_L,//52 +MOCAPNET_JOINT_ENDSITE_ORIS03_L,//53 +MOCAPNET_JOINT_ORIS04_R,//54 +MOCAPNET_JOINT_ORIS03_R,//55 +MOCAPNET_JOINT_ENDSITE_ORIS03_R,//56 +MOCAPNET_JOINT_ORIS06,//57 +MOCAPNET_JOINT_ORIS05,//58 +MOCAPNET_JOINT_ENDSITE_ORIS05,//59 +MOCAPNET_JOINT___SPECIAL03,//60 +MOCAPNET_JOINT_SPECIAL03,//61 +MOCAPNET_JOINT___LEVATOR06_L,//62 +MOCAPNET_JOINT_LEVATOR06_L,//63 +MOCAPNET_JOINT_ENDSITE_LEVATOR06_L,//64 +MOCAPNET_JOINT___LEVATOR06_R,//65 +MOCAPNET_JOINT_LEVATOR06_R,//66 +MOCAPNET_JOINT_ENDSITE_LEVATOR06_R,//67 +MOCAPNET_JOINT_SPECIAL06_L,//68 +MOCAPNET_JOINT_SPECIAL05_L,//69 +MOCAPNET_JOINT_EYE_L,//70 +MOCAPNET_JOINT_ENDSITE_EYE_L,//71 +MOCAPNET_JOINT_ORBICULARIS03_L,//72 +MOCAPNET_JOINT_ENDSITE_ORBICULARIS03_L,//73 +MOCAPNET_JOINT_ORBICULARIS04_L,//74 +MOCAPNET_JOINT_ENDSITE_ORBICULARIS04_L,//75 +MOCAPNET_JOINT_SPECIAL06_R,//76 +MOCAPNET_JOINT_SPECIAL05_R,//77 +MOCAPNET_JOINT_EYE_R,//78 +MOCAPNET_JOINT_ENDSITE_EYE_R,//79 +MOCAPNET_JOINT_ORBICULARIS03_R,//80 +MOCAPNET_JOINT_ENDSITE_ORBICULARIS03_R,//81 +MOCAPNET_JOINT_ORBICULARIS04_R,//82 +MOCAPNET_JOINT_ENDSITE_ORBICULARIS04_R,//83 +MOCAPNET_JOINT___TEMPORALIS01_L,//84 +MOCAPNET_JOINT_TEMPORALIS01_L,//85 +MOCAPNET_JOINT_OCULI02_L,//86 +MOCAPNET_JOINT_OCULI01_L,//87 +MOCAPNET_JOINT_ENDSITE_OCULI01_L,//88 +MOCAPNET_JOINT___TEMPORALIS01_R,//89 +MOCAPNET_JOINT_TEMPORALIS01_R,//90 +MOCAPNET_JOINT_OCULI02_R,//91 +MOCAPNET_JOINT_OCULI01_R,//92 +MOCAPNET_JOINT_ENDSITE_OCULI01_R,//93 +MOCAPNET_JOINT___TEMPORALIS02_L,//94 +MOCAPNET_JOINT_TEMPORALIS02_L,//95 +MOCAPNET_JOINT_RISORIUS02_L,//96 +MOCAPNET_JOINT_RISORIUS03_L,//97 +MOCAPNET_JOINT_ENDSITE_RISORIUS03_L,//98 +MOCAPNET_JOINT___TEMPORALIS02_R,//99 +MOCAPNET_JOINT_TEMPORALIS02_R,//100 +MOCAPNET_JOINT_RISORIUS02_R,//101 +MOCAPNET_JOINT_RISORIUS03_R,//102 +MOCAPNET_JOINT_ENDSITE_RISORIUS03_R,//103 +MOCAPNET_JOINT_RCOLLAR,//104 +MOCAPNET_JOINT_RSHOULDER,//105 +MOCAPNET_JOINT_RELBOW,//106 +MOCAPNET_JOINT_RHAND,//107 +MOCAPNET_JOINT_METACARPAL1_R,//108 +MOCAPNET_JOINT_FINGER2_1_R,//109 +MOCAPNET_JOINT_FINGER2_2_R,//110 +MOCAPNET_JOINT_FINGER2_3_R,//111 +MOCAPNET_JOINT_ENDSITE_FINGER2_3_R,//112 +MOCAPNET_JOINT_METACARPAL2_R,//113 +MOCAPNET_JOINT_FINGER3_1_R,//114 +MOCAPNET_JOINT_FINGER3_2_R,//115 +MOCAPNET_JOINT_FINGER3_3_R,//116 +MOCAPNET_JOINT_ENDSITE_FINGER3_3_R,//117 +MOCAPNET_JOINT___METACARPAL3_R,//118 +MOCAPNET_JOINT_METACARPAL3_R,//119 +MOCAPNET_JOINT_FINGER4_1_R,//120 +MOCAPNET_JOINT_FINGER4_2_R,//121 +MOCAPNET_JOINT_FINGER4_3_R,//122 +MOCAPNET_JOINT_ENDSITE_FINGER4_3_R,//123 +MOCAPNET_JOINT___METACARPAL4_R,//124 +MOCAPNET_JOINT_METACARPAL4_R,//125 +MOCAPNET_JOINT_FINGER5_1_R,//126 +MOCAPNET_JOINT_FINGER5_2_R,//127 +MOCAPNET_JOINT_FINGER5_3_R,//128 +MOCAPNET_JOINT_ENDSITE_FINGER5_3_R,//129 +MOCAPNET_JOINT_RTHUMBBASE,//130 +MOCAPNET_JOINT_RTHUMB,//131 +MOCAPNET_JOINT_FINGER1_2_R,//132 +MOCAPNET_JOINT_FINGER1_3_R,//133 +MOCAPNET_JOINT_ENDSITE_FINGER1_3_R,//134 +MOCAPNET_JOINT_LCOLLAR,//135 +MOCAPNET_JOINT_LSHOULDER,//136 +MOCAPNET_JOINT_LELBOW,//137 +MOCAPNET_JOINT_LHAND,//138 +MOCAPNET_JOINT_METACARPAL1_L,//139 +MOCAPNET_JOINT_FINGER2_1_L,//140 +MOCAPNET_JOINT_FINGER2_2_L,//141 +MOCAPNET_JOINT_FINGER2_3_L,//142 +MOCAPNET_JOINT_ENDSITE_FINGER2_3_L,//143 +MOCAPNET_JOINT_METACARPAL2_L,//144 +MOCAPNET_JOINT_FINGER3_1_L,//145 +MOCAPNET_JOINT_FINGER3_2_L,//146 +MOCAPNET_JOINT_FINGER3_3_L,//147 +MOCAPNET_JOINT_ENDSITE_FINGER3_3_L,//148 +MOCAPNET_JOINT___METACARPAL3_L,//149 +MOCAPNET_JOINT_METACARPAL3_L,//150 +MOCAPNET_JOINT_FINGER4_1_L,//151 +MOCAPNET_JOINT_FINGER4_2_L,//152 +MOCAPNET_JOINT_FINGER4_3_L,//153 +MOCAPNET_JOINT_ENDSITE_FINGER4_3_L,//154 +MOCAPNET_JOINT___METACARPAL4_L,//155 +MOCAPNET_JOINT_METACARPAL4_L,//156 +MOCAPNET_JOINT_FINGER5_1_L,//157 +MOCAPNET_JOINT_FINGER5_2_L,//158 +MOCAPNET_JOINT_FINGER5_3_L,//159 +MOCAPNET_JOINT_ENDSITE_FINGER5_3_L,//160 +MOCAPNET_JOINT_LTHUMBBASE,//161 +MOCAPNET_JOINT_LTHUMB,//162 +MOCAPNET_JOINT_FINGER1_2_L,//163 +MOCAPNET_JOINT_FINGER1_3_L,//164 +MOCAPNET_JOINT_ENDSITE_FINGER1_3_L,//165 +MOCAPNET_JOINT_RBUTTOCK,//166 +MOCAPNET_JOINT_RHIP,//167 +MOCAPNET_JOINT_RKNEE,//168 +MOCAPNET_JOINT_RFOOT,//169 +MOCAPNET_JOINT_TOE1_1_R,//170 +MOCAPNET_JOINT_TOE1_2_R,//171 +MOCAPNET_JOINT_ENDSITE_TOE1_2_R,//172 +MOCAPNET_JOINT_TOE2_1_R,//173 +MOCAPNET_JOINT_TOE2_2_R,//174 +MOCAPNET_JOINT_TOE2_3_R,//175 +MOCAPNET_JOINT_ENDSITE_TOE2_3_R,//176 +MOCAPNET_JOINT_TOE3_1_R,//177 +MOCAPNET_JOINT_TOE3_2_R,//178 +MOCAPNET_JOINT_TOE3_3_R,//179 +MOCAPNET_JOINT_ENDSITE_TOE3_3_R,//180 +MOCAPNET_JOINT_TOE4_1_R,//181 +MOCAPNET_JOINT_TOE4_2_R,//182 +MOCAPNET_JOINT_TOE4_3_R,//183 +MOCAPNET_JOINT_ENDSITE_TOE4_3_R,//184 +MOCAPNET_JOINT_TOE5_1_R,//185 +MOCAPNET_JOINT_TOE5_2_R,//186 +MOCAPNET_JOINT_TOE5_3_R,//187 +MOCAPNET_JOINT_ENDSITE_TOE5_3_R,//188 +MOCAPNET_JOINT_LBUTTOCK,//189 +MOCAPNET_JOINT_LHIP,//190 +MOCAPNET_JOINT_LKNEE,//191 +MOCAPNET_JOINT_LFOOT,//192 +MOCAPNET_JOINT_TOE1_1_L,//193 +MOCAPNET_JOINT_TOE1_2_L,//194 +MOCAPNET_JOINT_ENDSITE_TOE1_2_L,//195 +MOCAPNET_JOINT_TOE2_1_L,//196 +MOCAPNET_JOINT_TOE2_2_L,//197 +MOCAPNET_JOINT_TOE2_3_L,//198 +MOCAPNET_JOINT_ENDSITE_TOE2_3_L,//199 +MOCAPNET_JOINT_TOE3_1_L,//200 +MOCAPNET_JOINT_TOE3_2_L,//201 +MOCAPNET_JOINT_TOE3_3_L,//202 +MOCAPNET_JOINT_ENDSITE_TOE3_3_L,//203 +MOCAPNET_JOINT_TOE4_1_L,//204 +MOCAPNET_JOINT_TOE4_2_L,//205 +MOCAPNET_JOINT_TOE4_3_L,//206 +MOCAPNET_JOINT_ENDSITE_TOE4_3_L,//207 +MOCAPNET_JOINT_TOE5_1_L,//208 +MOCAPNET_JOINT_TOE5_2_L,//209 +MOCAPNET_JOINT_TOE5_3_L,//210 +MOCAPNET_JOINT_ENDSITE_TOE5_3_L//211 +}; + + + + +/** + * @brief An array with string labels for what each element of an input should be after concatenating uncompressed and compressed input. + * Use ./GroundTruthDumper --from dataset/headerWithHeadAndOneMotion.bvh --printc + * to extract this automatically + */ +static const char * MocapNETOutputArrayNames[] = +{ +"hip_Xposition", // 0 +"hip_Yposition", // 1 +"hip_Zposition", // 2 +"hip_Zrotation", // 3 +"hip_Yrotation", // 4 +"hip_Xrotation", // 5 +"abdomen_Zrotation", // 6 + "abdomen_Xrotation", // 7 + "abdomen_Yrotation", // 8 + "chest_Zrotation", // 9 + "chest_Xrotation", // 10 + "chest_Yrotation", // 11 + "neck_Zrotation", // 12 + "neck_Xrotation", // 13 + "neck_Yrotation", // 14 + "neck1_Zrotation", // 15 + "neck1_Xrotation", // 16 + "neck1_Yrotation", // 17 + "head_Zrotation", // 18 + "head_Xrotation", // 19 + "head_Yrotation", // 20 + "__jaw_Zrotation", // 21 + "__jaw_Xrotation", // 22 + "__jaw_Yrotation", // 23 + "jaw_Zrotation", // 24 + "jaw_Xrotation", // 25 + "jaw_Yrotation", // 26 + "special04_Zrotation", // 27 + "special04_Xrotation", // 28 + "special04_Yrotation", // 29 + "oris02_Zrotation", // 30 + "oris02_Xrotation", // 31 + "oris02_Yrotation", // 32 + "oris01_Zrotation", // 33 + "oris01_Xrotation", // 34 + "oris01_Yrotation", // 35 + "oris06.l_Zrotation", // 36 + "oris06.l_Xrotation", // 37 + "oris06.l_Yrotation", // 38 + "oris07.l_Zrotation", // 39 + "oris07.l_Xrotation", // 40 + "oris07.l_Yrotation", // 41 + "oris06.r_Zrotation", // 42 + "oris06.r_Xrotation", // 43 + "oris06.r_Yrotation", // 44 + "oris07.r_Zrotation", // 45 + "oris07.r_Xrotation", // 46 + "oris07.r_Yrotation", // 47 + "tongue00_Zrotation", // 48 + "tongue00_Xrotation", // 49 + "tongue00_Yrotation", // 50 + "tongue01_Zrotation", // 51 + "tongue01_Xrotation", // 52 + "tongue01_Yrotation", // 53 + "tongue02_Zrotation", // 54 + "tongue02_Xrotation", // 55 + "tongue02_Yrotation", // 56 + "tongue03_Zrotation", // 57 + "tongue03_Xrotation", // 58 + "tongue03_Yrotation", // 59 + "__tongue04_Zrotation", // 60 + "__tongue04_Xrotation", // 61 + "__tongue04_Yrotation", // 62 + "tongue04_Zrotation", // 63 + "tongue04_Xrotation", // 64 + "tongue04_Yrotation", // 65 + "tongue07.l_Zrotation", // 66 + "tongue07.l_Xrotation", // 67 + "tongue07.l_Yrotation", // 68 + "tongue07.r_Zrotation", // 69 + "tongue07.r_Xrotation", // 70 + "tongue07.r_Yrotation", // 71 + "tongue06.l_Zrotation", // 72 + "tongue06.l_Xrotation", // 73 + "tongue06.l_Yrotation", // 74 + "tongue06.r_Zrotation", // 75 + "tongue06.r_Xrotation", // 76 + "tongue06.r_Yrotation", // 77 + "tongue05.l_Zrotation", // 78 + "tongue05.l_Xrotation", // 79 + "tongue05.l_Yrotation", // 80 + "tongue05.r_Zrotation", // 81 + "tongue05.r_Xrotation", // 82 + "tongue05.r_Yrotation", // 83 + "__levator02.l_Zrotation", // 84 + "__levator02.l_Xrotation", // 85 + "__levator02.l_Yrotation", // 86 + "levator02.l_Zrotation", // 87 + "levator02.l_Xrotation", // 88 + "levator02.l_Yrotation", // 89 + "levator03.l_Zrotation", // 90 + "levator03.l_Xrotation", // 91 + "levator03.l_Yrotation", // 92 + "levator04.l_Zrotation", // 93 + "levator04.l_Xrotation", // 94 + "levator04.l_Yrotation", // 95 + "levator05.l_Zrotation", // 96 + "levator05.l_Xrotation", // 97 + "levator05.l_Yrotation", // 98 + "__levator02.r_Zrotation", // 99 + "__levator02.r_Xrotation", // 100 + "__levator02.r_Yrotation", // 101 + "levator02.r_Zrotation", // 102 + "levator02.r_Xrotation", // 103 + "levator02.r_Yrotation", // 104 + "levator03.r_Zrotation", // 105 + "levator03.r_Xrotation", // 106 + "levator03.r_Yrotation", // 107 + "levator04.r_Zrotation", // 108 + "levator04.r_Xrotation", // 109 + "levator04.r_Yrotation", // 110 + "levator05.r_Zrotation", // 111 + "levator05.r_Xrotation", // 112 + "levator05.r_Yrotation", // 113 + "__special01_Zrotation", // 114 + "__special01_Xrotation", // 115 + "__special01_Yrotation", // 116 + "special01_Zrotation", // 117 + "special01_Xrotation", // 118 + "special01_Yrotation", // 119 + "oris04.l_Zrotation", // 120 + "oris04.l_Xrotation", // 121 + "oris04.l_Yrotation", // 122 + "oris03.l_Zrotation", // 123 + "oris03.l_Xrotation", // 124 + "oris03.l_Yrotation", // 125 + "oris04.r_Zrotation", // 126 + "oris04.r_Xrotation", // 127 + "oris04.r_Yrotation", // 128 + "oris03.r_Zrotation", // 129 + "oris03.r_Xrotation", // 130 + "oris03.r_Yrotation", // 131 + "oris06_Zrotation", // 132 + "oris06_Xrotation", // 133 + "oris06_Yrotation", // 134 + "oris05_Zrotation", // 135 + "oris05_Xrotation", // 136 + "oris05_Yrotation", // 137 + "__special03_Zrotation", // 138 + "__special03_Xrotation", // 139 + "__special03_Yrotation", // 140 + "special03_Zrotation", // 141 + "special03_Xrotation", // 142 + "special03_Yrotation", // 143 + "__levator06.l_Zrotation", // 144 + "__levator06.l_Xrotation", // 145 + "__levator06.l_Yrotation", // 146 + "levator06.l_Zrotation", // 147 + "levator06.l_Xrotation", // 148 + "levator06.l_Yrotation", // 149 + "__levator06.r_Zrotation", // 150 + "__levator06.r_Xrotation", // 151 + "__levator06.r_Yrotation", // 152 + "levator06.r_Zrotation", // 153 + "levator06.r_Xrotation", // 154 + "levator06.r_Yrotation", // 155 + "special06.l_Zrotation", // 156 + "special06.l_Xrotation", // 157 + "special06.l_Yrotation", // 158 + "special05.l_Zrotation", // 159 + "special05.l_Xrotation", // 160 + "special05.l_Yrotation", // 161 + "eye.l_Zrotation", // 162 + "eye.l_Xrotation", // 163 + "eye.l_Yrotation", // 164 + "orbicularis03.l_Zrotation", // 165 + "orbicularis03.l_Xrotation", // 166 + "orbicularis03.l_Yrotation", // 167 + "orbicularis04.l_Zrotation", // 168 + "orbicularis04.l_Xrotation", // 169 + "orbicularis04.l_Yrotation", // 170 + "special06.r_Zrotation", // 171 + "special06.r_Xrotation", // 172 + "special06.r_Yrotation", // 173 + "special05.r_Zrotation", // 174 + "special05.r_Xrotation", // 175 + "special05.r_Yrotation", // 176 + "eye.r_Zrotation", // 177 + "eye.r_Xrotation", // 178 + "eye.r_Yrotation", // 179 + "orbicularis03.r_Zrotation", // 180 + "orbicularis03.r_Xrotation", // 181 + "orbicularis03.r_Yrotation", // 182 + "orbicularis04.r_Zrotation", // 183 + "orbicularis04.r_Xrotation", // 184 + "orbicularis04.r_Yrotation", // 185 + "__temporalis01.l_Zrotation", // 186 + "__temporalis01.l_Xrotation", // 187 + "__temporalis01.l_Yrotation", // 188 + "temporalis01.l_Zrotation", // 189 + "temporalis01.l_Xrotation", // 190 + "temporalis01.l_Yrotation", // 191 + "oculi02.l_Zrotation", // 192 + "oculi02.l_Xrotation", // 193 + "oculi02.l_Yrotation", // 194 + "oculi01.l_Zrotation", // 195 + "oculi01.l_Xrotation", // 196 + "oculi01.l_Yrotation", // 197 + "__temporalis01.r_Zrotation", // 198 + "__temporalis01.r_Xrotation", // 199 + "__temporalis01.r_Yrotation", // 200 + "temporalis01.r_Zrotation", // 201 + "temporalis01.r_Xrotation", // 202 + "temporalis01.r_Yrotation", // 203 + "oculi02.r_Zrotation", // 204 + "oculi02.r_Xrotation", // 205 + "oculi02.r_Yrotation", // 206 + "oculi01.r_Zrotation", // 207 + "oculi01.r_Xrotation", // 208 + "oculi01.r_Yrotation", // 209 + "__temporalis02.l_Zrotation", // 210 + "__temporalis02.l_Xrotation", // 211 + "__temporalis02.l_Yrotation", // 212 + "temporalis02.l_Zrotation", // 213 + "temporalis02.l_Xrotation", // 214 + "temporalis02.l_Yrotation", // 215 + "risorius02.l_Zrotation", // 216 + "risorius02.l_Xrotation", // 217 + "risorius02.l_Yrotation", // 218 + "risorius03.l_Zrotation", // 219 + "risorius03.l_Xrotation", // 220 + "risorius03.l_Yrotation", // 221 + "__temporalis02.r_Zrotation", // 222 + "__temporalis02.r_Xrotation", // 223 + "__temporalis02.r_Yrotation", // 224 + "temporalis02.r_Zrotation", // 225 + "temporalis02.r_Xrotation", // 226 + "temporalis02.r_Yrotation", // 227 + "risorius02.r_Zrotation", // 228 + "risorius02.r_Xrotation", // 229 + "risorius02.r_Yrotation", // 230 + "risorius03.r_Zrotation", // 231 + "risorius03.r_Xrotation", // 232 + "risorius03.r_Yrotation", // 233 + "rcollar_Zrotation", // 234 + "rcollar_Xrotation", // 235 + "rcollar_Yrotation", // 236 + "rshoulder_Zrotation", // 237 + "rshoulder_Xrotation", // 238 + "rshoulder_Yrotation", // 239 + "relbow_Zrotation", // 240 + "relbow_Xrotation", // 241 + "relbow_Yrotation", // 242 + "rhand_Zrotation", // 243 + "rhand_Xrotation", // 244 + "rhand_Yrotation", // 245 + "metacarpal1.r_Zrotation", // 246 + "metacarpal1.r_Xrotation", // 247 + "metacarpal1.r_Yrotation", // 248 + "finger2-1.r_Zrotation", // 249 + "finger2-1.r_Xrotation", // 250 + "finger2-1.r_Yrotation", // 251 + "finger2-2.r_Zrotation", // 252 + "finger2-2.r_Xrotation", // 253 + "finger2-2.r_Yrotation", // 254 + "finger2-3.r_Zrotation", // 255 + "finger2-3.r_Xrotation", // 256 + "finger2-3.r_Yrotation", // 257 + "metacarpal2.r_Zrotation", // 258 + "metacarpal2.r_Xrotation", // 259 + "metacarpal2.r_Yrotation", // 260 + "finger3-1.r_Zrotation", // 261 + "finger3-1.r_Xrotation", // 262 + "finger3-1.r_Yrotation", // 263 + "finger3-2.r_Zrotation", // 264 + "finger3-2.r_Xrotation", // 265 + "finger3-2.r_Yrotation", // 266 + "finger3-3.r_Zrotation", // 267 + "finger3-3.r_Xrotation", // 268 + "finger3-3.r_Yrotation", // 269 + "__metacarpal3.r_Zrotation", // 270 + "__metacarpal3.r_Xrotation", // 271 + "__metacarpal3.r_Yrotation", // 272 + "metacarpal3.r_Zrotation", // 273 + "metacarpal3.r_Xrotation", // 274 + "metacarpal3.r_Yrotation", // 275 + "finger4-1.r_Zrotation", // 276 + "finger4-1.r_Xrotation", // 277 + "finger4-1.r_Yrotation", // 278 + "finger4-2.r_Zrotation", // 279 + "finger4-2.r_Xrotation", // 280 + "finger4-2.r_Yrotation", // 281 + "finger4-3.r_Zrotation", // 282 + "finger4-3.r_Xrotation", // 283 + "finger4-3.r_Yrotation", // 284 + "__metacarpal4.r_Zrotation", // 285 + "__metacarpal4.r_Xrotation", // 286 + "__metacarpal4.r_Yrotation", // 287 + "metacarpal4.r_Zrotation", // 288 + "metacarpal4.r_Xrotation", // 289 + "metacarpal4.r_Yrotation", // 290 + "finger5-1.r_Zrotation", // 291 + "finger5-1.r_Xrotation", // 292 + "finger5-1.r_Yrotation", // 293 + "finger5-2.r_Zrotation", // 294 + "finger5-2.r_Xrotation", // 295 + "finger5-2.r_Yrotation", // 296 + "finger5-3.r_Zrotation", // 297 + "finger5-3.r_Xrotation", // 298 + "finger5-3.r_Yrotation", // 299 + "rthumbBase_Zrotation", // 300 + "rthumbBase_Xrotation", // 301 + "rthumbBase_Yrotation", // 302 + "rthumb_Zrotation", // 303 + "rthumb_Xrotation", // 304 + "rthumb_Yrotation", // 305 + "finger1-2.r_Zrotation", // 306 + "finger1-2.r_Xrotation", // 307 + "finger1-2.r_Yrotation", // 308 + "finger1-3.r_Zrotation", // 309 + "finger1-3.r_Xrotation", // 310 + "finger1-3.r_Yrotation", // 311 + "lcollar_Zrotation", // 312 + "lcollar_Xrotation", // 313 + "lcollar_Yrotation", // 314 + "lshoulder_Zrotation", // 315 + "lshoulder_Xrotation", // 316 + "lshoulder_Yrotation", // 317 + "lelbow_Zrotation", // 318 + "lelbow_Xrotation", // 319 + "lelbow_Yrotation", // 320 + "lhand_Zrotation", // 321 + "lhand_Xrotation", // 322 + "lhand_Yrotation", // 323 + "metacarpal1.l_Zrotation", // 324 + "metacarpal1.l_Xrotation", // 325 + "metacarpal1.l_Yrotation", // 326 + "finger2-1.l_Zrotation", // 327 + "finger2-1.l_Xrotation", // 328 + "finger2-1.l_Yrotation", // 329 + "finger2-2.l_Zrotation", // 330 + "finger2-2.l_Xrotation", // 331 + "finger2-2.l_Yrotation", // 332 + "finger2-3.l_Zrotation", // 333 + "finger2-3.l_Xrotation", // 334 + "finger2-3.l_Yrotation", // 335 + "metacarpal2.l_Zrotation", // 336 + "metacarpal2.l_Xrotation", // 337 + "metacarpal2.l_Yrotation", // 338 + "finger3-1.l_Zrotation", // 339 + "finger3-1.l_Xrotation", // 340 + "finger3-1.l_Yrotation", // 341 + "finger3-2.l_Zrotation", // 342 + "finger3-2.l_Xrotation", // 343 + "finger3-2.l_Yrotation", // 344 + "finger3-3.l_Zrotation", // 345 + "finger3-3.l_Xrotation", // 346 + "finger3-3.l_Yrotation", // 347 + "__metacarpal3.l_Zrotation", // 348 + "__metacarpal3.l_Xrotation", // 349 + "__metacarpal3.l_Yrotation", // 350 + "metacarpal3.l_Zrotation", // 351 + "metacarpal3.l_Xrotation", // 352 + "metacarpal3.l_Yrotation", // 353 + "finger4-1.l_Zrotation", // 354 + "finger4-1.l_Xrotation", // 355 + "finger4-1.l_Yrotation", // 356 + "finger4-2.l_Zrotation", // 357 + "finger4-2.l_Xrotation", // 358 + "finger4-2.l_Yrotation", // 359 + "finger4-3.l_Zrotation", // 360 + "finger4-3.l_Xrotation", // 361 + "finger4-3.l_Yrotation", // 362 + "__metacarpal4.l_Zrotation", // 363 + "__metacarpal4.l_Xrotation", // 364 + "__metacarpal4.l_Yrotation", // 365 + "metacarpal4.l_Zrotation", // 366 + "metacarpal4.l_Xrotation", // 367 + "metacarpal4.l_Yrotation", // 368 + "finger5-1.l_Zrotation", // 369 + "finger5-1.l_Xrotation", // 370 + "finger5-1.l_Yrotation", // 371 + "finger5-2.l_Zrotation", // 372 + "finger5-2.l_Xrotation", // 373 + "finger5-2.l_Yrotation", // 374 + "finger5-3.l_Zrotation", // 375 + "finger5-3.l_Xrotation", // 376 + "finger5-3.l_Yrotation", // 377 + "lthumbBase_Zrotation", // 378 + "lthumbBase_Xrotation", // 379 + "lthumbBase_Yrotation", // 380 + "lthumb_Zrotation", // 381 + "lthumb_Xrotation", // 382 + "lthumb_Yrotation", // 383 + "finger1-2.l_Zrotation", // 384 + "finger1-2.l_Xrotation", // 385 + "finger1-2.l_Yrotation", // 386 + "finger1-3.l_Zrotation", // 387 + "finger1-3.l_Xrotation", // 388 + "finger1-3.l_Yrotation", // 389 + "rbuttock_Zrotation", // 390 + "rbuttock_Xrotation", // 391 + "rbuttock_Yrotation", // 392 + "rhip_Zrotation", // 393 + "rhip_Xrotation", // 394 + "rhip_Yrotation", // 395 + "rknee_Zrotation", // 396 + "rknee_Xrotation", // 397 + "rknee_Yrotation", // 398 + "rfoot_Zrotation", // 399 + "rfoot_Xrotation", // 400 + "rfoot_Yrotation", // 401 + "toe1-1.r_Zrotation", // 402 + "toe1-1.r_Xrotation", // 403 + "toe1-1.r_Yrotation", // 404 + "toe1-2.r_Zrotation", // 405 + "toe1-2.r_Xrotation", // 406 + "toe1-2.r_Yrotation", // 407 + "toe2-1.r_Zrotation", // 408 + "toe2-1.r_Xrotation", // 409 + "toe2-1.r_Yrotation", // 410 + "toe2-2.r_Zrotation", // 411 + "toe2-2.r_Xrotation", // 412 + "toe2-2.r_Yrotation", // 413 + "toe2-3.r_Zrotation", // 414 + "toe2-3.r_Xrotation", // 415 + "toe2-3.r_Yrotation", // 416 + "toe3-1.r_Zrotation", // 417 + "toe3-1.r_Xrotation", // 418 + "toe3-1.r_Yrotation", // 419 + "toe3-2.r_Zrotation", // 420 + "toe3-2.r_Xrotation", // 421 + "toe3-2.r_Yrotation", // 422 + "toe3-3.r_Zrotation", // 423 + "toe3-3.r_Xrotation", // 424 + "toe3-3.r_Yrotation", // 425 + "toe4-1.r_Zrotation", // 426 + "toe4-1.r_Xrotation", // 427 + "toe4-1.r_Yrotation", // 428 + "toe4-2.r_Zrotation", // 429 + "toe4-2.r_Xrotation", // 430 + "toe4-2.r_Yrotation", // 431 + "toe4-3.r_Zrotation", // 432 + "toe4-3.r_Xrotation", // 433 + "toe4-3.r_Yrotation", // 434 + "toe5-1.r_Zrotation", // 435 + "toe5-1.r_Xrotation", // 436 + "toe5-1.r_Yrotation", // 437 + "toe5-2.r_Zrotation", // 438 + "toe5-2.r_Xrotation", // 439 + "toe5-2.r_Yrotation", // 440 + "toe5-3.r_Zrotation", // 441 + "toe5-3.r_Xrotation", // 442 + "toe5-3.r_Yrotation", // 443 + "lbuttock_Zrotation", // 444 + "lbuttock_Xrotation", // 445 + "lbuttock_Yrotation", // 446 + "lhip_Zrotation", // 447 + "lhip_Xrotation", // 448 + "lhip_Yrotation", // 449 + "lknee_Zrotation", // 450 + "lknee_Xrotation", // 451 + "lknee_Yrotation", // 452 + "lfoot_Zrotation", // 453 + "lfoot_Xrotation", // 454 + "lfoot_Yrotation", // 455 + "toe1-1.l_Zrotation", // 456 + "toe1-1.l_Xrotation", // 457 + "toe1-1.l_Yrotation", // 458 + "toe1-2.l_Zrotation", // 459 + "toe1-2.l_Xrotation", // 460 + "toe1-2.l_Yrotation", // 461 + "toe2-1.l_Zrotation", // 462 + "toe2-1.l_Xrotation", // 463 + "toe2-1.l_Yrotation", // 464 + "toe2-2.l_Zrotation", // 465 + "toe2-2.l_Xrotation", // 466 + "toe2-2.l_Yrotation", // 467 + "toe2-3.l_Zrotation", // 468 + "toe2-3.l_Xrotation", // 469 + "toe2-3.l_Yrotation", // 470 + "toe3-1.l_Zrotation", // 471 + "toe3-1.l_Xrotation", // 472 + "toe3-1.l_Yrotation", // 473 + "toe3-2.l_Zrotation", // 474 + "toe3-2.l_Xrotation", // 475 + "toe3-2.l_Yrotation", // 476 + "toe3-3.l_Zrotation", // 477 + "toe3-3.l_Xrotation", // 478 + "toe3-3.l_Yrotation", // 479 + "toe4-1.l_Zrotation", // 480 + "toe4-1.l_Xrotation", // 481 + "toe4-1.l_Yrotation", // 482 + "toe4-2.l_Zrotation", // 483 + "toe4-2.l_Xrotation", // 484 + "toe4-2.l_Yrotation", // 485 + "toe4-3.l_Zrotation", // 486 + "toe4-3.l_Xrotation", // 487 + "toe4-3.l_Yrotation", // 488 + "toe5-1.l_Zrotation", // 489 + "toe5-1.l_Xrotation", // 490 + "toe5-1.l_Yrotation", // 491 + "toe5-2.l_Zrotation", // 492 + "toe5-2.l_Xrotation", // 493 + "toe5-2.l_Yrotation", // 494 + "toe5-3.l_Zrotation", // 495 + "toe5-3.l_Xrotation", // 496 + "toe5-3.l_Yrotation" // 497 +}; + + + +/** + * @brief This is a programmer friendly enumerator of joint output extracted from MocapNET. + * Use ./GroundTruthDumper --from dataset/headerWithHeadAndOneMotion.bvh --printc + * to extract this automatically + */ +enum MOCAPNET_Output_Joints +{ +MOCAPNET_OUTPUT_HIP_XPOSITION = 0, +MOCAPNET_OUTPUT_HIP_YPOSITION,//1 +MOCAPNET_OUTPUT_HIP_ZPOSITION,//2 +MOCAPNET_OUTPUT_HIP_ZROTATION,//3 +MOCAPNET_OUTPUT_HIP_YROTATION,//4 +MOCAPNET_OUTPUT_HIP_XROTATION,//5 +MOCAPNET_OUTPUT_ABDOMEN_ZROTATION,//6 +MOCAPNET_OUTPUT_ABDOMEN_XROTATION,//7 +MOCAPNET_OUTPUT_ABDOMEN_YROTATION,//8 +MOCAPNET_OUTPUT_CHEST_ZROTATION,//9 +MOCAPNET_OUTPUT_CHEST_XROTATION,//10 +MOCAPNET_OUTPUT_CHEST_YROTATION,//11 +MOCAPNET_OUTPUT_NECK_ZROTATION,//12 +MOCAPNET_OUTPUT_NECK_XROTATION,//13 +MOCAPNET_OUTPUT_NECK_YROTATION,//14 +MOCAPNET_OUTPUT_NECK1_ZROTATION,//15 +MOCAPNET_OUTPUT_NECK1_XROTATION,//16 +MOCAPNET_OUTPUT_NECK1_YROTATION,//17 +MOCAPNET_OUTPUT_HEAD_ZROTATION,//18 +MOCAPNET_OUTPUT_HEAD_XROTATION,//19 +MOCAPNET_OUTPUT_HEAD_YROTATION,//20 +MOCAPNET_OUTPUT___JAW_ZROTATION,//21 +MOCAPNET_OUTPUT___JAW_XROTATION,//22 +MOCAPNET_OUTPUT___JAW_YROTATION,//23 +MOCAPNET_OUTPUT_JAW_ZROTATION,//24 +MOCAPNET_OUTPUT_JAW_XROTATION,//25 +MOCAPNET_OUTPUT_JAW_YROTATION,//26 +MOCAPNET_OUTPUT_SPECIAL04_ZROTATION,//27 +MOCAPNET_OUTPUT_SPECIAL04_XROTATION,//28 +MOCAPNET_OUTPUT_SPECIAL04_YROTATION,//29 +MOCAPNET_OUTPUT_ORIS02_ZROTATION,//30 +MOCAPNET_OUTPUT_ORIS02_XROTATION,//31 +MOCAPNET_OUTPUT_ORIS02_YROTATION,//32 +MOCAPNET_OUTPUT_ORIS01_ZROTATION,//33 +MOCAPNET_OUTPUT_ORIS01_XROTATION,//34 +MOCAPNET_OUTPUT_ORIS01_YROTATION,//35 +MOCAPNET_OUTPUT_ORIS06_L_ZROTATION,//36 +MOCAPNET_OUTPUT_ORIS06_L_XROTATION,//37 +MOCAPNET_OUTPUT_ORIS06_L_YROTATION,//38 +MOCAPNET_OUTPUT_ORIS07_L_ZROTATION,//39 +MOCAPNET_OUTPUT_ORIS07_L_XROTATION,//40 +MOCAPNET_OUTPUT_ORIS07_L_YROTATION,//41 +MOCAPNET_OUTPUT_ORIS06_R_ZROTATION,//42 +MOCAPNET_OUTPUT_ORIS06_R_XROTATION,//43 +MOCAPNET_OUTPUT_ORIS06_R_YROTATION,//44 +MOCAPNET_OUTPUT_ORIS07_R_ZROTATION,//45 +MOCAPNET_OUTPUT_ORIS07_R_XROTATION,//46 +MOCAPNET_OUTPUT_ORIS07_R_YROTATION,//47 +MOCAPNET_OUTPUT_TONGUE00_ZROTATION,//48 +MOCAPNET_OUTPUT_TONGUE00_XROTATION,//49 +MOCAPNET_OUTPUT_TONGUE00_YROTATION,//50 +MOCAPNET_OUTPUT_TONGUE01_ZROTATION,//51 +MOCAPNET_OUTPUT_TONGUE01_XROTATION,//52 +MOCAPNET_OUTPUT_TONGUE01_YROTATION,//53 +MOCAPNET_OUTPUT_TONGUE02_ZROTATION,//54 +MOCAPNET_OUTPUT_TONGUE02_XROTATION,//55 +MOCAPNET_OUTPUT_TONGUE02_YROTATION,//56 +MOCAPNET_OUTPUT_TONGUE03_ZROTATION,//57 +MOCAPNET_OUTPUT_TONGUE03_XROTATION,//58 +MOCAPNET_OUTPUT_TONGUE03_YROTATION,//59 +MOCAPNET_OUTPUT___TONGUE04_ZROTATION,//60 +MOCAPNET_OUTPUT___TONGUE04_XROTATION,//61 +MOCAPNET_OUTPUT___TONGUE04_YROTATION,//62 +MOCAPNET_OUTPUT_TONGUE04_ZROTATION,//63 +MOCAPNET_OUTPUT_TONGUE04_XROTATION,//64 +MOCAPNET_OUTPUT_TONGUE04_YROTATION,//65 +MOCAPNET_OUTPUT_TONGUE07_L_ZROTATION,//66 +MOCAPNET_OUTPUT_TONGUE07_L_XROTATION,//67 +MOCAPNET_OUTPUT_TONGUE07_L_YROTATION,//68 +MOCAPNET_OUTPUT_TONGUE07_R_ZROTATION,//69 +MOCAPNET_OUTPUT_TONGUE07_R_XROTATION,//70 +MOCAPNET_OUTPUT_TONGUE07_R_YROTATION,//71 +MOCAPNET_OUTPUT_TONGUE06_L_ZROTATION,//72 +MOCAPNET_OUTPUT_TONGUE06_L_XROTATION,//73 +MOCAPNET_OUTPUT_TONGUE06_L_YROTATION,//74 +MOCAPNET_OUTPUT_TONGUE06_R_ZROTATION,//75 +MOCAPNET_OUTPUT_TONGUE06_R_XROTATION,//76 +MOCAPNET_OUTPUT_TONGUE06_R_YROTATION,//77 +MOCAPNET_OUTPUT_TONGUE05_L_ZROTATION,//78 +MOCAPNET_OUTPUT_TONGUE05_L_XROTATION,//79 +MOCAPNET_OUTPUT_TONGUE05_L_YROTATION,//80 +MOCAPNET_OUTPUT_TONGUE05_R_ZROTATION,//81 +MOCAPNET_OUTPUT_TONGUE05_R_XROTATION,//82 +MOCAPNET_OUTPUT_TONGUE05_R_YROTATION,//83 +MOCAPNET_OUTPUT___LEVATOR02_L_ZROTATION,//84 +MOCAPNET_OUTPUT___LEVATOR02_L_XROTATION,//85 +MOCAPNET_OUTPUT___LEVATOR02_L_YROTATION,//86 +MOCAPNET_OUTPUT_LEVATOR02_L_ZROTATION,//87 +MOCAPNET_OUTPUT_LEVATOR02_L_XROTATION,//88 +MOCAPNET_OUTPUT_LEVATOR02_L_YROTATION,//89 +MOCAPNET_OUTPUT_LEVATOR03_L_ZROTATION,//90 +MOCAPNET_OUTPUT_LEVATOR03_L_XROTATION,//91 +MOCAPNET_OUTPUT_LEVATOR03_L_YROTATION,//92 +MOCAPNET_OUTPUT_LEVATOR04_L_ZROTATION,//93 +MOCAPNET_OUTPUT_LEVATOR04_L_XROTATION,//94 +MOCAPNET_OUTPUT_LEVATOR04_L_YROTATION,//95 +MOCAPNET_OUTPUT_LEVATOR05_L_ZROTATION,//96 +MOCAPNET_OUTPUT_LEVATOR05_L_XROTATION,//97 +MOCAPNET_OUTPUT_LEVATOR05_L_YROTATION,//98 +MOCAPNET_OUTPUT___LEVATOR02_R_ZROTATION,//99 +MOCAPNET_OUTPUT___LEVATOR02_R_XROTATION,//100 +MOCAPNET_OUTPUT___LEVATOR02_R_YROTATION,//101 +MOCAPNET_OUTPUT_LEVATOR02_R_ZROTATION,//102 +MOCAPNET_OUTPUT_LEVATOR02_R_XROTATION,//103 +MOCAPNET_OUTPUT_LEVATOR02_R_YROTATION,//104 +MOCAPNET_OUTPUT_LEVATOR03_R_ZROTATION,//105 +MOCAPNET_OUTPUT_LEVATOR03_R_XROTATION,//106 +MOCAPNET_OUTPUT_LEVATOR03_R_YROTATION,//107 +MOCAPNET_OUTPUT_LEVATOR04_R_ZROTATION,//108 +MOCAPNET_OUTPUT_LEVATOR04_R_XROTATION,//109 +MOCAPNET_OUTPUT_LEVATOR04_R_YROTATION,//110 +MOCAPNET_OUTPUT_LEVATOR05_R_ZROTATION,//111 +MOCAPNET_OUTPUT_LEVATOR05_R_XROTATION,//112 +MOCAPNET_OUTPUT_LEVATOR05_R_YROTATION,//113 +MOCAPNET_OUTPUT___SPECIAL01_ZROTATION,//114 +MOCAPNET_OUTPUT___SPECIAL01_XROTATION,//115 +MOCAPNET_OUTPUT___SPECIAL01_YROTATION,//116 +MOCAPNET_OUTPUT_SPECIAL01_ZROTATION,//117 +MOCAPNET_OUTPUT_SPECIAL01_XROTATION,//118 +MOCAPNET_OUTPUT_SPECIAL01_YROTATION,//119 +MOCAPNET_OUTPUT_ORIS04_L_ZROTATION,//120 +MOCAPNET_OUTPUT_ORIS04_L_XROTATION,//121 +MOCAPNET_OUTPUT_ORIS04_L_YROTATION,//122 +MOCAPNET_OUTPUT_ORIS03_L_ZROTATION,//123 +MOCAPNET_OUTPUT_ORIS03_L_XROTATION,//124 +MOCAPNET_OUTPUT_ORIS03_L_YROTATION,//125 +MOCAPNET_OUTPUT_ORIS04_R_ZROTATION,//126 +MOCAPNET_OUTPUT_ORIS04_R_XROTATION,//127 +MOCAPNET_OUTPUT_ORIS04_R_YROTATION,//128 +MOCAPNET_OUTPUT_ORIS03_R_ZROTATION,//129 +MOCAPNET_OUTPUT_ORIS03_R_XROTATION,//130 +MOCAPNET_OUTPUT_ORIS03_R_YROTATION,//131 +MOCAPNET_OUTPUT_ORIS06_ZROTATION,//132 +MOCAPNET_OUTPUT_ORIS06_XROTATION,//133 +MOCAPNET_OUTPUT_ORIS06_YROTATION,//134 +MOCAPNET_OUTPUT_ORIS05_ZROTATION,//135 +MOCAPNET_OUTPUT_ORIS05_XROTATION,//136 +MOCAPNET_OUTPUT_ORIS05_YROTATION,//137 +MOCAPNET_OUTPUT___SPECIAL03_ZROTATION,//138 +MOCAPNET_OUTPUT___SPECIAL03_XROTATION,//139 +MOCAPNET_OUTPUT___SPECIAL03_YROTATION,//140 +MOCAPNET_OUTPUT_SPECIAL03_ZROTATION,//141 +MOCAPNET_OUTPUT_SPECIAL03_XROTATION,//142 +MOCAPNET_OUTPUT_SPECIAL03_YROTATION,//143 +MOCAPNET_OUTPUT___LEVATOR06_L_ZROTATION,//144 +MOCAPNET_OUTPUT___LEVATOR06_L_XROTATION,//145 +MOCAPNET_OUTPUT___LEVATOR06_L_YROTATION,//146 +MOCAPNET_OUTPUT_LEVATOR06_L_ZROTATION,//147 +MOCAPNET_OUTPUT_LEVATOR06_L_XROTATION,//148 +MOCAPNET_OUTPUT_LEVATOR06_L_YROTATION,//149 +MOCAPNET_OUTPUT___LEVATOR06_R_ZROTATION,//150 +MOCAPNET_OUTPUT___LEVATOR06_R_XROTATION,//151 +MOCAPNET_OUTPUT___LEVATOR06_R_YROTATION,//152 +MOCAPNET_OUTPUT_LEVATOR06_R_ZROTATION,//153 +MOCAPNET_OUTPUT_LEVATOR06_R_XROTATION,//154 +MOCAPNET_OUTPUT_LEVATOR06_R_YROTATION,//155 +MOCAPNET_OUTPUT_SPECIAL06_L_ZROTATION,//156 +MOCAPNET_OUTPUT_SPECIAL06_L_XROTATION,//157 +MOCAPNET_OUTPUT_SPECIAL06_L_YROTATION,//158 +MOCAPNET_OUTPUT_SPECIAL05_L_ZROTATION,//159 +MOCAPNET_OUTPUT_SPECIAL05_L_XROTATION,//160 +MOCAPNET_OUTPUT_SPECIAL05_L_YROTATION,//161 +MOCAPNET_OUTPUT_EYE_L_ZROTATION,//162 +MOCAPNET_OUTPUT_EYE_L_XROTATION,//163 +MOCAPNET_OUTPUT_EYE_L_YROTATION,//164 +MOCAPNET_OUTPUT_ORBICULARIS03_L_ZROTATION,//165 +MOCAPNET_OUTPUT_ORBICULARIS03_L_XROTATION,//166 +MOCAPNET_OUTPUT_ORBICULARIS03_L_YROTATION,//167 +MOCAPNET_OUTPUT_ORBICULARIS04_L_ZROTATION,//168 +MOCAPNET_OUTPUT_ORBICULARIS04_L_XROTATION,//169 +MOCAPNET_OUTPUT_ORBICULARIS04_L_YROTATION,//170 +MOCAPNET_OUTPUT_SPECIAL06_R_ZROTATION,//171 +MOCAPNET_OUTPUT_SPECIAL06_R_XROTATION,//172 +MOCAPNET_OUTPUT_SPECIAL06_R_YROTATION,//173 +MOCAPNET_OUTPUT_SPECIAL05_R_ZROTATION,//174 +MOCAPNET_OUTPUT_SPECIAL05_R_XROTATION,//175 +MOCAPNET_OUTPUT_SPECIAL05_R_YROTATION,//176 +MOCAPNET_OUTPUT_EYE_R_ZROTATION,//177 +MOCAPNET_OUTPUT_EYE_R_XROTATION,//178 +MOCAPNET_OUTPUT_EYE_R_YROTATION,//179 +MOCAPNET_OUTPUT_ORBICULARIS03_R_ZROTATION,//180 +MOCAPNET_OUTPUT_ORBICULARIS03_R_XROTATION,//181 +MOCAPNET_OUTPUT_ORBICULARIS03_R_YROTATION,//182 +MOCAPNET_OUTPUT_ORBICULARIS04_R_ZROTATION,//183 +MOCAPNET_OUTPUT_ORBICULARIS04_R_XROTATION,//184 +MOCAPNET_OUTPUT_ORBICULARIS04_R_YROTATION,//185 +MOCAPNET_OUTPUT___TEMPORALIS01_L_ZROTATION,//186 +MOCAPNET_OUTPUT___TEMPORALIS01_L_XROTATION,//187 +MOCAPNET_OUTPUT___TEMPORALIS01_L_YROTATION,//188 +MOCAPNET_OUTPUT_TEMPORALIS01_L_ZROTATION,//189 +MOCAPNET_OUTPUT_TEMPORALIS01_L_XROTATION,//190 +MOCAPNET_OUTPUT_TEMPORALIS01_L_YROTATION,//191 +MOCAPNET_OUTPUT_OCULI02_L_ZROTATION,//192 +MOCAPNET_OUTPUT_OCULI02_L_XROTATION,//193 +MOCAPNET_OUTPUT_OCULI02_L_YROTATION,//194 +MOCAPNET_OUTPUT_OCULI01_L_ZROTATION,//195 +MOCAPNET_OUTPUT_OCULI01_L_XROTATION,//196 +MOCAPNET_OUTPUT_OCULI01_L_YROTATION,//197 +MOCAPNET_OUTPUT___TEMPORALIS01_R_ZROTATION,//198 +MOCAPNET_OUTPUT___TEMPORALIS01_R_XROTATION,//199 +MOCAPNET_OUTPUT___TEMPORALIS01_R_YROTATION,//200 +MOCAPNET_OUTPUT_TEMPORALIS01_R_ZROTATION,//201 +MOCAPNET_OUTPUT_TEMPORALIS01_R_XROTATION,//202 +MOCAPNET_OUTPUT_TEMPORALIS01_R_YROTATION,//203 +MOCAPNET_OUTPUT_OCULI02_R_ZROTATION,//204 +MOCAPNET_OUTPUT_OCULI02_R_XROTATION,//205 +MOCAPNET_OUTPUT_OCULI02_R_YROTATION,//206 +MOCAPNET_OUTPUT_OCULI01_R_ZROTATION,//207 +MOCAPNET_OUTPUT_OCULI01_R_XROTATION,//208 +MOCAPNET_OUTPUT_OCULI01_R_YROTATION,//209 +MOCAPNET_OUTPUT___TEMPORALIS02_L_ZROTATION,//210 +MOCAPNET_OUTPUT___TEMPORALIS02_L_XROTATION,//211 +MOCAPNET_OUTPUT___TEMPORALIS02_L_YROTATION,//212 +MOCAPNET_OUTPUT_TEMPORALIS02_L_ZROTATION,//213 +MOCAPNET_OUTPUT_TEMPORALIS02_L_XROTATION,//214 +MOCAPNET_OUTPUT_TEMPORALIS02_L_YROTATION,//215 +MOCAPNET_OUTPUT_RISORIUS02_L_ZROTATION,//216 +MOCAPNET_OUTPUT_RISORIUS02_L_XROTATION,//217 +MOCAPNET_OUTPUT_RISORIUS02_L_YROTATION,//218 +MOCAPNET_OUTPUT_RISORIUS03_L_ZROTATION,//219 +MOCAPNET_OUTPUT_RISORIUS03_L_XROTATION,//220 +MOCAPNET_OUTPUT_RISORIUS03_L_YROTATION,//221 +MOCAPNET_OUTPUT___TEMPORALIS02_R_ZROTATION,//222 +MOCAPNET_OUTPUT___TEMPORALIS02_R_XROTATION,//223 +MOCAPNET_OUTPUT___TEMPORALIS02_R_YROTATION,//224 +MOCAPNET_OUTPUT_TEMPORALIS02_R_ZROTATION,//225 +MOCAPNET_OUTPUT_TEMPORALIS02_R_XROTATION,//226 +MOCAPNET_OUTPUT_TEMPORALIS02_R_YROTATION,//227 +MOCAPNET_OUTPUT_RISORIUS02_R_ZROTATION,//228 +MOCAPNET_OUTPUT_RISORIUS02_R_XROTATION,//229 +MOCAPNET_OUTPUT_RISORIUS02_R_YROTATION,//230 +MOCAPNET_OUTPUT_RISORIUS03_R_ZROTATION,//231 +MOCAPNET_OUTPUT_RISORIUS03_R_XROTATION,//232 +MOCAPNET_OUTPUT_RISORIUS03_R_YROTATION,//233 +MOCAPNET_OUTPUT_RCOLLAR_ZROTATION,//234 +MOCAPNET_OUTPUT_RCOLLAR_XROTATION,//235 +MOCAPNET_OUTPUT_RCOLLAR_YROTATION,//236 +MOCAPNET_OUTPUT_RSHOULDER_ZROTATION,//237 +MOCAPNET_OUTPUT_RSHOULDER_XROTATION,//238 +MOCAPNET_OUTPUT_RSHOULDER_YROTATION,//239 +MOCAPNET_OUTPUT_RELBOW_ZROTATION,//240 +MOCAPNET_OUTPUT_RELBOW_XROTATION,//241 +MOCAPNET_OUTPUT_RELBOW_YROTATION,//242 +MOCAPNET_OUTPUT_RHAND_ZROTATION,//243 +MOCAPNET_OUTPUT_RHAND_XROTATION,//244 +MOCAPNET_OUTPUT_RHAND_YROTATION,//245 +MOCAPNET_OUTPUT_METACARPAL1_R_ZROTATION,//246 +MOCAPNET_OUTPUT_METACARPAL1_R_XROTATION,//247 +MOCAPNET_OUTPUT_METACARPAL1_R_YROTATION,//248 +MOCAPNET_OUTPUT_FINGER2_1_R_ZROTATION,//249 +MOCAPNET_OUTPUT_FINGER2_1_R_XROTATION,//250 +MOCAPNET_OUTPUT_FINGER2_1_R_YROTATION,//251 +MOCAPNET_OUTPUT_FINGER2_2_R_ZROTATION,//252 +MOCAPNET_OUTPUT_FINGER2_2_R_XROTATION,//253 +MOCAPNET_OUTPUT_FINGER2_2_R_YROTATION,//254 +MOCAPNET_OUTPUT_FINGER2_3_R_ZROTATION,//255 +MOCAPNET_OUTPUT_FINGER2_3_R_XROTATION,//256 +MOCAPNET_OUTPUT_FINGER2_3_R_YROTATION,//257 +MOCAPNET_OUTPUT_METACARPAL2_R_ZROTATION,//258 +MOCAPNET_OUTPUT_METACARPAL2_R_XROTATION,//259 +MOCAPNET_OUTPUT_METACARPAL2_R_YROTATION,//260 +MOCAPNET_OUTPUT_FINGER3_1_R_ZROTATION,//261 +MOCAPNET_OUTPUT_FINGER3_1_R_XROTATION,//262 +MOCAPNET_OUTPUT_FINGER3_1_R_YROTATION,//263 +MOCAPNET_OUTPUT_FINGER3_2_R_ZROTATION,//264 +MOCAPNET_OUTPUT_FINGER3_2_R_XROTATION,//265 +MOCAPNET_OUTPUT_FINGER3_2_R_YROTATION,//266 +MOCAPNET_OUTPUT_FINGER3_3_R_ZROTATION,//267 +MOCAPNET_OUTPUT_FINGER3_3_R_XROTATION,//268 +MOCAPNET_OUTPUT_FINGER3_3_R_YROTATION,//269 +MOCAPNET_OUTPUT___METACARPAL3_R_ZROTATION,//270 +MOCAPNET_OUTPUT___METACARPAL3_R_XROTATION,//271 +MOCAPNET_OUTPUT___METACARPAL3_R_YROTATION,//272 +MOCAPNET_OUTPUT_METACARPAL3_R_ZROTATION,//273 +MOCAPNET_OUTPUT_METACARPAL3_R_XROTATION,//274 +MOCAPNET_OUTPUT_METACARPAL3_R_YROTATION,//275 +MOCAPNET_OUTPUT_FINGER4_1_R_ZROTATION,//276 +MOCAPNET_OUTPUT_FINGER4_1_R_XROTATION,//277 +MOCAPNET_OUTPUT_FINGER4_1_R_YROTATION,//278 +MOCAPNET_OUTPUT_FINGER4_2_R_ZROTATION,//279 +MOCAPNET_OUTPUT_FINGER4_2_R_XROTATION,//280 +MOCAPNET_OUTPUT_FINGER4_2_R_YROTATION,//281 +MOCAPNET_OUTPUT_FINGER4_3_R_ZROTATION,//282 +MOCAPNET_OUTPUT_FINGER4_3_R_XROTATION,//283 +MOCAPNET_OUTPUT_FINGER4_3_R_YROTATION,//284 +MOCAPNET_OUTPUT___METACARPAL4_R_ZROTATION,//285 +MOCAPNET_OUTPUT___METACARPAL4_R_XROTATION,//286 +MOCAPNET_OUTPUT___METACARPAL4_R_YROTATION,//287 +MOCAPNET_OUTPUT_METACARPAL4_R_ZROTATION,//288 +MOCAPNET_OUTPUT_METACARPAL4_R_XROTATION,//289 +MOCAPNET_OUTPUT_METACARPAL4_R_YROTATION,//290 +MOCAPNET_OUTPUT_FINGER5_1_R_ZROTATION,//291 +MOCAPNET_OUTPUT_FINGER5_1_R_XROTATION,//292 +MOCAPNET_OUTPUT_FINGER5_1_R_YROTATION,//293 +MOCAPNET_OUTPUT_FINGER5_2_R_ZROTATION,//294 +MOCAPNET_OUTPUT_FINGER5_2_R_XROTATION,//295 +MOCAPNET_OUTPUT_FINGER5_2_R_YROTATION,//296 +MOCAPNET_OUTPUT_FINGER5_3_R_ZROTATION,//297 +MOCAPNET_OUTPUT_FINGER5_3_R_XROTATION,//298 +MOCAPNET_OUTPUT_FINGER5_3_R_YROTATION,//299 +MOCAPNET_OUTPUT_RTHUMBBASE_ZROTATION,//300 +MOCAPNET_OUTPUT_RTHUMBBASE_XROTATION,//301 +MOCAPNET_OUTPUT_RTHUMBBASE_YROTATION,//302 +MOCAPNET_OUTPUT_RTHUMB_ZROTATION,//303 +MOCAPNET_OUTPUT_RTHUMB_XROTATION,//304 +MOCAPNET_OUTPUT_RTHUMB_YROTATION,//305 +MOCAPNET_OUTPUT_FINGER1_2_R_ZROTATION,//306 +MOCAPNET_OUTPUT_FINGER1_2_R_XROTATION,//307 +MOCAPNET_OUTPUT_FINGER1_2_R_YROTATION,//308 +MOCAPNET_OUTPUT_FINGER1_3_R_ZROTATION,//309 +MOCAPNET_OUTPUT_FINGER1_3_R_XROTATION,//310 +MOCAPNET_OUTPUT_FINGER1_3_R_YROTATION,//311 +MOCAPNET_OUTPUT_LCOLLAR_ZROTATION,//312 +MOCAPNET_OUTPUT_LCOLLAR_XROTATION,//313 +MOCAPNET_OUTPUT_LCOLLAR_YROTATION,//314 +MOCAPNET_OUTPUT_LSHOULDER_ZROTATION,//315 +MOCAPNET_OUTPUT_LSHOULDER_XROTATION,//316 +MOCAPNET_OUTPUT_LSHOULDER_YROTATION,//317 +MOCAPNET_OUTPUT_LELBOW_ZROTATION,//318 +MOCAPNET_OUTPUT_LELBOW_XROTATION,//319 +MOCAPNET_OUTPUT_LELBOW_YROTATION,//320 +MOCAPNET_OUTPUT_LHAND_ZROTATION,//321 +MOCAPNET_OUTPUT_LHAND_XROTATION,//322 +MOCAPNET_OUTPUT_LHAND_YROTATION,//323 +MOCAPNET_OUTPUT_METACARPAL1_L_ZROTATION,//324 +MOCAPNET_OUTPUT_METACARPAL1_L_XROTATION,//325 +MOCAPNET_OUTPUT_METACARPAL1_L_YROTATION,//326 +MOCAPNET_OUTPUT_FINGER2_1_L_ZROTATION,//327 +MOCAPNET_OUTPUT_FINGER2_1_L_XROTATION,//328 +MOCAPNET_OUTPUT_FINGER2_1_L_YROTATION,//329 +MOCAPNET_OUTPUT_FINGER2_2_L_ZROTATION,//330 +MOCAPNET_OUTPUT_FINGER2_2_L_XROTATION,//331 +MOCAPNET_OUTPUT_FINGER2_2_L_YROTATION,//332 +MOCAPNET_OUTPUT_FINGER2_3_L_ZROTATION,//333 +MOCAPNET_OUTPUT_FINGER2_3_L_XROTATION,//334 +MOCAPNET_OUTPUT_FINGER2_3_L_YROTATION,//335 +MOCAPNET_OUTPUT_METACARPAL2_L_ZROTATION,//336 +MOCAPNET_OUTPUT_METACARPAL2_L_XROTATION,//337 +MOCAPNET_OUTPUT_METACARPAL2_L_YROTATION,//338 +MOCAPNET_OUTPUT_FINGER3_1_L_ZROTATION,//339 +MOCAPNET_OUTPUT_FINGER3_1_L_XROTATION,//340 +MOCAPNET_OUTPUT_FINGER3_1_L_YROTATION,//341 +MOCAPNET_OUTPUT_FINGER3_2_L_ZROTATION,//342 +MOCAPNET_OUTPUT_FINGER3_2_L_XROTATION,//343 +MOCAPNET_OUTPUT_FINGER3_2_L_YROTATION,//344 +MOCAPNET_OUTPUT_FINGER3_3_L_ZROTATION,//345 +MOCAPNET_OUTPUT_FINGER3_3_L_XROTATION,//346 +MOCAPNET_OUTPUT_FINGER3_3_L_YROTATION,//347 +MOCAPNET_OUTPUT___METACARPAL3_L_ZROTATION,//348 +MOCAPNET_OUTPUT___METACARPAL3_L_XROTATION,//349 +MOCAPNET_OUTPUT___METACARPAL3_L_YROTATION,//350 +MOCAPNET_OUTPUT_METACARPAL3_L_ZROTATION,//351 +MOCAPNET_OUTPUT_METACARPAL3_L_XROTATION,//352 +MOCAPNET_OUTPUT_METACARPAL3_L_YROTATION,//353 +MOCAPNET_OUTPUT_FINGER4_1_L_ZROTATION,//354 +MOCAPNET_OUTPUT_FINGER4_1_L_XROTATION,//355 +MOCAPNET_OUTPUT_FINGER4_1_L_YROTATION,//356 +MOCAPNET_OUTPUT_FINGER4_2_L_ZROTATION,//357 +MOCAPNET_OUTPUT_FINGER4_2_L_XROTATION,//358 +MOCAPNET_OUTPUT_FINGER4_2_L_YROTATION,//359 +MOCAPNET_OUTPUT_FINGER4_3_L_ZROTATION,//360 +MOCAPNET_OUTPUT_FINGER4_3_L_XROTATION,//361 +MOCAPNET_OUTPUT_FINGER4_3_L_YROTATION,//362 +MOCAPNET_OUTPUT___METACARPAL4_L_ZROTATION,//363 +MOCAPNET_OUTPUT___METACARPAL4_L_XROTATION,//364 +MOCAPNET_OUTPUT___METACARPAL4_L_YROTATION,//365 +MOCAPNET_OUTPUT_METACARPAL4_L_ZROTATION,//366 +MOCAPNET_OUTPUT_METACARPAL4_L_XROTATION,//367 +MOCAPNET_OUTPUT_METACARPAL4_L_YROTATION,//368 +MOCAPNET_OUTPUT_FINGER5_1_L_ZROTATION,//369 +MOCAPNET_OUTPUT_FINGER5_1_L_XROTATION,//370 +MOCAPNET_OUTPUT_FINGER5_1_L_YROTATION,//371 +MOCAPNET_OUTPUT_FINGER5_2_L_ZROTATION,//372 +MOCAPNET_OUTPUT_FINGER5_2_L_XROTATION,//373 +MOCAPNET_OUTPUT_FINGER5_2_L_YROTATION,//374 +MOCAPNET_OUTPUT_FINGER5_3_L_ZROTATION,//375 +MOCAPNET_OUTPUT_FINGER5_3_L_XROTATION,//376 +MOCAPNET_OUTPUT_FINGER5_3_L_YROTATION,//377 +MOCAPNET_OUTPUT_LTHUMBBASE_ZROTATION,//378 +MOCAPNET_OUTPUT_LTHUMBBASE_XROTATION,//379 +MOCAPNET_OUTPUT_LTHUMBBASE_YROTATION,//380 +MOCAPNET_OUTPUT_LTHUMB_ZROTATION,//381 +MOCAPNET_OUTPUT_LTHUMB_XROTATION,//382 +MOCAPNET_OUTPUT_LTHUMB_YROTATION,//383 +MOCAPNET_OUTPUT_FINGER1_2_L_ZROTATION,//384 +MOCAPNET_OUTPUT_FINGER1_2_L_XROTATION,//385 +MOCAPNET_OUTPUT_FINGER1_2_L_YROTATION,//386 +MOCAPNET_OUTPUT_FINGER1_3_L_ZROTATION,//387 +MOCAPNET_OUTPUT_FINGER1_3_L_XROTATION,//388 +MOCAPNET_OUTPUT_FINGER1_3_L_YROTATION,//389 +MOCAPNET_OUTPUT_RBUTTOCK_ZROTATION,//390 +MOCAPNET_OUTPUT_RBUTTOCK_XROTATION,//391 +MOCAPNET_OUTPUT_RBUTTOCK_YROTATION,//392 +MOCAPNET_OUTPUT_RHIP_ZROTATION,//393 +MOCAPNET_OUTPUT_RHIP_XROTATION,//394 +MOCAPNET_OUTPUT_RHIP_YROTATION,//395 +MOCAPNET_OUTPUT_RKNEE_ZROTATION,//396 +MOCAPNET_OUTPUT_RKNEE_XROTATION,//397 +MOCAPNET_OUTPUT_RKNEE_YROTATION,//398 +MOCAPNET_OUTPUT_RFOOT_ZROTATION,//399 +MOCAPNET_OUTPUT_RFOOT_XROTATION,//400 +MOCAPNET_OUTPUT_RFOOT_YROTATION,//401 +MOCAPNET_OUTPUT_TOE1_1_R_ZROTATION,//402 +MOCAPNET_OUTPUT_TOE1_1_R_XROTATION,//403 +MOCAPNET_OUTPUT_TOE1_1_R_YROTATION,//404 +MOCAPNET_OUTPUT_TOE1_2_R_ZROTATION,//405 +MOCAPNET_OUTPUT_TOE1_2_R_XROTATION,//406 +MOCAPNET_OUTPUT_TOE1_2_R_YROTATION,//407 +MOCAPNET_OUTPUT_TOE2_1_R_ZROTATION,//408 +MOCAPNET_OUTPUT_TOE2_1_R_XROTATION,//409 +MOCAPNET_OUTPUT_TOE2_1_R_YROTATION,//410 +MOCAPNET_OUTPUT_TOE2_2_R_ZROTATION,//411 +MOCAPNET_OUTPUT_TOE2_2_R_XROTATION,//412 +MOCAPNET_OUTPUT_TOE2_2_R_YROTATION,//413 +MOCAPNET_OUTPUT_TOE2_3_R_ZROTATION,//414 +MOCAPNET_OUTPUT_TOE2_3_R_XROTATION,//415 +MOCAPNET_OUTPUT_TOE2_3_R_YROTATION,//416 +MOCAPNET_OUTPUT_TOE3_1_R_ZROTATION,//417 +MOCAPNET_OUTPUT_TOE3_1_R_XROTATION,//418 +MOCAPNET_OUTPUT_TOE3_1_R_YROTATION,//419 +MOCAPNET_OUTPUT_TOE3_2_R_ZROTATION,//420 +MOCAPNET_OUTPUT_TOE3_2_R_XROTATION,//421 +MOCAPNET_OUTPUT_TOE3_2_R_YROTATION,//422 +MOCAPNET_OUTPUT_TOE3_3_R_ZROTATION,//423 +MOCAPNET_OUTPUT_TOE3_3_R_XROTATION,//424 +MOCAPNET_OUTPUT_TOE3_3_R_YROTATION,//425 +MOCAPNET_OUTPUT_TOE4_1_R_ZROTATION,//426 +MOCAPNET_OUTPUT_TOE4_1_R_XROTATION,//427 +MOCAPNET_OUTPUT_TOE4_1_R_YROTATION,//428 +MOCAPNET_OUTPUT_TOE4_2_R_ZROTATION,//429 +MOCAPNET_OUTPUT_TOE4_2_R_XROTATION,//430 +MOCAPNET_OUTPUT_TOE4_2_R_YROTATION,//431 +MOCAPNET_OUTPUT_TOE4_3_R_ZROTATION,//432 +MOCAPNET_OUTPUT_TOE4_3_R_XROTATION,//433 +MOCAPNET_OUTPUT_TOE4_3_R_YROTATION,//434 +MOCAPNET_OUTPUT_TOE5_1_R_ZROTATION,//435 +MOCAPNET_OUTPUT_TOE5_1_R_XROTATION,//436 +MOCAPNET_OUTPUT_TOE5_1_R_YROTATION,//437 +MOCAPNET_OUTPUT_TOE5_2_R_ZROTATION,//438 +MOCAPNET_OUTPUT_TOE5_2_R_XROTATION,//439 +MOCAPNET_OUTPUT_TOE5_2_R_YROTATION,//440 +MOCAPNET_OUTPUT_TOE5_3_R_ZROTATION,//441 +MOCAPNET_OUTPUT_TOE5_3_R_XROTATION,//442 +MOCAPNET_OUTPUT_TOE5_3_R_YROTATION,//443 +MOCAPNET_OUTPUT_LBUTTOCK_ZROTATION,//444 +MOCAPNET_OUTPUT_LBUTTOCK_XROTATION,//445 +MOCAPNET_OUTPUT_LBUTTOCK_YROTATION,//446 +MOCAPNET_OUTPUT_LHIP_ZROTATION,//447 +MOCAPNET_OUTPUT_LHIP_XROTATION,//448 +MOCAPNET_OUTPUT_LHIP_YROTATION,//449 +MOCAPNET_OUTPUT_LKNEE_ZROTATION,//450 +MOCAPNET_OUTPUT_LKNEE_XROTATION,//451 +MOCAPNET_OUTPUT_LKNEE_YROTATION,//452 +MOCAPNET_OUTPUT_LFOOT_ZROTATION,//453 +MOCAPNET_OUTPUT_LFOOT_XROTATION,//454 +MOCAPNET_OUTPUT_LFOOT_YROTATION,//455 +MOCAPNET_OUTPUT_TOE1_1_L_ZROTATION,//456 +MOCAPNET_OUTPUT_TOE1_1_L_XROTATION,//457 +MOCAPNET_OUTPUT_TOE1_1_L_YROTATION,//458 +MOCAPNET_OUTPUT_TOE1_2_L_ZROTATION,//459 +MOCAPNET_OUTPUT_TOE1_2_L_XROTATION,//460 +MOCAPNET_OUTPUT_TOE1_2_L_YROTATION,//461 +MOCAPNET_OUTPUT_TOE2_1_L_ZROTATION,//462 +MOCAPNET_OUTPUT_TOE2_1_L_XROTATION,//463 +MOCAPNET_OUTPUT_TOE2_1_L_YROTATION,//464 +MOCAPNET_OUTPUT_TOE2_2_L_ZROTATION,//465 +MOCAPNET_OUTPUT_TOE2_2_L_XROTATION,//466 +MOCAPNET_OUTPUT_TOE2_2_L_YROTATION,//467 +MOCAPNET_OUTPUT_TOE2_3_L_ZROTATION,//468 +MOCAPNET_OUTPUT_TOE2_3_L_XROTATION,//469 +MOCAPNET_OUTPUT_TOE2_3_L_YROTATION,//470 +MOCAPNET_OUTPUT_TOE3_1_L_ZROTATION,//471 +MOCAPNET_OUTPUT_TOE3_1_L_XROTATION,//472 +MOCAPNET_OUTPUT_TOE3_1_L_YROTATION,//473 +MOCAPNET_OUTPUT_TOE3_2_L_ZROTATION,//474 +MOCAPNET_OUTPUT_TOE3_2_L_XROTATION,//475 +MOCAPNET_OUTPUT_TOE3_2_L_YROTATION,//476 +MOCAPNET_OUTPUT_TOE3_3_L_ZROTATION,//477 +MOCAPNET_OUTPUT_TOE3_3_L_XROTATION,//478 +MOCAPNET_OUTPUT_TOE3_3_L_YROTATION,//479 +MOCAPNET_OUTPUT_TOE4_1_L_ZROTATION,//480 +MOCAPNET_OUTPUT_TOE4_1_L_XROTATION,//481 +MOCAPNET_OUTPUT_TOE4_1_L_YROTATION,//482 +MOCAPNET_OUTPUT_TOE4_2_L_ZROTATION,//483 +MOCAPNET_OUTPUT_TOE4_2_L_XROTATION,//484 +MOCAPNET_OUTPUT_TOE4_2_L_YROTATION,//485 +MOCAPNET_OUTPUT_TOE4_3_L_ZROTATION,//486 +MOCAPNET_OUTPUT_TOE4_3_L_XROTATION,//487 +MOCAPNET_OUTPUT_TOE4_3_L_YROTATION,//488 +MOCAPNET_OUTPUT_TOE5_1_L_ZROTATION,//489 +MOCAPNET_OUTPUT_TOE5_1_L_XROTATION,//490 +MOCAPNET_OUTPUT_TOE5_1_L_YROTATION,//491 +MOCAPNET_OUTPUT_TOE5_2_L_ZROTATION,//492 +MOCAPNET_OUTPUT_TOE5_2_L_XROTATION,//493 +MOCAPNET_OUTPUT_TOE5_2_L_YROTATION,//494 +MOCAPNET_OUTPUT_TOE5_3_L_ZROTATION,//495 +MOCAPNET_OUTPUT_TOE5_3_L_XROTATION,//496 +MOCAPNET_OUTPUT_TOE5_3_L_YROTATION,//497 +//----------------------------- +MOCAPNET_OUTPUT_NUMBER +}; + + + + + + + + + +/** + * @brief This is a programmer friendly enumerator to access 3D output extracted from MocapNET + * Use ./GroundTruthDumper --from dataset/headerWithHeadAndOneMotion.bvh --printc + * to extract this automatically + */ +enum MNET_3D_Output_Joints +{ +MOCAPNET_3DPOINT_HIPX,//0 +MOCAPNET_3DPOINT_HIPY,//1 +MOCAPNET_3DPOINT_HIPZ,//2 +MOCAPNET_3DPOINT_ABDOMENX,//3 +MOCAPNET_3DPOINT_ABDOMENY,//4 +MOCAPNET_3DPOINT_ABDOMENZ,//5 +MOCAPNET_3DPOINT_CHESTX,//6 +MOCAPNET_3DPOINT_CHESTY,//7 +MOCAPNET_3DPOINT_CHESTZ,//8 +MOCAPNET_3DPOINT_NECKX,//9 +MOCAPNET_3DPOINT_NECKY,//10 +MOCAPNET_3DPOINT_NECKZ,//11 +MOCAPNET_3DPOINT_NECK1X,//12 +MOCAPNET_3DPOINT_NECK1Y,//13 +MOCAPNET_3DPOINT_NECK1Z,//14 +MOCAPNET_3DPOINT_HEADX,//15 +MOCAPNET_3DPOINT_HEADY,//16 +MOCAPNET_3DPOINT_HEADZ,//17 +MOCAPNET_3DPOINT___JAWX,//18 +MOCAPNET_3DPOINT___JAWY,//19 +MOCAPNET_3DPOINT___JAWZ,//20 +MOCAPNET_3DPOINT_JAWX,//21 +MOCAPNET_3DPOINT_JAWY,//22 +MOCAPNET_3DPOINT_JAWZ,//23 +MOCAPNET_3DPOINT_SPECIAL04X,//24 +MOCAPNET_3DPOINT_SPECIAL04Y,//25 +MOCAPNET_3DPOINT_SPECIAL04Z,//26 +MOCAPNET_3DPOINT_ORIS02X,//27 +MOCAPNET_3DPOINT_ORIS02Y,//28 +MOCAPNET_3DPOINT_ORIS02Z,//29 +MOCAPNET_3DPOINT_ORIS01X,//30 +MOCAPNET_3DPOINT_ORIS01Y,//31 +MOCAPNET_3DPOINT_ORIS01Z,//32 +MOCAPNET_3DPOINT_ENDSITE_ORIS01X,//33 +MOCAPNET_3DPOINT_ENDSITE_ORIS01Y,//34 +MOCAPNET_3DPOINT_ENDSITE_ORIS01Z,//35 +MOCAPNET_3DPOINT_ORIS06_LX,//36 +MOCAPNET_3DPOINT_ORIS06_LY,//37 +MOCAPNET_3DPOINT_ORIS06_LZ,//38 +MOCAPNET_3DPOINT_ORIS07_LX,//39 +MOCAPNET_3DPOINT_ORIS07_LY,//40 +MOCAPNET_3DPOINT_ORIS07_LZ,//41 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_LX,//42 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_LY,//43 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_LZ,//44 +MOCAPNET_3DPOINT_ORIS06_RX,//45 +MOCAPNET_3DPOINT_ORIS06_RY,//46 +MOCAPNET_3DPOINT_ORIS06_RZ,//47 +MOCAPNET_3DPOINT_ORIS07_RX,//48 +MOCAPNET_3DPOINT_ORIS07_RY,//49 +MOCAPNET_3DPOINT_ORIS07_RZ,//50 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_RX,//51 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_RY,//52 +MOCAPNET_3DPOINT_ENDSITE_ORIS07_RZ,//53 +MOCAPNET_3DPOINT_TONGUE00X,//54 +MOCAPNET_3DPOINT_TONGUE00Y,//55 +MOCAPNET_3DPOINT_TONGUE00Z,//56 +MOCAPNET_3DPOINT_TONGUE01X,//57 +MOCAPNET_3DPOINT_TONGUE01Y,//58 +MOCAPNET_3DPOINT_TONGUE01Z,//59 +MOCAPNET_3DPOINT_TONGUE02X,//60 +MOCAPNET_3DPOINT_TONGUE02Y,//61 +MOCAPNET_3DPOINT_TONGUE02Z,//62 +MOCAPNET_3DPOINT_TONGUE03X,//63 +MOCAPNET_3DPOINT_TONGUE03Y,//64 +MOCAPNET_3DPOINT_TONGUE03Z,//65 +MOCAPNET_3DPOINT___TONGUE04X,//66 +MOCAPNET_3DPOINT___TONGUE04Y,//67 +MOCAPNET_3DPOINT___TONGUE04Z,//68 +MOCAPNET_3DPOINT_TONGUE04X,//69 +MOCAPNET_3DPOINT_TONGUE04Y,//70 +MOCAPNET_3DPOINT_TONGUE04Z,//71 +MOCAPNET_3DPOINT_ENDSITE_TONGUE04X,//72 +MOCAPNET_3DPOINT_ENDSITE_TONGUE04Y,//73 +MOCAPNET_3DPOINT_ENDSITE_TONGUE04Z,//74 +MOCAPNET_3DPOINT_TONGUE07_LX,//75 +MOCAPNET_3DPOINT_TONGUE07_LY,//76 +MOCAPNET_3DPOINT_TONGUE07_LZ,//77 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_LX,//78 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_LY,//79 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_LZ,//80 +MOCAPNET_3DPOINT_TONGUE07_RX,//81 +MOCAPNET_3DPOINT_TONGUE07_RY,//82 +MOCAPNET_3DPOINT_TONGUE07_RZ,//83 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_RX,//84 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_RY,//85 +MOCAPNET_3DPOINT_ENDSITE_TONGUE07_RZ,//86 +MOCAPNET_3DPOINT_TONGUE06_LX,//87 +MOCAPNET_3DPOINT_TONGUE06_LY,//88 +MOCAPNET_3DPOINT_TONGUE06_LZ,//89 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_LX,//90 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_LY,//91 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_LZ,//92 +MOCAPNET_3DPOINT_TONGUE06_RX,//93 +MOCAPNET_3DPOINT_TONGUE06_RY,//94 +MOCAPNET_3DPOINT_TONGUE06_RZ,//95 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_RX,//96 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_RY,//97 +MOCAPNET_3DPOINT_ENDSITE_TONGUE06_RZ,//98 +MOCAPNET_3DPOINT_TONGUE05_LX,//99 +MOCAPNET_3DPOINT_TONGUE05_LY,//100 +MOCAPNET_3DPOINT_TONGUE05_LZ,//101 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_LX,//102 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_LY,//103 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_LZ,//104 +MOCAPNET_3DPOINT_TONGUE05_RX,//105 +MOCAPNET_3DPOINT_TONGUE05_RY,//106 +MOCAPNET_3DPOINT_TONGUE05_RZ,//107 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_RX,//108 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_RY,//109 +MOCAPNET_3DPOINT_ENDSITE_TONGUE05_RZ,//110 +MOCAPNET_3DPOINT___LEVATOR02_LX,//111 +MOCAPNET_3DPOINT___LEVATOR02_LY,//112 +MOCAPNET_3DPOINT___LEVATOR02_LZ,//113 +MOCAPNET_3DPOINT_LEVATOR02_LX,//114 +MOCAPNET_3DPOINT_LEVATOR02_LY,//115 +MOCAPNET_3DPOINT_LEVATOR02_LZ,//116 +MOCAPNET_3DPOINT_LEVATOR03_LX,//117 +MOCAPNET_3DPOINT_LEVATOR03_LY,//118 +MOCAPNET_3DPOINT_LEVATOR03_LZ,//119 +MOCAPNET_3DPOINT_LEVATOR04_LX,//120 +MOCAPNET_3DPOINT_LEVATOR04_LY,//121 +MOCAPNET_3DPOINT_LEVATOR04_LZ,//122 +MOCAPNET_3DPOINT_LEVATOR05_LX,//123 +MOCAPNET_3DPOINT_LEVATOR05_LY,//124 +MOCAPNET_3DPOINT_LEVATOR05_LZ,//125 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_LX,//126 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_LY,//127 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_LZ,//128 +MOCAPNET_3DPOINT___LEVATOR02_RX,//129 +MOCAPNET_3DPOINT___LEVATOR02_RY,//130 +MOCAPNET_3DPOINT___LEVATOR02_RZ,//131 +MOCAPNET_3DPOINT_LEVATOR02_RX,//132 +MOCAPNET_3DPOINT_LEVATOR02_RY,//133 +MOCAPNET_3DPOINT_LEVATOR02_RZ,//134 +MOCAPNET_3DPOINT_LEVATOR03_RX,//135 +MOCAPNET_3DPOINT_LEVATOR03_RY,//136 +MOCAPNET_3DPOINT_LEVATOR03_RZ,//137 +MOCAPNET_3DPOINT_LEVATOR04_RX,//138 +MOCAPNET_3DPOINT_LEVATOR04_RY,//139 +MOCAPNET_3DPOINT_LEVATOR04_RZ,//140 +MOCAPNET_3DPOINT_LEVATOR05_RX,//141 +MOCAPNET_3DPOINT_LEVATOR05_RY,//142 +MOCAPNET_3DPOINT_LEVATOR05_RZ,//143 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_RX,//144 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_RY,//145 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR05_RZ,//146 +MOCAPNET_3DPOINT___SPECIAL01X,//147 +MOCAPNET_3DPOINT___SPECIAL01Y,//148 +MOCAPNET_3DPOINT___SPECIAL01Z,//149 +MOCAPNET_3DPOINT_SPECIAL01X,//150 +MOCAPNET_3DPOINT_SPECIAL01Y,//151 +MOCAPNET_3DPOINT_SPECIAL01Z,//152 +MOCAPNET_3DPOINT_ORIS04_LX,//153 +MOCAPNET_3DPOINT_ORIS04_LY,//154 +MOCAPNET_3DPOINT_ORIS04_LZ,//155 +MOCAPNET_3DPOINT_ORIS03_LX,//156 +MOCAPNET_3DPOINT_ORIS03_LY,//157 +MOCAPNET_3DPOINT_ORIS03_LZ,//158 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_LX,//159 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_LY,//160 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_LZ,//161 +MOCAPNET_3DPOINT_ORIS04_RX,//162 +MOCAPNET_3DPOINT_ORIS04_RY,//163 +MOCAPNET_3DPOINT_ORIS04_RZ,//164 +MOCAPNET_3DPOINT_ORIS03_RX,//165 +MOCAPNET_3DPOINT_ORIS03_RY,//166 +MOCAPNET_3DPOINT_ORIS03_RZ,//167 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_RX,//168 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_RY,//169 +MOCAPNET_3DPOINT_ENDSITE_ORIS03_RZ,//170 +MOCAPNET_3DPOINT_ORIS06X,//171 +MOCAPNET_3DPOINT_ORIS06Y,//172 +MOCAPNET_3DPOINT_ORIS06Z,//173 +MOCAPNET_3DPOINT_ORIS05X,//174 +MOCAPNET_3DPOINT_ORIS05Y,//175 +MOCAPNET_3DPOINT_ORIS05Z,//176 +MOCAPNET_3DPOINT_ENDSITE_ORIS05X,//177 +MOCAPNET_3DPOINT_ENDSITE_ORIS05Y,//178 +MOCAPNET_3DPOINT_ENDSITE_ORIS05Z,//179 +MOCAPNET_3DPOINT___SPECIAL03X,//180 +MOCAPNET_3DPOINT___SPECIAL03Y,//181 +MOCAPNET_3DPOINT___SPECIAL03Z,//182 +MOCAPNET_3DPOINT_SPECIAL03X,//183 +MOCAPNET_3DPOINT_SPECIAL03Y,//184 +MOCAPNET_3DPOINT_SPECIAL03Z,//185 +MOCAPNET_3DPOINT___LEVATOR06_LX,//186 +MOCAPNET_3DPOINT___LEVATOR06_LY,//187 +MOCAPNET_3DPOINT___LEVATOR06_LZ,//188 +MOCAPNET_3DPOINT_LEVATOR06_LX,//189 +MOCAPNET_3DPOINT_LEVATOR06_LY,//190 +MOCAPNET_3DPOINT_LEVATOR06_LZ,//191 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_LX,//192 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_LY,//193 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_LZ,//194 +MOCAPNET_3DPOINT___LEVATOR06_RX,//195 +MOCAPNET_3DPOINT___LEVATOR06_RY,//196 +MOCAPNET_3DPOINT___LEVATOR06_RZ,//197 +MOCAPNET_3DPOINT_LEVATOR06_RX,//198 +MOCAPNET_3DPOINT_LEVATOR06_RY,//199 +MOCAPNET_3DPOINT_LEVATOR06_RZ,//200 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_RX,//201 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_RY,//202 +MOCAPNET_3DPOINT_ENDSITE_LEVATOR06_RZ,//203 +MOCAPNET_3DPOINT_SPECIAL06_LX,//204 +MOCAPNET_3DPOINT_SPECIAL06_LY,//205 +MOCAPNET_3DPOINT_SPECIAL06_LZ,//206 +MOCAPNET_3DPOINT_SPECIAL05_LX,//207 +MOCAPNET_3DPOINT_SPECIAL05_LY,//208 +MOCAPNET_3DPOINT_SPECIAL05_LZ,//209 +MOCAPNET_3DPOINT_EYE_LX,//210 +MOCAPNET_3DPOINT_EYE_LY,//211 +MOCAPNET_3DPOINT_EYE_LZ,//212 +MOCAPNET_3DPOINT_ENDSITE_EYE_LX,//213 +MOCAPNET_3DPOINT_ENDSITE_EYE_LY,//214 +MOCAPNET_3DPOINT_ENDSITE_EYE_LZ,//215 +MOCAPNET_3DPOINT_ORBICULARIS03_LX,//216 +MOCAPNET_3DPOINT_ORBICULARIS03_LY,//217 +MOCAPNET_3DPOINT_ORBICULARIS03_LZ,//218 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_LX,//219 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_LY,//220 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_LZ,//221 +MOCAPNET_3DPOINT_ORBICULARIS04_LX,//222 +MOCAPNET_3DPOINT_ORBICULARIS04_LY,//223 +MOCAPNET_3DPOINT_ORBICULARIS04_LZ,//224 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_LX,//225 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_LY,//226 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_LZ,//227 +MOCAPNET_3DPOINT_SPECIAL06_RX,//228 +MOCAPNET_3DPOINT_SPECIAL06_RY,//229 +MOCAPNET_3DPOINT_SPECIAL06_RZ,//230 +MOCAPNET_3DPOINT_SPECIAL05_RX,//231 +MOCAPNET_3DPOINT_SPECIAL05_RY,//232 +MOCAPNET_3DPOINT_SPECIAL05_RZ,//233 +MOCAPNET_3DPOINT_EYE_RX,//234 +MOCAPNET_3DPOINT_EYE_RY,//235 +MOCAPNET_3DPOINT_EYE_RZ,//236 +MOCAPNET_3DPOINT_ENDSITE_EYE_RX,//237 +MOCAPNET_3DPOINT_ENDSITE_EYE_RY,//238 +MOCAPNET_3DPOINT_ENDSITE_EYE_RZ,//239 +MOCAPNET_3DPOINT_ORBICULARIS03_RX,//240 +MOCAPNET_3DPOINT_ORBICULARIS03_RY,//241 +MOCAPNET_3DPOINT_ORBICULARIS03_RZ,//242 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_RX,//243 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_RY,//244 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS03_RZ,//245 +MOCAPNET_3DPOINT_ORBICULARIS04_RX,//246 +MOCAPNET_3DPOINT_ORBICULARIS04_RY,//247 +MOCAPNET_3DPOINT_ORBICULARIS04_RZ,//248 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_RX,//249 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_RY,//250 +MOCAPNET_3DPOINT_ENDSITE_ORBICULARIS04_RZ,//251 +MOCAPNET_3DPOINT___TEMPORALIS01_LX,//252 +MOCAPNET_3DPOINT___TEMPORALIS01_LY,//253 +MOCAPNET_3DPOINT___TEMPORALIS01_LZ,//254 +MOCAPNET_3DPOINT_TEMPORALIS01_LX,//255 +MOCAPNET_3DPOINT_TEMPORALIS01_LY,//256 +MOCAPNET_3DPOINT_TEMPORALIS01_LZ,//257 +MOCAPNET_3DPOINT_OCULI02_LX,//258 +MOCAPNET_3DPOINT_OCULI02_LY,//259 +MOCAPNET_3DPOINT_OCULI02_LZ,//260 +MOCAPNET_3DPOINT_OCULI01_LX,//261 +MOCAPNET_3DPOINT_OCULI01_LY,//262 +MOCAPNET_3DPOINT_OCULI01_LZ,//263 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_LX,//264 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_LY,//265 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_LZ,//266 +MOCAPNET_3DPOINT___TEMPORALIS01_RX,//267 +MOCAPNET_3DPOINT___TEMPORALIS01_RY,//268 +MOCAPNET_3DPOINT___TEMPORALIS01_RZ,//269 +MOCAPNET_3DPOINT_TEMPORALIS01_RX,//270 +MOCAPNET_3DPOINT_TEMPORALIS01_RY,//271 +MOCAPNET_3DPOINT_TEMPORALIS01_RZ,//272 +MOCAPNET_3DPOINT_OCULI02_RX,//273 +MOCAPNET_3DPOINT_OCULI02_RY,//274 +MOCAPNET_3DPOINT_OCULI02_RZ,//275 +MOCAPNET_3DPOINT_OCULI01_RX,//276 +MOCAPNET_3DPOINT_OCULI01_RY,//277 +MOCAPNET_3DPOINT_OCULI01_RZ,//278 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_RX,//279 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_RY,//280 +MOCAPNET_3DPOINT_ENDSITE_OCULI01_RZ,//281 +MOCAPNET_3DPOINT___TEMPORALIS02_LX,//282 +MOCAPNET_3DPOINT___TEMPORALIS02_LY,//283 +MOCAPNET_3DPOINT___TEMPORALIS02_LZ,//284 +MOCAPNET_3DPOINT_TEMPORALIS02_LX,//285 +MOCAPNET_3DPOINT_TEMPORALIS02_LY,//286 +MOCAPNET_3DPOINT_TEMPORALIS02_LZ,//287 +MOCAPNET_3DPOINT_RISORIUS02_LX,//288 +MOCAPNET_3DPOINT_RISORIUS02_LY,//289 +MOCAPNET_3DPOINT_RISORIUS02_LZ,//290 +MOCAPNET_3DPOINT_RISORIUS03_LX,//291 +MOCAPNET_3DPOINT_RISORIUS03_LY,//292 +MOCAPNET_3DPOINT_RISORIUS03_LZ,//293 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_LX,//294 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_LY,//295 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_LZ,//296 +MOCAPNET_3DPOINT___TEMPORALIS02_RX,//297 +MOCAPNET_3DPOINT___TEMPORALIS02_RY,//298 +MOCAPNET_3DPOINT___TEMPORALIS02_RZ,//299 +MOCAPNET_3DPOINT_TEMPORALIS02_RX,//300 +MOCAPNET_3DPOINT_TEMPORALIS02_RY,//301 +MOCAPNET_3DPOINT_TEMPORALIS02_RZ,//302 +MOCAPNET_3DPOINT_RISORIUS02_RX,//303 +MOCAPNET_3DPOINT_RISORIUS02_RY,//304 +MOCAPNET_3DPOINT_RISORIUS02_RZ,//305 +MOCAPNET_3DPOINT_RISORIUS03_RX,//306 +MOCAPNET_3DPOINT_RISORIUS03_RY,//307 +MOCAPNET_3DPOINT_RISORIUS03_RZ,//308 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_RX,//309 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_RY,//310 +MOCAPNET_3DPOINT_ENDSITE_RISORIUS03_RZ,//311 +MOCAPNET_3DPOINT_RCOLLARX,//312 +MOCAPNET_3DPOINT_RCOLLARY,//313 +MOCAPNET_3DPOINT_RCOLLARZ,//314 +MOCAPNET_3DPOINT_RSHOULDERX,//315 +MOCAPNET_3DPOINT_RSHOULDERY,//316 +MOCAPNET_3DPOINT_RSHOULDERZ,//317 +MOCAPNET_3DPOINT_RELBOWX,//318 +MOCAPNET_3DPOINT_RELBOWY,//319 +MOCAPNET_3DPOINT_RELBOWZ,//320 +MOCAPNET_3DPOINT_RHANDX,//321 +MOCAPNET_3DPOINT_RHANDY,//322 +MOCAPNET_3DPOINT_RHANDZ,//323 +MOCAPNET_3DPOINT_METACARPAL1_RX,//324 +MOCAPNET_3DPOINT_METACARPAL1_RY,//325 +MOCAPNET_3DPOINT_METACARPAL1_RZ,//326 +MOCAPNET_3DPOINT_FINGER2_1_RX,//327 +MOCAPNET_3DPOINT_FINGER2_1_RY,//328 +MOCAPNET_3DPOINT_FINGER2_1_RZ,//329 +MOCAPNET_3DPOINT_FINGER2_2_RX,//330 +MOCAPNET_3DPOINT_FINGER2_2_RY,//331 +MOCAPNET_3DPOINT_FINGER2_2_RZ,//332 +MOCAPNET_3DPOINT_FINGER2_3_RX,//333 +MOCAPNET_3DPOINT_FINGER2_3_RY,//334 +MOCAPNET_3DPOINT_FINGER2_3_RZ,//335 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_RX,//336 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_RY,//337 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_RZ,//338 +MOCAPNET_3DPOINT_METACARPAL2_RX,//339 +MOCAPNET_3DPOINT_METACARPAL2_RY,//340 +MOCAPNET_3DPOINT_METACARPAL2_RZ,//341 +MOCAPNET_3DPOINT_FINGER3_1_RX,//342 +MOCAPNET_3DPOINT_FINGER3_1_RY,//343 +MOCAPNET_3DPOINT_FINGER3_1_RZ,//344 +MOCAPNET_3DPOINT_FINGER3_2_RX,//345 +MOCAPNET_3DPOINT_FINGER3_2_RY,//346 +MOCAPNET_3DPOINT_FINGER3_2_RZ,//347 +MOCAPNET_3DPOINT_FINGER3_3_RX,//348 +MOCAPNET_3DPOINT_FINGER3_3_RY,//349 +MOCAPNET_3DPOINT_FINGER3_3_RZ,//350 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_RX,//351 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_RY,//352 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_RZ,//353 +MOCAPNET_3DPOINT___METACARPAL3_RX,//354 +MOCAPNET_3DPOINT___METACARPAL3_RY,//355 +MOCAPNET_3DPOINT___METACARPAL3_RZ,//356 +MOCAPNET_3DPOINT_METACARPAL3_RX,//357 +MOCAPNET_3DPOINT_METACARPAL3_RY,//358 +MOCAPNET_3DPOINT_METACARPAL3_RZ,//359 +MOCAPNET_3DPOINT_FINGER4_1_RX,//360 +MOCAPNET_3DPOINT_FINGER4_1_RY,//361 +MOCAPNET_3DPOINT_FINGER4_1_RZ,//362 +MOCAPNET_3DPOINT_FINGER4_2_RX,//363 +MOCAPNET_3DPOINT_FINGER4_2_RY,//364 +MOCAPNET_3DPOINT_FINGER4_2_RZ,//365 +MOCAPNET_3DPOINT_FINGER4_3_RX,//366 +MOCAPNET_3DPOINT_FINGER4_3_RY,//367 +MOCAPNET_3DPOINT_FINGER4_3_RZ,//368 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_RX,//369 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_RY,//370 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_RZ,//371 +MOCAPNET_3DPOINT___METACARPAL4_RX,//372 +MOCAPNET_3DPOINT___METACARPAL4_RY,//373 +MOCAPNET_3DPOINT___METACARPAL4_RZ,//374 +MOCAPNET_3DPOINT_METACARPAL4_RX,//375 +MOCAPNET_3DPOINT_METACARPAL4_RY,//376 +MOCAPNET_3DPOINT_METACARPAL4_RZ,//377 +MOCAPNET_3DPOINT_FINGER5_1_RX,//378 +MOCAPNET_3DPOINT_FINGER5_1_RY,//379 +MOCAPNET_3DPOINT_FINGER5_1_RZ,//380 +MOCAPNET_3DPOINT_FINGER5_2_RX,//381 +MOCAPNET_3DPOINT_FINGER5_2_RY,//382 +MOCAPNET_3DPOINT_FINGER5_2_RZ,//383 +MOCAPNET_3DPOINT_FINGER5_3_RX,//384 +MOCAPNET_3DPOINT_FINGER5_3_RY,//385 +MOCAPNET_3DPOINT_FINGER5_3_RZ,//386 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_RX,//387 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_RY,//388 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_RZ,//389 +MOCAPNET_3DPOINT_RTHUMBBASEX,//390 +MOCAPNET_3DPOINT_RTHUMBBASEY,//391 +MOCAPNET_3DPOINT_RTHUMBBASEZ,//392 +MOCAPNET_3DPOINT_RTHUMBX,//393 +MOCAPNET_3DPOINT_RTHUMBY,//394 +MOCAPNET_3DPOINT_RTHUMBZ,//395 +MOCAPNET_3DPOINT_FINGER1_2_RX,//396 +MOCAPNET_3DPOINT_FINGER1_2_RY,//397 +MOCAPNET_3DPOINT_FINGER1_2_RZ,//398 +MOCAPNET_3DPOINT_FINGER1_3_RX,//399 +MOCAPNET_3DPOINT_FINGER1_3_RY,//400 +MOCAPNET_3DPOINT_FINGER1_3_RZ,//401 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_RX,//402 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_RY,//403 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_RZ,//404 +MOCAPNET_3DPOINT_LCOLLARX,//405 +MOCAPNET_3DPOINT_LCOLLARY,//406 +MOCAPNET_3DPOINT_LCOLLARZ,//407 +MOCAPNET_3DPOINT_LSHOULDERX,//408 +MOCAPNET_3DPOINT_LSHOULDERY,//409 +MOCAPNET_3DPOINT_LSHOULDERZ,//410 +MOCAPNET_3DPOINT_LELBOWX,//411 +MOCAPNET_3DPOINT_LELBOWY,//412 +MOCAPNET_3DPOINT_LELBOWZ,//413 +MOCAPNET_3DPOINT_LHANDX,//414 +MOCAPNET_3DPOINT_LHANDY,//415 +MOCAPNET_3DPOINT_LHANDZ,//416 +MOCAPNET_3DPOINT_METACARPAL1_LX,//417 +MOCAPNET_3DPOINT_METACARPAL1_LY,//418 +MOCAPNET_3DPOINT_METACARPAL1_LZ,//419 +MOCAPNET_3DPOINT_FINGER2_1_LX,//420 +MOCAPNET_3DPOINT_FINGER2_1_LY,//421 +MOCAPNET_3DPOINT_FINGER2_1_LZ,//422 +MOCAPNET_3DPOINT_FINGER2_2_LX,//423 +MOCAPNET_3DPOINT_FINGER2_2_LY,//424 +MOCAPNET_3DPOINT_FINGER2_2_LZ,//425 +MOCAPNET_3DPOINT_FINGER2_3_LX,//426 +MOCAPNET_3DPOINT_FINGER2_3_LY,//427 +MOCAPNET_3DPOINT_FINGER2_3_LZ,//428 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_LX,//429 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_LY,//430 +MOCAPNET_3DPOINT_ENDSITE_FINGER2_3_LZ,//431 +MOCAPNET_3DPOINT_METACARPAL2_LX,//432 +MOCAPNET_3DPOINT_METACARPAL2_LY,//433 +MOCAPNET_3DPOINT_METACARPAL2_LZ,//434 +MOCAPNET_3DPOINT_FINGER3_1_LX,//435 +MOCAPNET_3DPOINT_FINGER3_1_LY,//436 +MOCAPNET_3DPOINT_FINGER3_1_LZ,//437 +MOCAPNET_3DPOINT_FINGER3_2_LX,//438 +MOCAPNET_3DPOINT_FINGER3_2_LY,//439 +MOCAPNET_3DPOINT_FINGER3_2_LZ,//440 +MOCAPNET_3DPOINT_FINGER3_3_LX,//441 +MOCAPNET_3DPOINT_FINGER3_3_LY,//442 +MOCAPNET_3DPOINT_FINGER3_3_LZ,//443 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_LX,//444 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_LY,//445 +MOCAPNET_3DPOINT_ENDSITE_FINGER3_3_LZ,//446 +MOCAPNET_3DPOINT___METACARPAL3_LX,//447 +MOCAPNET_3DPOINT___METACARPAL3_LY,//448 +MOCAPNET_3DPOINT___METACARPAL3_LZ,//449 +MOCAPNET_3DPOINT_METACARPAL3_LX,//450 +MOCAPNET_3DPOINT_METACARPAL3_LY,//451 +MOCAPNET_3DPOINT_METACARPAL3_LZ,//452 +MOCAPNET_3DPOINT_FINGER4_1_LX,//453 +MOCAPNET_3DPOINT_FINGER4_1_LY,//454 +MOCAPNET_3DPOINT_FINGER4_1_LZ,//455 +MOCAPNET_3DPOINT_FINGER4_2_LX,//456 +MOCAPNET_3DPOINT_FINGER4_2_LY,//457 +MOCAPNET_3DPOINT_FINGER4_2_LZ,//458 +MOCAPNET_3DPOINT_FINGER4_3_LX,//459 +MOCAPNET_3DPOINT_FINGER4_3_LY,//460 +MOCAPNET_3DPOINT_FINGER4_3_LZ,//461 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_LX,//462 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_LY,//463 +MOCAPNET_3DPOINT_ENDSITE_FINGER4_3_LZ,//464 +MOCAPNET_3DPOINT___METACARPAL4_LX,//465 +MOCAPNET_3DPOINT___METACARPAL4_LY,//466 +MOCAPNET_3DPOINT___METACARPAL4_LZ,//467 +MOCAPNET_3DPOINT_METACARPAL4_LX,//468 +MOCAPNET_3DPOINT_METACARPAL4_LY,//469 +MOCAPNET_3DPOINT_METACARPAL4_LZ,//470 +MOCAPNET_3DPOINT_FINGER5_1_LX,//471 +MOCAPNET_3DPOINT_FINGER5_1_LY,//472 +MOCAPNET_3DPOINT_FINGER5_1_LZ,//473 +MOCAPNET_3DPOINT_FINGER5_2_LX,//474 +MOCAPNET_3DPOINT_FINGER5_2_LY,//475 +MOCAPNET_3DPOINT_FINGER5_2_LZ,//476 +MOCAPNET_3DPOINT_FINGER5_3_LX,//477 +MOCAPNET_3DPOINT_FINGER5_3_LY,//478 +MOCAPNET_3DPOINT_FINGER5_3_LZ,//479 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_LX,//480 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_LY,//481 +MOCAPNET_3DPOINT_ENDSITE_FINGER5_3_LZ,//482 +MOCAPNET_3DPOINT_LTHUMBBASEX,//483 +MOCAPNET_3DPOINT_LTHUMBBASEY,//484 +MOCAPNET_3DPOINT_LTHUMBBASEZ,//485 +MOCAPNET_3DPOINT_LTHUMBX,//486 +MOCAPNET_3DPOINT_LTHUMBY,//487 +MOCAPNET_3DPOINT_LTHUMBZ,//488 +MOCAPNET_3DPOINT_FINGER1_2_LX,//489 +MOCAPNET_3DPOINT_FINGER1_2_LY,//490 +MOCAPNET_3DPOINT_FINGER1_2_LZ,//491 +MOCAPNET_3DPOINT_FINGER1_3_LX,//492 +MOCAPNET_3DPOINT_FINGER1_3_LY,//493 +MOCAPNET_3DPOINT_FINGER1_3_LZ,//494 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_LX,//495 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_LY,//496 +MOCAPNET_3DPOINT_ENDSITE_FINGER1_3_LZ,//497 +MOCAPNET_3DPOINT_RBUTTOCKX,//498 +MOCAPNET_3DPOINT_RBUTTOCKY,//499 +MOCAPNET_3DPOINT_RBUTTOCKZ,//500 +MOCAPNET_3DPOINT_RHIPX,//501 +MOCAPNET_3DPOINT_RHIPY,//502 +MOCAPNET_3DPOINT_RHIPZ,//503 +MOCAPNET_3DPOINT_RKNEEX,//504 +MOCAPNET_3DPOINT_RKNEEY,//505 +MOCAPNET_3DPOINT_RKNEEZ,//506 +MOCAPNET_3DPOINT_RFOOTX,//507 +MOCAPNET_3DPOINT_RFOOTY,//508 +MOCAPNET_3DPOINT_RFOOTZ,//509 +MOCAPNET_3DPOINT_TOE1_1_RX,//510 +MOCAPNET_3DPOINT_TOE1_1_RY,//511 +MOCAPNET_3DPOINT_TOE1_1_RZ,//512 +MOCAPNET_3DPOINT_TOE1_2_RX,//513 +MOCAPNET_3DPOINT_TOE1_2_RY,//514 +MOCAPNET_3DPOINT_TOE1_2_RZ,//515 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_RX,//516 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_RY,//517 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_RZ,//518 +MOCAPNET_3DPOINT_TOE2_1_RX,//519 +MOCAPNET_3DPOINT_TOE2_1_RY,//520 +MOCAPNET_3DPOINT_TOE2_1_RZ,//521 +MOCAPNET_3DPOINT_TOE2_2_RX,//522 +MOCAPNET_3DPOINT_TOE2_2_RY,//523 +MOCAPNET_3DPOINT_TOE2_2_RZ,//524 +MOCAPNET_3DPOINT_TOE2_3_RX,//525 +MOCAPNET_3DPOINT_TOE2_3_RY,//526 +MOCAPNET_3DPOINT_TOE2_3_RZ,//527 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_RX,//528 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_RY,//529 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_RZ,//530 +MOCAPNET_3DPOINT_TOE3_1_RX,//531 +MOCAPNET_3DPOINT_TOE3_1_RY,//532 +MOCAPNET_3DPOINT_TOE3_1_RZ,//533 +MOCAPNET_3DPOINT_TOE3_2_RX,//534 +MOCAPNET_3DPOINT_TOE3_2_RY,//535 +MOCAPNET_3DPOINT_TOE3_2_RZ,//536 +MOCAPNET_3DPOINT_TOE3_3_RX,//537 +MOCAPNET_3DPOINT_TOE3_3_RY,//538 +MOCAPNET_3DPOINT_TOE3_3_RZ,//539 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_RX,//540 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_RY,//541 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_RZ,//542 +MOCAPNET_3DPOINT_TOE4_1_RX,//543 +MOCAPNET_3DPOINT_TOE4_1_RY,//544 +MOCAPNET_3DPOINT_TOE4_1_RZ,//545 +MOCAPNET_3DPOINT_TOE4_2_RX,//546 +MOCAPNET_3DPOINT_TOE4_2_RY,//547 +MOCAPNET_3DPOINT_TOE4_2_RZ,//548 +MOCAPNET_3DPOINT_TOE4_3_RX,//549 +MOCAPNET_3DPOINT_TOE4_3_RY,//550 +MOCAPNET_3DPOINT_TOE4_3_RZ,//551 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_RX,//552 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_RY,//553 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_RZ,//554 +MOCAPNET_3DPOINT_TOE5_1_RX,//555 +MOCAPNET_3DPOINT_TOE5_1_RY,//556 +MOCAPNET_3DPOINT_TOE5_1_RZ,//557 +MOCAPNET_3DPOINT_TOE5_2_RX,//558 +MOCAPNET_3DPOINT_TOE5_2_RY,//559 +MOCAPNET_3DPOINT_TOE5_2_RZ,//560 +MOCAPNET_3DPOINT_TOE5_3_RX,//561 +MOCAPNET_3DPOINT_TOE5_3_RY,//562 +MOCAPNET_3DPOINT_TOE5_3_RZ,//563 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_RX,//564 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_RY,//565 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_RZ,//566 +MOCAPNET_3DPOINT_LBUTTOCKX,//567 +MOCAPNET_3DPOINT_LBUTTOCKY,//568 +MOCAPNET_3DPOINT_LBUTTOCKZ,//569 +MOCAPNET_3DPOINT_LHIPX,//570 +MOCAPNET_3DPOINT_LHIPY,//571 +MOCAPNET_3DPOINT_LHIPZ,//572 +MOCAPNET_3DPOINT_LKNEEX,//573 +MOCAPNET_3DPOINT_LKNEEY,//574 +MOCAPNET_3DPOINT_LKNEEZ,//575 +MOCAPNET_3DPOINT_LFOOTX,//576 +MOCAPNET_3DPOINT_LFOOTY,//577 +MOCAPNET_3DPOINT_LFOOTZ,//578 +MOCAPNET_3DPOINT_TOE1_1_LX,//579 +MOCAPNET_3DPOINT_TOE1_1_LY,//580 +MOCAPNET_3DPOINT_TOE1_1_LZ,//581 +MOCAPNET_3DPOINT_TOE1_2_LX,//582 +MOCAPNET_3DPOINT_TOE1_2_LY,//583 +MOCAPNET_3DPOINT_TOE1_2_LZ,//584 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_LX,//585 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_LY,//586 +MOCAPNET_3DPOINT_ENDSITE_TOE1_2_LZ,//587 +MOCAPNET_3DPOINT_TOE2_1_LX,//588 +MOCAPNET_3DPOINT_TOE2_1_LY,//589 +MOCAPNET_3DPOINT_TOE2_1_LZ,//590 +MOCAPNET_3DPOINT_TOE2_2_LX,//591 +MOCAPNET_3DPOINT_TOE2_2_LY,//592 +MOCAPNET_3DPOINT_TOE2_2_LZ,//593 +MOCAPNET_3DPOINT_TOE2_3_LX,//594 +MOCAPNET_3DPOINT_TOE2_3_LY,//595 +MOCAPNET_3DPOINT_TOE2_3_LZ,//596 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_LX,//597 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_LY,//598 +MOCAPNET_3DPOINT_ENDSITE_TOE2_3_LZ,//599 +MOCAPNET_3DPOINT_TOE3_1_LX,//600 +MOCAPNET_3DPOINT_TOE3_1_LY,//601 +MOCAPNET_3DPOINT_TOE3_1_LZ,//602 +MOCAPNET_3DPOINT_TOE3_2_LX,//603 +MOCAPNET_3DPOINT_TOE3_2_LY,//604 +MOCAPNET_3DPOINT_TOE3_2_LZ,//605 +MOCAPNET_3DPOINT_TOE3_3_LX,//606 +MOCAPNET_3DPOINT_TOE3_3_LY,//607 +MOCAPNET_3DPOINT_TOE3_3_LZ,//608 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_LX,//609 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_LY,//610 +MOCAPNET_3DPOINT_ENDSITE_TOE3_3_LZ,//611 +MOCAPNET_3DPOINT_TOE4_1_LX,//612 +MOCAPNET_3DPOINT_TOE4_1_LY,//613 +MOCAPNET_3DPOINT_TOE4_1_LZ,//614 +MOCAPNET_3DPOINT_TOE4_2_LX,//615 +MOCAPNET_3DPOINT_TOE4_2_LY,//616 +MOCAPNET_3DPOINT_TOE4_2_LZ,//617 +MOCAPNET_3DPOINT_TOE4_3_LX,//618 +MOCAPNET_3DPOINT_TOE4_3_LY,//619 +MOCAPNET_3DPOINT_TOE4_3_LZ,//620 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_LX,//621 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_LY,//622 +MOCAPNET_3DPOINT_ENDSITE_TOE4_3_LZ,//623 +MOCAPNET_3DPOINT_TOE5_1_LX,//624 +MOCAPNET_3DPOINT_TOE5_1_LY,//625 +MOCAPNET_3DPOINT_TOE5_1_LZ,//626 +MOCAPNET_3DPOINT_TOE5_2_LX,//627 +MOCAPNET_3DPOINT_TOE5_2_LY,//628 +MOCAPNET_3DPOINT_TOE5_2_LZ,//629 +MOCAPNET_3DPOINT_TOE5_3_LX,//630 +MOCAPNET_3DPOINT_TOE5_3_LY,//631 +MOCAPNET_3DPOINT_TOE5_3_LZ,//632 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_LX,//633 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_LY,//634 +MOCAPNET_3DPOINT_ENDSITE_TOE5_3_LZ,//635 +//------------------------------------------------------------------- +MOCAPNET_3DPOINT_NUMBER +}; + + + + +/** + * @brief An array with BVH string labels + */ +static const char * MocapNET3DPositionalOutputArrayNames[] = +{ +"hip_Xposition", // 0 +"hip_Yposition", // 1 +"hip_Zposition", // 2 +"abdomen_Xposition", // 3 +"abdomen_Yposition", // 4 +"abdomen_Zposition", // 5 +"chest_Xposition", // 6 +"chest_Yposition", // 7 +"chest_Zposition", // 8 +"neck_Xposition", // 9 +"neck_Yposition", // 10 +"neck_Zposition", // 11 +"neck1_Xposition", // 12 +"neck1_Yposition", // 13 +"neck1_Zposition", // 14 +"head_Xposition", // 15 +"head_Yposition", // 16 +"head_Zposition", // 17 +"__jaw_Xposition", // 18 +"__jaw_Yposition", // 19 +"__jaw_Zposition", // 20 +"jaw_Xposition", // 21 +"jaw_Yposition", // 22 +"jaw_Zposition", // 23 +"special04_Xposition", // 24 +"special04_Yposition", // 25 +"special04_Zposition", // 26 +"oris02_Xposition", // 27 +"oris02_Yposition", // 28 +"oris02_Zposition", // 29 +"oris01_Xposition", // 30 +"oris01_Yposition", // 31 +"oris01_Zposition", // 32 +"endsite_oris01_Xposition", // 33 +"endsite_oris01_Yposition", // 34 +"endsite_oris01_Zposition", // 35 +"oris06.l_Xposition", // 36 +"oris06.l_Yposition", // 37 +"oris06.l_Zposition", // 38 +"oris07.l_Xposition", // 39 +"oris07.l_Yposition", // 40 +"oris07.l_Zposition", // 41 +"endsite_oris07.l_Xposition", // 42 +"endsite_oris07.l_Yposition", // 43 +"endsite_oris07.l_Zposition", // 44 +"oris06.r_Xposition", // 45 +"oris06.r_Yposition", // 46 +"oris06.r_Zposition", // 47 +"oris07.r_Xposition", // 48 +"oris07.r_Yposition", // 49 +"oris07.r_Zposition", // 50 +"endsite_oris07.r_Xposition", // 51 +"endsite_oris07.r_Yposition", // 52 +"endsite_oris07.r_Zposition", // 53 +"tongue00_Xposition", // 54 +"tongue00_Yposition", // 55 +"tongue00_Zposition", // 56 +"tongue01_Xposition", // 57 +"tongue01_Yposition", // 58 +"tongue01_Zposition", // 59 +"tongue02_Xposition", // 60 +"tongue02_Yposition", // 61 +"tongue02_Zposition", // 62 +"tongue03_Xposition", // 63 +"tongue03_Yposition", // 64 +"tongue03_Zposition", // 65 +"__tongue04_Xposition", // 66 +"__tongue04_Yposition", // 67 +"__tongue04_Zposition", // 68 +"tongue04_Xposition", // 69 +"tongue04_Yposition", // 70 +"tongue04_Zposition", // 71 +"endsite_tongue04_Xposition", // 72 +"endsite_tongue04_Yposition", // 73 +"endsite_tongue04_Zposition", // 74 +"tongue07.l_Xposition", // 75 +"tongue07.l_Yposition", // 76 +"tongue07.l_Zposition", // 77 +"endsite_tongue07.l_Xposition", // 78 +"endsite_tongue07.l_Yposition", // 79 +"endsite_tongue07.l_Zposition", // 80 +"tongue07.r_Xposition", // 81 +"tongue07.r_Yposition", // 82 +"tongue07.r_Zposition", // 83 +"endsite_tongue07.r_Xposition", // 84 +"endsite_tongue07.r_Yposition", // 85 +"endsite_tongue07.r_Zposition", // 86 +"tongue06.l_Xposition", // 87 +"tongue06.l_Yposition", // 88 +"tongue06.l_Zposition", // 89 +"endsite_tongue06.l_Xposition", // 90 +"endsite_tongue06.l_Yposition", // 91 +"endsite_tongue06.l_Zposition", // 92 +"tongue06.r_Xposition", // 93 +"tongue06.r_Yposition", // 94 +"tongue06.r_Zposition", // 95 +"endsite_tongue06.r_Xposition", // 96 +"endsite_tongue06.r_Yposition", // 97 +"endsite_tongue06.r_Zposition", // 98 +"tongue05.l_Xposition", // 99 +"tongue05.l_Yposition", // 100 +"tongue05.l_Zposition", // 101 +"endsite_tongue05.l_Xposition", // 102 +"endsite_tongue05.l_Yposition", // 103 +"endsite_tongue05.l_Zposition", // 104 +"tongue05.r_Xposition", // 105 +"tongue05.r_Yposition", // 106 +"tongue05.r_Zposition", // 107 +"endsite_tongue05.r_Xposition", // 108 +"endsite_tongue05.r_Yposition", // 109 +"endsite_tongue05.r_Zposition", // 110 +"__levator02.l_Xposition", // 111 +"__levator02.l_Yposition", // 112 +"__levator02.l_Zposition", // 113 +"levator02.l_Xposition", // 114 +"levator02.l_Yposition", // 115 +"levator02.l_Zposition", // 116 +"levator03.l_Xposition", // 117 +"levator03.l_Yposition", // 118 +"levator03.l_Zposition", // 119 +"levator04.l_Xposition", // 120 +"levator04.l_Yposition", // 121 +"levator04.l_Zposition", // 122 +"levator05.l_Xposition", // 123 +"levator05.l_Yposition", // 124 +"levator05.l_Zposition", // 125 +"endsite_levator05.l_Xposition", // 126 +"endsite_levator05.l_Yposition", // 127 +"endsite_levator05.l_Zposition", // 128 +"__levator02.r_Xposition", // 129 +"__levator02.r_Yposition", // 130 +"__levator02.r_Zposition", // 131 +"levator02.r_Xposition", // 132 +"levator02.r_Yposition", // 133 +"levator02.r_Zposition", // 134 +"levator03.r_Xposition", // 135 +"levator03.r_Yposition", // 136 +"levator03.r_Zposition", // 137 +"levator04.r_Xposition", // 138 +"levator04.r_Yposition", // 139 +"levator04.r_Zposition", // 140 +"levator05.r_Xposition", // 141 +"levator05.r_Yposition", // 142 +"levator05.r_Zposition", // 143 +"endsite_levator05.r_Xposition", // 144 +"endsite_levator05.r_Yposition", // 145 +"endsite_levator05.r_Zposition", // 146 +"__special01_Xposition", // 147 +"__special01_Yposition", // 148 +"__special01_Zposition", // 149 +"special01_Xposition", // 150 +"special01_Yposition", // 151 +"special01_Zposition", // 152 +"oris04.l_Xposition", // 153 +"oris04.l_Yposition", // 154 +"oris04.l_Zposition", // 155 +"oris03.l_Xposition", // 156 +"oris03.l_Yposition", // 157 +"oris03.l_Zposition", // 158 +"endsite_oris03.l_Xposition", // 159 +"endsite_oris03.l_Yposition", // 160 +"endsite_oris03.l_Zposition", // 161 +"oris04.r_Xposition", // 162 +"oris04.r_Yposition", // 163 +"oris04.r_Zposition", // 164 +"oris03.r_Xposition", // 165 +"oris03.r_Yposition", // 166 +"oris03.r_Zposition", // 167 +"endsite_oris03.r_Xposition", // 168 +"endsite_oris03.r_Yposition", // 169 +"endsite_oris03.r_Zposition", // 170 +"oris06_Xposition", // 171 +"oris06_Yposition", // 172 +"oris06_Zposition", // 173 +"oris05_Xposition", // 174 +"oris05_Yposition", // 175 +"oris05_Zposition", // 176 +"endsite_oris05_Xposition", // 177 +"endsite_oris05_Yposition", // 178 +"endsite_oris05_Zposition", // 179 +"__special03_Xposition", // 180 +"__special03_Yposition", // 181 +"__special03_Zposition", // 182 +"special03_Xposition", // 183 +"special03_Yposition", // 184 +"special03_Zposition", // 185 +"__levator06.l_Xposition", // 186 +"__levator06.l_Yposition", // 187 +"__levator06.l_Zposition", // 188 +"levator06.l_Xposition", // 189 +"levator06.l_Yposition", // 190 +"levator06.l_Zposition", // 191 +"endsite_levator06.l_Xposition", // 192 +"endsite_levator06.l_Yposition", // 193 +"endsite_levator06.l_Zposition", // 194 +"__levator06.r_Xposition", // 195 +"__levator06.r_Yposition", // 196 +"__levator06.r_Zposition", // 197 +"levator06.r_Xposition", // 198 +"levator06.r_Yposition", // 199 +"levator06.r_Zposition", // 200 +"endsite_levator06.r_Xposition", // 201 +"endsite_levator06.r_Yposition", // 202 +"endsite_levator06.r_Zposition", // 203 +"special06.l_Xposition", // 204 +"special06.l_Yposition", // 205 +"special06.l_Zposition", // 206 +"special05.l_Xposition", // 207 +"special05.l_Yposition", // 208 +"special05.l_Zposition", // 209 +"eye.l_Xposition", // 210 +"eye.l_Yposition", // 211 +"eye.l_Zposition", // 212 +"endsite_eye.l_Xposition", // 213 +"endsite_eye.l_Yposition", // 214 +"endsite_eye.l_Zposition", // 215 +"orbicularis03.l_Xposition", // 216 +"orbicularis03.l_Yposition", // 217 +"orbicularis03.l_Zposition", // 218 +"endsite_orbicularis03.l_Xposition", // 219 +"endsite_orbicularis03.l_Yposition", // 220 +"endsite_orbicularis03.l_Zposition", // 221 +"orbicularis04.l_Xposition", // 222 +"orbicularis04.l_Yposition", // 223 +"orbicularis04.l_Zposition", // 224 +"endsite_orbicularis04.l_Xposition", // 225 +"endsite_orbicularis04.l_Yposition", // 226 +"endsite_orbicularis04.l_Zposition", // 227 +"special06.r_Xposition", // 228 +"special06.r_Yposition", // 229 +"special06.r_Zposition", // 230 +"special05.r_Xposition", // 231 +"special05.r_Yposition", // 232 +"special05.r_Zposition", // 233 +"eye.r_Xposition", // 234 +"eye.r_Yposition", // 235 +"eye.r_Zposition", // 236 +"endsite_eye.r_Xposition", // 237 +"endsite_eye.r_Yposition", // 238 +"endsite_eye.r_Zposition", // 239 +"orbicularis03.r_Xposition", // 240 +"orbicularis03.r_Yposition", // 241 +"orbicularis03.r_Zposition", // 242 +"endsite_orbicularis03.r_Xposition", // 243 +"endsite_orbicularis03.r_Yposition", // 244 +"endsite_orbicularis03.r_Zposition", // 245 +"orbicularis04.r_Xposition", // 246 +"orbicularis04.r_Yposition", // 247 +"orbicularis04.r_Zposition", // 248 +"endsite_orbicularis04.r_Xposition", // 249 +"endsite_orbicularis04.r_Yposition", // 250 +"endsite_orbicularis04.r_Zposition", // 251 +"__temporalis01.l_Xposition", // 252 +"__temporalis01.l_Yposition", // 253 +"__temporalis01.l_Zposition", // 254 +"temporalis01.l_Xposition", // 255 +"temporalis01.l_Yposition", // 256 +"temporalis01.l_Zposition", // 257 +"oculi02.l_Xposition", // 258 +"oculi02.l_Yposition", // 259 +"oculi02.l_Zposition", // 260 +"oculi01.l_Xposition", // 261 +"oculi01.l_Yposition", // 262 +"oculi01.l_Zposition", // 263 +"endsite_oculi01.l_Xposition", // 264 +"endsite_oculi01.l_Yposition", // 265 +"endsite_oculi01.l_Zposition", // 266 +"__temporalis01.r_Xposition", // 267 +"__temporalis01.r_Yposition", // 268 +"__temporalis01.r_Zposition", // 269 +"temporalis01.r_Xposition", // 270 +"temporalis01.r_Yposition", // 271 +"temporalis01.r_Zposition", // 272 +"oculi02.r_Xposition", // 273 +"oculi02.r_Yposition", // 274 +"oculi02.r_Zposition", // 275 +"oculi01.r_Xposition", // 276 +"oculi01.r_Yposition", // 277 +"oculi01.r_Zposition", // 278 +"endsite_oculi01.r_Xposition", // 279 +"endsite_oculi01.r_Yposition", // 280 +"endsite_oculi01.r_Zposition", // 281 +"__temporalis02.l_Xposition", // 282 +"__temporalis02.l_Yposition", // 283 +"__temporalis02.l_Zposition", // 284 +"temporalis02.l_Xposition", // 285 +"temporalis02.l_Yposition", // 286 +"temporalis02.l_Zposition", // 287 +"risorius02.l_Xposition", // 288 +"risorius02.l_Yposition", // 289 +"risorius02.l_Zposition", // 290 +"risorius03.l_Xposition", // 291 +"risorius03.l_Yposition", // 292 +"risorius03.l_Zposition", // 293 +"endsite_risorius03.l_Xposition", // 294 +"endsite_risorius03.l_Yposition", // 295 +"endsite_risorius03.l_Zposition", // 296 +"__temporalis02.r_Xposition", // 297 +"__temporalis02.r_Yposition", // 298 +"__temporalis02.r_Zposition", // 299 +"temporalis02.r_Xposition", // 300 +"temporalis02.r_Yposition", // 301 +"temporalis02.r_Zposition", // 302 +"risorius02.r_Xposition", // 303 +"risorius02.r_Yposition", // 304 +"risorius02.r_Zposition", // 305 +"risorius03.r_Xposition", // 306 +"risorius03.r_Yposition", // 307 +"risorius03.r_Zposition", // 308 +"endsite_risorius03.r_Xposition", // 309 +"endsite_risorius03.r_Yposition", // 310 +"endsite_risorius03.r_Zposition", // 311 +"rcollar_Xposition", // 312 +"rcollar_Yposition", // 313 +"rcollar_Zposition", // 314 +"rshoulder_Xposition", // 315 +"rshoulder_Yposition", // 316 +"rshoulder_Zposition", // 317 +"relbow_Xposition", // 318 +"relbow_Yposition", // 319 +"relbow_Zposition", // 320 +"rhand_Xposition", // 321 +"rhand_Yposition", // 322 +"rhand_Zposition", // 323 +"metacarpal1.r_Xposition", // 324 +"metacarpal1.r_Yposition", // 325 +"metacarpal1.r_Zposition", // 326 +"finger2-1.r_Xposition", // 327 +"finger2-1.r_Yposition", // 328 +"finger2-1.r_Zposition", // 329 +"finger2-2.r_Xposition", // 330 +"finger2-2.r_Yposition", // 331 +"finger2-2.r_Zposition", // 332 +"finger2-3.r_Xposition", // 333 +"finger2-3.r_Yposition", // 334 +"finger2-3.r_Zposition", // 335 +"endsite_finger2-3.r_Xposition", // 336 +"endsite_finger2-3.r_Yposition", // 337 +"endsite_finger2-3.r_Zposition", // 338 +"metacarpal2.r_Xposition", // 339 +"metacarpal2.r_Yposition", // 340 +"metacarpal2.r_Zposition", // 341 +"finger3-1.r_Xposition", // 342 +"finger3-1.r_Yposition", // 343 +"finger3-1.r_Zposition", // 344 +"finger3-2.r_Xposition", // 345 +"finger3-2.r_Yposition", // 346 +"finger3-2.r_Zposition", // 347 +"finger3-3.r_Xposition", // 348 +"finger3-3.r_Yposition", // 349 +"finger3-3.r_Zposition", // 350 +"endsite_finger3-3.r_Xposition", // 351 +"endsite_finger3-3.r_Yposition", // 352 +"endsite_finger3-3.r_Zposition", // 353 +"__metacarpal3.r_Xposition", // 354 +"__metacarpal3.r_Yposition", // 355 +"__metacarpal3.r_Zposition", // 356 +"metacarpal3.r_Xposition", // 357 +"metacarpal3.r_Yposition", // 358 +"metacarpal3.r_Zposition", // 359 +"finger4-1.r_Xposition", // 360 +"finger4-1.r_Yposition", // 361 +"finger4-1.r_Zposition", // 362 +"finger4-2.r_Xposition", // 363 +"finger4-2.r_Yposition", // 364 +"finger4-2.r_Zposition", // 365 +"finger4-3.r_Xposition", // 366 +"finger4-3.r_Yposition", // 367 +"finger4-3.r_Zposition", // 368 +"endsite_finger4-3.r_Xposition", // 369 +"endsite_finger4-3.r_Yposition", // 370 +"endsite_finger4-3.r_Zposition", // 371 +"__metacarpal4.r_Xposition", // 372 +"__metacarpal4.r_Yposition", // 373 +"__metacarpal4.r_Zposition", // 374 +"metacarpal4.r_Xposition", // 375 +"metacarpal4.r_Yposition", // 376 +"metacarpal4.r_Zposition", // 377 +"finger5-1.r_Xposition", // 378 +"finger5-1.r_Yposition", // 379 +"finger5-1.r_Zposition", // 380 +"finger5-2.r_Xposition", // 381 +"finger5-2.r_Yposition", // 382 +"finger5-2.r_Zposition", // 383 +"finger5-3.r_Xposition", // 384 +"finger5-3.r_Yposition", // 385 +"finger5-3.r_Zposition", // 386 +"endsite_finger5-3.r_Xposition", // 387 +"endsite_finger5-3.r_Yposition", // 388 +"endsite_finger5-3.r_Zposition", // 389 +"rthumbBase_Xposition", // 390 +"rthumbBase_Yposition", // 391 +"rthumbBase_Zposition", // 392 +"rthumb_Xposition", // 393 +"rthumb_Yposition", // 394 +"rthumb_Zposition", // 395 +"finger1-2.r_Xposition", // 396 +"finger1-2.r_Yposition", // 397 +"finger1-2.r_Zposition", // 398 +"finger1-3.r_Xposition", // 399 +"finger1-3.r_Yposition", // 400 +"finger1-3.r_Zposition", // 401 +"endsite_finger1-3.r_Xposition", // 402 +"endsite_finger1-3.r_Yposition", // 403 +"endsite_finger1-3.r_Zposition", // 404 +"lcollar_Xposition", // 405 +"lcollar_Yposition", // 406 +"lcollar_Zposition", // 407 +"lshoulder_Xposition", // 408 +"lshoulder_Yposition", // 409 +"lshoulder_Zposition", // 410 +"lelbow_Xposition", // 411 +"lelbow_Yposition", // 412 +"lelbow_Zposition", // 413 +"lhand_Xposition", // 414 +"lhand_Yposition", // 415 +"lhand_Zposition", // 416 +"metacarpal1.l_Xposition", // 417 +"metacarpal1.l_Yposition", // 418 +"metacarpal1.l_Zposition", // 419 +"finger2-1.l_Xposition", // 420 +"finger2-1.l_Yposition", // 421 +"finger2-1.l_Zposition", // 422 +"finger2-2.l_Xposition", // 423 +"finger2-2.l_Yposition", // 424 +"finger2-2.l_Zposition", // 425 +"finger2-3.l_Xposition", // 426 +"finger2-3.l_Yposition", // 427 +"finger2-3.l_Zposition", // 428 +"endsite_finger2-3.l_Xposition", // 429 +"endsite_finger2-3.l_Yposition", // 430 +"endsite_finger2-3.l_Zposition", // 431 +"metacarpal2.l_Xposition", // 432 +"metacarpal2.l_Yposition", // 433 +"metacarpal2.l_Zposition", // 434 +"finger3-1.l_Xposition", // 435 +"finger3-1.l_Yposition", // 436 +"finger3-1.l_Zposition", // 437 +"finger3-2.l_Xposition", // 438 +"finger3-2.l_Yposition", // 439 +"finger3-2.l_Zposition", // 440 +"finger3-3.l_Xposition", // 441 +"finger3-3.l_Yposition", // 442 +"finger3-3.l_Zposition", // 443 +"endsite_finger3-3.l_Xposition", // 444 +"endsite_finger3-3.l_Yposition", // 445 +"endsite_finger3-3.l_Zposition", // 446 +"__metacarpal3.l_Xposition", // 447 +"__metacarpal3.l_Yposition", // 448 +"__metacarpal3.l_Zposition", // 449 +"metacarpal3.l_Xposition", // 450 +"metacarpal3.l_Yposition", // 451 +"metacarpal3.l_Zposition", // 452 +"finger4-1.l_Xposition", // 453 +"finger4-1.l_Yposition", // 454 +"finger4-1.l_Zposition", // 455 +"finger4-2.l_Xposition", // 456 +"finger4-2.l_Yposition", // 457 +"finger4-2.l_Zposition", // 458 +"finger4-3.l_Xposition", // 459 +"finger4-3.l_Yposition", // 460 +"finger4-3.l_Zposition", // 461 +"endsite_finger4-3.l_Xposition", // 462 +"endsite_finger4-3.l_Yposition", // 463 +"endsite_finger4-3.l_Zposition", // 464 +"__metacarpal4.l_Xposition", // 465 +"__metacarpal4.l_Yposition", // 466 +"__metacarpal4.l_Zposition", // 467 +"metacarpal4.l_Xposition", // 468 +"metacarpal4.l_Yposition", // 469 +"metacarpal4.l_Zposition", // 470 +"finger5-1.l_Xposition", // 471 +"finger5-1.l_Yposition", // 472 +"finger5-1.l_Zposition", // 473 +"finger5-2.l_Xposition", // 474 +"finger5-2.l_Yposition", // 475 +"finger5-2.l_Zposition", // 476 +"finger5-3.l_Xposition", // 477 +"finger5-3.l_Yposition", // 478 +"finger5-3.l_Zposition", // 479 +"endsite_finger5-3.l_Xposition", // 480 +"endsite_finger5-3.l_Yposition", // 481 +"endsite_finger5-3.l_Zposition", // 482 +"lthumbBase_Xposition", // 483 +"lthumbBase_Yposition", // 484 +"lthumbBase_Zposition", // 485 +"lthumb_Xposition", // 486 +"lthumb_Yposition", // 487 +"lthumb_Zposition", // 488 +"finger1-2.l_Xposition", // 489 +"finger1-2.l_Yposition", // 490 +"finger1-2.l_Zposition", // 491 +"finger1-3.l_Xposition", // 492 +"finger1-3.l_Yposition", // 493 +"finger1-3.l_Zposition", // 494 +"endsite_finger1-3.l_Xposition", // 495 +"endsite_finger1-3.l_Yposition", // 496 +"endsite_finger1-3.l_Zposition", // 497 +"rbuttock_Xposition", // 498 +"rbuttock_Yposition", // 499 +"rbuttock_Zposition", // 500 +"rhip_Xposition", // 501 +"rhip_Yposition", // 502 +"rhip_Zposition", // 503 +"rknee_Xposition", // 504 +"rknee_Yposition", // 505 +"rknee_Zposition", // 506 +"rfoot_Xposition", // 507 +"rfoot_Yposition", // 508 +"rfoot_Zposition", // 509 +"toe1-1.r_Xposition", // 510 +"toe1-1.r_Yposition", // 511 +"toe1-1.r_Zposition", // 512 +"toe1-2.r_Xposition", // 513 +"toe1-2.r_Yposition", // 514 +"toe1-2.r_Zposition", // 515 +"endsite_toe1-2.r_Xposition", // 516 +"endsite_toe1-2.r_Yposition", // 517 +"endsite_toe1-2.r_Zposition", // 518 +"toe2-1.r_Xposition", // 519 +"toe2-1.r_Yposition", // 520 +"toe2-1.r_Zposition", // 521 +"toe2-2.r_Xposition", // 522 +"toe2-2.r_Yposition", // 523 +"toe2-2.r_Zposition", // 524 +"toe2-3.r_Xposition", // 525 +"toe2-3.r_Yposition", // 526 +"toe2-3.r_Zposition", // 527 +"endsite_toe2-3.r_Xposition", // 528 +"endsite_toe2-3.r_Yposition", // 529 +"endsite_toe2-3.r_Zposition", // 530 +"toe3-1.r_Xposition", // 531 +"toe3-1.r_Yposition", // 532 +"toe3-1.r_Zposition", // 533 +"toe3-2.r_Xposition", // 534 +"toe3-2.r_Yposition", // 535 +"toe3-2.r_Zposition", // 536 +"toe3-3.r_Xposition", // 537 +"toe3-3.r_Yposition", // 538 +"toe3-3.r_Zposition", // 539 +"endsite_toe3-3.r_Xposition", // 540 +"endsite_toe3-3.r_Yposition", // 541 +"endsite_toe3-3.r_Zposition", // 542 +"toe4-1.r_Xposition", // 543 +"toe4-1.r_Yposition", // 544 +"toe4-1.r_Zposition", // 545 +"toe4-2.r_Xposition", // 546 +"toe4-2.r_Yposition", // 547 +"toe4-2.r_Zposition", // 548 +"toe4-3.r_Xposition", // 549 +"toe4-3.r_Yposition", // 550 +"toe4-3.r_Zposition", // 551 +"endsite_toe4-3.r_Xposition", // 552 +"endsite_toe4-3.r_Yposition", // 553 +"endsite_toe4-3.r_Zposition", // 554 +"toe5-1.r_Xposition", // 555 +"toe5-1.r_Yposition", // 556 +"toe5-1.r_Zposition", // 557 +"toe5-2.r_Xposition", // 558 +"toe5-2.r_Yposition", // 559 +"toe5-2.r_Zposition", // 560 +"toe5-3.r_Xposition", // 561 +"toe5-3.r_Yposition", // 562 +"toe5-3.r_Zposition", // 563 +"endsite_toe5-3.r_Xposition", // 564 +"endsite_toe5-3.r_Yposition", // 565 +"endsite_toe5-3.r_Zposition", // 566 +"lbuttock_Xposition", // 567 +"lbuttock_Yposition", // 568 +"lbuttock_Zposition", // 569 +"lhip_Xposition", // 570 +"lhip_Yposition", // 571 +"lhip_Zposition", // 572 +"lknee_Xposition", // 573 +"lknee_Yposition", // 574 +"lknee_Zposition", // 575 +"lfoot_Xposition", // 576 +"lfoot_Yposition", // 577 +"lfoot_Zposition", // 578 +"toe1-1.l_Xposition", // 579 +"toe1-1.l_Yposition", // 580 +"toe1-1.l_Zposition", // 581 +"toe1-2.l_Xposition", // 582 +"toe1-2.l_Yposition", // 583 +"toe1-2.l_Zposition", // 584 +"endsite_toe1-2.l_Xposition", // 585 +"endsite_toe1-2.l_Yposition", // 586 +"endsite_toe1-2.l_Zposition", // 587 +"toe2-1.l_Xposition", // 588 +"toe2-1.l_Yposition", // 589 +"toe2-1.l_Zposition", // 590 +"toe2-2.l_Xposition", // 591 +"toe2-2.l_Yposition", // 592 +"toe2-2.l_Zposition", // 593 +"toe2-3.l_Xposition", // 594 +"toe2-3.l_Yposition", // 595 +"toe2-3.l_Zposition", // 596 +"endsite_toe2-3.l_Xposition", // 597 +"endsite_toe2-3.l_Yposition", // 598 +"endsite_toe2-3.l_Zposition", // 599 +"toe3-1.l_Xposition", // 600 +"toe3-1.l_Yposition", // 601 +"toe3-1.l_Zposition", // 602 +"toe3-2.l_Xposition", // 603 +"toe3-2.l_Yposition", // 604 +"toe3-2.l_Zposition", // 605 +"toe3-3.l_Xposition", // 606 +"toe3-3.l_Yposition", // 607 +"toe3-3.l_Zposition", // 608 +"endsite_toe3-3.l_Xposition", // 609 +"endsite_toe3-3.l_Yposition", // 610 +"endsite_toe3-3.l_Zposition", // 611 +"toe4-1.l_Xposition", // 612 +"toe4-1.l_Yposition", // 613 +"toe4-1.l_Zposition", // 614 +"toe4-2.l_Xposition", // 615 +"toe4-2.l_Yposition", // 616 +"toe4-2.l_Zposition", // 617 +"toe4-3.l_Xposition", // 618 +"toe4-3.l_Yposition", // 619 +"toe4-3.l_Zposition", // 620 +"endsite_toe4-3.l_Xposition", // 621 +"endsite_toe4-3.l_Yposition", // 622 +"endsite_toe4-3.l_Zposition", // 623 +"toe5-1.l_Xposition", // 624 +"toe5-1.l_Yposition", // 625 +"toe5-1.l_Zposition", // 626 +"toe5-2.l_Xposition", // 627 +"toe5-2.l_Yposition", // 628 +"toe5-2.l_Zposition", // 629 +"toe5-3.l_Xposition", // 630 +"toe5-3.l_Yposition", // 631 +"toe5-3.l_Zposition", // 632 +"endsite_toe5-3.l_Xposition", // 633 +"endsite_toe5-3.l_Yposition", // 634 +"endsite_toe5-3.l_Zposition"// 635 +}; + +/** + * @brief This is a structure to encode model limits, not currently used + */ +struct MocapNETModelLimits +{ + int numberOfLimits; + float minimumYaw1; + float maximumYaw1; + float minimumYaw2; + float maximumYaw2; + int isFlipped; +}; + +/** + * @brief This is a MocapNET orientation. + */ +enum MOCAPNET_Orientation +{ + MOCAPNET_ORIENTATION_NONE=0, + MOCAPNET_ORIENTATION_FRONT, + MOCAPNET_ORIENTATION_BACK, + MOCAPNET_ORIENTATION_LEFT, + MOCAPNET_ORIENTATION_RIGHT, + //----------------------------- + MOCAPNET_ORIENTATION_NUMBER +}; + +/** + * @brief This is an array of names for all uncompressed inputs expected from MocapNET. + * Please notice that these 171 values correspond to triplets of 57 x,y,v ( v for visibility ) information for each joint. + */ +static const char * MocapNETOrientationNames[] = +{ + "None", + "Front", + "Back", + "Left", + "Right" +}; + + +/** + * @brief Each part of our 3D pose output is solved by a dedicated ensemble, this structure organizes this data + */ +struct MocapNET2SolutionPart +{ + int test; +}; + + +#if USE_BVH + #include "../../../dependencies/RGBDAcquisition/tools/PThreadWorkerPool/pthreadWorkerPool.h" +#endif + + +/** + * @brief MocapNET consists of separate classes/ensembles that are invoked for particular orientations. + * This structure holds the required tensorflow instances to make MocapNET work. + */ +struct MocapNET4 +{ + int test; +}; + +/** + * @brief Load a MocapNET from .pb files on disk + * @ingroup mocapnet + * @param Pointer to a struct MocapNET that will hold the tensorflow instances on load. + * @param Description of instance + * @retval 1 = Success loading the files , 0 = Failure + */ +int loadMocapNET4( + struct MocapNET4 * mnet, + const char * description + ); + + +/** + * @brief run MocapNET on an input vector that has the correct formatting. If getting data from an external source + * the prepareMocapNETInputFromUncompressedInput function could be used to prepare the input for this function. + * @param Pointer to a valid and populated MocapNET instance + * @param Vector of input values according to MocapNETUncompressedAndCompressedArrayNames + * @retval 1=Success,0=Failure + */ +std::vector runMocapNET4( + struct MocapNET4 * mnet, + struct skeletonSerialized * input, + int doLowerbody, + int doHands, + int doFace, + int doGestureDetection, + unsigned int useInverseKinematics, + int doOutputFiltering + ); + +/** + * @brief Deallocate tensorflow instances and free memory + * @param Pointer to a valid and populated MocapNET instance + * @retval 1=Success,0=Failure + */ +int unloadMocapNET4(struct MocapNET4 * mnet); + diff --git a/src/MocapNET4/MocapNETLib4/tools.h b/src/MocapNET4/MocapNETLib4/tools.h new file mode 100644 index 0000000..68be579 --- /dev/null +++ b/src/MocapNET4/MocapNETLib4/tools.h @@ -0,0 +1,86 @@ +/** @file tools.h + * @brief Various Tools! + * @author Ammar Qammaz (AmmarkoV) + */ + +#ifndef MNET4_TOOLS_H_INCLUDED +#define MNET4_TOOLS_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define NORMAL "\033[0m" +#define BLACK "\033[30m" /* Black */ +#define RED "\033[31m" /* Red */ +#define GREEN "\033[32m" /* Green */ +#define YELLOW "\033[33m" /* Yellow */ + + +#include +#include + + +static char * readFileToMemory(const char * filename,unsigned int *length) +{ + if (filename==0) + { + fprintf(stderr,RED "No Path Given to readFileToMemory\n" NORMAL); + return 0; + } + + *length = 0; + FILE * pFile = fopen ( filename , "rb" ); + + if (pFile==0) + { + fprintf(stderr,RED "readFileToMemory failed\n" NORMAL); + fprintf(stderr,RED "Could not read file %s \n" NORMAL,filename); + return 0; + } + + // obtain file size: + fseek (pFile , 0 , SEEK_END); + unsigned long lSize = ftell (pFile); + rewind (pFile); + + // allocate memory to contain the whole file: + unsigned long bufferSize = sizeof(char)*(lSize+1); + char * buffer = (char*) malloc (bufferSize); + if (buffer == 0 ) + { + fprintf(stderr,RED "Could not allocate enough memory for file %s \n" NORMAL,filename); + fclose(pFile); + return 0; + } + + // copy the file into the buffer: + size_t result = fread (buffer,1,lSize,pFile); + if (result != lSize) + { + free(buffer); + fprintf(stderr,RED "Could not read the whole file onto memory %s \n" NORMAL,filename); + fclose(pFile); + return 0; + } + + /* the whole file is now loaded in the memory buffer. */ + + // terminate + fclose (pFile); + + buffer[lSize]=0; //Null Terminate Buffer! + *length = (unsigned int) lSize; + return buffer; +} + + +#ifdef __cplusplus +} +#endif + + + +#endif \ No newline at end of file diff --git a/src/python/2d_pose_estimation/readCOCO.py b/src/python/2d_pose_estimation/readCOCO.py new file mode 100644 index 0000000..7a80f43 --- /dev/null +++ b/src/python/2d_pose_estimation/readCOCO.py @@ -0,0 +1,1300 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2023 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +#Dependencies should be : +#python3 -m pip install tensorflow==2.15.0 numpy tensorboard opencv-python wget + +import sys +import os +import numpy as np +import datetime +#---------------------------------------------- +useGPU = True +if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--cpu"): + useGPU = False +# Set CUDA_VISIBLE_DEVICES to an empty string to force TensorFlow to use the CPU +if (not useGPU): + os.environ['CUDA_VISIBLE_DEVICES'] = '' #<- Force CPU +#---------------------------------------------- +import cv2 +import tensorflow as tf +from tensorflow import keras +from tensorflow.keras.callbacks import TensorBoard +from tensorflow.keras import layers, models +from tensorflow.keras.losses import Loss +from tensorflow.keras.metrics import Metric +from tensorflow.keras.layers import Input, DepthwiseConv2D, Flatten, Dropout, Conv2D, Conv2DTranspose, AvgPool2D, BatchNormalization, ReLU, Reshape, Dense, Add, UpSampling2D, MaxPooling2D +from tensorflow.keras.models import Model +#---------------------------------------------- +dataType = np.uint8 +dataTypeTF = tf.uint8 + +if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--float32"): + dataType = np.float32 + dataTypeTF = tf.float32 + if (sys.argv[i]=="--uint8"): + dataType = np.uint8 + dataTypeTF = tf.uint8 +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#------------------------------------------------------------------------------- +def retrieveModelOutputDimensions(model): + output_layer = model.layers[-1] # Assuming the output layer is the last layer + output_shape = output_layer.output_shape + output_size = (output_shape[1],output_shape[2]) + numberOfHeatmaps = output_shape[3] + print("Number of Heatmaps is ", numberOfHeatmaps) + print("Output Shape is ", output_size) + return output_shape[1],output_shape[2],output_shape[3] +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +def compose(*funcs): + from functools import reduce + if funcs: + return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs) + else: + raise ValueError('Composition of empty sequence not supported.') + +class Conv_Bn_Relu6(keras.layers.Layer): + def __init__(self, filters, kernel_size, strides, padding, name): + super(Conv_Bn_Relu6, self).__init__() + self._name = name + self.block = keras.Sequential() + if name.find('depthwise') == -1: + self.block.add(keras.layers.Conv2D(filters, kernel_size, strides, padding=padding)) + else: + self.block.add(keras.layers.DepthwiseConv2D(kernel_size, strides, padding=padding)) + self.block.add(keras.layers.BatchNormalization()) + if name.find('relu') != -1: + self.block.add(keras.layers.ReLU(6)) + def call(self, inputs, **kwargs): + return self.block(inputs) + +def block(x, filters, t, strides, name): + shortcut = x + + x = compose(Conv_Bn_Relu6(t * filters, (1, 1), (1, 1), 'same', name='{}_conv_bn_relu6'.format(name)), + Conv_Bn_Relu6(None, (3, 3), strides, 'same', name='{}_depthwiseconv_bn_relu6'.format(name)), + Conv_Bn_Relu6(filters, (1, 1), (1, 1), 'same', name='{}_conv_bn'.format(name)))(x) + + if shortcut.shape[-1] == filters and strides == (1, 1): + x = keras.layers.Add(name='{}_add'.format(name))([x, shortcut]) + + return x + +def add_block(x, filters, t, strides, n, name): + x = block(x, filters, t, strides, name='{}_1'.format(name)) + for i in range(n - 1): + x = block(x, filters, t, (1, 1), name='{}_{}'.format(name, i + 2)) + + return x + +#https://ustccoder.github.io/2020/03/22/feature_extraction%20MobileNet_V2/ +def create_keypoints_modelNew(inputHeight, inputWidth, inputChannels, outputWidth, outputHeight, numKeypoints, midSectionRepetitions=5, activation='swish'): + input_shape = (inputHeight, inputWidth, inputChannels) + input_tensor = Input(shape=input_shape) + #x = input_tensor + + # Create Rescaling layer + rescale_layer = tf.keras.layers.experimental.preprocessing.Rescaling(scale=1./255) + normalized_tensor = rescale_layer(input_tensor) + x = normalized_tensor + + x = mobilenet_block(x, filters=32, strides=(2, 2), activation=activation, name='block1') + x = add_block(x, filters=16, t=1, strides=(1, 1), n=1, name='block2') + x = add_block(x, filters=24, t=6, strides=(2, 2), n=2, name='block3') + x = add_block(x, filters=32, t=6, strides=(2, 2), n=3, name='block4') + x = add_block(x, filters=64, t=6, strides=(2, 2), n=4, name='block5') + x = add_block(x, filters=96, t=6, strides=(1, 1), n=3, name='block6') + x = add_block(x, filters=160, t=6, strides=(2, 2), n=3, name='block7') + x = add_block(x, filters=320, t=6, strides=(1, 1), n=1, name='block8') + + x = compose(Conv_Bn_Relu6(1280, (1, 1), (1, 1), 'same', name='conv2'),AvgPool2D(pool_size=4, name='global_averagepool'))(x) + + x = Dense(units=int((outputHeight * outputWidth * numKeypoints) // 9), activation=activation, name='dense')(x) + + x = Reshape((numKeypoints, outputHeight, outputWidth), name='reshape')(x) + + model = Model(inputs=input_tensor, outputs=x, name='MobileNet-V2-KeyPoints') + model.summary() + + return model +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#------------------------------------------------------------------------------- +def mobilenet_block(x, filters, strides, activation, dropoutRate, name=None): + x = DepthwiseConv2D(kernel_size = 3, strides = strides, padding = 'same', activation=activation)(x) #, activation=activation + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2D(filters = filters, kernel_size = 1, strides = 1, activation=activation)(x) # , activation=activation + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + if dropoutRate>0.0: + x = Dropout(dropoutRate)(x) + + return x + +# Define the CNN model for keypoints prediction +def create_keypoints_model(inputHeight, inputWidth, inputChannels, outputWidth, outputHeight, numKeypoints, midSectionRepetitions=5 ,activation='relu', dropoutRate=0.0, baseChannels = 64): + input_shape = (inputHeight, inputWidth, inputChannels) + + input_tensor = Input(shape=input_shape) + + # Create Rescaling layer + rescale_layer = tf.keras.layers.experimental.preprocessing.Rescaling(scale=1./255) + normalized_tensor = rescale_layer(input_tensor) + + + x = mobilenet_block(normalized_tensor, filters=baseChannels, strides=1, dropoutRate=dropoutRate, activation=activation) + #x = Dropout(0.2)(x) + x = mobilenet_block(x, filters=baseChannels*2, strides=2, dropoutRate=dropoutRate, activation=activation) + #x = Dropout(0.2)(x) + x = mobilenet_block(x, filters=baseChannels*3, strides=1, dropoutRate=dropoutRate, activation=activation) + #x = Dropout(0.2)(x) + x = mobilenet_block(x, filters=baseChannels*4, strides=2, dropoutRate=dropoutRate, activation=activation) + x = mobilenet_block(x, filters=baseChannels*5, strides=1, dropoutRate=dropoutRate, activation=activation) + x = mobilenet_block(x, filters=baseChannels*8, strides=2, dropoutRate=dropoutRate, activation=activation) + + for _ in range(midSectionRepetitions): + x = mobilenet_block(x, filters=baseChannels*16, strides=1, dropoutRate=dropoutRate, activation=activation) + + x = mobilenet_block(x, filters=baseChannels*24, strides=2, dropoutRate=dropoutRate, activation=activation) + x = mobilenet_block(x, filters=baseChannels*16, strides=1, dropoutRate=dropoutRate, activation=activation) + + x = AvgPool2D(pool_size=4)(x) # Adjust pool_size based on your desired output size + + x = Conv2DTranspose(filters=baseChannels*12, kernel_size=4, strides=2, padding='same', activation=activation)(x) + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2DTranspose(filters=baseChannels*10, kernel_size=4, strides=2, padding='same', activation=activation)(x) + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2DTranspose(filters=baseChannels*8, kernel_size=4, strides=2, padding='same', activation=activation)(x) + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2DTranspose(filters=baseChannels*3, kernel_size=4, strides=2, padding='same', activation=activation)(x) + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2DTranspose(filters=baseChannels, kernel_size=4, strides=2, padding='same', activation=activation)(x) + if (activation=='relu'): #Disabled after switching to swish/selu + x = BatchNormalization()(x) + x = ReLU()(x) + + x = Conv2D(filters=numKeypoints, kernel_size=1, strides=1, activation='linear')(x) + + # Adjust the Reshape layer based on the original output size + x = Reshape((outputHeight, outputWidth,numKeypoints))(x) # Reshape to the desired size + model = Model(inputs=input_tensor, outputs=x) + model.summary() + + return model +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +def Conv2D_BN_Leaky(*args, **kwargs): + conv_kwargs = { + 'use_bias': True, + 'padding': 'same', + 'kernel_initializer': 'he_normal' + } + conv_kwargs.update(kwargs) + return Conv2D(*args, **conv_kwargs) + +def resblock_module(tensor, num_filters): + skip = Conv2D_BN_Leaky(num_filters, (1, 1))(tensor) + + tensor = Conv2D_BN_Leaky(num_filters//2, (1, 1))(tensor) + tensor = Conv2D_BN_Leaky(num_filters//2, (3, 3), padding='same')(tensor) + tensor = Conv2D_BN_Leaky(num_filters, (1, 1))(tensor) + tensor = Add()([skip, tensor]) + + return tensor + +def hourglass_module(input_tensor, stage): + stage -= 1 + skip = resblock_module(input_tensor, 256) + tensor = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(input_tensor) + tensor = resblock_module(tensor, 256) + if stage == 0: + tensor = resblock_module(tensor, 256) + else: + tensor = hourglass_module(tensor, stage) + tensor = resblock_module(tensor, 256) + tensor = UpSampling2D(2)(tensor) + tensor = Add()([skip, tensor]) + + return tensor + +def front_module(input_tensor, num_filters=256): + tensor = Conv2D_BN_Leaky(num_filters//4, (7, 7), (2, 2), padding='same')(input_tensor) + tensor = resblock_module(tensor, num_filters//2) + tensor = MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(tensor) + tensor = resblock_module(tensor, num_filters//2) + tensor = resblock_module(tensor, num_filters) + + return tensor + +def stack_module(input_tensor, num_points, num_filters=256, stage=4, activation="sigmoid", is_head=False): + tensor = hourglass_module(input_tensor, stage) + tensor = resblock_module(tensor, num_filters) + tensor = Conv2D_BN_Leaky(num_filters, (1, 1))(tensor) + outputs = Conv2D(num_points, (1, 1))(tensor) + if activation == "sigmoid": + outputs = tf.keras.layers.Activation("sigmoid")(outputs) + elif activation == "softmax": + outputs = tf.keras.layers.Softmax(axis=-1)(outputs) + + if is_head: + return outputs + else: + tensor = Conv2D(num_filters, (1, 1))(tensor) + skip = Conv2D(num_filters, (1, 1))(outputs) + tensor = Add()([skip, tensor]) + return outputs, tensor + +def create_hourglass_keypoints_model(input_shape, num_stacks=8, num_points=15, num_filters=256, stage=4, activation="sigmoid", pretrained_weights=None): + output_list = [] + inputs = Input(input_shape) + tensor = front_module(inputs, num_filters=num_filters) + + for _ in range(num_stacks - 1): + skip = tensor + outputs, tensor = stack_module( + tensor, num_points, + num_filters=num_filters, + stage=stage, + activation=activation) + tensor = Add()([skip, tensor]) + output_list.append(outputs) + + outputs = stack_module( + tensor, num_points, + num_filters=num_filters, + stage=stage, + activation=activation, + is_head=True) + output_list.append(outputs) + + model = tf.keras.models.Model(inputs, output_list) + + model.summary() + + x,y,num = retrieveModelOutputDimensions(model) + + return model +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +def logTrainingParameters(cfg, log_dir): + try: + # Create a summary writer + param_log = tf.summary.create_file_writer(log_dir) + + with param_log.as_default(): + # Convert the dictionary to a formatted string + params_str = "\n".join([f"{key}: {value}" for key, value in cfg.items()]) + # Log the parameters as text + tf.summary.text("Training Parameters", params_str, step=0) + except Exception as e: + print(f"Error storing logging parameters in tensorboard: {e}") +#------------------------------------------------------------------------------- +def logSomeInputsAndOutputs(inputs, outputs, labels, log_dir, samples=100): + try: + # Create a summary writer + image_log = tf.summary.create_file_writer(log_dir) + + with image_log.as_default(): + + sample_indices = np.random.choice(len(inputs), size=min(samples,len(inputs)), replace=False) + + for logID in sample_indices: + #Store the image as float32 [0..1] RGB to make sure tensorboard visualizes it correctly + image_as_float = inputs[logID].astype(np.float32) + image = image_as_float / 255.0 + + # Convert input and output arrays to TensorFlow tensors + bgr_image_tensor = tf.convert_to_tensor([image], dtype=tf.float32) + + # Swap BGR to RGB + rgb_image_tensor = tf.reverse(bgr_image_tensor, axis=[-1]) + + # Write input image summary + tf.summary.image(f"Image {logID} Input", rgb_image_tensor , step=logID) + + # Write output image summary + heatmapID = 0 + for heatmapID in range(0,18): + heatmap = outputs[logID,:,:,heatmapID] + #print(f"Heatmap {heatmapID} dimensions: {heatmap.shape}") + # Add batch and channel dimensions + heatmapS = np.squeeze(heatmap) + heatmapS = np.expand_dims(heatmapS, axis=-1) + heatmap_as_float = heatmapS.astype(np.float32) + output_image_tensor = tf.convert_to_tensor([heatmap_as_float], dtype=tf.float32) + + thisOutputlabel = "#%u" % heatmapID + if (heatmapID < len(labels)): + thisOutputlabel = labels[heatmapID] + + tf.summary.image(f"Image {logID} Output / {thisOutputlabel}", output_image_tensor, step=logID) + heatmapID = heatmapID + 1 + except Exception as e: + print(f"Error storing image in tensorboard: {e}") + sys.exit(1) +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#------------------------------------------------------------------------------- +def checkIfFileExists(filename): + return os.path.isfile(filename) +#------------------------------------------------------------------------------- +def convert_bytes(num): + #This function will convert bytes to MB.... GB... strings + step_unit = 1000.0 #1024 bad the size + for x in ['bytes', 'KB', 'MB', 'GB', 'TB']: + if num < step_unit: + return "%3.1f %s" % (num, x) + num /= step_unit +#------------------------------------------------------------------------------- +def printTFVersion(): + global useGPU + print("") + print("Tensorflow version : ",tf.__version__) + #print("Keras version : ",keras.__version__) <- no longer available in TF-2.13 + print("Numpy version : ",np.__version__) + #----------------------------- + from tensorflow.python.platform import build_info as tf_build_info + print("TF/CUDA version : ",tf_build_info.build_info['cuda_version']) + print("TF/CUDNN version : ",tf_build_info.build_info['cudnn_version']) + print("Use GPU : ",useGPU) + #----------------------------- + if useGPU: + physical_devices = tf.config.list_physical_devices('GPU') + if physical_devices: + gpuID = 0 + for gpu in physical_devices: + print("GPU #",gpuID," Name:", gpu.name) + try: + # Note: The following code may not be available in older versions of TensorFlow + memory_info = tf.config.experimental.get_memory_info('GPU:%u'%gpuID) + print("GPU #",gpuID," Memory Currently Used (in MB):", memory_info['current'] / (1024**2)) + print("GPU #",gpuID," Memory Peak Used (in MB):", memory_info['peak'] / (1024**2)) + except Exception as e: + print(f"Error getting memory info for GPU #{gpuID}: {e}") + gpuID += 1 + else: + print("No GPU available.") + print("") + #----------------------------- +#------------------------------------------------------------------------------- +class RSquaredMetric(Metric): + def __init__(self, name='r_squared', **kwargs): + super(RSquaredMetric, self).__init__(name=name, **kwargs) + self.ssr = self.add_weight(name='ssr', initializer='zeros') + self.sst = self.add_weight(name='sst', initializer='zeros') + + def update_state(self, y_true, y_pred, sample_weight=None): + # Explicitly cast y_true to float32 + y_true = tf.cast(y_true, tf.float32) + y_pred = tf.cast(y_pred, tf.float32) + + # Reshape y_true and y_pred to ensure they're 1D tensors + y_true = tf.reshape(y_true, [-1]) + y_pred = tf.reshape(y_pred, [-1]) + + # Calculate the sum of squares of residuals + ssr_update = tf.reduce_sum(tf.square(y_true - y_pred)) + self.ssr.assign_add(ssr_update) + + # Calculate the total sum of squares + mean_y_true = tf.reduce_mean(y_true) + sst_update = tf.reduce_sum(tf.square(y_true - mean_y_true)) + self.sst.assign_add(sst_update) + + def result(self): + return 1 - (self.ssr / self.sst) + + def reset_state(self): + self.ssr.assign(0.0) + self.sst.assign(0.0) +#------------------------------------------------------------------------------- +class PCKMetric(Metric): + def __init__(self, name='pck', threshold=0.01, **kwargs): + super(PCKMetric, self).__init__(name=name, **kwargs) + self.threshold = threshold + self.total_correct_keypoints = self.add_weight(name='total_correct_keypoints', initializer='zeros') + self.total_keypoints = self.add_weight(name='total_keypoints', initializer='zeros') + + def update_state(self, y_true, y_pred, sample_weight=None): + # Explicitly cast y_true to float32 + y_true = tf.cast(y_true, tf.float32) + y_pred = tf.cast(y_pred, tf.float32) + + # Assuming y_true and y_pred have shape (batch_size, num_keypoints*2) + batch_size = tf.shape(y_true)[0] + + # Reshape to (batch_size, num_keypoints, 2) + y_true = tf.reshape(y_true, (batch_size, -1, 2)) + y_pred = tf.reshape(y_pred, (batch_size, -1, 2)) + + # Calculate Euclidean distances between true and predicted keypoints + distances = tf.norm(y_true - y_pred, axis=-1) + + # Count correct keypoints within the threshold + correct_keypoints = tf.cast(tf.reduce_sum(tf.cast(distances <= self.threshold, tf.float32)), tf.float32) + + # Update total correct keypoints and total keypoints + self.total_correct_keypoints.assign_add(correct_keypoints) + self.total_keypoints.assign_add(tf.cast(tf.reduce_sum(tf.ones_like(distances)), tf.float32)) + + def result(self): + # Calculate the percentage of correct keypoints + return self.total_correct_keypoints / self.total_keypoints if self.total_keypoints > 0 else 0.0 + + def reset_state(self): + # Reset counts at the start of each epoch or batch + self.total_correct_keypoints.assign(0.0) + self.total_keypoints.assign(0.0) +#------------------------------------------------------------------------------- +# Define Focal Loss +# categorical_focal_loss and binary_focal_loss +# https://github.com/aldi-dimara/keras-focal-loss/blob/master/focal_loss.py +class FocalLoss(Loss): + def __init__(self, alpha=0.25, gamma=2.0, num_joints=17, **kwargs): + super(FocalLoss, self).__init__(**kwargs) + self.alpha = alpha + self.gamma = gamma + self.num_joints = num_joints + + def call(self, y_true, y_pred): + #batch_size = tf.shape(y_true)[0] + y_true = tf.cast(y_true, tf.float32)# / 255.0 + y_pred = tf.cast(y_pred, tf.float32)# / 255.0 + + # Split the y_true into joint heatmaps and background heatmap + y_true_joints = y_true[:, :, :, :self.num_joints] + y_true_background = y_true[:, :, :, self.num_joints:] + + # Split the y_pred into joint predictions and background prediction + y_pred_joints = y_pred[:, :, :, :self.num_joints] + y_pred_background = y_pred[:, :, :, self.num_joints:] + + # Calculate focal loss for joint heatmaps + joint_pos_mask = tf.cast(y_true_joints > 0, dtype=tf.float32) + joint_neg_mask = tf.cast(y_true_joints == 0, dtype=tf.float32) + + alpha_factor_joint = self.alpha * joint_pos_mask + (1 - self.alpha) * joint_neg_mask + focal_weight_joint = alpha_factor_joint * tf.pow(1 - y_pred_joints, self.gamma) + + joint_pos_loss = focal_weight_joint * tf.square(y_pred_joints - y_true_joints) + joint_neg_loss = (1 - focal_weight_joint) * tf.square(y_pred_joints) + + num_joint_pos = tf.reduce_sum(joint_pos_mask) + joint_pos_loss = tf.reduce_sum(joint_pos_loss) + joint_neg_loss = tf.reduce_sum(joint_neg_loss) + + joint_loss = tf.cond(tf.greater(num_joint_pos, 0), lambda: (joint_pos_loss + joint_neg_loss) / num_joint_pos, lambda: joint_neg_loss) + + # Calculate focal loss for the background heatmap + background_pos_mask = tf.cast(y_true_background > 0, dtype=tf.float32) + background_neg_mask = tf.cast(y_true_background == 0, dtype=tf.float32) + + alpha_factor_background = self.alpha * background_pos_mask + (1 - self.alpha) * background_neg_mask + focal_weight_background = alpha_factor_background * tf.pow(1 - y_pred_background, self.gamma) + + background_pos_loss = focal_weight_background * tf.square(y_pred_background - y_true_background) + background_neg_loss = (1 - focal_weight_background) * tf.square(y_pred_background) + + num_background_pos = tf.reduce_sum(background_pos_mask) + background_pos_loss = tf.reduce_sum(background_pos_loss) + background_neg_loss = tf.reduce_sum(background_neg_loss) + + background_loss = tf.cond(tf.greater(num_background_pos, 0), lambda: (background_pos_loss + background_neg_loss) / num_background_pos, lambda: background_neg_loss) + + # Combine joint and background losses + total_loss = joint_loss + background_loss + + return total_loss +# Define your custom loss function +def focal_loss(y_true, y_pred): + return FocalLoss()(y_true, y_pred) +#------------------------------------------------------------------------------- +#Define some more losses : https://github.com/stefanopini/simple-HRNet/blob/master/losses/loss.py#L58 +#https://github.com/microsoft/human-pose-estimation.pytorch/blob/master/lib/core/loss.py +class JointsMSELoss(Loss): + def __init__(self, num_joints=17, weight_factor=10.0, **kwargs): + super(JointsMSELoss, self).__init__(**kwargs) + self.num_joints = num_joints + self.weight_factor = weight_factor + self.criterion = tf.keras.losses.MeanSquaredError() + + def call(self, y_true, y_pred): + y_true = tf.cast(y_true, tf.float32)# / 255.0 + y_pred = tf.cast(y_pred, tf.float32)# / 255.0 + + # Split the y_true and y_pred into joint heatmaps and background heatmap + y_true_joints = y_true[:, :, :, :self.num_joints] + y_true_background = y_true[:, :, :, self.num_joints:] + + y_pred_joints = y_pred[:, :, :, :self.num_joints] + y_pred_background = y_pred[:, :, :, self.num_joints:] + + # Compute loss for joint heatmaps + joint_loss = 0.0 + for idx in range(self.num_joints): + heatmap_pred = y_pred_joints[:, :, :, idx] + heatmap_gt = y_true_joints[:, :, :, idx] + + # Calculate the weight for this joint + weight = tf.reduce_mean(heatmap_gt) * self.weight_factor + 1.0 + + joint_loss += 0.5 * self.criterion(heatmap_gt, heatmap_pred) * weight + + joint_loss /= tf.cast(self.num_joints, dtype=tf.float32) + + # Compute loss for the background heatmap + background_loss = 0.5 * self.criterion(y_true_background, y_pred_background) + + # Combine joint and background losses + total_loss = joint_loss + background_loss + + return total_loss +# Define your custom loss function +def jointsMSE_loss(y_true, y_pred): + return JointsMSELoss()(y_true, y_pred) +#------------------------------------------------------------------------------- +class VanillaMSELoss(tf.keras.losses.Loss): + def __init__(self, **kwargs): + super(VanillaMSELoss, self).__init__(**kwargs) + + def call(self, y_true, y_pred): + # Ensure both y_true and y_pred are cast to float32 + y_true = tf.cast(y_true, tf.float32) + y_pred = tf.cast(y_pred, tf.float32) + + # Compute the squared difference + squared_difference = tf.square(y_true - y_pred) + + # Compute the mean over all elements + mse_loss = tf.reduce_mean(squared_difference) + + return mse_loss + +# Define your custom loss function +def vanilla_mse_loss(y_true, y_pred): + return VanillaMSELoss()(y_true, y_pred) +#------------------------------------------------------------------------------- +class WeightedMSELoss(tf.keras.losses.Loss): + def __init__(self, last_heatmap_weight=2.0, **kwargs): + super(WeightedMSELoss, self).__init__(**kwargs) + self.last_heatmap_weight = last_heatmap_weight + + def call(self, y_true, y_pred): + # Ensure both y_true and y_pred are cast to float32 + y_true = tf.cast(y_true, tf.float32) + y_pred = tf.cast(y_pred, tf.float32) + + # Compute the squared difference + squared_difference = tf.square(y_true - y_pred) + + # Apply weighting to the last heatmap + last_heatmap_weighted = squared_difference[..., -1] * self.last_heatmap_weight + squared_difference = tf.concat([squared_difference[..., :-1], tf.expand_dims(last_heatmap_weighted, axis=-1)], axis=-1) + + # Compute the mean over all elements + mse_loss = tf.reduce_mean(squared_difference) + + return mse_loss + +# Define your custom loss function +def weighted_mse_loss(y_true, y_pred): + return WeightedMSELoss(last_heatmap_weight=2.0)(y_true, y_pred) +#------------------------------------------------------------------------------- +def flip_data(inputs_tf, outputs_tf, flip_x=True, flip_y=True): + print("Flipping data X:",flip_x," Y:",flip_y) + # Flip along the X dimension if requested + if flip_x: + inputs_tf = tf.image.flip_left_right(inputs_tf) + outputs_tf = tf.image.flip_left_right(outputs_tf) + + # Flip along the Y dimension if requested + if flip_y: + inputs_tf = tf.image.flip_up_down(inputs_tf) + outputs_tf = tf.image.flip_up_down(outputs_tf) + + return inputs_tf, outputs_tf +#------------------------------------------------------------------------------- +def read_json_file(file_path): + import json + try: + with open(file_path, 'r') as file: + data = json.load(file) + return data + except FileNotFoundError: + print(f"Error: File '{file_path}' not found.") + except json.JSONDecodeError: + print(f"Error: Invalid JSON format in file '{file_path}'.") + except Exception as e: + print(f"Error: {e}") +#------------------------------------------------------------------------------- +def download_image(url, save_path): + import wget + try: + # Check if the file already exists at the save_path + if os.path.exists(save_path): + #print(f"File already exists at {save_path}. Skipping download.") + return True + else: + # Download the image using wget + wget.download(url, save_path) + print(f"\nImage downloaded successfully to {save_path}") + except Exception as e: + print(f"Error downloading image: {e}") + return False +#------------------------------------------------------------------------------- +def resize_image_with_borders(image, target_size=(300, 300)): + try: + # Get the original image size + originalWidth = image.shape[1] + originalHeight = image.shape[0] + #Notice that we use a different convention than OpenCV + newWidth = target_size[0] + newHeight = target_size[1] + + # Calculate the aspect ratios of the original and target sizes + aspect_ratio_original = originalWidth / originalHeight + aspect_ratio_target = newWidth / newHeight + + # Determine the resizing factor and size for maintaining the aspect ratio + if aspect_ratio_original > aspect_ratio_target: + newHeight = int(newWidth / aspect_ratio_original) + else: + newWidth = int(newHeight * aspect_ratio_original) + + # Resize the image while maintaining the aspect ratio + resized_image = cv2.resize(image, (newWidth, newHeight)) + + # Create a new image with a black background + new_image = np.zeros((target_size[1], target_size[0], 3), dtype=dataType) + + # Calculate the position to paste the resized image onto the new image + x_offset = (target_size[0] - newWidth) // 2 + y_offset = (target_size[1] - newHeight) // 2 + + # Paste the resized image onto the new image + new_image[y_offset:y_offset + newHeight, x_offset:x_offset + newWidth] = resized_image + + keypointXMultiplier = newWidth / originalWidth + keypointYMultiplier = newHeight / originalHeight + keypointXOffset = x_offset + keypointYOffset = y_offset + + return new_image, keypointXMultiplier, keypointYMultiplier, keypointXOffset, keypointYOffset + + except Exception as e: + print(f"Error resizing image: {e}") + return image, 0.0, 0.0, 0.0, 0.0 +#------------------------------------------------------------------------------- +def emptyImage(labels=None, target_size=(300, 300), output_target_size=(64, 64), heatmapActive=0, heatmapDeactivated=255): + # Create an empty black image + image = np.zeros((target_size[1], target_size[0], 3), dtype=dataType) + + # Fill the image with randomized grayscale values + image[:, :] = np.ones((target_size[1], target_size[0],3), dtype=dataType) * 255 + scale = np.random.rand() + image = image * (scale) + + heatmap = np.full((output_target_size[1], output_target_size[0], len(labels)+1), heatmapDeactivated, dtype=dataType) + heatmap[:,:,-1] = heatmapActive + + return image, heatmap +#------------------------------------------------------------------------------- +def syntheticImage(labels=None, target_size=(300, 300), output_target_size=(64, 64), heatmapActive=0, heatmapDeactivated=255): + # Create an empty black image + image = np.zeros((target_size[1], target_size[0], 3), dtype=dataType) + + # Fill the image with randomized grayscale values + image[:, :] = np.random.rand(target_size[1], target_size[0], 3) * 255 + + heatmap = np.full((output_target_size[1], output_target_size[0], len(labels)+1), heatmapDeactivated, dtype=dataType) + heatmap[:,:,-1] = heatmapActive + + return image, heatmap +#------------------------------------------------------------------------------- +def add_gaussian_noise(image, maxValue=1.0, magnitude=0.01): + # Generate Gaussian noise with the same shape as the input image + noise = np.random.normal(scale=magnitude, size=image.shape) + + # Add noise to the image + corrupted_image = image + noise + + # Clip values to be within [0, 1] + corrupted_image = np.clip(corrupted_image, 0, maxValue) + + return corrupted_image +#------------------------------------------------------------------------------- +def generate_one_hot_images(keypointsList,labels, target_size=(64, 64), heatmapActive=0, heatmapDeactivated=255, simple=True): + num_labels = len(labels) + numberOfHeatmaps = num_labels + 1 #Labels + Bkg label + heatmaps = np.full((target_size[1], target_size[0], numberOfHeatmaps), heatmapDeactivated, dtype=dataType) + heatmaps[:,:,-1] = heatmapActive + + """ + # Define the kernel outside the loop + kernel = np.array([[0.2, 0.4, 0.2], + [0.4, 1.0, 0.4], + [0.2, 0.4, 0.2]]) * heatmapActive + # Normalize the kernel to ensure that the total intensity remains the same + kernel /= np.sum(kernel) #Don;t normalize to boost gradient + """ + + for i in range(num_labels): + #For each joint Label for each keypoint list + for keypoints in keypointsList: + x = int(keypoints[i*3+0]*target_size[0]) + y = int(keypoints[i*3+1]*target_size[1]) + v = keypoints[i*3+2] + if (x!=0) and (y!=0) and (v!=0): + #if (simple): + heatmaps[y,x,i] = heatmapActive #1 hot + heatmaps[y,x,-1] = heatmapDeactivated + """ + else: + #Complex pattern for bigger training targets.. + if (y <= 1 or y >= target_size[1]-1 ) or (x <= 1 or x >= target_size[0]-1): + # If at the border, set the center pixel to v without interpolation + heatmap[y,x,i] = heatmapMagnitude + union_heatmap[y,x,-1] = 0 + else: + heatmap[y-1:y+2, x-1:x+2,i] += kernel #more spread.. + union_heatmap[y-1:y+2, x-1:x+2,-1] -= kernel + """ + #----------------------------------------------------------------------------- + + #add union heatmap making sure it is in range + #heatmaps = np.clip(heatmaps, 0, heatmapMagnitude) + return heatmaps +#------------------------------------------------------------------------------- +def processImage(inputDataset,outputDataset,sampleNumber,image_path, keypointsList=list(), labels=list(), target_size=(300, 300), output_target_size=(64,64), augment=False, heatmapActive=0, heatmapDeactivated=255): + # Step 1: Read the image using OpenCV + image_raw = cv2.imread(image_path) + # Step 2: Convert the image to float32 + image = image_raw.astype(dataType) + + #if (augment): + # image = add_gaussian_noise(image, maxValue=1.0, magnitude=0.01) # <- This is not working well.. + + originalWidth = image.shape[1] + originalHeight = image.shape[0] + keypointXMultiplier = 1.0 + keypointYMultiplier = 1.0 + keypointXOffset = 0.0 + keypointYOffset = 0.0 + + image, keypointXMultiplier, keypointYMultiplier, keypointXOffset, keypointYOffset = resize_image_with_borders(image,target_size) + inputDataset[sampleNumber,:,:,:] = image + resizedWidth = image.shape[1] + resizedHeight = image.shape[0] + + for keypoints in keypointsList: + numberOfJoints = len(keypoints) + for i in range(0,int(numberOfJoints/3)): + x = (keypointXMultiplier * keypoints[i*3+0]) + keypointXOffset + y = (keypointYMultiplier * keypoints[i*3+1]) + keypointYOffset + v = keypoints[i*3+2] + if v > 0: # Only correct visible keypoints + #Correct keypoints + keypoints[i*3+0] = x / target_size[0] + keypoints[i*3+1] = y / target_size[1] + + # Calculate the heatmaps + oneHotImages = generate_one_hot_images(keypointsList, labels, target_size=output_target_size, heatmapActive=heatmapActive, heatmapDeactivated=heatmapDeactivated) + outputDataset[sampleNumber,:,:,:] = oneHotImages + + return inputDataset , outputDataset +#------------------------------------------------------------------------------- +def compileJSONAssociations(json_data): + print("Compiling annotation->image associations") + annotations={} + for annotation in json_data['annotations']: + image_id = annotation['image_id'] + if image_id not in annotations: + annotations[image_id] = [] + annotations[image_id].append(annotation) + return annotations +#------------------------------------------------------------------------------- +def createTrainingSetFromJSONFile(jsonPath, + database, + cache_directory = 'cache/data/coco/val2017/', + start_index = 0, + end_index = None, + target_size = (200,200), + output_target_size = (120,120), + RGBMagnitude = 255, + heatmapActive = 0, + heatmapDeactivated = 255, + mem = 1.0, + visualize = False, + preloadedJSON = None, + preloadedAssociations = None + ): + # Call the function to read the JSON file + if (not preloadedJSON): + json_data = read_json_file(jsonPath) + print("JSON has ",len(json_data['annotations'])," annotations") + print("JSON has ",len(json_data['images'])," images") + else: + json_data = preloadedJSON + + augment = False + if ("train" in jsonPath) and (not preloadedJSON): + augment = True + + msgTick = 0 + labels = json_data['categories'][0]['keypoints'] + + # Check if the file was successfully read + if json_data: + if not os.path.exists(cache_directory): + # Create the 'cache' directory if it doesn't exist to allow downloading to work + os.makedirs(cache_directory) + print(f"Directory '{cache_directory}' created.") + + # Create a dictionary to organize annotations by image ID and optimize reading if not already populated + if (preloadedAssociations): + annotations_of_image = preloadedAssociations + else: + annotations_of_image = compileJSONAssociations(json_data) + + #Decide on the number of entries to read + end_index = end_index or len(json_data['images']) + if ("train" in jsonPath) and (mem!=1.0): + end_index = start_index + int(mem) + + #Allocate all of the I/O tensors in a contiguous memory block in a single step + inputDataset = np.zeros((end_index - start_index,*target_size,3), dtype=dataType) + outputDataset = np.zeros((end_index - start_index,*output_target_size,len(labels)+1), dtype=dataType) + + for imageID in range(start_index,end_index): + source = "%s/%s" % (database,json_data['images'][imageID]["file_name"]) + cacheTarget = "%s/%s" % (cache_directory,json_data['images'][imageID]["file_name"]) + download_image(source,cacheTarget) + + width = int(json_data['images'][imageID]["width"]) + height = int(json_data['images'][imageID]["height"]) + ID = int(json_data['images'][imageID]["id"]) + + keypointsList = list() + numberOfSkeletons = 0 + + if ID in annotations_of_image: + annotations = annotations_of_image[ID] + for annotation in annotations: + keypointsList.append(annotation["keypoints"]) + numberOfSkeletons = numberOfSkeletons + 1 + #break #<- Single person + + if (msgTick%31==0) and not preloadedJSON : + print(f"\r Image ID: {imageID} /",len(json_data['images']) , end=" ") + print(f"Width: {width}, Height: {height}, Skeletons: {numberOfSkeletons} \r",end=" ") + msgTick = msgTick + 1 + + inputDataset,outputDataset = processImage( + inputDataset, + outputDataset, + imageID, + cacheTarget, + keypointsList, + labels=labels, + target_size=target_size, + output_target_size = output_target_size, + heatmapActive = heatmapActive, + heatmapDeactivated = heatmapDeactivated, + augment=augment + ) + """ + if (augment) and not preloadedJSON: + #Add random noise Images.. + for syntheticID in range(100): + thisInput,thisOutputs = syntheticImage(labels=json_data['categories'][0]['keypoints'], + target_size=target_size, + output_target_size=output_target_size, + heatmapMagnitude=heatmapMagnitude + ) + inputs.append(thisInput) + outputs.append(thisOutputs) + + #Add random empty Images.. + for emptyID in range(100): + thisInput,thisOutputs = emptyImage(labels=json_data['categories'][0]['keypoints'], + target_size=target_size, + output_target_size=output_target_size, + heatmapMagnitude=heatmapMagnitude + ) + inputs.append(thisInput) + outputs.append(thisOutputs) + """ + return inputDataset,outputDataset,labels + return inputs,outputs,labels +#============================================================================================ +#============================================================================================ +#============================================================================================ +#============================================================================================ +#============================================================================================ +class TrainingDataGenerator(tf.keras.utils.Sequence): + def __init__(self, json_path, database, cache_directory, target_size, output_target_size, RGBMagnitude, heatmapActive, heatmapDeactivated, mem, visualize, batch_size): + self.json_path = json_path + self.json_data = read_json_file(json_path) + self.labels = self.json_data['categories'][0]['keypoints'] + self.associations = compileJSONAssociations(self.json_data) + self.database = database + self.cache_directory = cache_directory + self.target_size = target_size + self.output_target_size = output_target_size + self.RGBMagnitude = RGBMagnitude + self.heatmapActive = heatmapActive, + self.heatmapDeactivated = heatmapDeactivated, + self.mem = mem + self.visualize = visualize + self.batch_size = batch_size + + # Load JSON data + self.json_data = read_json_file(json_path) + self.indices = np.arange(len(self.json_data['images'])) + np.random.shuffle(self.indices) + + def __len__(self): + return len(self.indices) // self.batch_size + + def __getitem__(self, index): + start_index = index * self.batch_size + end_index = min(start_index + self.batch_size, len(self.indices)) + batch_indices = self.indices[start_index:end_index] + + xs = np.zeros((len(batch_indices), self.target_size[1], self.target_size[0], 3), dtype=dataType) + ys = np.zeros((len(batch_indices), len(self.labels) + 1, self.output_target_size[1], self.output_target_size[0]), dtype=dataType) + + for i, imageID in enumerate(batch_indices): + source = f"{self.database}/{self.json_data['images'][imageID]['file_name']}" + cacheTarget = f"{self.cache_directory}/{self.json_data['images'][imageID]['file_name']}" + + inputs, outputs, labels = createTrainingSetFromJSONFile( + self.json_path, + self.database, + self.cache_directory, + target_size = self.target_size, + output_target_size = self.output_target_size, + RGBMagnitude = self.RGBMagnitude, + heatmapActive = self.heatmapActive, + heatmapDeactivated = self.heatmapDeactivated, + mem = self.mem, + visualize = self.visualize, + start_index = imageID, + end_index = imageID + 1, + preloadedJSON = self.json_data, + preloadedAssociations = self.associations + ) + xs[i] = inputs[0] + ys[i] = outputs[0] + + return xs, ys + +def streamDataset(json_path, + database, + cache_directory = 'cache/data/coco/val2017/', + target_size = (200, 200), + output_target_size = (64, 64), + RGBMagnitude = 255, + heatmapActive = 0, + heatmapDeactivated = 255, + mem = 1.0, + visualize = False, + batch_size = 48, + shuffle_buffer_size = 100 + ): + json_data = read_json_file(json_path) + labels = json_data['categories'][0]['keypoints'] + datasetLength = len(json_data['images']) + + dataset_generator = TrainingDataGenerator(json_path, database, cache_directory, target_size, output_target_size, RGBMagnitude, heatmapActive, heatmapDeactivated, mem, visualize, batch_size) + + dataset = tf.data.Dataset.from_generator( + lambda: iter(dataset_generator), + output_signature=( + tf.TensorSpec(shape=(batch_size, target_size[1], target_size[0], 3), dtype=dataTypeTF), + tf.TensorSpec(shape=(batch_size, len(labels) + 1, output_target_size[1], output_target_size[0]), dtype=dataTypeTF) + ) + ) + + dataset = dataset.shuffle(buffer_size=shuffle_buffer_size) + dataset = dataset.repeat() # Add repeat to make the dataset repeat indefinitely + dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE) + + return dataset, datasetLength, labels +#============================================================================================ +#============================================================================================ +# Main Function +if __name__ == '__main__': + cfg = { + 'COCOTrainingJSONPath':'cache/data/annotations/person_keypoints_train2017.json', + 'COCOTrainingURI':'http://ammar.gr/COCO_HumanPose/data/coco/train2017', + 'COCOTrainingLocalCache':'cache/data/coco/train2017/', + + 'COCOValidationJSONPath':'cache/data/annotations/person_keypoints_val2017.json', + 'COCOValidationURI':'http://ammar.gr/COCO_HumanPose/data/coco/val2017', + 'COCOValidationLocalCache':'cache/data/coco/val2017/', + + 'inputWidth' :220, #140 + 'inputHeight' :220, #140 + 'outputWidth' :96, + 'outputHeight':96, + + 'dropoutRate':0.1, + 'midSectionRepetitions':5, + 'activation':'relu', + 'baseChannels' : 78, + + 'RGBMagnitude': 255, + 'heatmapActive': 0, + 'heatmapDeactivated': 255, + 'streamDataset': False, + 'streamBufferLength': 100, + + 'earlyStoppingPatience':3, + 'earlyStoppingMinDelta':0.0001, + 'datasetUsage':1.0, + 'learningRate':0.0005, + 'batchSize':24, + 'epochs':54, #54 + 'pCK_AP_Threshold':0.05, + 'loss':'mse' + } + + if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--mem"): + cfg['datasetUsage']=float(sys.argv[i+1]) + if (sys.argv[i]=="--stream"): + cfg['streamDataset'] = True + if (sys.argv[i]=="--clear"): + os.system("rm -rf 2d_pose_estimation/tensorboard") + os.system("rm 2d_pose_estimation.zip") + if (sys.argv[i]=="--test"): + model = create_keypoints_model( + cfg['inputHeight'], + cfg['inputWidth'], + 3, + cfg['outputWidth'], + cfg['outputHeight'], + 17+1, + midSectionRepetitions = cfg['midSectionRepetitions'], + activation = cfg['activation'], + baseChannels = cfg['baseChannels'], + dropoutRate = cfg['dropoutRate'] + ) + #hourglass_model = create_hourglass_keypoints_model( input_shape=(cfg['inputHeight'],cfg['inputWidth'],3), num_points=17+1) + sys.exit(0) + + # Set up TensorBoard logging + log_dir = "2d_pose_estimation/tensorboard/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + tensorboard_callback = TensorBoard(log_dir=log_dir, histogram_freq=1) + + + #First of all create the Neural Network model + #Don't load other data in vain if this step fails due to bad configuration.. + #model = create_hourglass_keypoints_model( input_shape=(cfg['inputHeight'],cfg['inputWidth'],3), num_points=18) + model = create_keypoints_model( + cfg['inputHeight'], + cfg['inputWidth'], + 3, + cfg['outputWidth'], + cfg['outputHeight'], + 18, + midSectionRepetitions = cfg['midSectionRepetitions'], + activation = cfg['activation'], + baseChannels = cfg['baseChannels'], + dropoutRate = cfg['dropoutRate'] + ) + cfg['outputWidth'],cfg['outputHeight'],numHeatmaps = retrieveModelOutputDimensions(model) + + if (cfg['streamDataset']): + mem=1.0 #When streaming use everything.. + + #Validation data is not so big and is loaded first in memory.. + onlyTrainingData = True + if (checkIfFileExists(cfg['COCOValidationJSONPath'])): + onlyTrainingData = False + rawValInputs,rawValOutputs,outValLabels = createTrainingSetFromJSONFile( + cfg['COCOValidationJSONPath'], + cfg['COCOValidationURI'], + cfg['COCOValidationLocalCache'], + target_size=(cfg['inputHeight'],cfg['inputWidth']), + output_target_size=(cfg['outputHeight'],cfg['outputWidth']), + heatmapActive = cfg['heatmapActive'], + heatmapDeactivated = cfg['heatmapDeactivated'] + ) + val_inputs = tf.constant(np.array(rawValInputs), dtype=dataTypeTF) + val_outputs = tf.constant(np.array(rawValOutputs), dtype=dataTypeTF) + validationDataset = tf.data.Dataset.from_tensor_slices((val_inputs, val_outputs)) + logSomeInputsAndOutputs(rawValInputs, rawValOutputs, outValLabels, log_dir, samples=100) + + + #The training set is very large, so depending on the system there are two ways to use it + #try streaming it which is very slow due to I/O operations but can work on small VRAM systems + #or load it all in memory and use regular TF mechanisms to train on it + if (checkIfFileExists(cfg['COCOTrainingJSONPath'])): + if (cfg['streamDataset']): + # Modify the way you call the dataset + trainingDataset,trainingDatasetLength,outLabels = streamDataset( + cfg['COCOTrainingJSONPath'], + cfg['COCOTrainingURI'], + cfg['COCOTrainingLocalCache'], + target_size=(cfg['inputHeight'], cfg['inputWidth']), + output_target_size=(cfg['outputHeight'], cfg['outputWidth']), + RGBMagnitude=cfg['RGBMagnitude'], + heatmapActive = cfg['heatmapActive'], + heatmapDeactivated = cfg['heatmapDeactivated'], + batch_size=cfg['batchSize'], + shuffle_buffer_size=cfg['streamBufferLength'] + ) + stepsPerEpoch = trainingDatasetLength // cfg['batchSize'] + else: + rawInputs,rawOutputs,outLabels = createTrainingSetFromJSONFile( + cfg['COCOTrainingJSONPath'], + cfg['COCOTrainingURI'], + cfg['COCOTrainingLocalCache'], + target_size=(cfg['inputHeight'],cfg['inputWidth']), + output_target_size=(cfg['outputHeight'],cfg['outputWidth']), + RGBMagnitude=cfg['RGBMagnitude'], + heatmapActive = cfg['heatmapActive'], + heatmapDeactivated = cfg['heatmapDeactivated'], + mem=cfg['datasetUsage'] + ) + trainingDatasetLength = len(rawInputs) + print("Number of samples:", trainingDatasetLength ) + inputs = tf.constant(np.array(rawInputs) , dtype=dataTypeTF) + outputs = tf.constant(np.array(rawOutputs), dtype=dataTypeTF) + #inputs,outputs = flip_data(inputs,outputs,flip_x=True,flip_y=False) + print("Inputs shape:", inputs.shape) + print("Outputs shape:", outputs.shape) + trainingDataset = tf.data.Dataset.from_tensor_slices((inputs, outputs)).batch(cfg['batchSize']) + stepsPerEpoch = None #<- None is the default and lets TF manage this value + + + # Print the shapes of inputs and outputs + print("Training Configuration :", cfg) + logTrainingParameters(cfg,log_dir) + + + + # Initialize the Adam Optimizer using configuration + optimizer = tf.keras.optimizers.Adam(learning_rate=cfg['learningRate']) + + # Define EarlyStopping callback + from tensorflow.keras.callbacks import EarlyStopping + early_stopping = EarlyStopping( + #Regular loss monitoring + monitor = 'loss', # Monitor the loss metric + mode = 'min', # Mode should be 'min' because we want to minimize the loss metric + + #Smarter pck monitoring + #monitor = 'val_pck_metric', # Monitor the PCK metric + #mode = 'max', # Mode should be 'max' because we want to maximize the PCK metric + + patience = cfg['earlyStoppingPatience'], # Number of epochs with no improvement after which training will be stopped + min_delta = cfg['earlyStoppingMinDelta'], # Minimum change in the monitored quantity to qualify as an improvement + verbose = 1, # Set to 1 for more verbose output + restore_best_weights = True # Restore model weights from the epoch with the best value of the monitored quantity + ) + + # Create the PCKMetric to have a better grasp of what is happening with the model + pck_metric = PCKMetric(threshold=cfg['pCK_AP_Threshold']) + rsq_metric = RSquaredMetric() + + #Compile a model with the requested loss + if (cfg['loss']=="focal"): + model.compile(optimizer=optimizer, loss=focal_loss, metrics=[pck_metric,rsq_metric]) + elif (cfg['loss']=="mse"): + model.compile(optimizer=optimizer, loss=vanilla_mse_loss, metrics=[pck_metric,rsq_metric]) + elif (cfg['loss']=="jointsMSE"): + model.compile(optimizer=optimizer, loss=jointsMSE_loss, metrics=[pck_metric,rsq_metric]) + else: + model.compile(optimizer=optimizer, loss=cfg['loss'], metrics=[pck_metric,rsq_metric]) + + #Before starting training log TF Versions + printTFVersion() + + #Printout data in screen + #-------------------------------------------------------------------------------------------------------------------------------- + if dataType == np.float32: + bytesPerValue = 4 + else: + bytesPerValue = 1 + estimatedInputByteSize = cfg['inputWidth'] * cfg['inputHeight'] * 3 * trainingDatasetLength * bytesPerValue + estimatedOutputByteSize = cfg['outputWidth'] * cfg['outputHeight'] * (1+len(outLabels)) * trainingDatasetLength * bytesPerValue + print("Input Data Size : ",convert_bytes(estimatedInputByteSize)) + print("Output Data Size : ",convert_bytes(estimatedOutputByteSize)) + print("Total Data Size : ",convert_bytes(estimatedInputByteSize+estimatedOutputByteSize)) + #-------------------------------------------------------------------------------------------------------------------------------- + + # Train the model + if (onlyTrainingData): + model.fit( + trainingDataset, + batch_size = cfg['batchSize'], + epochs = cfg['epochs'], + validation_split = 0.2, + callbacks = [tensorboard_callback,early_stopping], + steps_per_epoch = stepsPerEpoch + ) + else: + model.fit( + trainingDataset, + batch_size = cfg['batchSize'], + epochs = cfg['epochs'], + validation_data = validationDataset.batch(cfg['batchSize']), + callbacks = [tensorboard_callback,early_stopping], + steps_per_epoch = stepsPerEpoch + ) + + # Save the trained model + print('Saving result model') + model.save('2d_pose_estimation', save_format='tf') + + print('PCK threshold was set to ',pck_metric.threshold) + + print('Training complete') + + os.system("date +\"%y-%m-%d_%H-%M-%S\" > 2d_pose_estimation/date.txt") #Tag date + os.system("zip -r 2d_pose_estimation.zip 2d_pose_estimation/") #Create zip of models + print('You can see a summary using :\n tensorboard --logdir=2d_pose_estimation/tensorboard --bind_all && firefox http://127.0.0.1:6006') + + print('Attempting to upload results (if you take too long it will timeout)') + os.system("scp -P 2222 2d_pose_estimation.zip ammar@ammar.gr:/home/ammar/public_html") + print("scp -P 2222 2d_pose_estimation.zip ammar@ammar.gr:/home/ammar/public_html") + diff --git a/src/python/2d_pose_estimation/runCOCO.py b/src/python/2d_pose_estimation/runCOCO.py new file mode 100644 index 0000000..b2289ac --- /dev/null +++ b/src/python/2d_pose_estimation/runCOCO.py @@ -0,0 +1,177 @@ +import sys +import cv2 +import numpy as np +import tensorflow as tf +from tensorflow.keras.models import load_model +from readCOCO import resize_image_with_borders + + +dataType = np.uint8 # + +useGPU = True +if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--float32"): + dataType = np.float32 + if (sys.argv[i]=="--uint8"): + dataType = np.uint8 + if (sys.argv[i]=="--cpu"): + useGPU = False + +# Set CUDA_VISIBLE_DEVICES to an empty string to force TensorFlow to use the CPU +if (not useGPU): + os.environ['CUDA_VISIBLE_DEVICES'] = '' #<- Force CPU + +def load_keypoints_model(model_path): + from readCOCO import PCKMetric,RSquaredMetric,FocalLoss,focal_loss,JointsMSELoss,jointsMSE_loss,VanillaMSELoss,vanilla_mse_loss + model = load_model(model_path, custom_objects={'PCKMetric': PCKMetric, 'RSquaredMetric':RSquaredMetric, 'focal_loss': focal_loss, 'jointsMSE_loss': jointsMSE_loss, 'vanilla_mse_loss':vanilla_mse_loss}) + + # Check the shape of the input layer + input_layer = model.layers[0] # Assuming the input layer is the first layer + input_shape = input_layer.input_shape + input_size = (input_shape[0][1],input_shape[0][2]) + print("Input shape is ",input_size) + + # Get the output layer of the model + output_layer = model.layers[-1] # Assuming the output layer is the last layer + output_shape = output_layer.output_shape + output_size = (output_shape[2],output_shape[3]) + numberOfHeatmaps = output_shape[1] + print("Number of Heatmaps is ", numberOfHeatmaps) + print("Output Shape is ", output_size) + + return model,input_size,output_size,numberOfHeatmaps + +def preprocess_image(frame, target_size=(128, 128)): + # Resize the frame to the target size and normalize pixel values + # Step 1: Convert the image to float32 + image = frame.astype(dataType) + + #print("Input Image Range:", np.min(image), np.max(image)) + #print("Input Image Mean:", np.mean(image)) + #print("Input Image STD:", np.std(image)) + + # Step 2: Normalize the pixel values to be in the range [0, 1] + #image = image / 255 + image, keypointXMultiplier, keypointYMultiplier, keypointXOffset, keypointYOffset = resize_image_with_borders(image,target_size) + + return image + +def predict_keypoints(model, image): + # Use the model call for predictions + image_batch = np.expand_dims(image, axis=0) + predictions = model(image_batch, training=False) + return predictions[0] + + +def visualize_heatmaps(frame, frameNumber, heatmaps, keypoint_names, threshold=0.0): + i=0 + wnd_x = 0 + wnd_y = 0 + + #Scale back window + #rgb_uint8_image = frame * 255 + rgb_uint8_image = frame + + # Convert to uint8 type for display + rgb_uint8_image = np.uint8(rgb_uint8_image) + + # Display the result + cv2.imshow('RGB Input', rgb_uint8_image) + if (frameNumber==0): + cv2.moveWindow("RGB Input", wnd_x, wnd_y) + wnd_y+=231 + + + #print("Heatmaps ",heatmaps.shape) + for i in range(0,heatmaps.shape[2]): + heatmap = heatmaps[:,:,i] + resized_heatmap = np.array(heatmap,dtype=dataType) + + if (threshold>0.0): + # Create a boolean mask for values above the threshold + resized_heatmap[resized_heatmap <= threshold] = 0 + + #heatmap_uint8 = np.uint8(resized_heatmap) + #print("Heatmap ",i," shape ",heatmap.shape) + cv2.imshow('Heatmap %s'% keypoint_names[i], resized_heatmap) + if (frameNumber==0): + cv2.moveWindow('Heatmap %s'% keypoint_names[i], wnd_x, wnd_y) + wnd_y+=170 + if ( wnd_y > 900 ): + wnd_x+=384 + wnd_y =0 + + + i=i+1 + + cv2.waitKey(1) + +def webcam_keypoints_detection(model_path, keypoint_names, threshold=0.0): + # Load the 2D Pose Estimation model + keypoints_model,input_size,output_size,numberOfHeatmaps = load_keypoints_model(model_path) + + # Open a connection to the webcam (0 indicates the default camera) + cap = cv2.VideoCapture(0) + + frameNumber = 0 + + while True: + # Capture a single frame from the webcam + ret, frame = cap.read() + if not ret: + print("Failed to capture frame") + break + + # Preprocess the frame for the model + input_image = preprocess_image(frame, target_size=input_size) + + # Make predictions using the model + keypoints_predictions = predict_keypoints(keypoints_model, input_image) + + #i=0 + #for keypoint in keypoints_predictions: + # print("Prediction ",keypoint_names[i]," Range:", np.min(keypoint), np.max(keypoint)) + # print("Prediction ",keypoint_names[i]," Mean:", np.mean(keypoint)) + # print("Prediction ",keypoint_names[i]," STD:", np.std(keypoint)) + # i=i+1 + + # Visualize the heatmaps on the source image + visualize_heatmaps(input_image,frameNumber, keypoints_predictions, keypoint_names, threshold=threshold) + frameNumber = frameNumber + 1 + + # Release the webcam and close all OpenCV windows + cap.release() + cv2.destroyAllWindows() + +if __name__ == '__main__': + # Specify the path to the trained 2D Pose Estimation model + model_path = '2d_pose_estimation' + + # Specify the names of keypoints (change accordingly based on your model's keypoint order) + keypoint_names = [ + "nose", + "left_eye", + "right_eye", + "left_ear", + "right_ear", + "left_shoulder", + "right_shoulder", + "left_elbow", + "right_elbow", + "left_wrist", + "right_wrist", + "left_hip", + "right_hip", + "left_knee", + "right_knee", + "left_ankle", + "right_ankle", + "bkg" + ] + + print("Reported Keypoints : ",keypoint_names) + # Run the webcam keypoints detection + webcam_keypoints_detection(model_path, keypoint_names, threshold = 0.0) + diff --git a/src/python/blender/blender_face.py b/src/python/blender/blender_face.py new file mode 100644 index 0000000..9f55683 --- /dev/null +++ b/src/python/blender/blender_face.py @@ -0,0 +1,1614 @@ +#Written by Ammar Qammaz 2022-2023 +#This is a Blender Python script that upon loaded can facilitate animating a skinned model created by +#the MakeHuman plugin for Blender ( http://static.makehumancommunity.org/mpfb.html ) +mnetPluginVersion=float(0.34) + +import bpy +from bpy.props import EnumProperty + +import os +import random +import math +import gc +import numpy as np +import array +import csv + + +#Steps to generate a good dataset! +#Load a BVH file and point to it +#Load an armature and point to it +#Run this python script in blender +#Run : sobolRandomDistributionForFace.py to generate a sobol/quasi-random dataset +#Point to the target dataset directory in Dataset Path +#Point to your dataset in Dataset Path: and click Create Dataset from CSV file +# Either : +# run mediapipeDumpHead2DFromRGB.py --from your dataset path +# or +# run associate2DFiles.py to remake associations , update them here and rely on them as 2D data +csvResolutionErrors = 0 + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +class vertexHolder(): + def __init__(self,v,vID): + self.co = v.co + self.index = vID + +def timeDuration(startTimeInSeconds,endTimeInSeconds): + timeElapsed = endTimeInSeconds - startTimeInSeconds + timeUnit = "seconds" + if (timeElapsed>86400): + timeElapsed = timeElapsed / 86400 + timeUnit = "days" + elif (timeElapsed>3600): + timeElapsed = timeElapsed / 3600 + timeUnit = "hours" + return timeElapsed,timeUnit + +def printSelectedVertex(): + mode = bpy.context.active_object.mode + # Keep track of previous mode + bpy.ops.object.mode_set(mode='OBJECT') + # Go into object mode to update the selected vertices + + obj = bpy.context.object + # Get the currently select object + sel = np.zeros(len(obj.data.vertices), dtype=np.bool) + # Create a numpy array with empty values for each vertex + + obj.data.vertices.foreach_get('select', sel) + # Populate the array with True/False if the vertex is selected + + for ind in np.where(sel==True)[0]: + # Loop over each currently selected vertex + v = obj.data.vertices[ind] + print(bcolors.OKGREEN) + print('Vertex {} at position {} is selected'.format(v.index, v.co)) + print(bcolors.ENDC) + # If you just want the first one you can break directly here + # break + + bpy.ops.object.mode_set(mode=mode) + + +def combineCSVFiles(outputCSVPath,inputCSVPathList): + targetPath = bpy.context.scene.datasetPath + filenames = list() + #================================================= + for csvF in inputCSVPathList.keys(): + #print(sys.argv[i]) + csvFilename = "%s/2d_face_all_blender_%s.csv" % (targetPath,csvF) + filenames.append(csvFilename) + + numberOfFiles = len(filenames) + #================================================= + files = list() + for i in range(0,numberOfFiles): + f = open(filenames[i],'r') + files.append(f) + #================================================= + + #================================================= + f = open(outputCSVPath, 'w') + #================================================= + readingMoreLines = True + while readingMoreLines: + thisTotalLine = "" + for i in range(0,numberOfFiles): + line = files[i].readline().replace('\n', '') + #-------------------------------- + if (i!=0): + f.write(",") + f.write(line) + #-------------------------------- + if not line: + readingMoreLines = False + break + if (readingMoreLines): + f.write("\n") + #================================================= + f.close() + #================================================= + for i in range(0,numberOfFiles): + files[i].close() + #================================================= + + return + #----------------------------------------------------------------- + + +def write_csv_2d_data_header(csvFilename,vertices,verticeCSVWhitelist=dict()): + f = open(csvFilename, 'w') + print(csvFilename," Number of vertices :",len(vertices)) + for v in range(0,len(vertices)): + if (v>0): + f.write(',') + if 'label' in verticeCSVWhitelist: + f.write('2DX_%s,2DY_%s,visible_%s' % (verticeCSVWhitelist['label'][v],verticeCSVWhitelist['label'][v],verticeCSVWhitelist['label'][v])) + else: + f.write('2DX_v%u,2DY_v%u,visible_%u' % (v,v,v)) + f.write('\n') + f.close() + +def write_vertex_csv_2d_data(objName="",baseDirectory="/home/ammar/",fID=0,csvFile=True,svgFile=True,verticeCSVWhitelistForAllObjects=dict()): + import bpy_extras + from bpy_extras.object_utils import world_to_camera_view + + #--------------------------------------------------------------------------- + if (objName==""): + print("Cannot write_vertex_csv_2d_data without an obj name!") + return + #--------------------------------------------------------------------------- + + #--------------------------------------------------------------------------- + verticeCSVWhitelist = dict() + if (len(verticeCSVWhitelistForAllObjects.keys())>0 ): #There is a whitelist declared a.k.a. dict not empty! + if objName in verticeCSVWhitelistForAllObjects: + verticeCSVWhitelist = verticeCSVWhitelistForAllObjects[objName] + else: + #If vertice whitelists exist but not for the particular object then + #we completely ignore the object and just return.. + #print("No vertex whitelist for ",objName,end=" ") + #print("We assume that this means that the whole object is blacklisted! ") + return + #if there is no white list declared we go on as usual dumping everything.. + #--------------------------------------------------------------------------- + + # Get the active object + scene = bpy.context.scene + camera = scene.objects.get("Camera") #bpy.context.scene.camera + obj = scene.objects.get(objName) # "newgirl.body" + + #Apply modifiers + dg = bpy.context.evaluated_depsgraph_get() + obj = obj.evaluated_get(dg) + mesh = obj.to_mesh(preserve_all_data_layers=True, depsgraph=dg) + + #Point vertices to either all vertices or a specific list of vertices + if ('label' in verticeCSVWhitelist) and ('body' in verticeCSVWhitelist): + #We have an active list of vertices to select/transform so we will be economic + #print(verticeCSVWhitelist['label']) + numberOfVertices = len(verticeCSVWhitelist['label']) + vertices = list() + for i in range (0,numberOfVertices): + vertexID = int(verticeCSVWhitelist['body'][0][i]) + newV = vertexHolder(mesh.vertices[vertexID],vertexID) + newV.co = obj.matrix_world @ newV.co + vertices.append(newV) + else: + #Just transform all vertices and pass them all + mesh.transform(obj.matrix_world) # apply loc/rot/scale + vertices = mesh.vertices + + render_scale = scene.render.resolution_percentage / 100 + render_size = (int(scene.render.resolution_x * render_scale),int(scene.render.resolution_y * render_scale),) + width = render_size[0] + height = render_size[1] + + #Zoomed Debugging Vertices + zoom = bpy.context.scene.zoomSVG + if (zoom): + width = 10*width + height = 10*height + + #---------------------------------------------------------------------- + csvFilename = "%s/2d_face_all_blender_%s.csv" % (baseDirectory,objName) + if (fID==0): + write_csv_2d_data_header(csvFilename=csvFilename,vertices=vertices,verticeCSVWhitelist=verticeCSVWhitelist) + if (csvFile): + fCSV = open(csvFilename, 'a') + #---------------------------------------------------------------------- + if (svgFile): + f = open('%s/blender_%s_face_dataset_%04u.svg'%(baseDirectory,objName,fID), 'w') + f.write('\n'%(height,width)) + f.write('\n'%(width,height)) + #---------------------------------------------------------------------- + + + vNum = 0 + for v in vertices: + co_final= v.co# @ obj.matrix_world + # Get the 2D projection of the vertex + coords_2d = bpy_extras.object_utils.world_to_camera_view(bpy.context.scene,camera,co_final) + #print("world_to_camera_view :",coords_2d) + x = coords_2d.x * width + y = (1.0-coords_2d.y) * height + visible = 0.0 + if (0'%(round(x),round(y))); + if ('label' in verticeCSVWhitelist): + f.write('%u %s(%u)\n' % (round(x+2),round(y),vNum,verticeCSVWhitelist['label'][vNum],v.index)); + else: + f.write('%u\n' % (round(x),round(y),vNum)); + f.write('\n' % (x,y,vNum)); + #------------- + if (csvFile): + if (vNum>0): + fCSV.write(',') + fCSV.write('%f,%f,%0.1f' % (coords_2d.x,(1.0-coords_2d.y),visible)) + #------------- + vNum = vNum + 1 + + if (svgFile): + f.write('\n') + f.close() + #--------------------------------------------------------- + if (csvFile): + fCSV.write('\n') + fCSV.close() + #print("fID ",fID," number of vertices :",len(vertices)) + return True + + +def write_csv_2d_data_all_objects(baseDirectory="/home/ammar/",fID=0,csvFile=True,svgFile=True,verticeCSVWhitelistForAllObjects=dict()): + skinnedObjectName = bpy.context.scene.mnetTarget + for obj in bpy.data.objects[skinnedObjectName].children: + #print(skinnedObjectName," has a child ",obj.name) + write_vertex_csv_2d_data( + objName=obj.name, + baseDirectory=baseDirectory, + fID=fID, + csvFile=csvFile, + svgFile=svgFile, + verticeCSVWhitelistForAllObjects=verticeCSVWhitelistForAllObjects + ) + +def write_csv_3d_data_header(filename): + scene = bpy.context.scene + obj = scene.objects.get("newgirl.body") + f = open(filename, 'w') + vertices = obj.data.vertices + print("number of vertices :",len(vertices)) + for v in range(0,len(vertices)): + if (v>0): + f.write(',') + f.write('3DX_v%u,3DY_v%u,3DZ_v%u' % (v,v,v)) + f.write('\n') + f.close() + +def write_vertex_csv_3d_data(filename="/home/ammar/",fID=0): + return get_vertex_2d_projection(filename=filename,fID=fID) + import bpy_extras + from bpy_extras.object_utils import world_to_camera_view + #----------------------------------------------------------------- + scene = bpy.context.scene + camera = scene.objects.get("Camera") #bpy.context.scene.camera + obj = scene.objects.get("newgirl.body") + vertices = obj.data.vertices + #Apply modifiers + dg = bpy.context.evaluated_depsgraph_get() + obj = obj.evaluated_get(dg) + + mesh = obj.to_mesh(preserve_all_data_layers=True, depsgraph=dg) + #co = mesh.vertices[0].co + #co_final = obj.matrix_world @ co + + #mesh = obj.to_mesh(scene, True, 'PREVIEW') # apply modifiers with preview settings + mesh.transform(obj.matrix_world) # apply loc/rot/scale + vertices = mesh.vertices + #----------------------------------------------------------------- + """ + bpy.ops.export_scene.obj( + filepath="%s/blender_%04u.obj"%(filename,fID), + check_existing=False, + axis_forward='-Z', + axis_up='Y', + filter_glob="*.obj;*.mtl", + use_selection=False, + use_animation=False, + use_mesh_modifiers=True, + use_edges=True, + use_smooth_groups=False, + use_smooth_groups_bitflags=False, + use_normals=True, + use_uvs=True, + use_materials=True, + use_triangles=False, + use_nurbs=False, + use_vertex_groups=False, + use_blen_objects=True, + group_by_object=False, + group_by_material=False, + keep_vertex_order=False, + global_scale=1, + path_mode='AUTO' +)""" + #----------------------------------------------------------------- + csvFileName = '%s/blender.csv'%filename + if (fID==0): + write_csv_3d_data_header(csvFileName) + #----------------------------------------------------------------- + f = open(csvFileName,'a') + vNum = 0 + for v in vertices: + print("v :",v.co) + #----------------------------------------------------------------- + if (vNum!=0): + f.write(',') + f.write('%f,%f,%f' % (v.co.x,v.co.y,v.co.z)) + #----------------------------------------------------------------- + vNum = vNum + 1 + #----------------------------------------------------------------- + f.write('\n') + f.close() + print("number of vertices :",len(vertices)) + return True + + +# =================================================================================================================== +# =================================================================================================================== +# =================================================================================================================== +# =================================================================================================================== +def resolveCSVRowColumn(data,label,sampleID): + #--------------------------- + column = 0 + labelLowerCase = label.lower() + for columnLabel in data['label']: + if (columnLabel.lower()==labelLowerCase): + return float(data['body'][sampleID][column]) + column = column+1 + #--------------------------- + global csvResolutionErrors + csvResolutionErrors += 1 + if (csvResolutionErrors < 100): + print("Could not resolve ",label," sample ",sampleID) + elif (csvResolutionErrors == 100): + print("Could not resolve ",label," sample ",sampleID) + print("From now on will supress error output to speed up computation ") + elif (csvResolutionErrors % 30000 == 0): + print("Reminder : Could not resolve ",label," sample ",sampleID," surpressed ",csvResolutionErrors," errors.. ") + + return float(0.0) + +def convert_bytes(num): + """ + this function will convert bytes to MB.... GB... etc + """ + step_unit = 1000.0 #1024 bad the size + + for x in ['bytes', 'KB', 'MB', 'GB', 'TB']: + if num < step_unit: + return "%3.1f %s" % (num, x) + num /= step_unit + +def getNumberOfLines(filename): + print("Counting number of lines in file ",filename) + with open(filename) as f: + return sum(1 for line in f) + +def checkIfPathExists(filename): + import os + return os.path.exists(filename) + +def checkIfFileExists(filename): + import os + return os.path.isfile(filename) + +def readCSVFile(filename,memPercentage=1.0,useHalfFloats=0): + import os + import time + print("CSV file :",filename,"..\n") + + if (not checkIfFileExists(filename)): + print( bcolors.FAIL + "Input file "+filename+" does not exist, cannot read ground truth.." + bcolors.ENDC) + print("Current Directory was "+os.getcwd()) + return dict() + start = time.time() + + dtypeSelected=np.dtype(np.float32) + dtypeSelectedByteSize=int(dtypeSelected.itemsize) + if (useHalfFloats): + dtypeSelected=np.dtype(np.float16) + dtypeSelectedByteSize=int(dtypeSelected.itemsize) + + progress=0.0 + sampleNumber=0 + receivedHeader=False + inputNumberOfColumns=0 + outputNumberOfColumns=0 + + inputLabels=list() + + #------------------------------------------------------------------------------------------------- + numberOfSamplesInput=getNumberOfLines(filename)-1 + print(" Input file has ",numberOfSamplesInput," training samples\n") + #------------------------------------------------------------------------------------------------- + + + numberOfSamples = numberOfSamplesInput + numberOfSamplesLimit=int(numberOfSamples*memPercentage) + #------------------------------------------------------------------------------------------------- + if (memPercentage==0.0): + print("readCSVFile was asked to occupy 0 memory so this probably means we just want one record") + numberOfSamplesLimit=2 + if (memPercentage>1.0): + print("Memory Limit will be interpreted as a raw value..") + numberOfSamplesLimit=int(memPercentage) + #------------------------------------------------------------------------------------------------- + + + #--------------------------------- + thisInput = array.array('f') + #--------------------------------- + + fi = open(filename, "r") + readerIn = csv.reader( fi , delimiter =',', skipinitialspace=True) + for rowIn in readerIn: + #------------------------------------------------------ + if (not receivedHeader): #use header to get labels + #------------------------------------------------------ + inputNumberOfColumns=len(rowIn) + inputLabels = list(rowIn[i] for i in range(0,inputNumberOfColumns) ) + print("Number of Input elements : ",len(inputLabels)) + #------------------------------------------------------ + + if (memPercentage==0): + print("Will only return labels\n") + return {'label':inputLabels}; + + #--------------------------------- + # Allocate Lists + #--------------------------------- + for i in range(inputNumberOfColumns): + thisInput.append(0.0) + #--------------------------------- + + + #--------------------------------- + # Allocate Numpy Arrays + #--------------------------------- + inputSize=0 + startCompressed=0 + inputSize=inputNumberOfColumns + startCompressed=inputNumberOfColumns + + npInputBytesize=0+numberOfSamplesLimit * inputSize * dtypeSelectedByteSize + print(" Input file on disk has a shape of [",numberOfSamples,",",inputSize,"]") + print(" Input we will read has a shape of [",numberOfSamplesLimit,",",inputSize,"]") + print(" Input will occupy ",convert_bytes(npInputBytesize)," of RAM\n") + npInput = np.full([numberOfSamplesLimit,inputSize],fill_value=0,dtype=dtypeSelected,order='C') + #---------------------------------------------------------------------------------------------------------- + receivedHeader=True + #---------------------------------------------------------------------------------------------------------- + else: + #------------------------------------------- + # First convert our string INPUT to floats + #------------------------------------------- + for i in range(inputNumberOfColumns): + try: + thisInput[i]=float(rowIn[i]) + except: + thisInput[i]=0.0 + #------------------------------------------- + for num in range(0,inputNumberOfColumns): + npInput[sampleNumber,num]=float(thisInput[num]); + #------------------------------------------- + sampleNumber=sampleNumber+1 + + if (numberOfSamples>0): + progress=sampleNumber/numberOfSamplesLimit + + if (sampleNumber%1000==0) : + progressString = "%0.2f"%float(100*progress) + print("\rReading from disk (",sampleNumber,") - ",progressString," % \r", end="", flush=True) + + if (numberOfSamplesLimit<=sampleNumber): + print("\rStopping reading file to obey memory limit given by parameter --mem ",memPercentage,"\n") + break + #------------------------------------------- + fi.close() + del readerIn + gc.collect() + + + print("\n read, Samples: ",sampleNumber,", was expecting ",numberOfSamples," samples\n") + print(npInput.shape) + + totalNumberOfBytes=npInput.nbytes; + totalNumberOfGigaBytes=totalNumberOfBytes/1073741824; + print("GPU Size Occupied by data = ",totalNumberOfGigaBytes," GB \n") + + end = time.time() + print("Time elapsed : ",(end-start)/60," mins") + #--------------------------------------------------------------------- + return {'label':inputLabels, 'body':npInput }; + +# =================================================================================================================== +# =================================================================================================================== +# =================================================================================================================== +# =================================================================================================================== + + + +def retrieveSkinToBVHAssotiationDict(doFace=False): + r = dict() + if (doFace): + r["root"]="hip" + r["neck1"]="neck1" + r["head"]="head" + #r["__jaw"]="__jaw" + r["jaw"]="jaw" + #r["special04"]="special04" + #r["oris02"]="oris02" + r["oris01"]="oris01" #<-- + r["oris06.L"]="oris06.l" ##### + r["oris07.L"]="oris07.l" + r["oris06.R"]="oris06.r" ##### + r["oris07.R"]="oris07.r" + #r["tongue00"]="tongue00" + #r["tongue01"]="tongue01" + #r["tongue02"]="tongue02" + #r["tongue03"]="tongue03" + #r["__tongue04"]="__tongue04" + #r["tongue04"]="tongue04" + #r["tongue07.L"]="tongue07.l" + #r["tongue07.R"]="tongue07.r" + #r["tongue06.L"]="tongue06.l" + #r["tongue06.R"]="tongue06.r" + #r["tongue05.L"]="tongue05.l" + #r["tongue05.R"]="tongue05.r" + #r["__levator02.L"]="__levator02.l" + #r["levator02.L"]="levator02.l" + r["levator03.L"]="levator03.l" + #r["levator04.L"]="levator04.l" + #r["levator05.L"]="levator05.l" + #r["__levator02.R"]="__levator02.r" + #r["levator02.R"]="levator02.r" + r["levator03.R"]="levator03.r" + #r["levator04.R"]="levator04.r" + #r["levator05.R"]="levator05.r" + #r["__special01"]="__special01" + #r["special01"]="special01" + r["oris04.L"]="oris04.l" + r["oris03.L"]="oris03.l" + r["oris04.R"]="oris04.r" + r["oris03.R"]="oris03.r" + #r["oris06"]="oris06" + r["oris05"]="oris05" #<-- + #r["__special03"]="__special03" + #r["special03"]="special03" + #r["__levator06.L"]="__levator06.l" + r["levator06.L"]="levator06.l" + #r["__levator06.R"]="__levator06.r" + r["levator06.R"]="levator06.r" + #r["special06.L"]="special06.l" + #r["special05.L"]="special05.l" + r["eye.L"]="eye.l" + r["orbicularis03.L"]="orbicularis03.l" + r["orbicularis04.L"]="orbicularis04.l" + #r["special06.R"]="special06.r" + #r["special05.R"]="special05.r" + r["eye.R"]="eye.r" + r["orbicularis03.R"]="orbicularis03.r" + r["orbicularis04.R"]="orbicularis04.r" + #r["__temporalis01.L"]="__temporalis01.l" + #r["temporalis01.L"]="temporalis01.l" + #r["oculi02.L"]="oculi02.l" ## + r["oculi01.L"]="oculi01.l" + #r["__temporalis01.R"]="__temporalis01.r" + #r["temporalis01.R"]="temporalis01.r" + #r["oculi02.R"]="oculi02.r" ## + r["oculi01.R"]="oculi01.r" + #r["__temporalis02.L"]="__temporalis02.l" + #r["temporalis02.L"]="temporalis02.l" + #r["risorius02.L"]="risorius02.l" + #r["risorius03.L"]="risorius03.l" ## + #r["__temporalis02.R"]="__temporalis02.r" + #r["temporalis02.R"]="temporalis02.r" + #r["risorius02.R"]="risorius02.r" + #r["risorius03.R"]="risorius03.r" ## + return r + +def degToRad(degrees): + return degrees * math.pi / 180 + +def randomize_property(propName): + #prop = bpy.data.scenes[0][propName] + scene = bpy.data.scenes[0] + + prop = None + for p in scene.bl_rna.properties: + #print(p.name) + if (p.name == propName): + #print("FOUND ",propName) + prop=p + break + #else: + # print("`%s`!=`%s`" % (p.name,propName)) + + #print(type(prop)) + if isinstance(prop, bpy.types.FloatProperty): # and prop.has_min and prop.has_max: + # Property is a float with a defined range, use min and max attributes + min_value = prop.soft_min + max_value = prop.soft_max + random_value = random.uniform(min_value, max_value) + # Set the property to the random value + #print(propName,"randomize(%0.1f,%0.1f) = %0.2f "%(min_value,max_value,random_value)) + return random_value + else: + # Property is a different type, handle it differently + # (for example, generate a random value within a reasonable range for the property type) + print("Unable to use min/max for prop ",propName) + return 0.0 + +def dumpBVHFile(r,targetPath,frameID): + if (frameID==0): + f = open('%s/bvh_face_all.csv' % targetPath, 'w') + i=0 + for joint in r.keys(): + if (i>0): + f.write(',') + f.write(joint) + i=i+1 + f.write('\n') + f.close() + f = open('%s/bvh_face_all.csv' % targetPath, 'a') + i=0 + for joint in r.keys(): + if (i>0): + f.write(',') + f.write("%0.2f"%r[joint]) + i=i+1 + f.write('\n') + f.close() + +def setSkeletonRaw(jointName,z,x,y): + context = bpy.context + scene = context.scene + #------------------------------------------------------- + skinnedObjectName = bpy.context.scene.mnetSource + jointName = jointName.lower() + armature_obj = bpy.context.scene.objects.get(bpy.context.scene.mnetSource) + #skinnedObjectName = bpy.context.scene.mnetTarget + #print("skinnedObjectName",skinnedObjectName) + #------------------------------------------------------- + + + skinnedObject = scene.objects.get(skinnedObjectName) + if (skinnedObject is not None) : + # Get the joint object + armature = bpy.data.objects[skinnedObjectName] + bone = armature.pose.bones[jointName] + + # Set the rotation mode to ZXY + bone.rotation_mode = 'ZXY' + + # Set the rotation values + bone.rotation_euler = (degToRad(z),degToRad(x),degToRad(y)) + + #Animation set + #-------------------------------------------------------------------- + # Set the joint values for the current frame + armature_obj.pose.bones[jointName].rotation_euler = (degToRad(z),degToRad(x),degToRad(y)) + # Add a keyframe for the joint values + armature_obj.pose.bones[jointName].keyframe_insert(data_path="rotation_euler", index=-1) + + +def setSkeletonPositionRaw(jointName,x,y,z): + context = bpy.context + scene = context.scene + #------------------------------------------------------- + skinnedObjectName = bpy.context.scene.mnetSource + jointName = jointName.lower() + armature_obj = bpy.context.scene.objects.get(bpy.context.scene.mnetSource) + #skinnedObjectName = bpy.context.scene.mnetTarget + #print("skinnedObjectName",skinnedObjectName) + #------------------------------------------------------- + + skinnedObject = scene.objects.get(skinnedObjectName) + if (skinnedObject is not None) : + # Get the joint object + armature = bpy.data.objects[skinnedObjectName] + bone = armature.pose.bones[jointName] + + # Set the rotation mode to ZXY + #bone.rotation_mode = 'ZXY' + # Set the rotation values + #bone.rotation_euler = (degToRad(90.0),degToRad(0.0),degToRad(0.0)) + + #Animation set + #-------------------------------------------------------------------- + # Set the joint values for the current frame + armature_obj.pose.bones[jointName].location = (x,y,z) + # Add a keyframe for the joint values + armature_obj.pose.bones[jointName].keyframe_insert(data_path="location", index=-1) + + + + +class FaceBVHAnimationPanel(bpy.types.Panel): + """Creates a Panel in the Object properties window""" + bl_label = "Face BVH Animation Helper" + bl_idname = "OBJECT_PT_face_panel" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + + def draw(self, context): + context = bpy.context + scene = context.scene + layout = self.layout + + obj = context.object + + #layout = layout.split(factor=0.96, align=True) + #------------------------------------------------------------------ + #------------------------------------------------------------------ + row = layout.row() + row.label(text="Face BVH MocapNET Helper v%0.2f" % mnetPluginVersion, icon='WORLD_DATA') + #------------------------------------------------------------------ + row = layout.row() + row.label(text="BVH file to use as source: ") + row = layout.row() + row.prop_search(scene, "mnetSource", scene, "objects", icon='ARMATURE_DATA') + row = layout.row() + row.operator("face.face_op",text='Link BVH').action='LINKBVH' + #------------------------------------------------------------------ + row = layout.row() + row.label(text="Skinned Body to use as target: ") + row = layout.row() + row.prop_search(scene, "mnetTarget", scene, "objects", icon='OUTLINER_OB_ARMATURE') + #------------------------------------------------------------------ + row = layout.row() + row.label(text="Parts of armature to animate: ") + row = layout.row() + row.operator("face.face_op",text='Open Mouth').action='OPENMOUTH' + row.operator("face.face_op",text='Close Mouth').action='CLOSEMOUTH' + row = layout.row() + row.label(text="Positional Component : ") + row = layout.row() + row.operator("face.face_op",text='Open Eyes').action='OPENEYES' + row.operator("face.face_op",text='Close Eyes').action='CLOSEEYES' + + + row = layout.row() + row.label(text="Depth : ") + row = layout.row() + row.prop(scene, 'posX', slider=True) + row = layout.row() + row.prop(scene, 'posY', slider=True) + row = layout.row() + row.prop(scene, 'depth', slider=True) + + row = layout.row() + row.label(text="Neck : ") + row = layout.row() + row.prop(scene, 'neck1Z', slider=True) + row = layout.row() + row.prop(scene, 'neck1X', slider=True) + row = layout.row() + row.prop(scene, 'neck1Y', slider=True) + + + row = layout.row() + row.label(text="Eyes : ") + row = layout.row() + row.prop(scene, 'eyelidLUD', slider=True) + row = layout.row() + row.prop(scene, 'eyelidRUD', slider=True) + row = layout.row() + row.prop(scene, 'eyeLR', slider=True) + row = layout.row() + row.prop(scene, 'eyeUD', slider=True) + + + row = layout.row() + row.label(text="Nose : ") + row = layout.row() + row.prop(scene, 'noseLR', slider=True) + + + row = layout.row() + row.prop(scene, 'REyebrowInUD', slider=True) + row = layout.row() + row.prop(scene, 'LEyebrowInUD', slider=True) + + row = layout.row() + row.label(text="Mouth : ") + row = layout.row() + row.prop(scene, 'smileAD', slider=True) + row = layout.row() + row.prop(scene, 'mouthUD', slider=True) + row = layout.row() + row.prop(scene, 'mouthLR', slider=True) + row = layout.row() + row.prop(scene, 'mouthOC', slider=True) + row = layout.row() + row.prop(scene, 'moustacheLUD', slider=True) + row = layout.row() + row.prop(scene, 'moustacheRUD', slider=True) + + row = layout.row() + row.prop(scene, 'mouthTopL', slider=True) + row = layout.row() + row.prop(scene, 'mouthTopR', slider=True) + + row = layout.row() + row.prop(scene, 'mouthBotL', slider=True) + row = layout.row() + row.prop(scene, 'mouthBotR', slider=True) + + + row = layout.row() + row.label(text="Export controls : ") + row = layout.row() + row.prop(scene, 'dumpSVG') + row.prop(scene, 'zoomSVG') + row = layout.row() + row.prop(scene, 'dumpPNG') + row = layout.row() + row.prop(scene, 'dump2D') + row.prop(scene, 'dump3D') + row = layout.row() + row.prop(scene, 'dumpSpecificVertices') + + row = layout.row() + row.operator("face.face_op",text='Take Picture').action='PHOTO' + row = layout.row() + row.label(text="Path to store generated dataset : ") + row = layout.row() + row.prop_search(scene, "datasetPath", scene, "objects", icon='FILE_FOLDER') + row = layout.row() + row.prop(scene, 'randomFramesNumber', slider=True) + row = layout.row() + row.operator("face.face_op",text='Create Randomized Dataset').action='RANDOM' + + row = layout.row() + row.label(text="Path to load pre-generated dataset : ") + row = layout.row() + row.prop_search(scene, "readDatasetCSVPath", scene, "objects", icon='FILE_HIDDEN') + row = layout.row() + row.operator("face.face_op",text='Create Dataset from CSV file').action='LOADCSV' + row = layout.row() + row.operator("face.face_op",text='Just Render CSV Dataset').action='RENDERCSV' + + + + +class FaceBVHAnimation(bpy.types.Operator): + """Creates a Panel in the Object properties window""" + bl_label = "Face BVH Animation" + bl_idname = "face.face_op" + bl_description = 'MocapNET operation control' + bl_options = {'REGISTER', 'UNDO'} + + action: EnumProperty( + items=[ + ('LOADCSV', 'Load Pose Data From CSV File', 'Load Pose Data From CSV File'), + ('RENDERCSV', 'Render Pose Data From CSV File', 'Render Pose Data From CSV File'), + ('RANDOM', 'Create Randomized Data', 'Create Randomized Data'), + ('PHOTO', 'Take a Picture', 'Take a Picture'), + ('LINKBVH', 'Link BVH File to Face', 'Link BVH File to Face'), + ('OPENMOUTH', 'Link MocapNET to Skinned Model', 'Link MocapNET to Skinned Model'), + ('CLOSEMOUTH', 'Link MocapNET to Upper Body Only', 'Link MocapNET to Upper Body Only'), + ('OPENEYES', 'Link MocapNET to Face', 'Link MocapNET to Face'), + ('CLOSEEYES', 'Link MocapNET positional component', 'Link MocapNET positional component') + ] + ) + + @staticmethod + def add_cube(context): + bpy.ops.mesh.primitive_cube_add() + + @staticmethod + def add_sphere(context): + bpy.ops.mesh.primitive_uv_sphere_add() + + + @staticmethod + def cameraLightAction(context): + #bpy.context.scene.camera object and set its properties such as focal_length, sensor_width, and sensor_height. + bpy.context.scene.camera.location = 0.0,0.0,0.7 + bpy.context.scene.camera.rotation_mode = 'ZXY' + bpy.context.scene.camera.rotation_euler = degToRad(90),degToRad(0),degToRad(0) + #bpy.ops.object.lens_distort + + light = bpy.data.objects['Light'] + light.location.x = 0.0 + light.location.y = -5.0 + light.location.z = 1.0 + + @staticmethod + def takePicture(self,context): + print("takePicture called") + + targetPath = bpy.context.scene.datasetPath + + import os + if (checkIfPathExists(targetPath)): + os.system("rm %s/blender_face_dataset_*.jpg" % (targetPath)) + os.system("rm %s/blender_*_face_dataset_*.svg" % (targetPath)) + else: + print("Cannot take picture, given path %s does not exist" % (targetPath)) + return; + + bpy.context.scene.frame_set(0) # Always revert to first frame on dataset generation + #------------------------------------------------------------- + dumpSVG = bpy.context.scene.dumpSVG + dumpPNG = bpy.context.scene.dumpPNG + dump2D = bpy.context.scene.dump2D + dump3D = bpy.context.scene.dump3D + dumpSpecificVertices = bpy.context.scene.dumpSpecificVertices + #------------------------------------------------------------- + printSelectedVertex() + #------------------------------------------------------------- + csvVertexWhitelist=dict() + if(dumpSpecificVertices): + skinnedObjectName = bpy.context.scene.mnetTarget + for obj in bpy.data.objects[skinnedObjectName].children: + if (checkIfPathExists("%s/vertexWhitelist_%s.csv"%(targetPath,obj.name))): + csvVertexWhitelist[obj.name] = readCSVFile("%s/vertexWhitelist_%s.csv"%(targetPath,obj.name)) + else: + print("Could not find %s/vertexWhitelist_%s.csv "%(targetPath,obj.name)) + #------------------------------------------------------------- + if (dump2D or dumpSVG): + write_csv_2d_data_all_objects(baseDirectory=targetPath,fID=0,csvFile=dump2D,svgFile=dumpSVG,verticeCSVWhitelistForAllObjects=csvVertexWhitelist) + if(dump3D): + write_vertex_csv_3d_data(filename="/home/ammar/",fID=0) + #------------------------------------------------------------- + self.cameraLightAction(context=context) + #------------------------------------------------------------- + bpy.context.scene.render.image_settings.file_format='JPEG' + bpy.context.scene.render.filepath = "/home/ammar/test.jpg" + bpy.ops.render.render(write_still = True) + bpy.data.images['Render Result'].save_render + + @staticmethod + def generateRandomDataset(self,context,useCSV=False,useFFMPEG=False): + randomFramesNumber = bpy.context.scene.randomFramesNumber + print("generateRandomDataset called ",randomFramesNumber) + self.cameraLightAction(context=context) + + targetPath = bpy.context.scene.datasetPath + import time + startAt = time.time() + import os + if (checkIfPathExists(targetPath)): + os.system("rm %s/blender_face_dataset_*.jpg" % (targetPath)) + os.system("rm %s/blender_*_face_dataset_*.svg" % (targetPath)) + else: + print("Cannot generate random dataset, given path %s does not exist" % (targetPath)) + return; + + bpy.context.scene.frame_set(0) # Always revert to first frame on start of dataset generation + #--------------------------------------------------------------------------- + dumpSVG = bpy.context.scene.dumpSVG + dumpPNG = bpy.context.scene.dumpPNG + dump2D = bpy.context.scene.dump2D + dump3D = bpy.context.scene.dump3D + dumpSpecificVertices = bpy.context.scene.dumpSpecificVertices + #--------------------------------------------------------------------------- + csvVertexWhitelist=dict() + if(dumpSpecificVertices): + skinnedObjectName = bpy.context.scene.mnetTarget + for obj in bpy.data.objects[skinnedObjectName].children: + if (checkIfPathExists("%s/vertexWhitelist_%s.csv"%(targetPath,obj.name))): + csvVertexWhitelist[obj.name] = readCSVFile("%s/vertexWhitelist_%s.csv"%(targetPath,obj.name)) + else: + print("Could not find %s/vertexWhitelist_%s.csv "%(targetPath,obj.name)) + #print(csvVertexWhitelist) + #--------------------------------------------------------------------------- + wm = bpy.context.window_manager + #--------------------------------------------------------------------------- + armature_obj = bpy.context.scene.objects.get(bpy.context.scene.mnetSource) + target_obj = bpy.context.scene.objects.get(bpy.context.scene.mnetTarget) + #--------------------------------------------------------------------------- + + if armature_obj and target_obj: + armature_mod = target_obj.modifiers.new(name='Armature', type='ARMATURE') + #--------------------------------------------------------------------------- + if (useCSV): + #In this mode we will use the random poses found in the readDatasetCSVPath given by the user + csvFile = bpy.context.scene.readDatasetCSVPath + csvData = readCSVFile(csvFile) #,memPercentage=100 <- to test + randomFramesNumber = csvData["body"].shape[0] + print("Will now attempt to transmit ",randomFramesNumber," frames from ",csvFile) + wm.progress_begin(0,randomFramesNumber) + for fID in range(0,randomFramesNumber): + if (randomFramesNumber<10000) or (fID%1000==0): + wm.progress_update(fID) # Update mouse pointer progress in a conservative way to avoid X-Server error(?) + if (dumpPNG): + bpy.context.scene.frame_set(fID) # Set the current frame + # Set the joint values for the current frame + thisFaceConfig = self.retrieveFaceControls(self=self,context=context,csvData=csvData,fID=fID) + dumpBVHFile(thisFaceConfig,targetPath,fID) + if (dump2D or dumpSVG): + write_csv_2d_data_all_objects(baseDirectory=targetPath,fID=fID,csvFile=dump2D,svgFile=dumpSVG,verticeCSVWhitelistForAllObjects=csvVertexWhitelist) + if (dump3D): + write_vertex_csv_3d_data(filename="/home/ammar/",fID=0) + else: + wm.progress_begin(0,randomFramesNumber) + #In this more we will generate random poses + for fID in range(0,randomFramesNumber): + if (randomFramesNumber<10000) or (fID%1000==0): + wm.progress_update(fID) # Update mouse pointer progress in a conservative way to avoid X-Server error(?) + if (dumpPNG): + bpy.context.scene.frame_set(fID) # Set the current frame + # Set the joint values for the current frame + thisFaceConfig = self.retrieveFaceControls(self=self,context=context) + dumpBVHFile(thisFaceConfig,targetPath,fID) + if (dump2D or dumpSVG): + write_csv_2d_data_all_objects(baseDirectory=targetPath,fID=fID,csvFile=dump2D,svgFile=dumpSVG,verticeCSVWhitelistForAllObjects=csvVertexWhitelist) + if(dump3D): + write_vertex_csv_3d_data(filename="/home/ammar/",fID=0) + #--------------------------------------------------------------------------- + + if(dumpSpecificVertices): + combineCSVFiles("%s/2d_face_all.csv"%(targetPath),csvVertexWhitelist) + + #At this point we have added all new states to animation + #it has happened that after a lot of hours errors like #X Error of failed request: BadWindow (invalid Window parameter) Major opcode of failed request: 18 (X_ChangeProperty) + #might occur so let's save our blend file to make sure we can re-render if something goes wrong! + os.system("rm %s/faceRandomized.blend" % (targetPath)) + bpy.ops.wm.save_as_mainfile(filepath="%s/faceRandomized.blend" % (targetPath)) + + if (dumpPNG): + print("Will now attempt to render ",randomFramesNumber," frames ") + renderAsAnimation=True + #--------------------------------------------------------------------------- + if (renderAsAnimation): + wm.progress_end() + # Set the render engine and animation settings + #bpy.context.scene.render.engine = "CYCLES" + bpy.context.scene.render.image_settings.file_format='JPEG' + bpy.context.scene.render.filepath = "%s/blender_face_dataset_" % (targetPath) + bpy.context.scene.frame_start = 0 + bpy.context.scene.frame_end = randomFramesNumber-1 + + # Click the Render Animation button + bpy.ops.render.render('INVOKE_AREA',use_viewport = True, animation=True) + else: + #Then playback animation and save each frame as jpeg + for fID in range(0,randomFramesNumber): + bpy.context.scene.frame_set(fID) + wm.progress_update(fID) + bpy.context.view_layer.update() #function to update the view layer and trigger a redraw of the UI. + bpy.context.scene.render.image_settings.file_format='JPEG' + bpy.context.scene.render.filepath = "%s/blender_face_dataset_%04u.jpg" % (targetPath,fID) + bpy.ops.render.render(write_still = True) + bpy.data.images['Render Result'].save_render + if (fID%1000==0): + gc.collect() #Do garbage collection to help with memory leaks ? + wm.progress_end() + #--------------------------------------------------------------------------- + if (useFFMPEG): + print("Will attempt to execute : ") + print("ffmpeg -framerate 30 -i %s/blender_face_dataset_%%04d.jpg -s 1200x720 -y -r 30 -pix_fmt yuv420p -threads 8 %s/blender.mp4"% (targetPath,targetPath)) + os.system("ffmpeg -framerate 30 -i %s/blender_face_dataset_%%04d.jpg -s 1200x720 -y -r 30 -pix_fmt yuv420p -threads 8 %s/blender.mp4" % (targetPath,targetPath)) + #--------------------------------------------------------------------------- + endAt = time.time() + timeElapsed,timeUnit = timeDuration(startAt,endAt) + print("Time required to generate dataset was ",timeElapsed,timeUnit) + #--------------------------------------------------------------------------- + #ffmpeg -framerate 30 -i blender_face_dataset_%04d.jpg -s 1200x720 -y -r 30 -pix_fmt yuv420p -threads 8 livelastRun3DHiRes.mp4 + #ffmpeg -i /media/ammar/CVRL2/ammar/frames/ammarFaceFar.mp4-data/colorFrame_0_%05d.jpg -i /media/ammar/CVRL2/ammar/rendering/blender_face_dataset_%04d.jpg -filter_complex '[1:v]colorkey=0x464646:0.01:0.02[ckout];[0:v][ckout]overlay[out]' -map '[out]' output.mp4 + # or + #ffmpeg -i /media/ammar/games/ammarFaceFar.mp4-data/colorFrame_0_%05d.jpg -i /media/ammar/games/render/blender_face_dataset_%04d.jpg -filter_complex '[1:v]colorkey=0x464646:0.01:0.02[ckout];[0:v][ckout]overlay[out]' -map '[out]' output.mp4 + + @staticmethod + def setSkeleton(context,jointName,z,x,y): + setSkeletonRaw(jointName,z,x,y) + + @staticmethod + def retrieveConstantControls(self,context): + r = dict() + r["orbicularis03.R_Yrotation"]=172.0 + r["orbicularis04.R_Yrotation"]=172.0 + r["orbicularis03.L_Yrotation"]=-172.0 + r["orbicularis04.L_Yrotation"]=172.0 + r["levator06.L_Yrotation"]=-247.0 + r["levator06.R_Yrotation"]=247.0 + r["oris03.L_Xrotation"]=-40.0 + r["oris03.L_Yrotation"]=172.0 + r["oris07.L_Yrotation"]=172.0 + r["oris03.R_Xrotation"]=-40.0 + r["oris03.R_Yrotation"]=179.0 + r["oris07.R_Yrotation"]=172.0 + r["oris05_Xrotation"]=-35.0 + r["oris05_Yrotation"]=-176.0 + return r + + @staticmethod + def retrieveFaceControlsI(self,context): + scene = bpy.data.scenes[0] + #-------------------------------------------------------- + r = dict() + #-------------------------------------------------------- + r["hip_Xposition"] = randomize_property("Pos X") + r["hip_Yposition"] = randomize_property("Pos Y") + r["hip_Zposition"] = randomize_property("Depth") + #-------------------------------------------------------- + r["neck1_Zrotation"] = randomize_property("Neck Z") + r["neck1_Xrotation"] = randomize_property("Neck X") + r["neck1_Yrotation"] = randomize_property("Neck Y") + #-------------------------------------------------------- + r["eye.R_Zrotation"] = randomize_property("Eye Gaze L/R") + r["eye.R_Xrotation"] = randomize_property("Eye Gaze U/D") + r["eye.L_Zrotation"] = r["eye.R_Zrotation"] + r["eye.L_Xrotation"] = r["eye.R_Xrotation"] + #-------------------------------------------------------- + r["oculi01.R_Zrotation"] = randomize_property("R Eyebrow In U/D") + r["oculi01.L_Zrotation"] = randomize_property("L Eyebrow In U/D") + #-------------------------------------------------------- + r["orbicularis03.R_Xrotation"] = randomize_property("Eye Lid R U/D") + r["orbicularis04.R_Xrotation"] = -r["orbicularis03.R_Xrotation"] + r["orbicularis03.L_Xrotation"] = randomize_property("Eye Lid L U/D") + r["orbicularis04.L_Xrotation"] = -r["orbicularis03.L_Xrotation"] + blinkRand = random.uniform(0,1.0) + if (blinkRand<0.85): + r["orbicularis04.R_Xrotation"]=r["orbicularis04.L_Xrotation"] + #-------------------------------------------------------- + r["levator06.L_Xrotation"] = randomize_property("Nose L/R") + r["levator06.R_Xrotation"] = r["levator06.L_Xrotation"] + #-------------------------------------------------------- + r["levator03.L_Zrotation"] = randomize_property("Smile Active/Deactivated") + r["levator03.R_Zrotation"] = -r["levator03.L_Zrotation"] + #-------------------------------------------------------- + r["oris03.L_Zrotation"] = randomize_property("Mouth Top L") + r["oris07.L_Zrotation"] = min(randomize_property("Mouth Sides U/D"),0) + r["oris03.R_Zrotation"] = randomize_property("Mouth Top R") + r["oris07.R_Zrotation"] = r["oris07.L_Zrotation"] + #-------------------------------------------------------- + r["jaw_Xrotation"] = randomize_property("Mouth Open/Close") + r["jaw_Yrotation"] = randomize_property("Mouth L/R") + #-------------------------------------------------------- + r["oris04.L_Zrotation"] = randomize_property("Moustache L U/D") + r["oris04.R_Zrotation"] = -randomize_property("Moustache R U/D") + #-------------------------------------------------------- + r["oris06.L_Zrotation"] = randomize_property("Mouth Bot L") + r["oris06.R_Zrotation"] = -randomize_property("Mouth Bot R") + #-------------------------------------------------------- + return r + + @staticmethod + def retrieveFaceControlsFromCSV(self,context,csvData,fID): + scene = bpy.data.scenes[0] + #-------------------------------------------------------- + r = dict() + #-------------------------------------------------------- + r["hip_Xposition"] = resolveCSVRowColumn(csvData,"hip_Xposition",fID) + r["hip_Yposition"] = resolveCSVRowColumn(csvData,"hip_Yposition",fID) + r["hip_Zposition"] = resolveCSVRowColumn(csvData,"hip_Zposition",fID) + #-------------------------------------------------------- + r["neck1_Zrotation"] = resolveCSVRowColumn(csvData,"neck1_Zrotation",fID) + r["neck1_Xrotation"] = resolveCSVRowColumn(csvData,"neck1_Xrotation",fID) + r["neck1_Yrotation"] = resolveCSVRowColumn(csvData,"neck1_Yrotation",fID) + #-------------------------------------------------------- + r["eye.R_Zrotation"] = resolveCSVRowColumn(csvData,"eye.R_Zrotation",fID) + r["eye.R_Xrotation"] = resolveCSVRowColumn(csvData,"eye.R_Xrotation",fID) + r["eye.L_Zrotation"] = r["eye.R_Zrotation"] + r["eye.L_Xrotation"] = r["eye.R_Xrotation"] + #-------------------------------------------------------- + r["oculi01.R_Zrotation"] = resolveCSVRowColumn(csvData,"oculi01.R_Zrotation",fID) + r["oculi01.L_Zrotation"] = resolveCSVRowColumn(csvData,"oculi01.L_Zrotation",fID) + #-------------------------------------------------------- + r["orbicularis03.R_Xrotation"] = resolveCSVRowColumn(csvData,"orbicularis03.R_Xrotation",fID) + r["orbicularis04.R_Xrotation"] = -r["orbicularis03.R_Xrotation"] + r["orbicularis03.L_Xrotation"] = resolveCSVRowColumn(csvData,"orbicularis03.L_Xrotation",fID) + r["orbicularis04.L_Xrotation"] = -r["orbicularis03.L_Xrotation"] + #-------------------------------------------------------- + r["levator06.L_Xrotation"] = resolveCSVRowColumn(csvData,"levator06.L_Xrotation",fID) + r["levator06.R_Xrotation"] = r["levator06.L_Xrotation"] + #-------------------------------------------------------- + r["levator03.L_Zrotation"] = resolveCSVRowColumn(csvData,"levator03.L_Zrotation",fID) + r["levator03.R_Zrotation"] = -r["levator03.L_Zrotation"] + #-------------------------------------------------------- + r["oris03.L_Zrotation"] = resolveCSVRowColumn(csvData,"oris03.L_Zrotation",fID) + r["oris07.L_Zrotation"] = resolveCSVRowColumn(csvData,"oris07.L_Zrotation",fID) + r["oris03.R_Zrotation"] = resolveCSVRowColumn(csvData,"oris03.R_Zrotation",fID) + r["oris07.R_Zrotation"] = resolveCSVRowColumn(csvData,"oris07.R_Zrotation",fID) + #-------------------------------------------------------- + r["jaw_Xrotation"] = resolveCSVRowColumn(csvData,"jaw_Xrotation",fID) + r["jaw_Yrotation"] = resolveCSVRowColumn(csvData,"jaw_Yrotation",fID) + #-------------------------------------------------------- + r["oris04.L_Zrotation"] = resolveCSVRowColumn(csvData,"oris04.L_Zrotation",fID) + r["oris04.R_Zrotation"] = resolveCSVRowColumn(csvData,"oris04.R_Zrotation",fID) + #-------------------------------------------------------- + r["oris06.L_Zrotation"] = resolveCSVRowColumn(csvData,"oris06.L_Zrotation",fID) + r["oris06.R_Zrotation"] = resolveCSVRowColumn(csvData,"oris06.R_Zrotation",fID) + #-------------------------------------------------------- + return r + + + @staticmethod + def retrieveFaceControls(self,context,csvData=dict(),fID=0): + scene = bpy.data.scenes[0] + doIt=True + r = dict() + if (doIt): + #----------------------------------------------------------------- + if ("body" in csvData): + r.update(self.retrieveFaceControlsFromCSV(self=self,context=context,csvData=csvData,fID=fID)) + else: + r.update(self.retrieveFaceControlsI(self=self,context=context)) + #----------------------------------------------------------------- + scene['posX'] = r["hip_Xposition"] + scene['posY'] = r["hip_Yposition"] + scene['depth'] = r["hip_Zposition"] + bpy.context.scene.depth = scene['depth'] + #----------------------------------------------------------------- + scene['neck1Z'] = r["neck1_Zrotation"] + scene['neck1X'] = r["neck1_Xrotation"] + scene['neck1Y'] = r["neck1_Yrotation"] + bpy.context.scene.neckZ = scene['neck1Z'] + bpy.context.scene.neckX = scene['neck1X'] + bpy.context.scene.neckY = scene['neck1Y'] + #----------------------------------------------------------------- + scene['eyeLR'] = r["eye.R_Zrotation"] + scene['eyeUD'] = r["eye.R_Xrotation"] + bpy.context.scene.eyeLR = scene['eyeLR'] + bpy.context.scene.eyeUD = scene['eyeUD'] + eyeLR = bpy.context.scene.eyeLR + eyeUD = bpy.context.scene.eyeUD + #----------------------------------------------------------------- + scene['REyebrowInUD'] = -r["oculi01.R_Zrotation"] + scene['LEyebrowInUD'] = r["oculi01.L_Zrotation"] + bpy.context.scene.REyebrowInUD = scene['REyebrowInUD'] + bpy.context.scene.LEyebrowInUD = scene['LEyebrowInUD'] + REyebrowInUD = bpy.context.scene.REyebrowInUD + LEyebrowInUD = bpy.context.scene.LEyebrowInUD + #----------------------------------------------------------------- + scene['eyelidLUD'] = r["orbicularis04.L_Xrotation"] + scene['eyelidRUD'] = r["orbicularis04.R_Xrotation"] + bpy.context.scene.eyelidLUD = scene['eyelidLUD'] + bpy.context.scene.eyelidRUD = scene['eyelidRUD'] + eyelidRUD = bpy.context.scene.eyelidRUD + eyelidLUD = bpy.context.scene.eyelidLUD + #----------------------------------------------------------------- + scene['noseLR'] = r["levator06.L_Xrotation"] + bpy.context.scene.noseLR = scene['noseLR'] + noseLR = bpy.context.scene.noseLR + #----------------------------------------------------------------- + scene['smileAD'] = r["levator03.L_Zrotation"] + bpy.context.scene.smileAD = scene['smileAD'] + smileAD = bpy.context.scene.smileAD + #----------------------------------------------------------------- + scene['mouthTopL'] = r["oris03.L_Zrotation"] + scene['mouthTopR'] = -r["oris03.R_Zrotation"] + bpy.context.scene.mouthTopL = scene['mouthTopL'] + bpy.context.scene.mouthTopR = scene['mouthTopR'] + mouthTopL = bpy.context.scene.mouthTopL + mouthTopR = bpy.context.scene.mouthTopR + scene['mouthUD'] = r["oris07.L_Zrotation"] + bpy.context.scene.mouthUD = scene['mouthUD'] + mouthUD = bpy.context.scene.mouthUD + #----------------------------------------------------------------- + scene['mouthLR'] = r["jaw_Yrotation"] + bpy.context.scene.mouthLR = scene['mouthLR'] + mouthLR = bpy.context.scene.mouthLR + scene['mouthOC'] = r["jaw_Xrotation"] + bpy.context.scene.mouthOC = scene['mouthOC'] + mouthOC = bpy.context.scene.mouthOC + #----------------------------------------------------------------- + scene['moustacheLUD'] = r["oris04.L_Zrotation"] + scene['moustacheRUD'] = -r["oris04.R_Zrotation"] + bpy.context.scene.moustacheLUD = scene['moustacheLUD'] + bpy.context.scene.moustacheRUD = scene['moustacheRUD'] + moustacheLUD = bpy.context.scene.moustacheLUD + moustacheRUD = bpy.context.scene.moustacheRUD + #----------------------------------------------------------------- + scene['mouthBotL'] = r["oris06.L_Zrotation"] + scene['mouthBotR'] = -r["oris06.R_Zrotation"] + bpy.context.scene.mouthBotL = scene['mouthBotL'] + bpy.context.scene.mouthBotR = scene['mouthBotR'] + mouthBotL = bpy.context.scene.mouthBotL + mouthBotR = bpy.context.scene.mouthBotR + #----------------------------------------------------------------- + r.update(self.retrieveConstantControls(self=self,context=context)) + #----------------------------------------------------------------- + self.neckUpdate(self=self,context=context) + self.eyeGazeUpdate(self=self,context=context) + self.noseUpdate(self=self,context=context) + self.mouthUpdate(self=self,context=context) + return r + + + @staticmethod + def eyeGazeUpdate(self, context): + eyeLR = bpy.context.scene.eyeLR + eyeUD = bpy.context.scene.eyeUD #x #y #z + FaceBVHAnimation.setSkeleton(context,"eye.R",eyeUD,0,eyeLR) + FaceBVHAnimation.setSkeleton(context,"eye.L",eyeUD,0,eyeLR) + #----------------------------------------------------------------- + REyebrowInUD = bpy.context.scene.REyebrowInUD + LEyebrowInUD = bpy.context.scene.LEyebrowInUD #x #y #z + FaceBVHAnimation.setSkeleton(context,"oculi01.R",0,0,-REyebrowInUD) + FaceBVHAnimation.setSkeleton(context,"oculi01.L",0,0,LEyebrowInUD) + #----------------------------------------------------------------- + eyelidRUD = bpy.context.scene.eyelidRUD + eyelidLUD = bpy.context.scene.eyelidLUD #x #y #z + FaceBVHAnimation.setSkeleton(context,"orbicularis03.R",-eyelidRUD,172,0) + FaceBVHAnimation.setSkeleton(context,"orbicularis04.R",eyelidRUD,172,0) + FaceBVHAnimation.setSkeleton(context,"orbicularis03.L",-eyelidLUD,-172,0) + FaceBVHAnimation.setSkeleton(context,"orbicularis04.L",eyelidLUD,172,0) + + @staticmethod + def noseUpdate(self, context): + noseLR = bpy.context.scene.noseLR #x #y #z + FaceBVHAnimation.setSkeleton(context,"levator06.L",noseLR,-247,0) + FaceBVHAnimation.setSkeleton(context,"levator06.R",noseLR,+247,0) + + @staticmethod + def neckUpdate(self, context): + neckZ = bpy.context.scene.neckZ #x #y #z + neckX = bpy.context.scene.neckX #x #y #z + neckY = bpy.context.scene.neckY #x #y #z + FaceBVHAnimation.setSkeleton(context,"neck1",neckX,neckY,neckZ) + target_obj = bpy.context.scene.objects.get(bpy.context.scene.mnetTarget) + target_obj.location.x = 0.0 + target_obj.location.y = 0.0 + bpy.context.scene.depth + target_obj.location.z = 0.0 + target_obj.rotation_mode = 'ZXY' + target_obj.rotation_euler = (degToRad(0.0),degToRad(0.0),degToRad(0.0)) + setSkeletonPositionRaw("hip",bpy.context.scene.posX,bpy.context.scene.posY,bpy.context.scene.depth) + + @staticmethod + def mouthUpdate(self, context): + mouthTopL = bpy.context.scene.mouthTopL + mouthTopR = bpy.context.scene.mouthTopR + mouthUD = bpy.context.scene.mouthUD #x #y #z + FaceBVHAnimation.setSkeleton(context,"oris03.L",-40,172,mouthUD+mouthTopL) + FaceBVHAnimation.setSkeleton(context,"oris07.L",0,172,max(mouthUD,0)) + FaceBVHAnimation.setSkeleton(context,"oris03.R",-40,179,-mouthUD+mouthTopR) + FaceBVHAnimation.setSkeleton(context,"oris07.R",0,172,min(mouthUD,0)) + #----------------------------------------------------------------- + mouthOC = bpy.context.scene.mouthOC + mouthLR = bpy.context.scene.mouthLR + FaceBVHAnimation.setSkeleton(context,"jaw",mouthOC,mouthLR,0) + #----------------------------------------------------------------- + FaceBVHAnimation.setSkeleton(context,"oris05",-35,-176,0) + #----------------------------------------------------------------- + moustacheLUD = bpy.context.scene.moustacheLUD + moustacheRUD = bpy.context.scene.moustacheRUD #x #y #z + FaceBVHAnimation.setSkeleton(context,"oris04.L",0,0,moustacheLUD) + FaceBVHAnimation.setSkeleton(context,"oris04.R",0,0,moustacheRUD) + #----------------------------------------------------------------- + mouthBotL = bpy.context.scene.mouthBotL + mouthBotR = bpy.context.scene.mouthBotR #x #y #z + FaceBVHAnimation.setSkeleton(context,"oris06.L",0,0,mouthBotL) + FaceBVHAnimation.setSkeleton(context,"oris06.R",0,0,mouthBotR) + #----------------------------------------------------------------- + smileAD = bpy.context.scene.smileAD #x #y #z + FaceBVHAnimation.setSkeleton(context,"levator03.L",0,0,smileAD) + FaceBVHAnimation.setSkeleton(context,"levator03.R",0,0,-smileAD) + #----------------------------------------------------------------- + + @staticmethod + def copyFaceConstraints(context,doPosition=False,doRotation=True,doReverse=False): + FaceBVHAnimation.cameraLightAction(context=context) + context = bpy.context + scene = context.scene + #------------------------------------------------------- + associations = retrieveSkinToBVHAssotiationDict(doFace=True) + #------------------------------------------------------- + bvhObjectName = bpy.context.scene.mnetSource + skinnedObjectName = bpy.context.scene.mnetTarget + print("bvhObjectName",bvhObjectName) + print("skinnedObjectName",skinnedObjectName) + #------------------------------------------------------- + skinnedObject = scene.objects.get(skinnedObjectName) + bvhObject = scene.objects.get(bvhObjectName) + if (skinnedObject is not None) and (bvhObject is not None): + for skinnedBoneName in associations: + #------------------------------------------------ + bvhBoneName = associations[skinnedBoneName] + #------------------------------------------------ + skinnedBone = skinnedObject.pose.bones.get(skinnedBoneName) + bvhBone = bvhObject.pose.bones.get(bvhBoneName) + + if (doReverse): + # give it a copy rotation constraint + if (skinnedBone is not None) and (bvhBone is not None): + if (len(skinnedBone.constraints)>0): + for c in bvhBone.constraints: + bvhBone.constraints.remove(c) # Remove constraint + if (skinnedBoneName=="root") and (doPosition): + crc = bvhBone.constraints.new('COPY_LOCATION') + crc.target = skinnedObject + crc.subtarget = skinnedBoneName + elif (skinnedBoneName!="root") and (doRotation): + crc = bvhBone.constraints.new('COPY_ROTATION') + crc.target = skinnedObject + crc.subtarget = skinnedBoneName + else: + # give it a copy rotation constraint + if (skinnedBone is not None) and (bvhBone is not None): + if (len(skinnedBone.constraints)>0): + for c in skinnedBone.constraints: + skinnedBone.constraints.remove(c) # Remove constraint + if (skinnedBoneName=="root") and (doPosition): + crc = skinnedBone.constraints.new('COPY_LOCATION') + crc.target = bvhObject + crc.subtarget = bvhBoneName + elif (skinnedBoneName!="root") and (doRotation): + crc = skinnedBone.constraints.new('COPY_ROTATION') + crc.target = bvhObject + crc.subtarget = bvhBoneName + #------------------------------------------------------- + + def execute(self, context): + if self.action == 'LOADCSV': + self.generateRandomDataset(self=self,context=context,useCSV=True) + elif self.action == 'RENDERCSV': + dumpSVG = bpy.context.scene.dumpSVG; bpy.context.scene.dumpSVG = False + dumpPNG = bpy.context.scene.dumpPNG; bpy.context.scene.dumpPNG = True + dump2D = bpy.context.scene.dump2D; bpy.context.scene.dump2D = False + dump3D = bpy.context.scene.dump3D; bpy.context.scene.dump3D = False + dumpSpecificVertices = bpy.context.scene.dumpSpecificVertices; bpy.context.scene.dumpSpecificVertices = False + self.generateRandomDataset(self=self,context=context,useCSV=True,useFFMPEG=True) + elif self.action == 'RANDOM': + self.generateRandomDataset(self=self,context=context) + elif self.action == 'LINKBVH': + self.copyFaceConstraints(context=context,doPosition=True) + self.eyeGazeUpdate(self=self,context=context) + self.noseUpdate(self=self,context=context) + self.neckUpdate(self=self,context=context) + self.mouthUpdate(self=self,context=context) + elif self.action == 'REVERSELINKBVH': + self.copyFaceConstraints(context=context,doReverse=True) + elif self.action == 'PHOTO': + self.takePicture(self=self,context=context) + elif self.action == 'OPENMOUTH': + self.setSkeleton(context,"jaw",20,0,0) + elif self.action == 'CLOSEMOUTH': + self.setSkeleton(context,"jaw",0,0,0) + elif self.action == 'OPENEYES': + self.setSkeleton(context,"orbicularis03.R",0,172,0) + self.setSkeleton(context,"orbicularis04.R",0,172,0) + self.setSkeleton(context,"orbicularis03.L",0,172,0) + self.setSkeleton(context,"orbicularis04.L",0,172,0) + self.eyeGazeUpdate(self=self,context=context) + elif self.action == 'CLOSEEYES': + self.setSkeleton(context,"orbicularis03.R",-15,149,0) + self.setSkeleton(context,"orbicularis04.R",15,172,0) + self.setSkeleton(context,"orbicularis03.L",-15,193,0) + self.setSkeleton(context,"orbicularis04.L",15,172,0) + return {'FINISHED'} + +classes = (FaceBVHAnimationPanel,FaceBVHAnimation) + +def register(): + for cls in classes: + bpy.utils.register_class(cls) + + bpy.types.Scene.datasetPath = bpy.props.StringProperty(name="Dataset Path", default="~/", subtype="DIR_PATH") + bpy.types.Scene.readDatasetCSVPath = bpy.props.StringProperty(name="Dataset Path", default="~/", subtype="FILE_PATH") + bpy.types.Scene.mnetSource = bpy.props.StringProperty(name="Source BVH", default="Select Armature Object") + bpy.types.Scene.mnetTarget = bpy.props.StringProperty(name="Target Obj", default="Select Skinned Object") + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.zoomSVG = bpy.props.BoolProperty(name="Zoom SVG", default=False) + bpy.types.Scene.dumpSVG = bpy.props.BoolProperty(name="Dump SVG", default=False) + bpy.types.Scene.dumpPNG = bpy.props.BoolProperty(name="Dump PNG", default=True) + bpy.types.Scene.dumpSpecificVertices = bpy.props.BoolProperty(name="Only Dump 2D/3D for Specific Vertices", default=True) + bpy.types.Scene.dump2D = bpy.props.BoolProperty(name="Dump 2D CSV", default=True) + bpy.types.Scene.dump3D = bpy.props.BoolProperty(name="Dump 3D CSV", default=False) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.neckZ = bpy.props.FloatProperty(name="Neck Z", default=0.0, min=-20.0, max=20.0, update=FaceBVHAnimation.neckUpdate) + bpy.types.Scene.neckX = bpy.props.FloatProperty(name="Neck X", default=0.0, min=-20.0, max=20.0, update=FaceBVHAnimation.neckUpdate) + bpy.types.Scene.neckY = bpy.props.FloatProperty(name="Neck Y", default=0.0, min=-30.0, max=30.0, update=FaceBVHAnimation.neckUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.posX = bpy.props.FloatProperty(name="Pos X", default=0.0, min=-0.24, max=0.24, update=FaceBVHAnimation.neckUpdate) + bpy.types.Scene.posY = bpy.props.FloatProperty(name="Pos Y", default=0.0, min=-0.1, max=0.1, update=FaceBVHAnimation.neckUpdate) + bpy.types.Scene.depth = bpy.props.FloatProperty(name="Depth", default=-1.0, min=-2.4, max=-1.0, update=FaceBVHAnimation.neckUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.eyeLR = bpy.props.FloatProperty(name="Eye Gaze L/R", default=0.0, min=-45.36, max=45.36, update=FaceBVHAnimation.eyeGazeUpdate) + bpy.types.Scene.eyeUD = bpy.props.FloatProperty(name="Eye Gaze U/D", default=0.0, min=-10.0, max=16.0, update=FaceBVHAnimation.eyeGazeUpdate) + bpy.types.Scene.eyelidLUD = bpy.props.FloatProperty(name="Eye Lid L U/D", default=0.0, min=-15.0, max=15.0, update=FaceBVHAnimation.eyeGazeUpdate) + bpy.types.Scene.eyelidRUD = bpy.props.FloatProperty(name="Eye Lid R U/D", default=0.0, min=-15.0, max=15.0, update=FaceBVHAnimation.eyeGazeUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.noseLR = bpy.props.FloatProperty(name="Nose L/R", default=0.0, min=-9.0, max=9.0, update=FaceBVHAnimation.noseUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.mouthUD = bpy.props.FloatProperty(name="Mouth Sides U/D", default=0.0, min=-30.0, max=0.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthLR = bpy.props.FloatProperty(name="Mouth L/R", default=0.0, min=-15.0, max=15.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthOC = bpy.props.FloatProperty(name="Mouth Open/Close", default=0.0, min=-4.0, max=20.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.moustacheLUD = bpy.props.FloatProperty(name="Moustache L U/D", default=0.0, min=-30.0, max=0.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.moustacheRUD = bpy.props.FloatProperty(name="Moustache R U/D", default=0.0, min=0.0, max=30.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthTopL = bpy.props.FloatProperty(name="Mouth Top L", default=0.0, min=-30.0, max=30.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthTopR = bpy.props.FloatProperty(name="Mouth Top R", default=0.0, min=-30.0, max=30.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthBotL = bpy.props.FloatProperty(name="Mouth Bot L", default=0.0, min=-30.0, max=30.0, update=FaceBVHAnimation.mouthUpdate) + bpy.types.Scene.mouthBotR = bpy.props.FloatProperty(name="Mouth Bot R", default=0.0, min=-30.0, max=30.0, update=FaceBVHAnimation.mouthUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.smileAD = bpy.props.FloatProperty(name="Smile Active/Deactivated", default=0.0, min=-8.0, max=9.0, update=FaceBVHAnimation.mouthUpdate) + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.randomFramesNumber = bpy.props.IntProperty(name="Random Frames", default=0, min=0, max=200000) #, update=FaceBVHAnimation.generateRandomDataset + #-------------------------------------------------------------------------------------------------------------------------------------------- + bpy.types.Scene.REyebrowInUD = bpy.props.FloatProperty(name="R Eyebrow In U/D", default=0.0, min=-20.0, max=20.0, update=FaceBVHAnimation.eyeGazeUpdate) + bpy.types.Scene.LEyebrowInUD = bpy.props.FloatProperty(name="L Eyebrow In U/D", default=0.0, min=-20.0, max=20.0, update=FaceBVHAnimation.eyeGazeUpdate) + +def unregister(): + for cls in classes: + bpy.utils.unregister_class(cls) + + del bpy.types.Scene.datasetPath + del bpy.types.Scene.readDatasetCSVPath + del bpy.types.Scene.mnetSource + del bpy.types.Scene.mnetTarget + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.zoomSVG + del bpy.types.Scene.dumpSVG + del bpy.types.Scene.dumpPNG + del bpy.types.Scene.dumpSpecificVertices + del bpy.types.Scene.dump2D + del bpy.types.Scene.dump3D + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.neck1Z + del bpy.types.Scene.neck1X + del bpy.types.Scene.neck1Y + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.posX + del bpy.types.Scene.posY + del bpy.types.Scene.depth + del bpy.types.Scene.eyeLR + del bpy.types.Scene.eyeUD + del bpy.types.Scene.eyelidLUD + del bpy.types.Scene.eyelidRUD + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.noseLR + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.mouthUD + del bpy.types.Scene.mouthLR + del bpy.types.Scene.mouthOC + del bpy.types.Scene.moustacheLUD + del bpy.types.Scene.moustacheRUD + del bpy.types.Scene.mouthTopL + del bpy.types.Scene.mouthTopR + del bpy.types.Scene.mouthBotL + del bpy.types.Scene.mouthBotR + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.smileAD + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.randomFramesNumber + #-------------------------------------------------------------------------------------------------------------------------------------------- + del bpy.types.Scene.REyebrowInUD + del bpy.types.Scene.LEyebrowInUD + +if __name__ == "__main__": + register() + # Get a list of all add-ons that are currently activated + activated_addons = [addon.module for addon in bpy.context.preferences.addons if addon] + + haveMPFB = False + # Print the name of each activated add-on + for addon in activated_addons: + print(addon) + if (addon=="mpfb"): + haveMPFB = True + print("We already have MPFB!") + elif (addon=="bl_ext.user_default.mpfb"): + haveMPFB = True + print("We already have MPFB!") + + if (not haveMPFB): + import os + print("Receiving a fresh copy of MPFB!") + current_directory = os.getcwd() + print("Working from ",current_directory," directory") + os.system("wget http://download.tuxfamily.org/makehuman/plugins/mpfb2-latest.zip") + print(" Downloaded mpfb2-latest.zip and will now auto-install it for your convinience ") + bpy.ops.preferences.addon_install(filepath='%s/mpfb2-latest.zip' % os.getcwd()) + bpy.ops.preferences.addon_enable(module='mpfb') + bpy.ops.wm.save_userpref() + + # Get the path to the user preferences file + prefs_file = bpy.utils.user_resource('CONFIG') #bpy.context.preferences.filepath + + # Get the directory that contains the preferences file + prefs_dir = os.path.dirname(prefs_file) + + print(" Also installing the makehuman system assets!") + os.system("cd %s/mpfb/data && wget http://files.makehumancommunity.org/asset_packs/makehuman_system_assets/makehuman_system_assets_cc0.zip && unzip makehuman_system_assets_cc0.zip && rm makehuman_system_assets_cc0.zip" % prefs_dir) diff --git a/src/python/blender/blender_mocapnet.py b/src/python/blender/blender_mocapnet.py old mode 100755 new mode 100644 index 4b9f9b5..b123bb8 --- a/src/python/blender/blender_mocapnet.py +++ b/src/python/blender/blender_mocapnet.py @@ -1,28 +1,249 @@ #Written by Ammar Qammaz 2022-2023 #This is a Blender Python script that upon loaded can facilitate animating a skinned model created by #the MakeHuman plugin for Blender ( http://static.makehumancommunity.org/mpfb.html ) -mnetPluginVersion=float(0.02) +mnetPluginVersion=float(0.12) + +import math import bpy from bpy.props import EnumProperty +def setup_camera_from_intrinsics( + fx, fy, cx, cy, + width, height, + camera_name="RenderCamera", + camera_location=(0.0, -3.0, 1.5), + camera_rotation=(math.radians(90), 0.0, 0.0), + clip_start=0.01, + clip_end=100.0, + sensor_fit='HORIZONTAL' +): + """ + Configure Blender's render camera using OpenCV-style intrinsics. + + Args: + fx, fy: focal lengths in pixels + cx, cy: principal point (in pixels) + width, height: image resolution in pixels + camera_location: camera position in meters + camera_rotation: rotation (Euler XYZ) in radians + clip_start, clip_end: near/far clipping distances in meters + sensor_fit: 'HORIZONTAL' or 'VERTICAL' (how Blender fits the sensor) + """ + # Ensure there is a camera in the scene + if camera_name in bpy.data.objects: + cam_obj = bpy.data.objects[camera_name] + cam = cam_obj.data + else: + cam_data = bpy.data.cameras.new(camera_name) + cam_obj = bpy.data.objects.new(camera_name, cam_data) + bpy.context.collection.objects.link(cam_obj) + cam = cam_data + + # Calculate sensor width/height in mm + # Blender expects: focal_length_mm / sensor_width_mm = fx / width_in_pixels + # We assume fx, fy in pixel units; pick sensor size that preserves aspect ratio + sensor_width_mm = 36.0 + sensor_height_mm = sensor_width_mm * (height / width) + + # Compute focal length in mm + focal_length_mm = fx * sensor_width_mm / width + + cam.lens = focal_length_mm + cam.sensor_width = sensor_width_mm + cam.sensor_height = sensor_height_mm + cam.sensor_fit = sensor_fit + cam.clip_start = clip_start + cam.clip_end = clip_end + + # Set resolution + scene = bpy.context.scene + scene.render.resolution_x = width + scene.render.resolution_y = height + + # Set the principal point (optical center) + # Blender’s principal point offset is in normalized sensor coordinates (mm) + cam.shift_x = -(cx - width / 2) / width + cam.shift_y = (cy - height / 2) / height # Blender Y shift has opposite sign to image space + + # Apply transform + cam_obj.location = camera_location + cam_obj.rotation_euler = camera_rotation + + # Activate camera + scene.camera = cam_obj + + print(f"[INFO] Camera configured with fx={fx:.2f}, fy={fy:.2f}, cx={cx:.2f}, cy={cy:.2f}") + print(f"[INFO] Sensor: {sensor_width_mm:.2f}mm x {sensor_height_mm:.2f}mm, Focal length: {focal_length_mm:.2f}mm") + print(f"[INFO] Image: {width}x{height}, Shift: ({cam.shift_x:.4f}, {cam.shift_y:.4f})") + + return cam_obj + + + +def place_light_near_camera(scene=None, camera_name="RenderCamera", light_name="RenderLight", + distance=1.0, height=1.5, energy=1000, light_type='AREA'): + """ + Place or create a light near the camera to illuminate the scene. + + Args: + scene: Blender scene (default = bpy.context.scene) + camera_name: Name of the camera to follow + light_name: Name of the light to create or move + distance: Distance in meters in front of camera + energy: Light intensity + light_type: 'POINT', 'SUN', 'SPOT', 'AREA' + """ + + import mathutils + + if scene is None: + scene = bpy.context.scene + + # Get the camera object + cam = scene.objects.get(camera_name) + if cam is None: + print(f"[WARN] Camera '{camera_name}' not found. Cannot place light.") + return None + + # Check if light exists + light_obj = scene.objects.get(light_name) + if light_obj is None: + # Create a new light + light_data = bpy.data.lights.new(name=light_name, type=light_type) + light_obj = bpy.data.objects.new(light_name, light_data) + scene.collection.objects.link(light_obj) + + # Place light in front of camera + forward = cam.matrix_world.to_quaternion() @ mathutils.Vector((0.0, 0.0, -1.0)) + up = cam.matrix_world.to_quaternion() @ mathutils.Vector((0.0, 0.0, 1.0)) + + light_obj.location = cam.location + forward * distance + up * height # slightly above + light_obj.rotation_euler = cam.rotation_euler + + # Set light energy + light_obj.data.energy = energy + + print(f"[INFO] Light '{light_name}' placed near camera '{camera_name}'") + return light_obj + + +def set_eevee_background(scene=None,R=1.0,G=1.0,B=1.0): + """ + Set the render background to pure white in Eevee, handling world nodes. + """ + if scene is None: + scene = bpy.context.scene + + if scene.world is None: + world = bpy.data.worlds.new("World") + scene.world = world + + world = scene.world + + if not world.use_nodes: + world.use_nodes = True + + nodes = world.node_tree.nodes + links = world.node_tree.links + + # Find or create Background node + bg_node = next((n for n in nodes if n.type == 'BACKGROUND'), None) + if bg_node is None: + bg_node = nodes.new('ShaderNodeBackground') + + # Set color to white + bg_node.inputs['Color'].default_value = (R, G, B, 1.0) + + # Find or create World Output node + output_node = next((n for n in nodes if n.type == 'OUTPUT_WORLD'), None) + if output_node is None: + output_node = nodes.new('ShaderNodeOutputWorld') + + # Connect Background node to World Output if not already connected + if not any(link.to_node == output_node for link in bg_node.outputs['Background'].links): + links.new(bg_node.outputs['Background'], output_node.inputs['Surface']) + + # Optional: disable ambient lighting to prevent gray tint in Eevee + #scene.eevee.use_gtao = False + #scene.eevee.use_bloom = False + #scene.eevee.use_ssr = False + #scene.eevee.use_ssr_refraction = False + + + +class MocapNETSetupCameraOperator(bpy.types.Operator): + """Set up Blender Camera from Intrinsics""" + bl_label = "Set Camera From Intrinsics" + bl_idname = "mocapnet.setup_camera" + bl_description = "Configure the active camera using fx, fy, cx, cy, width, height" + bl_options = {'REGISTER', 'UNDO'} -def retrieveSkinToBVHAssotiationDict(doBody=True,doHands=True,doFeet=True,doFace=False): + def execute(self, context): + scene = context.scene + + fx = scene.mnet_fx + fy = scene.mnet_fy + cx = scene.mnet_cx + cy = scene.mnet_cy + width = scene.mnet_width + height = scene.mnet_height + + camera_name="Camera" + light_name="Light" + + setup_camera_from_intrinsics( + fx=fx, fy=fy, cx=cx, cy=cy, + width=width, height=height, + camera_name=camera_name, + camera_location=(0.0, -4.0, 1.7), + camera_rotation=(math.radians(90), 0.0, 0.0) + ) + + + # Set Eevee background to white + set_eevee_background(scene,0.01,0.01,0.01) + + # Place light 1 meter in front of the camera, slightly above + place_light_near_camera(distance=1.0, camera_name=camera_name, light_name=light_name, + energy=2000, light_type='AREA') + + self.report({'INFO'}, f"Scene color set to {scene.world.color}") + + + self.report({'INFO'}, f"Camera set with fx={fx}, fy={fy}, cx={cx}, cy={cy}, res={width}x{height}") + return {'FINISHED'} + + + + +def decide_name(bvh_object, possible_names): + if bvh_object is None or not hasattr(bvh_object, "pose"): + return None + + for name in possible_names: + if name in bvh_object.pose.bones: + return name + return None + +def retrieveSkinToBVHAssotiationDict(bvhObj=None,doBody=True,doHands=True,doFeet=True,doFace=False): r = dict() if (doBody): r["root"]="Hip" #This should be hip not Hip but for some reason (ZYX rotation) there is a discrepancy r["spine03"]="abdomen" r["spine04"]="chest" + r["neck01"]="neck1" + r["neck03"]="neck2" #--------------------------- - r["shoulder01.L"]="lCollar" - r["upperarm01.L"]="lShldr" - r["lowerarm01.L"]="lForeArm" - r["wrist.L"]="lHand" + r["shoulder01.L"]=decide_name(bvhObj,["lCollar","lcollar"]) + r["upperarm01.L"]=decide_name(bvhObj,["lShldr","lshoulder"]) + r["lowerarm01.L"]=decide_name(bvhObj,["lForeArm","lelbow"]) + r["wrist.L"] =decide_name(bvhObj,["lHand","lhand"]) #--------------------------- - r["shoulder01.R"]="rCollar" - r["upperarm01.R"]="rShldr" - r["lowerarm01.R"]="rForeArm" - r["wrist.R"]="rHand" + r["shoulder01.R"]=decide_name(bvhObj,["rCollar","rcollar"]) + r["upperarm01.R"]=decide_name(bvhObj,["rShldr","rshoulder"]) + r["lowerarm01.R"]=decide_name(bvhObj,["rForeArm","relbow"]) + r["wrist.R"] =decide_name(bvhObj,["rHand","rhand"]) #--------------------------- if (doHands): @@ -81,32 +302,36 @@ def retrieveSkinToBVHAssotiationDict(doBody=True,doHands=True,doFeet=True,doFace #--------------------------- if (doFeet): - r["upperleg02.L"]="lThigh" - r["lowerleg01.L"]="lShin" - r["foot.L"]="lFoot" + r["upperleg02.L"]=decide_name(bvhObj,["lThigh","lhip"]) + r["lowerleg01.L"]=decide_name(bvhObj,["lShin","lknee"]) + r["foot.L"] =decide_name(bvhObj,["lFoot","lfoot"]) #--------------------------- - r["upperleg02.R"]="rThigh" - r["lowerleg01.R"]="rShin" - r["foot.R"]="rFoot" + r["upperleg02.R"]=decide_name(bvhObj,["rThigh","rhip"]) + r["lowerleg01.R"]=decide_name(bvhObj,["rShin","rknee"]) + r["foot.R"] =decide_name(bvhObj,["rFoot","rfoot"]) #--------------------------- # L Foot #--------------------------- - r["toe1-1.L"]="toe1-1.L" - r["toe1-2.L"]="toe1-2.L" + r["toe1-1.L"]=decide_name(bvhObj,["toe1-1.L","toe1-1.l"]) + r["toe1-2.L"]=decide_name(bvhObj,["toe1-2.L","toe1-2.l"]) for toe in range(2,6): for part in range(1,4): - r["toe%u-%u.L"%(toe,part)]="toe%u-%u.L"%(toe,part) + r["toe%u-%u.L"%(toe,part)]=decide_name(bvhObj,[ + "toe%u-%u.L"%(toe,part), + "toe%u-%u.l"%(toe,part) ] ) #--------------------------- #--------------------------- # R Foot #--------------------------- - r["toe1-1.R"]="toe1-1.R" - r["toe1-2.R"]="toe1-2.R" + r["toe1-1.R"]=decide_name(bvhObj,["toe1-1.R","toe1-1.r"]) + r["toe1-2.R"]=decide_name(bvhObj,["toe1-2.R","toe1-2.r"]) for toe in range(2,6): for part in range(1,4): - r["toe%u-%u.R"%(toe,part)]="toe%u-%u.R"%(toe,part) + r["toe%u-%u.R"%(toe,part)]=decide_name(bvhObj,[ + "toe%u-%u.R"%(toe,part), + "toe%u-%u.r"%(toe,part) ] ) #--------------------------- @@ -115,81 +340,78 @@ def retrieveSkinToBVHAssotiationDict(doBody=True,doHands=True,doFeet=True,doFace #--------------------------- #--------------------------- if (doFace): - r["neck01"]="neck1" - r["head"]="head" - r["__jaw"]="__jaw" + #r["head"]="head" + #r["__jaw"]="__jaw" r["jaw"]="jaw" - r["special04"]="special04" - r["oris02"]="oris02" - r["oris01"]="oris01" - r["oris06.L"]="oris06.l" + #r["special04"]="special04" + #r["oris02"]="oris02" + #r["oris01"]="oris01" + ##r["oris06.L"]="oris06.l" r["oris07.L"]="oris07.l" - r["oris06.R"]="oris06.r" + ##r["oris06.R"]="oris06.r" r["oris07.R"]="oris07.r" - r["tongue00"]="tongue00" - r["tongue01"]="tongue01" - r["tongue02"]="tongue02" - r["tongue03"]="tongue03" - r["__tongue04"]="__tongue04" - r["tongue04"]="tongue04" - r["tongue07.L"]="tongue07.l" - r["tongue07.R"]="tongue07.r" - r["tongue06.L"]="tongue06.l" - r["tongue06.R"]="tongue06.r" - r["tongue05.L"]="tongue05.l" - r["tongue05.R"]="tongue05.r" - r["__levator02.L"]="__levator02.l" - r["levator02.L"]="levator02.l" + #r["tongue00"]="tongue00" + #r["tongue01"]="tongue01" + #r["tongue02"]="tongue02" + #r["tongue03"]="tongue03" + #r["__tongue04"]="__tongue04" + #r["tongue04"]="tongue04" + #r["tongue07.L"]="tongue07.l" + #r["tongue07.R"]="tongue07.r" + #r["tongue06.L"]="tongue06.l" + #r["tongue06.R"]="tongue06.r" + #r["tongue05.L"]="tongue05.l" + #r["tongue05.R"]="tongue05.r" + #r["__levator02.L"]="__levator02.l" + #r["levator02.L"]="levator02.l" r["levator03.L"]="levator03.l" - r["levator04.L"]="levator04.l" - r["levator05.L"]="levator05.l" - r["__levator02.R"]="__levator02.r" - r["levator02.R"]="levator02.r" + #r["levator04.L"]="levator04.l" + #r["levator05.L"]="levator05.l" + #r["__levator02.R"]="__levator02.r" + #r["levator02.R"]="levator02.r" r["levator03.R"]="levator03.r" - r["levator04.R"]="levator04.r" - r["levator05.R"]="levator05.r" - r["__special01"]="__special01" - r["special01"]="special01" + #r["levator04.R"]="levator04.r" + #r["levator05.R"]="levator05.r" + #r["__special01"]="__special01" + #r["special01"]="special01" r["oris04.L"]="oris04.l" r["oris03.L"]="oris03.l" r["oris04.R"]="oris04.r" r["oris03.R"]="oris03.r" - r["oris06"]="oris06" - r["oris05"]="oris05" - r["__special03"]="__special03" - r["special03"]="special03" - r["__levator06.L"]="__levator06.l" + #r["oris06"]="oris06" + #r["oris05"]="oris05" + #r["__special03"]="__special03" + #r["special03"]="special03" + #r["__levator06.L"]="__levator06.l" r["levator06.L"]="levator06.l" - r["__levator06.R"]="__levator06.r" + #r["__levator06.R"]="__levator06.r" r["levator06.R"]="levator06.r" - r["special06.L"]="special06.l" - r["special05.L"]="special05.l" + #r["special06.L"]="special06.l" + #r["special05.L"]="special05.l" r["eye.L"]="eye.l" r["orbicularis03.L"]="orbicularis03.l" r["orbicularis04.L"]="orbicularis04.l" - r["special06.R"]="special06.r" - r["special05.R"]="special05.r" + #r["special06.R"]="special06.r" + #r["special05.R"]="special05.r" r["eye.R"]="eye.r" r["orbicularis03.R"]="orbicularis03.r" r["orbicularis04.R"]="orbicularis04.r" - r["__temporalis01.L"]="__temporalis01.l" - r["temporalis01.L"]="temporalis01.l" - r["oculi02.L"]="oculi02.l" + #r["__temporalis01.L"]="__temporalis01.l" + #r["temporalis01.L"]="temporalis01.l" + #r["oculi02.L"]="oculi02.l" r["oculi01.L"]="oculi01.l" - r["__temporalis01.R"]="__temporalis01.r" - r["temporalis01.R"]="temporalis01.r" - r["oculi02.R"]="oculi02.r" + #r["__temporalis01.R"]="__temporalis01.r" + #r["temporalis01.R"]="temporalis01.r" + #r["oculi02.R"]="oculi02.r" r["oculi01.R"]="oculi01.r" - r["__temporalis02.L"]="__temporalis02.l" - r["temporalis02.L"]="temporalis02.l" - r["risorius02.L"]="risorius02.l" - r["risorius03.L"]="risorius03.l" - r["__temporalis02.R"]="__temporalis02.r" - r["temporalis02.R"]="temporalis02.r" - r["risorius02.R"]="risorius02.r" - r["risorius03.R"]="risorius03.r" - - + #r["__temporalis02.L"]="__temporalis02.l" + #r["temporalis02.L"]="temporalis02.l" + #r["risorius02.L"]="risorius02.l" + #r["risorius03.L"]="risorius03.l" + #r["__temporalis02.R"]="__temporalis02.r" + #r["temporalis02.R"]="temporalis02.r" + #r["risorius02.R"]="risorius02.r" + #r["risorius03.R"]="risorius03.r" return r @@ -238,6 +460,23 @@ def draw(self, context): row.operator("mocapnet.mocapnet_op",text='Link Position').action='LINKPOS' + #------------------------------------------------------------------ + row = layout.row() + row.label(text="Camera Intrinsics (OpenCV style):", icon='CAMERA_DATA') + + col = layout.column(align=True) + col.prop(scene, "mnet_fx", text="fx (px)") + col.prop(scene, "mnet_fy", text="fy (px)") + col.prop(scene, "mnet_cx", text="cx (px)") + col.prop(scene, "mnet_cy", text="cy (px)") + col.prop(scene, "mnet_width", text="Width (px)") + col.prop(scene, "mnet_height", text="Height (px)") + + row = layout.row() + row.operator("mocapnet.setup_camera", text="Set Camera From Intrinsics", icon='CAMERA_DATA') + + + class MocapNETBVHAnimation(bpy.types.Operator): """Creates a Panel in the Object properties window""" bl_label = "MocapNET BVH Animation" @@ -300,8 +539,6 @@ def copySkeletonConstraints(context,doBody=True,doHands=True,doFeet=True,doFace= context = bpy.context scene = context.scene #------------------------------------------------------- - associations = retrieveSkinToBVHAssotiationDict(doBody=doBody,doHands=doHands,doFeet=doFeet,doFace=doFace) - #------------------------------------------------------- bvhObjectName = bpy.context.scene.mnetSource skinnedObjectName = bpy.context.scene.mnetTarget print("bvhObjectName",bvhObjectName) @@ -309,6 +546,9 @@ def copySkeletonConstraints(context,doBody=True,doHands=True,doFeet=True,doFace= #------------------------------------------------------- skinnedObject = scene.objects.get(skinnedObjectName) bvhObject = scene.objects.get(bvhObjectName) + #------------------------------------------------------- + associations = retrieveSkinToBVHAssotiationDict(bvhObj=bvhObject,doBody=doBody,doHands=doHands,doFeet=doFeet,doFace=doFace) + #------------------------------------------------------- if (skinnedObject is not None) and (bvhObject is not None): for skinnedBoneName in associations: #------------------------------------------------ @@ -343,7 +583,8 @@ def execute(self, context): classes = (MocapNETBVHAnimationPanel, - MocapNETBVHAnimation) + MocapNETBVHAnimation, + MocapNETSetupCameraOperator) def register(): for cls in classes: @@ -351,13 +592,25 @@ def register(): bpy.types.Scene.mnetSource = bpy.props.StringProperty(name="Source BVH", default="Select BVH Object") bpy.types.Scene.mnetTarget = bpy.props.StringProperty(name="Target Obj", default="Select Skinned Object") - + bpy.types.Scene.mnet_fx = bpy.props.FloatProperty(name="fx", default=1200.0) + bpy.types.Scene.mnet_fy = bpy.props.FloatProperty(name="fy", default=1200.0) + bpy.types.Scene.mnet_cx = bpy.props.FloatProperty(name="cx", default=960.0) + bpy.types.Scene.mnet_cy = bpy.props.FloatProperty(name="cy", default=540.0) + bpy.types.Scene.mnet_width = bpy.props.IntProperty(name="Width", default=1920) + bpy.types.Scene.mnet_height = bpy.props.IntProperty(name="Height", default=1080) def unregister(): for cls in classes: bpy.utils.unregister_class(cls) del bpy.types.Scene.mnetSource del bpy.types.Scene.mnetTarget + + del bpy.types.Scene.mnet_fx + del bpy.types.Scene.mnet_fy + del bpy.types.Scene.mnet_cx + del bpy.types.Scene.mnet_cy + del bpy.types.Scene.mnet_width + del bpy.types.Scene.mnet_height if __name__ == "__main__": register() @@ -372,6 +625,9 @@ def unregister(): if (addon=="mpfb"): haveMPFB = True print("We already have MPFB!") + elif (addon=="bl_ext.user_default.mpfb"): + haveMPFB = True + print("We already have MPFB!") if (not haveMPFB): import os diff --git a/src/python/blender/faceWhiteLists/label.tag b/src/python/blender/faceWhiteLists/label.tag new file mode 100644 index 0000000..9271a15 --- /dev/null +++ b/src/python/blender/faceWhiteLists/label.tag @@ -0,0 +1 @@ +face diff --git a/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.body.csv b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.body.csv new file mode 100644 index 0000000..315c54e --- /dev/null +++ b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.body.csv @@ -0,0 +1,2 @@ +head_reye_2,head_reye_5,head_leye_1,head_leye_4,head_nostrills_2,head_chin,head_outmouth_0,head_outmouth_3,head_outmouth_6,head_outmouth_9 +4865,36,11483,6820,297,5171,402,466,7162,492 diff --git a/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv new file mode 100644 index 0000000..38de554 --- /dev/null +++ b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv @@ -0,0 +1,2 @@ +head_reyebrow_2,head_reyebrow_4,head_leyebrow_2,head_leyebrow_4 +102,121,40,59 diff --git a/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.high-poly.csv b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.high-poly.csv new file mode 100644 index 0000000..9a5e314 --- /dev/null +++ b/src/python/blender/faceWhiteLists/vertexWhitelist_newgirl.high-poly.csv @@ -0,0 +1,2 @@ +head_reye,head_leye +1023,485 diff --git a/src/python/blender/fullfaceWhiteLists/label.tag b/src/python/blender/fullfaceWhiteLists/label.tag new file mode 100644 index 0000000..9271a15 --- /dev/null +++ b/src/python/blender/fullfaceWhiteLists/label.tag @@ -0,0 +1 @@ +face diff --git a/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.body.csv b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.body.csv new file mode 100644 index 0000000..16396c3 --- /dev/null +++ b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.body.csv @@ -0,0 +1,2 @@ +head_reye_0,head_reye_1,head_reye_2,head_reye_3,head_reye_4,head_reye_5,head_leye_0,head_leye_1,head_leye_2,head_leye_3,head_leye_4,head_leye_5,head_nosebone_0,head_nosebone_1,head_nosebone_2,head_nosebone_3,head_nostrills_0,head_nostrills_1,head_nostrills_2,head_nostrills_3,head_nostrills_4,head_rchin_0,head_rchin_1,head_rchin_2,head_rchin_3,head_rchin_4,head_rchin_5,head_rchin_6,head_rchin_7,head_chin,head_lchin_7,head_lchin_6,head_lchin_5,head_lchin_4,head_lchin_3,head_lchin_2,head_lchin_1,head_lchin_0,head_outmouth_0,head_outmouth_1,head_outmouth_2,head_outmouth_3,head_outmouth_4,head_outmouth_5,head_outmouth_6,head_outmouth_7,head_outmouth_8,head_outmouth_9,head_outmouth_10,head_outmouth_11,head_inmouth_0,head_inmouth_1,head_inmouth_2,head_inmouth_3,head_inmouth_4,head_inmouth_5,head_inmouth_6,head_inmouth_7 +4854,4849,4865,67,47,36,6851,11483,11467,11472,6820,13368,136,135,5063,5134,5095,295,297,7062,11710,256,5181,5176,5153,5299,5227,5172,5166,5171,11779,11835,11903,11904,11767,11929,11796,7025,402,448,460,466,7219,7208,7162,7233,7245,492,490,478,432,704,468,7429,7192,7279,494,534 diff --git a/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv new file mode 100644 index 0000000..cbc1cb2 --- /dev/null +++ b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv @@ -0,0 +1,2 @@ +head_reyebrow_0,head_reyebrow_1,head_reyebrow_2,head_reyebrow_3,head_reyebrow_4,head_leyebrow_4,head_leyebrow_3,head_leyebrow_2,head_leyebrow_1,head_leyebrow_0 +96,93,102,111,121,59,49,40,31,57 diff --git a/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.high-poly.csv b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.high-poly.csv new file mode 100644 index 0000000..9a5e314 --- /dev/null +++ b/src/python/blender/fullfaceWhiteLists/vertexWhitelist_newgirl.high-poly.csv @@ -0,0 +1,2 @@ +head_reye,head_leye +1023,485 diff --git a/src/python/blender/headerWithHeadAndOneMotion.bvh b/src/python/blender/headerWithHeadAndOneMotion.bvh new file mode 100644 index 0000000..5009ccf --- /dev/null +++ b/src/python/blender/headerWithHeadAndOneMotion.bvh @@ -0,0 +1,1022 @@ +HIERARCHY +ROOT hip +{ + OFFSET 0 0 0 + CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation + JOINT abdomen + { + OFFSET 0 20.6881 -0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT chest + { + OFFSET 0 11.7043 -0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT neck + { + OFFSET 0 22.1894 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT neck1 + { + OFFSET 0.000000 5.364170 1.574630 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT head + { + OFFSET 0.000000 5.364141 1.574630 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT __jaw + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT jaw + { + OFFSET 0.000000 -13.499860 2.500710 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT special04 + { + OFFSET -0.000000 -6.835370 4.375500 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris02 + { + OFFSET 0.000000 1.711150 2.820850 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris01 + { + OFFSET -0.000000 0.972390 0.845650 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.000000 1.162291 0.607091 + } + } + } + JOINT oris06.l + { + OFFSET 0.000000 1.711150 2.820850 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris07.l + { + OFFSET 1.168850 0.445180 0.506110 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.450611 1.195178 0.204519 + } + } + } + JOINT oris06.r + { + OFFSET 0.000000 1.711150 2.820850 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris07.r + { + OFFSET -1.168850 0.445180 0.506110 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.450611 1.195173 0.204519 + } + } + } + } + JOINT tongue00 + { + OFFSET -0.000000 -6.835370 4.375500 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT tongue01 + { + OFFSET 0.000000 3.973650 -3.762340 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT tongue02 + { + OFFSET 0.000000 0.429760 2.924710 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT tongue03 + { + OFFSET 0.000000 0.018530 2.059010 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT __tongue04 + { + OFFSET 0.000000 -0.440240 0.838860 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT tongue04 + { + OFFSET 0.000000 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.000000 -0.440230 0.838860 + } + } + } + JOINT tongue07.l + { + OFFSET 0.000000 -0.440240 0.838860 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.160923 -0.331531 0.018227 + } + } + JOINT tongue07.r + { + OFFSET 0.000000 -0.440240 0.838860 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.160922 -0.331531 0.018227 + } + } + } + JOINT tongue06.l + { + OFFSET 0.000000 0.018530 2.059010 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.644752 -0.526075 -0.203281 + } + } + JOINT tongue06.r + { + OFFSET 0.000000 0.018530 2.059010 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.644752 -0.526075 -0.203282 + } + } + } + JOINT tongue05.l + { + OFFSET 0.000000 0.429760 2.924710 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.971028 -0.388618 0.239206 + } + } + JOINT tongue05.r + { + OFFSET 0.000000 0.429760 2.924710 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.971028 -0.388618 0.239205 + } + } + } + } + } + } + JOINT __levator02.l + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator02.l + { + OFFSET 0.313580 -11.321120 11.599360 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator03.l + { + OFFSET 1.681690 -1.563730 -1.357570 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator04.l + { + OFFSET 0.504730 -1.676760 -0.058160 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator05.l + { + OFFSET 0.145440 -1.643170 -0.225470 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.310116 -0.760198 -0.121474 + } + } + } + } + } + } + JOINT __levator02.r + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator02.r + { + OFFSET -0.313580 -11.321120 11.599360 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator03.r + { + OFFSET -1.681690 -1.563740 -1.357570 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator04.r + { + OFFSET -0.504730 -1.676750 -0.058160 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator05.r + { + OFFSET -0.145440 -1.643170 -0.225470 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.310116 -0.760193 -0.121474 + } + } + } + } + } + } + JOINT __special01 + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT special01 + { + OFFSET -0.000000 -14.026930 -5.716970 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris04.l + { + OFFSET -0.000000 -0.492640 17.312620 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris03.l + { + OFFSET 1.215520 -0.627430 -0.393050 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.288776 -0.560899 -0.149645 + } + } + } + JOINT oris04.r + { + OFFSET -0.000000 -0.492640 17.312620 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris03.r + { + OFFSET -1.215520 -0.627440 -0.393050 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.288776 -0.560894 -0.149645 + } + } + } + JOINT oris06 + { + OFFSET -0.000000 -0.492640 17.312620 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oris05 + { + OFFSET -0.000000 -0.486000 0.000950 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.000000 -0.630493 0.197635 + } + } + } + } + } + JOINT __special03 + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT special03 + { + OFFSET 0.000000 -13.499860 2.500710 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT __levator06.l + { + OFFSET -0.000000 1.035800 10.090229 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator06.l + { + OFFSET 0.522240 -0.615720 0.045900 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.107505 -0.131898 -2.142227 + } + } + } + JOINT __levator06.r + { + OFFSET -0.000000 1.035800 10.090229 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT levator06.r + { + OFFSET -0.522240 -0.615730 0.045900 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.107506 -0.131893 -2.142227 + } + } + } + } + } + JOINT special06.l + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT special05.l + { + OFFSET 2.108890 0.153870 5.595070 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT eye.l + { + OFFSET 0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.111609 0.006490 4.051424 + } + } + JOINT orbicularis03.l + { + OFFSET 0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.097789 1.146431 3.788029 + } + } + JOINT orbicularis04.l + { + OFFSET 0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.113609 -1.130505 3.863064 + } + } + } + } + JOINT special06.r + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT special05.r + { + OFFSET -2.108890 0.153870 5.595070 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT eye.r + { + OFFSET -0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.111609 0.006490 4.051424 + } + } + JOINT orbicularis03.r + { + OFFSET -0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.097789 1.146431 3.788029 + } + } + JOINT orbicularis04.r + { + OFFSET -0.857170 -10.254801 2.414670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.113609 -1.130505 3.863064 + } + } + } + } + JOINT __temporalis01.l + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT temporalis01.l + { + OFFSET 6.332510 -9.444281 6.595120 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oculi02.l + { + OFFSET -0.804920 0.053010 1.621140 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oculi01.l + { + OFFSET -2.161570 1.690970 2.142660 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.740622 0.281157 0.647323 + } + } + } + } + } + JOINT __temporalis01.r + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT temporalis01.r + { + OFFSET -6.332510 -9.444281 6.595120 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oculi02.r + { + OFFSET 0.804920 0.053010 1.621140 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT oculi01.r + { + OFFSET 2.161570 1.690970 2.142660 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.740622 0.281157 0.647323 + } + } + } + } + } + JOINT __temporalis02.l + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT temporalis02.l + { + OFFSET 6.377600 -11.680510 6.235180 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT risorius02.l + { + OFFSET -0.814250 0.451130 1.721730 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT risorius03.l + { + OFFSET -0.649710 -2.514660 0.612550 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.479556 -1.760402 -1.642659 + } + } + } + } + } + JOINT __temporalis02.r + { + OFFSET 0.000000 13.604700 -0.502080 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT temporalis02.r + { + OFFSET -6.377600 -11.680510 6.235180 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT risorius02.r + { + OFFSET 0.814250 0.451130 1.721730 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT risorius03.r + { + OFFSET 0.649710 -2.514660 0.612550 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.479556 -1.760402 -1.642659 + } + } + } + } + } + } + } + } + JOINT rCollar + { + OFFSET -2.68224 19.2634 -4.8768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rShldr + { + OFFSET -8.77824 -1.95073 1.46304 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rForeArm + { + OFFSET -28.1742 -1.7115 0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rHand + { + + OFFSET -21.049400 0.002190 -0.634230 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal1.r + { + OFFSET -2.815680 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-1.r + { + OFFSET -6.292930 0.272380 2.520090 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-2.r + { + OFFSET -2.310530 -0.320530 -0.060510 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-3.r + { + OFFSET -2.051030 -0.295400 -0.164890 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.376838 -0.681367 -0.183877 + } + } + } + } + } + JOINT metacarpal2.r + { + OFFSET -2.815680 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-1.r + { + OFFSET -6.313640 0.626130 0.318530 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-2.r + { + OFFSET -3.015730 -0.589480 -0.088540 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-3.r + { + OFFSET -2.482120 -0.426280 0.076670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.344174 -0.731969 0.003260 + } + } + } + } + } + JOINT __metacarpal3.r + { + OFFSET -2.815680 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal3.r + { + OFFSET -0.606080 -0.162120 -1.874870 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-1.r + { + OFFSET -5.355730 0.702040 0.402510 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-2.r + { + OFFSET -2.643900 -0.485530 -0.117520 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-3.r + { + OFFSET -2.215850 -0.353160 0.066220 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.350273 -0.621223 -0.046375 + } + } + } + } + } + } + JOINT __metacarpal4.r + { + OFFSET -2.815680 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal4.r + { + OFFSET -0.606080 -0.162120 -1.874870 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-1.r + { + OFFSET -4.761700 0.175470 -1.109590 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-2.r + { + OFFSET -1.916360 -0.173360 -0.146170 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-3.r + { + OFFSET -1.411290 -0.108670 -0.020110 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.799226 -0.102363 -0.078601 + } + } + } + } + } + } + JOINT __rthumb + { + OFFSET -2.815680 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rthumb + { + OFFSET -0.283040 -0.142720 1.950690 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger1-2.r + { + OFFSET -0.915590 -2.152150 1.546760 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger1-3.r + { + OFFSET -3.213140 -0.470060 0.247480 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.521224 -0.161543 -0.511272 + } + } + } + } + } + } + } + } + } + JOINT lCollar + { + OFFSET 2.68224 19.2634 -4.8768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lShldr + { + OFFSET 8.77824 -1.95073 1.46304 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lForeArm + { + OFFSET 28.1742 -1.7115 0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lHand + { + + OFFSET 21.049408 0.002200 -0.634230 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal1.l + { + OFFSET 2.815670 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-1.l + { + OFFSET 6.292930 0.272390 2.520090 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-2.l + { + OFFSET 2.310530 -0.320520 -0.060510 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger2-3.l + { + OFFSET 2.051030 -0.295400 -0.164880 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.376823 -0.681367 -0.183876 + } + } + } + } + } + JOINT metacarpal2.l + { + OFFSET 2.815670 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-1.l + { + OFFSET 6.313640 0.626120 0.318530 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-2.l + { + OFFSET 3.015730 -0.589470 -0.088540 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger3-3.l + { + OFFSET 2.482120 -0.426270 0.076670 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.344170 -0.731978 0.003260 + } + } + } + } + } + JOINT __metacarpal3.l + { + OFFSET 2.815670 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal3.l + { + OFFSET 0.606080 -0.162120 -1.874870 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-1.l + { + OFFSET 5.355730 0.702050 0.402510 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-2.l + { + OFFSET 2.643900 -0.485530 -0.117510 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger4-3.l + { + OFFSET 2.215840 -0.353150 0.066210 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.350273 -0.621228 -0.046377 + } + } + } + } + } + } + JOINT __metacarpal4.l + { + OFFSET 2.815670 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT metacarpal4.l + { + OFFSET 0.606080 -0.162120 -1.874870 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-1.l + { + OFFSET 4.761700 0.175480 -1.109600 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-2.l + { + OFFSET 1.916350 -0.173360 -0.146170 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger5-3.l + { + OFFSET 1.411290 -0.108670 -0.020110 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.799216 -0.102372 -0.078600 + } + } + } + } + } + } + JOINT __lthumb + { + OFFSET 2.815670 -0.279180 0.531660 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lthumb + { + OFFSET 0.283040 -0.142710 1.950690 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger1-2.l + { + OFFSET 0.915930 -2.151960 1.546820 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT finger1-3.l + { + OFFSET 3.213210 -0.469680 0.247300 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.521210 -0.161290 -0.511422 + } + } + } + } + } + } + } + } + } + } + } + JOINT rButtock + { + OFFSET -8.77824 4.35084 1.2192 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rThigh + { + OFFSET 0 -1.70687 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rShin + { + OFFSET 0 -36.8199 0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rFoot + { + + OFFSET 0.73152 -45.1104 -5.12064 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe1-1.R + { + OFFSET 2.454000 -4.050002 13.194999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe1-2.R + { + OFFSET -0.214000 -0.646000 2.427000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.401900 -0.827789 2.725930 + } + } + } + JOINT toe2-1.R + { + OFFSET 0.177000 -4.299998 13.329000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe2-2.R + { + OFFSET -0.177000 -0.323000 2.039000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe2-3.R + { + OFFSET -0.067000 -0.440998 1.248000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.042990 -0.647306 1.660872 + } + } + } + } + JOINT toe3-1.R + { + OFFSET -1.396000 -4.461999 13.078999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe3-2.R + { + OFFSET -0.161000 -0.247002 1.809000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe3-3.R + { + OFFSET -0.033000 -0.441999 1.202000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.032040 -0.433550 1.271800 + } + } + } + } + JOINT toe4-1.R + { + OFFSET -2.888001 -4.480000 12.376999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe4-2.R + { + OFFSET -0.160000 -0.331998 1.491001 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe4-3.R + { + OFFSET 0.035999 -0.251002 1.138999 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.088911 -0.568814 0.969530 + } + } + } + } + JOINT toe5-1.R + { + OFFSET -4.257999 -4.467001 11.711999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe5-2.R + { + OFFSET -0.046000 -0.265999 0.982000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe5-3.R + { + OFFSET 0.086999 -0.372000 0.791000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.044329 -0.555482 1.085780 + } + } + } + } + } + } + } + } + JOINT lButtock + { + OFFSET 8.77824 4.35084 1.2192 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lThigh + { + OFFSET 0 -1.70687 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lShin + { + OFFSET 0 -36.8199 0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lFoot + { + + OFFSET -0.73152 -45.1104 -5.12064 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe1-1.L + { + OFFSET -2.454000 -4.050002 13.194999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe1-2.L + { + OFFSET 0.214000 -0.646000 2.427000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.401900 -0.827789 2.725930 + } + } + } + JOINT toe2-1.L + { + OFFSET -0.177000 -4.299998 13.329000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe2-2.L + { + OFFSET 0.177000 -0.323000 2.039000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe2-3.L + { + OFFSET 0.067000 -0.440998 1.248000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.042990 -0.647306 1.660872 + } + } + } + } + JOINT toe3-1.L + { + OFFSET 1.396000 -4.461999 13.078999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe3-2.L + { + OFFSET 0.161000 -0.247002 1.809000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe3-3.L + { + OFFSET 0.033000 -0.441999 1.202000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.032040 -0.433550 1.271800 + } + } + } + } + JOINT toe4-1.L + { + OFFSET 2.888001 -4.480000 12.376999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe4-2.L + { + OFFSET 0.160000 -0.331998 1.491001 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe4-3.L + { + OFFSET -0.035999 -0.251002 1.138999 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.088911 -0.568814 0.969530 + } + } + } + } + JOINT toe5-1.L + { + OFFSET 4.257999 -4.467001 11.711999 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe5-2.L + { + OFFSET 0.046000 -0.265999 0.982000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT toe5-3.L + { + OFFSET -0.086999 -0.372000 0.791000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.044329 -0.555482 1.085780 + } + } + } + } + } + } + } + } +} +MOTION +Frames: 1 +Frame Time: 0.04 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/src/python/blender/mouthWhiteLists/label.tag b/src/python/blender/mouthWhiteLists/label.tag new file mode 100644 index 0000000..bbdb241 --- /dev/null +++ b/src/python/blender/mouthWhiteLists/label.tag @@ -0,0 +1 @@ +mouth diff --git a/src/python/blender/mouthWhiteLists/vertexWhitelist_newgirl.body.csv b/src/python/blender/mouthWhiteLists/vertexWhitelist_newgirl.body.csv new file mode 100644 index 0000000..f1e57fe --- /dev/null +++ b/src/python/blender/mouthWhiteLists/vertexWhitelist_newgirl.body.csv @@ -0,0 +1,2 @@ +head_nostrills_2,head_chin,head_outmouth_0,head_outmouth_1,head_outmouth_2,head_outmouth_3,head_outmouth_4,head_outmouth_5,head_outmouth_6,head_outmouth_7,head_outmouth_8,head_outmouth_9,head_outmouth_10,head_outmouth_11,head_inmouth_0,head_inmouth_1,head_inmouth_2,head_inmouth_3,head_inmouth_4,head_inmouth_5,head_inmouth_6,head_inmouth_7 +297,5171,402,448,460,466,7219,7208,7162,7233,7245,492,490,478,432,704,468,7429,7192,7279,494,534 diff --git a/src/python/blender/reyeWhiteLists/label.tag b/src/python/blender/reyeWhiteLists/label.tag new file mode 100644 index 0000000..45dea56 --- /dev/null +++ b/src/python/blender/reyeWhiteLists/label.tag @@ -0,0 +1 @@ +reye diff --git a/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.body.csv b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.body.csv new file mode 100644 index 0000000..c4f6a6e --- /dev/null +++ b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.body.csv @@ -0,0 +1,2 @@ +head_reye_0,head_reye_1,head_reye_2,head_reye_3,head_reye_4,head_reye_5,head_nostrills_2,head_rchin_0,head_chin +4854,4849,4865,67,47,36,297,256,5171 diff --git a/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv new file mode 100644 index 0000000..3991b22 --- /dev/null +++ b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.eyebrow002.csv @@ -0,0 +1,2 @@ +head_reyebrow_0,head_reyebrow_1,head_reyebrow_2,head_reyebrow_3,head_reyebrow_4 +96,93,102,111,121 diff --git a/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.high-poly.csv b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.high-poly.csv new file mode 100644 index 0000000..7454ec8 --- /dev/null +++ b/src/python/blender/reyeWhiteLists/vertexWhitelist_newgirl.high-poly.csv @@ -0,0 +1,2 @@ +head_reye +764 diff --git a/src/python/mnet4/DNNAutoencoder.py b/src/python/mnet4/DNNAutoencoder.py new file mode 100755 index 0000000..f260046 --- /dev/null +++ b/src/python/mnet4/DNNAutoencoder.py @@ -0,0 +1,249 @@ + #!/usr/bin/python3 +import os +import sys +import gc +import time +from keras.layers import Input, Dense +from keras.models import Model +from keras.models import Sequential +from keras.models import model_from_json +from keras.utils import plot_model + +import keras.callbacks +import numpy as np +from readCSV import checkIfTestAndTrainListsAreOk,readGroundTruthFile,splitNumpyArray +from DNNModel import loadModel,saveModel,newAutoencoderModel,saveConfiguration,saveLastCompletedJob +from plotDNN import plotDNNHistory +from compress2DPoints import getNumberOfCompressedJoints + +def printArray(X): + for sample in range(0,len(X)): + for item in range(0,len(X[sample])): + print(X[sample][item],end=" ") + print("\n\n\n") + +def zeroFirstSixParameters(X): + for sample in range (0,len(X)): + X[sample][0]=0.0 + X[sample][1]=0.0 + X[sample][2]=0.0 + X[sample][3]=0.0 + X[sample][4]=0.0 + X[sample][5]=0.0 + +def normalizeParameters(X,labels): + useSTD=0 + startPosition=0 + endPosition=len(X[0]) + for item in range(startPosition,endPosition): + outputName=labels[item] + #---------------------------------------- + itemOnlyArray = splitNumpyArray(X,item) + #---------------------------------------- + maximum=np.max(itemOnlyArray) + minimum=np.min(itemOnlyArray) + median=np.median(itemOnlyArray) + mean=np.mean(itemOnlyArray) + std=np.std(itemOnlyArray) + var=np.var(itemOnlyArray) + #--------------------------------- + titleString="%s : Median=%0.2f,Mean=%0.2f,Std=%0.2f,Var=%0.2f" % (outputName,median,mean,std,var) + print("Stats of",titleString) + + if (useSTD): + if (std==0.0): + std=1.0 + for sample in range(0,len(X)): + meanCorrected = X[sample][item] - mean + X[sample][item] = float(meanCorrected/std) + else: + normalizationValue = (maximum-minimum) + if (normalizationValue==0.0): + normalizationValue=1.0 + for sample in range(0,len(X)): + meanCorrected = X[sample][item] - minimum + X[sample][item] = float(meanCorrected/normalizationValue) + + +def writeSkeleton(X,name): + os.system('cp header.bvh %s.bvh' % name) + fileP = open("%s.bvh" % name,"a") + fileP.write("\nMOTION\n") + fileP.write("Frames: 1\n") + fileP.write("Frame Time: 0.04\n") #25fps + for i in range(0,len(X)): + fileP.write("%0.4f" % X[i]) + fileP.write(" ") + fileP.write("\n") + +def getNumberOfSkeletonsCloseToNeedle(haystack,needle,hitAccuracy,threshold): + numberOfMatches=0 + startPosition=6 + endPosition=len(haystack[0]) + numberOfItems=endPosition-startPosition + for sample in range(0,len(haystack)): + thisItemMatches=0 + for item in range(startPosition,endPosition): + if (np.abs(needle[item]-haystack[sample][item])1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--mem"): + print("\nMemory usage ",sys.argv[i+1]); + memPercentage=float(sys.argv[i+1]) + if (sys.argv[i]=="--compression"): + print("\nCompression Factor",sys.argv[i+1]); + compressionFactor=float(sys.argv[i+1]) + if (sys.argv[i]=="--all"): + #Don't mix this --all with the step2_OrientatonClassifier.py --all + hierarchyPartName=sys.argv[i+1] + dataFile="%s_all" % hierarchyPartName + outputDirectoryForTrainedModels="autoencoder_%s" % dataFile + if (sys.argv[i]=="--back"): + hierarchyPartName=sys.argv[i+1] + dataFile="%s_back" % hierarchyPartName + outputDirectoryForTrainedModels="autoencoder_%s" % dataFile + if (sys.argv[i]=="--front"): + hierarchyPartName=sys.argv[i+1] + dataFile="%s_front" % hierarchyPartName + outputDirectoryForTrainedModels="autoencoder_%s" % dataFile + if (sys.argv[i]=="--left"): + hierarchyPartName=sys.argv[i+1] + dataFile="%s_left" % hierarchyPartName + outputDirectoryForTrainedModels="autoencoder_%s" % dataFile + if (sys.argv[i]=="--right"): + hierarchyPartName=sys.argv[i+1] + dataFile="%s_right" % hierarchyPartName + outputDirectoryForTrainedModels="autoencoder_%s" % dataFile + #----------------------------------------------- + if (sys.argv[i]=="--in"): + mode=0 + if (sys.argv[i]=="--out"): + mode=1 + +print("\n-------------------------------------------------"); +useNonCompressed=1 +useCompressed=1 +numberOfEpochs=1000 +useRadians=0 + +testMemPercentage=0.01 +if (memPercentage==0): testMemPercentage=memPercentage +if (memPercentage>1): testMemPercentage=memPercentage + +groundTruthTrain = readGroundTruthFile("Train","../../DNNTracker/dataset/2d_%s.csv" % dataFile,"../../DNNTracker/dataset/bvh_%s.csv" % dataFile,memPercentage,useNonCompressed,useCompressed,useRadians) +groundTruthTest = readGroundTruthFile("Test","../../DNNTracker/dataset/2d_test.csv" ,"../../DNNTracker/dataset/bvh_test.csv",testMemPercentage,useNonCompressed,useCompressed,useRadians) +checkIfTestAndTrainListsAreOk(groundTruthTest,groundTruthTrain) +print("\n-------------------------------------------------"); + + +if (mode==0): + field='in' + fieldLabel='labelIn' +else: + field='out' + fieldLabel='labelOut' + +outputName="autoencoder" +startAt = time.time() +autoencoder = newAutoencoderModel( + outputName, + len(groundTruthTrain[field][0]), + compressionFactor + ) + +getMeanSkeleton(groundTruthTrain[field],groundTruthTrain[fieldLabel]) + +normalizeParameters(groundTruthTrain[field],groundTruthTrain[fieldLabel]) +sys.exit(0) + + + +if (field=='out'): + zeroFirstSixParameters(groundTruthTest[field]) + +#printArray(groundTruthTest[field]) + +history = autoencoder.fit( + groundTruthTrain[field],groundTruthTrain[field], + epochs=numberOfEpochs, + batch_size=128, + shuffle=True, + validation_data=(groundTruthTest[field],groundTruthTest[field]) + ) + + +#--------------------------------------------------------------------- +#-------------------- STORE CONFIGURATION ---------------------------- +endAt = time.time() +print("Time required to train ",outputName," was ",(endAt-startAt)/60," mins") +#--------------------------------------------------------------------- +saveModel(outputName,autoencoder) +#--------------------------------------------------------------------- + +print("Sucessfully ended..\n") diff --git a/src/python/mnet4/DNNModel.py b/src/python/mnet4/DNNModel.py new file mode 100755 index 0000000..3cc3fd8 --- /dev/null +++ b/src/python/mnet4/DNNModel.py @@ -0,0 +1,1234 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import sys +import os +import time + +import tensorflow as tf +import keras +from keras.layers import concatenate, Add, Input, Dense, GlobalMaxPooling1D, GlobalAveragePooling1D, Flatten, Reshape, AlphaDropout, Dropout, Lambda, MaxPooling1D, MaxPooling2D, Conv2D, ZeroPadding1D +from keras.models import Model,Sequential, model_from_json +from keras.utils import plot_model + +from keras import layers +from keras import activations + +from tools import bcolors,createDirectory,tensorflowFriendlyModelName + +import math +import numpy as np + +#Defaults, will get overridden by setupDNNModelsUsingJSONConfiguration +useLambdas = 0 +numberOfLayers = 12 +dropoutRate = 0.15 #Global dropout rate +learningRate = 0.00025 #0.00045 #0.00025=MocapNET2019 +useModuloMetric = 0 +useQuadMetric = 0 +useSquaredMetric = 1 + +# ------------------------------------------------------------ +#https://github.com/cpuimage/HardMish +def hard_mish(x): + return tf.minimum(2., tf.nn.relu(x + 2.)) * 0.5 * x +# ------------------------------------------------------------ + +#https://stackoverflow.com/questions/46355068/keras-loss-function-for-360-degree-prediction + +# y in radians +#def mean_squared_error_360(y_true, y_pred): +# yTrueRads=tf.math.scalar_mul(0.017453292519943295,y_true) +# yPredRads=tf.math.scalar_mul(0.017453292519943295,y_pred) + +# return tf.reduce_mean(tf.math.square(tf.math.scalar_mul(57.295779513,tf.atan2(tf.sin(yTrueRads - yPredRads), tf.cos(yTrueRads - yPredRads))))) + #return tf.math.scalar_mul(57.295779513,tf.reduce_mean(tf.abs(tf.atan2(tf.sin(yTrueRads - yPredRads), tf.cos(yTrueRads - yPredRads))))) + +#def rmse_360(y_true, y_pred): +# return K.sqrt(mean_squared_error_360(y_true, y_pred)) + +def testMyObjective(): + X = tf.compat.v1.placeholder("float32", name="input") + Y = tf.compat.v1.placeholder("float32", name="input") + OUT = tf.abs(tf.subtract(tf.math.floormod(tf.add(X,180),360),tf.math.floormod(tf.add(Y,180),360))) + with tf.compat.v1.Session() as sess: + for x in range (-360,360): + for y in range (-360,360): + print("x=",x," y=",y," val=",sess.run(OUT, feed_dict={X:x,Y:y})) + sys.exit(0) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def mean_quad_error(yTrue,yPred): + #reduce_mean reduce_sum + return tf.reduce_mean(input_tensor=tf.math.square(tf.math.square(tf.subtract(yTrue,yPred)))) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def mean_squared_error_modulo_360(yTrue,yPred): + #reduce_mean reduce_sum + return tf.reduce_mean(input_tensor=tf.math.square(tf.abs(tf.subtract(tf.math.floormod(tf.add(yTrue,180),360),tf.math.floormod(tf.add(yPred,180),360))))) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def average_error_modulo_360(yTrue,yPred): + return tf.reduce_mean(input_tensor=tf.abs(tf.subtract(tf.math.floormod(tf.add(yTrue,180),360),tf.math.floormod(tf.add(yPred,180),360)))) + + +#=================================================================================================================================================================================== + + + + +#SWISH - https://arxiv.org/abs/1710.05941 +#MISH - https://arxiv.org/vc/arxiv/papers/1908/1908.08681v1.pdf / https://github.com/cpuimage/HardMish +#https://krutikabapat.github.io/Swish-Vs-Mish-Latest-Activation-Functions/ + +#------------------------------------------------------------- +#------------------------------------------------------------- +theActivationMethod='selu' # hard_mish 'selu' 'swish' +initializer = keras.initializers.LecunNormal() #'lecun_normal' +#------------------------------------------------------------- +#------------------------------------------------------------- +def startProfiling(): + print(bcolors.WARNING,"Starting Tensorflow Profiling (this run will be slower than usual)..\n",bcolors.ENDC) + os.system("rm -rf profiling") + tf.profiler.experimental.start('profiling') +#------------------------------------------------------------- +def stopProfiling(): + print(bcolors.WARNING,"Stopping Tensorflow Profiling..\n",bcolors.ENDC) + tf.profiler.experimental.stop() +#------------------------------------------------------------- +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== + + + +def getActivationRandomization(configuration): + global theActivationMethod + theActivationMethod=configuration['activationFunction'] + global initializer + theRandomizationMethod=configuration['weightRandomizationFunction'] + #---------------------------------------------------------------------- + print(bcolors.OKGREEN,"Activation/Randomization set to ",theActivationMethod," -> ",theRandomizationMethod," ",bcolors.ENDC) + #---------------------------------------------------------------------- + thisSeed = 0 + if (configuration["setConstantSeedForReproducibleTraining"]==0): + import random + seed = random.randint(0,1024) + print("Setting random seed to ",seed,"! ") + + if (theRandomizationMethod=="auto"): + if (theActivationMethod=='selu'): + initializer = keras.initializers.LecunNormal(seed=thisSeed) + print("Automatic resolution SeLU -> LeCun Normal") + elif (theActivationMethod=='swish'): + #Draws samples from a uniform distribution within [-limit, limit], where limit = sqrt(6 / fan_in) (fan_in is the number of input units in the weight tensor). + initializer=keras.initializers.HeUniform(seed=thisSeed) #https://www.cv-foundation.org/openaccess/content_iccv_2015/html/He_Delving_Deep_into_ICCV_2015_paper.html + print("Automatic resolution SWISH -> He Uniform") + elif (theRandomizationMethod=="glorot_uniform"): #Xavier + initializer=keras.initializers.GlorotUniform(seed=thisSeed) + elif (theRandomizationMethod=="lecun_normal"): + initializer=keras.initializers.LecunNormal(seed=thisSeed) + elif (theRandomizationMethod=="he_uniform"): #Kaiming + initializer=keras.initializers.HeUniform(seed=thisSeed) + else: + print(bcolors.FAIL,"Please add ",theActivationMethod,"/",theRandomizationMethod," to getActivationRandomization",bcolors.ENDC) + sys.exit(1) + + + + + +def setupDNNModelsUsingJSONConfiguration(configuration): + #Copy settings from configuration json file + #--------------------------------------------------------------- + global numberOfLayers + numberOfLayers=configuration['neuralNetworkDepth'] + #--------------------------------------------------------------- + #New code that handles activation/randomization + getActivationRandomization(configuration) + #--------------------------------------------------------------- + global dropoutRate + dropoutRate=configuration['dropoutRate'] + global learningRate + learningRate=configuration['learningRate'] #0.00045 #0.00025=MocapNET2019 + global useQuadMetric + useQuadMetric=configuration['useQuadLoss'] + global useSquaredMetric + useSquaredMetric=configuration['useSquaredLoss'] + #--------------------------------------------------------------- + print("Configuration setting numberOfLayers to ",numberOfLayers) + print("Configuration setting activationMethod to ",theActivationMethod) + print("Configuration setting dropoutRate to ",dropoutRate) + print("Configuration setting learningRate to ",learningRate) + print("Configuration setting useQuadMetric to ",useQuadMetric) + #--------------------------------------------------------------- +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def saveProgressEnded(message): + file = open("progress.txt","w") + file.write(str(message)) + file.write("\n") +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def saveLastCompletedJob(lastCompletedJob,currentJob,lastJob,approxTime): + file = open("progress.txt","w") + file.write(str(lastCompletedJob)) + file.write("\n") + file.write(str(currentJob)) + file.write("/") + file.write(str(lastJob)) + file.write("\n") + file.write("TimeApprox:") + file.write(str(approxTime)) + file.write(" minutes") + file.write("\n") +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def saveConfiguration(path,model,modelName,modelParameters,numberOfEpochs,batchSize,trainInput,trainOutput,outputType,history,startAt,endAt,median,mean,std,var): + finalPath = ("%s/%s") % (path,modelName) + createDirectory(path) + createDirectory(finalPath) + file = open("%s/configuration.txt" % finalPath,"w") + file.write("Number of Model Parameters:") + file.write(str(modelParameters)) + file.write("\nNumber of Epochs:") + file.write(str(numberOfEpochs)) + file.write("\nBatch Size:") + file.write(str(batchSize)) + #-------------------------------- + file.write("\nTrain Input Number of elements:") + file.write(str(len(trainInput))) + file.write("\nTrain Input: ") + for item in trainInput: + file.write("%s " % item) + #-------------------------------- + + + #file.write("\nModel:") + #file.write(model.summary()) #TypeError: write() argument must be str, not None + #file.write("\n") + + + #-------------------------------- + file.write("\nTest Output Number of elements:") + file.write(str(len(trainOutput))) + file.write("\nTest Output: ") + for item in trainOutput: + file.write("%s " % item) + #-------------------------------- + file.write("\n\nSpecific Output: ") + file.write(trainOutput[outputType]) + file.write("\n") + + + file.write("\n\nOutput Statistics for ") + title_string=" %s : Median=%0.2f,Mean=%0.2f,Std=%0.2f,Var=%0.2f" % (modelName,median,mean,std,var) + file.write(title_string) + file.write("\n") + + try: + file.write("\nTraining History Loss: ") + file.write(str(history.history['loss'])) + file.write("\n") + except: + print("Tried to save loss but no such history element was found..") + + try: + file.write("\nTraining History MAE: ") + file.write(str(history.history['mean_absolute_error'])) + file.write("\n") + except: + print("Tried to save mean absolute error but no such history element was found..") + + #file.write("\nTraining History MAE: ") + #file.write(str(history.history['mean_absolute_error'])) + #file.write("\n") + + #file.write("\nTraining History Accuracy: ") + #file.write(str(history.history['acc'])) + #file.write("\n") + + #file.write("\nTesting History Loss: ") + #file.write(str(history.history['val_loss'])) + #file.write("\n") + + #file.write("\nTesting History MAE: ") + #file.write(str(history.history['val_mean_absolute_error'])) + #file.write("\n") + + #file.write("\nTesting History Accuracy: ") + #file.write(str(history.history['val_acc'])) + #file.write("\n") + + file.write("\nDuration: ") + file.write(str((endAt-startAt)/60)) + file.write(" mins\n") + file.close() +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def loadNewModel(path): + #loaded_model = tf.saved_model.load(path) + #loaded_model.compile(loss='mse', optimizer='rmsprop', metrics=['mae', 'acc']) + #return loaded_model + return keras.models.load_model(path) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +""" +Load the model to the supplied path (first try TF, then JSON/H5) +""" +def loadModelOLD(path,filename): + if (os.path.isfile('%s/saved_model.pb' % (path))): + print(bcolors.OKGREEN,"Loading TF Model %s/saved_model.pb from disk " % (path),bcolors.ENDC) + loaded_model = keras.models.load_model(path,custom_objects={'mean_quad_error':mean_quad_error}) + return loaded_model + elif (os.path.isfile('%s/%s.json' % (path,filename))) and (os.path.isfile('%s/%s.h5' % (path,filename))): + print(bcolors.FAIL,"File %s/%s.json does not exist\n",bcolors.ENDC) + json_file = open('%s/%s.json' % (path,filename),'r') + loaded_model_json = json_file.read() + json_file.close() + loaded_model = model_from_json(loaded_model_json) + loaded_model.load_weights("%s/%s.h5" % (path,filename)) + print(bcolors.OKGREEN,"Success ",bcolors.ENDC) + loaded_model.compile(loss='mse', optimizer='rmsprop', metrics=['mae', 'acc']) + print("Loading Model %s/%s from disk" % (path,filename)) + return loaded_model + else: + print(bcolors.FAIL,"Could not find model %s \n" % path,bcolors.ENDC) + sys.exit(1) +#=================================================================================================================================================================================== +""" +Save the model to the supplied path +""" +def saveModelOLD(path,model,name="model"): + createDirectory(path) + # serialize model to JSON + model_json = model.to_json() + with open("%s/%s.json" % (path,name) , "w") as json_file: + json_file.write(model_json) + json_file.close() + # serialize weights to HDF5 + model.save_weights("%s/%s.h5" % (path,name)) + print("Saved model to disk at %s/%s.(h5/json)" % (path,name)) + #24/5/23: TF 2.12.0 : emmits you must feed a value for placeholder tensor 'inputs' with dtype float and shape [?,76] + model.save(path, save_format='tf') #save directory.. + print("Saved model to disk at %s/saved_model.pb (TF)" % (path)) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def loadModel(path,filename): + from tools import bcolors + print(bcolors.OKGREEN,"Loading %s model.. " % path,bcolors.ENDC) + try: + #Regular keras loading until V3 that breaks + import keras + model_path = '%s/%s.keras' % (path,filename) + model = keras.saving.load_model(model_path, custom_objects={'mean_quad_error':mean_quad_error} , compile=True, safe_mode=True) + except Exception as e: + #Fallback to TF2 saved_model loading + print(bcolors.FAIL,"An exception occurred trying to load keras model:", str(e),bcolors.ENDC) + print(bcolors.OKGREEN,"Falling back to TF saved_model loader.. ",bcolors.ENDC) + model = tf.saved_model.load(path) + signatures = model.signatures + signature_keys = signatures.keys() + if 'serving_default' in signature_keys: + signature_key = 'serving_default' + else: + signature_key = list(signature_keys)[0] + + return model +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def saveModel(path,model,name="model"): + createDirectory(path) + + print(bcolors.OKGREEN,"Saving result as keras model..",bcolors.ENDC) + model.save('%s/%s.keras' % (path,name)) + + # Save the exported tensorflow model + print(bcolors.OKGREEN,"Saving result as tensorflow model..",bcolors.ENDC) + model.export(path, "tf_saved_model") +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +""" +Counts the number of trainable parameters in a TensorFlow model +""" +def countModelParameters(model): + total_parameters = 0 + for variable in model.trainable_variables: + shape = variable.shape + variable_parametes = 1 + for dim in shape: + variable_parametes *= dim + total_parameters += variable_parametes + return total_parameters +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +# define autoencoder model +def newAutoencoderModel(modelName,inputDimension,shrinkRatio): + # create model + model = Sequential(name=modelName) #----------------------------------------------------------------------------------------------------------------------------------- + model.add(Dense(inputDimension, input_dim=inputDimension, activation='relu' , name='encoder')) + model.add(Dense(int(inputDimension/shrinkRatio), kernel_initializer='normal', activation='relu')) + model.add(Dense(inputDimension, activation='sigmoid' , name='decoder')) #----------------------------------------------------------------------------------------------------------------------------------- + # Compile model + model.compile(optimizer='adadelta',loss='binary_crossentropy', metrics=['accuracy']) + #model.compile(optimizer='rmsprop', loss='mse', metrics=['mae']) + model.summary() + return model + + +#---------------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------------- +# LAMBDA LAYERS +#---------------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------------- +#Found in https://github.com/keras-team/keras/issues/890 +#by https://github.com/marc-moreaux +def sliceL(dimension, start, end): + # Crops (or slices) a Tensor on a given dimension from start to end + # example : to crop tensor x[:, :, 5:10] + # call slice(2, 5, 10) as you want to crop on the second dimension + def func(x): + if dimension == 0: + return x[start: end] + if dimension == 1: + return x[:, start: end] + if dimension == 2: + return x[:, :, start: end] + if dimension == 3: + return x[:, :, :, start: end] + if dimension == 4: + return x[:, :, :, :, start: end] + return Lambda(func, name='Slice_from_%u_to_%u_in_%u-D'%(start,end,dimension)) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def modL(): + def func(x): + return (360+x)%360 + return Lambda(func, name='360_plus_x_modulo_360') +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def tag(kind="layer",name="mnet",index=0,number=0): + return "%s_%u_%s_%u"%(name,index,kind,number) + + +#---------------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------------- +# FIRST STAGE +#---------------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------------- +def newCategorizeOneHotModel(modelName,inputDimension,nonNSDMInputSize,numberOfChannelsPerNSDMElement,outputSize,networkCompression): + print('newCategorizeOneHotModel has input ',inputDimension,' elements and output of ',outputSize,' elements e:',keras.backend.epsilon()) + print('Learning Rate is 0.00001, Dropout Rate is ',dropoutRate,' ') + modelName = tensorflowFriendlyModelName(modelName) + print("Model renamed to ",modelName," to make sure it doesn't call tensorflow problems ") + + inputs = Input(shape=(inputDimension,)) + + #positionalInput=nonNSDMInputSize + #differentiatedInput=inputDimension-positionalInput + #splitInput = sliceL(1,positionalInput,inputDimension)(inputs) + #inputDimension = inputDimension-positionalInput + + #initializer = keras.initializers.lecun_normal(seed=0) + + outputArrayIndex=0 + + doInputSplit = 0 + selectedInput = inputs + + if (doInputSplit): + positionalInput=nonNSDMInputSize + differentiatedInput=inputDimension-positionalInput + splitInput = sliceL(1,positionalInput,inputDimension)(inputs) + inputDimension = inputDimension-positionalInput + selectedInput = splitInput + + # a layer instance is callable on a tensor, and returns a tensor + #goldenRatio=1.61803398875 // was 2 3 4 5 6 + #----------------------------------------------------------------------------------------------------------------------------------- + #Input | 320 + + #Shorthand names that fit in screen + act = theActivationMethod + kinit = initializer + indim = inputDimension + l = networkCompression + + # Layer 1 + thisLayerRatio=2 + layerNumber=1 + xA = Dense(int(indim/(thisLayerRatio*l)), input_shape=(inputDimension,) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(selectedInput)#(inputs) + xA = Dropout(0.2, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xA) + + # Layer 2 + thisLayerRatio=2 + layerNumber=layerNumber+1 + xB = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xA) + xB = Dropout(0.3, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xB) + + # Layer 3 + thisLayerRatio=3 + layerNumber=layerNumber+1 + xC = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xB) + xC = Dropout(0.3, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xC) + + # Layer 4 + thisLayerRatio=4 + layerNumber=layerNumber+1 + xD = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xC) + xD = Dropout(0.4, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xD) + sBD = Dense(int(indim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xB) + xD = Add()([xD,sBD]) # main + skip + + # Layer 5 + thisLayerRatio=5 + layerNumber=layerNumber+1 + xE = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xD) + xE = Dropout(0.4, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xE) + + # Layer 6 + thisLayerRatio=6 + layerNumber=layerNumber+1 + xF = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xE) + xF = Dropout(0.4, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xF) + + # Layer 7 + thisLayerRatio=7 + layerNumber=layerNumber+1 + xG = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xF) + xG = Dropout(0.4, name='classifier_%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xG) + sDG = Dense(int(indim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xD) + xG = Add()([xG,sDG]) # main + skip + + # Layer 8 + thisLayerRatio=8 + layerNumber=layerNumber+1 + xH = Dense(int(indim/(thisLayerRatio*l)) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xG) + #xH = Dropout(0.4)(xH) + + # Layer 9 + layerNumber=layerNumber+1 + xOut = Dense(int(outputSize) , kernel_initializer=kinit, activation=act, name='classifier_%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber) )(xH) + + #----------------------------------------------------------------------------------------------------------------------------------- + predictions = keras.layers.Dense(int(outputSize),name='Category' , kernel_initializer='normal', activation='softmax')(xOut) + #----------------------------------------------------------------------------------------------------------------------------------- + + # This creates a model that includes + # the Input layer and three Dense layers + model = Model(name=modelName, inputs=inputs, outputs=predictions) + + #from keras.metrics import categorical_accuracy + #model.compile(optimizer='adam', loss='binary_crossentropy', metrics=[categorical_accuracy]) + + #special slower learning.. + #For 3.8M samples 0.00001 is a good value.. + if (optimizer=="adam"): + activeOptimizer=keras.optimizers.Adam(learning_rate=learningRate,epsilon=keras.backend.epsilon()) + else: + activeOptimizer=keras.optimizers.RMSprop(learning_rate=learningRate, rho=0.9, epsilon=keras.backend.epsilon()) # epsilon=1e-6, lr=0.00025 is the old value + #------------------------------------------------------------------------------------------------------- + model.compile(optimizer=activeOptimizer, loss='categorical_crossentropy', metrics=['accuracy']) + #------------------------------------------------------------------------------------------------------- + model.summary() + #plot_model(model, to_file='modelXYZStage.png') + return model +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +""" + The regular MocapNETv3 Densely Connected Encoder Along With its Skip Connections +""" +def newDeepRotModel(configuration,modelName,outputArrayIndex,inputDimension,nonNSDMInputSize,numberOfChannelsPerNSDMElement,outputSize,networkCompression,probabilisticMode=0,quantize=False,optimizer="rmsprop"): + #return convDeepRotModel(modelName,outputArrayIndex,inputDimension,nonNSDMInputSize,numberOfChannelsPerNSDMElement,outputSize,networkCompression) + + dropoutRate = float(configuration["dropoutRate"]) + print('newXYZROTModel with skip connections has input ',inputDimension,' elements , compression λ=',networkCompression,' and output of ',outputSize,' elements') + print('Learning Rate is ',learningRate,', Dropout Rate is ',dropoutRate,' ') + print('Use Quad Loss is ',useQuadMetric,', Use Modulo Loss is ',useModuloMetric,' ') + print('Use Squared Loss is ',useSquaredMetric) + modelName = tensorflowFriendlyModelName(modelName) + print("Model renamed to ",modelName," to make sure it doesn't call tensorflow problems ") + + inputs = Input(shape=(inputDimension,)) + + doInputSplit = 0 + selectedInput = inputs + + if (doInputSplit): + positionalInput=nonNSDMInputSize + differentiatedInput=inputDimension-positionalInput + splitInput = sliceL(1,positionalInput,inputDimension)(inputs) + inputDimension = inputDimension-positionalInput + selectedInput = splitInput + + # a layer instance is callable on a tensor, and returns a tensor + #goldenRatio=1.61803398875 // was 2 3 4 5 6 + #----------------------------------------------------------------------------------------------------------------------------------- + #Input | 320 | 463 + + #Shorthand names that fit in screen + act = theActivationMethod + kinit = initializer + inptdim = inputDimension + + if (numberOfLayers==0): + print("Garbage configuration with 0 layers") + sys.exit(1) + + + # Layer 1 | 2 |160 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=1): + layerNumber=1 + thisLayerRatio=2.2 #BMVC21 this was 2.2 + xA = Dense(int(inptdim/(thisLayerRatio*networkCompression)), input_shape=(inptdim,) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(selectedInput) + if (dropoutRate>0.0): + xA = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xA) #0.2 vs dropoutRate + if (numberOfLayers==1): + xOut = xA + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 2 | 3 | 106 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=2): + layerNumber=layerNumber+1 + thisLayerRatio=3.0 #BMVC21 this was 3.0 + xB = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xA) + if (dropoutRate>0.0): + xB = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xB) #0.3 vs dropoutRate + if (numberOfLayers==2): + xOut = xB + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + + # Layer 3 | 4 | 80 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=3): + layerNumber=layerNumber+1 + thisLayerRatio=3.2 #BMVC21 this was 3.2 + sAC = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xA) + if (dropoutRate>0.0): + sAC = Dropout(dropoutRate, name='%s_%u_rdropout_%u'%(modelName,outputArrayIndex,layerNumber))(sAC) #0.4 vs dropoutRate + xC = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xB) + if (dropoutRate>0.0): + xC = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xC) #0.4 vs dropoutRate + xC = Add(name='%s_%u_add_%u'%(modelName,outputArrayIndex,layerNumber))([xC,sAC]) # main + skip + if (numberOfLayers==3): + xOut = xC + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 4 | 5 | 64 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=4): + layerNumber=layerNumber+1 + thisLayerRatio=3.4 #BMVC21 this was 3.4 + xD = Dense(int(inptdim/(thisLayerRatio*networkCompression)), kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xC) + if (dropoutRate>0.0): + xD = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xD) + if (numberOfLayers==4): + xOut = xD + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 5 | 6 | 53 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=5): + layerNumber=layerNumber+1 + thisLayerRatio=3.5 #BMVC21 this was 3.5 + sCE = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xC) + if (dropoutRate>0.0): + sCE = Dropout(dropoutRate, name='%s_%u_rdropout_%u'%(modelName,outputArrayIndex,layerNumber))(sCE) + xE = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xD) + if (dropoutRate>0.0): + xE = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xE) + xE = Add(name='%s_%u_add_%u'%(modelName,outputArrayIndex,layerNumber))([xE,sCE]) # main + skip + if (numberOfLayers==5): + xOut = xE + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 6 | 8 | 106 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=6): + layerNumber=layerNumber+1 + thisLayerRatio=3.8 #BMVC21 this was 3.8 + xF = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xE) + if (dropoutRate>0.0): + xF = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xF) + if (numberOfLayers==6): + xOut = xF + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 7 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=7): + layerNumber=layerNumber+1 + thisLayerRatio=4.2 #BMVC21 this was 4.2 + sEG = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xE) + if (dropoutRate>0.0): + sEG = Dropout(dropoutRate, name='%s_%u_rdropout_%u'%(modelName,outputArrayIndex,layerNumber))(sEG) + xG = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xF) + if (dropoutRate>0.0): + xG = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xG) + xG = Add(name='%s_%u_add_%u'%(modelName,outputArrayIndex,layerNumber))([xG,sEG]) # main + skip + if (numberOfLayers==7): + xOut = xG + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 8 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=8): + layerNumber=layerNumber+1 + thisLayerRatio=5 #BMVC21 this was 5 + xH = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xG) + if (dropoutRate>0.0): + xH = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xH) + if (numberOfLayers==8): + xOut = xH + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 9 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=9): + layerNumber=layerNumber+1 + thisLayerRatio=6 #BMVC21 this was 6 + sGI = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xG) + #if (dropoutRate>0.0): + # sGI = Dropout(dropoutRate, name='%s_%u_rdropout_%u'%(modelName,outputArrayIndex,layerNumber))(sGI) + xI = Dense(int(inptdim/(thisLayerRatio*networkCompression)) , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xH) + #if (dropoutRate>0.0): #After experiment 269A, dropout this late seems like a bad idea + # xI = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xI) + xI = Add(name='%s_%u_add_%u'%(modelName,outputArrayIndex,layerNumber))([xI,sGI]) # main + skip + if (numberOfLayers==9): + xOut = xI + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 10 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=10): + layerNumber=layerNumber+1 + preLastDimension = 60 #Pre probabilities was 32 + #Last Dimension before max pooling + xPreLast = Dense(preLastDimension , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xI) + #if (dropoutRate>0.0): #After experiment 269A, dropout this late seems like a bad idea + # xPreLast = Dropout(dropoutRate, name='%s_%u_dropout_%u'%(modelName,outputArrayIndex,layerNumber))(xPreLast) + if (numberOfLayers==10): + xOut = xPreLast + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 11 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=11): + layerNumber=layerNumber+1 + lastDimension = 60 #Pre probabilities was 16 + #Last Dimension before max pooling + xLast = Dense(lastDimension , kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u'%(modelName,outputArrayIndex,layerNumber))(xPreLast) + + #Lets pick the maximum response as our final output + #reshaped = Reshape([lastDimension,1])(xLast) + #flat = GlobalMaxPooling1D() (reshaped) + #flat = GlobalAveragePooling1D() (reshaped) + flat = xLast + if (numberOfLayers==11): + xOut = flat + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + # Layer 12 + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (numberOfLayers>=12): + layerNumber=layerNumber+1 #last activation no longer linear 'linear' + sEOut= Dense(int(outputSize), kernel_initializer=kinit, activation=act, name='%s_%u_residual_%u'%(modelName,outputArrayIndex,layerNumber))(xE) + xOut = Dense(int(outputSize), kernel_initializer=kinit, activation=act, name='%s_%u_layer_%u' %(modelName,outputArrayIndex,layerNumber))(flat) + xOut = Add(name='%s_%u_add_%u'%(modelName,outputArrayIndex,layerNumber))([xOut,sEOut]) # main + skip + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + if (probabilisticMode): + #----------------------------------------------------------------------------------------------------------------------------------- + predictions = keras.layers.Dense(int(outputSize), name='output_rotation_%u_%s'%(outputArrayIndex,modelName), kernel_initializer='normal', activation='softmax')(xOut) + #----------------------------------------------------------------------------------------------------------------------------------- + else: + #And also have a weight to scale it. + predictions = keras.layers.Dense(int(outputSize),kernel_initializer='normal', activation='linear', name='output_rotation_%u_%s'%(outputArrayIndex,modelName))(xOut) + #----------------------------------------------------------------------------------------------------------------------------------- + + + # the Input layer and three Dense layers + model = Model(name=modelName, inputs=inputs, outputs=predictions) + #model.compile(optimizer='adam', loss='mse', metrics=['mae']) + if (optimizer=="adam"): + activeOptimizer=keras.optimizers.Adam(learning_rate=learningRate,epsilon=keras.backend.epsilon()) + else: + activeOptimizer=keras.optimizers.RMSprop(learning_rate=learningRate, rho=0.9, epsilon=keras.backend.epsilon()) # epsilon=1e-6, lr=0.00025 is the old value + #------------------------------------------------------------------------------------------------------- + + if (quantize): + #Careful not all activations are quantization aware + print("Will attempt to quantize model..!") + try: + import tensorflow_model_optimization as tfmot + quantize_model = tfmot.quantization.keras.quantize_model + model = quantize_model(model) + except: + print("Could not quantize model, you are using a non quantization aware activation function..!") + print("https://github.com/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/python/core/quantization/keras/quantize_aware_activation.py") + + if (probabilisticMode): + model.compile(optimizer=activeOptimizer, loss='categorical_crossentropy', metrics=['accuracy']) + elif (useQuadMetric): + model.compile(optimizer=activeOptimizer, loss=mean_quad_error, metrics=['mae'] ) #Penalize really bad output.. + elif (useSquaredMetric): + model.compile(optimizer=activeOptimizer, loss='mse', metrics=['mae']) + elif (useModuloMetric): + model.compile(optimizer=activeOptimizer, loss=mean_squared_error_modulo_360, metrics=[average_error_modulo_360] ) #With 360 metrics=[rmse_360] + else: + model.compile(optimizer=activeOptimizer, loss='mae', metrics=['mae']) #Without 360 + + model.summary() + #plot_model(model, to_file='modelXYZStage.png') + return model +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +""" + A dummy model made to pad outputs and keep signature compatibility between different + NN ensembles +""" +def newTrivialModel(modelName,outputArrayIndex,inputDimension,outputSize): + modelName = tensorflowFriendlyModelName(modelName) + print("Model renamed to ",modelName," to make sure it doesn't call tensorflow problems ") + inputs = Input(shape=(inputDimension,)) + trivialInitializer = keras.initializers.Zeros() + + #The trivial model is basically dead and useless and we want to simplify it as much as possible + #so that it occupies as little space in our network as possible + + #There are two ways to do this, first is by a tf split and the second by doing a Lambda function that ignores most inputs + #----------------------------------------------------------------------------------------------------------------------------------- + if (useLambdas==0): + #This broke at TF 2.16.1 + #ignoreInput = tf.split(inputs,inputDimension,num=inputDimension,axis=1,name='ignore_layer_for_%u_%s'%(outputArrayIndex,modelName)) #split inputs to single elements + #splitInput = ignoreInput[0] #try to do same thing without lambdas + + # Define the cropping layer + + reshaped_inputs = keras.layers.Reshape((inputDimension, 1))(inputs) + splitInput = keras.layers.Cropping1D(cropping=(1))(reshaped_inputs) + + else: + #partOfInputToKeep=int(inputDimension) + partOfInputToKeep=1 + splitInput = sliceL(1,0,partOfInputToKeep)(inputs) + #----------------------------------------------------------------------------------------------------------------------------------- + + #Now connect our single input with a mock layer with one set of weights so that it can learn to send zeros out :P + #----------------------------------------------------------------------------------------------------------------------------------- + xOut = Dense(int(outputSize), kernel_initializer=trivialInitializer , activation='linear', name='mock_layer_for_%u_%s'%(outputArrayIndex,modelName) )(splitInput) + predictions = keras.layers.Dense(int(outputSize), name='output_trivial_%u_%s'%(outputArrayIndex,modelName) )(xOut) + #----------------------------------------------------------------------------------------------------------------------------------- + + # This creates a model that includes + model = Model(name=modelName, inputs=inputs, outputs=predictions) + model.compile(optimizer='rmsprop', loss='mse', metrics=['mae']) + + #Do not emmit a summary for trivial models + #model.summary() + print("Not emitting a summary for trivial model ",modelName," with input dim ",inputDimension," and output ",outputSize) + return model +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def combineModels(configuration,directory,outputFilename,modelInputSize,modelOutputSize,modelPaths,startModel,endModel,label,lpdnnPadding=0,doPNGPlot=0,optimizer="rmsprop",skipTrivialModels=False): + inputLabel = "input_%s" % label + outputLabel = "result_%s" % label + + if (lpdnnPadding!=0): + print(bcolors.WARNING) + print("Using LPDNN Padding.. ") + print(bcolors.ENDC) + #Not setting batch_size=1 will result in a [?,..] shape which breaks things in the case of BonsApps/LPDNN + rawInput = Input(shape=(1,1,modelInputSize,), batch_size=1, name=inputLabel) + rawInput.set_shape((1,1,1,modelInputSize)) + singleInput = Reshape((modelInputSize,), input_shape=(1,1,1,modelInputSize,) , name='reshaped_'+inputLabel)(rawInput) + else: + print(bcolors.OKGREEN) + print("Using No Input Padding.. ") + print(bcolors.ENDC) + rawInput = Input(shape=(modelInputSize,), batch_size=1, name=inputLabel) + singleInput = rawInput + + print(bcolors.OKGREEN) + print("Combining model Input:%s / Output:%s / modelInputSize:(1,%u) / modelOutputSize:(1,%u) / Models Combined:(%u->%u) "%(inputLabel,outputLabel,modelInputSize,modelOutputSize,startModel,endModel)) + #print("Input Shape is : ") + #print(singleInput.get_shape()) + print(bcolors.ENDC) + + #Initialize our RMSProp optimizer + #---------------------------------------------------------------------------------------------------------------------------------------- + if (optimizer=="adam"): + activeOptimizer=keras.optimizers.Adam(learning_rate=learningRate,epsilon=keras.backend.epsilon()) + else: + activeOptimizer=keras.optimizers.RMSprop(learning_rate=learningRate, rho=0.9, epsilon=keras.backend.epsilon()) # epsilon=1e-6, lr=0.00025 is the old value + #------------------------------------------------------------------------------------------------------- + + #Start combining models + #inModelList = list() # empty list + #outModelList = list() # empty list + allModelList = list() # empty list + includedOutputs = list() # empty list + selectedColumns = list() # empty list + selectedColumnsCount = 0 + cumulativeTime = 0.0 + + from tools import getConfigurationJointIsDeclaredInHierarchy,getConfigurationJointPriority + + #if ((startModel==0) and (endModel==0)): + # print("THIS LOOKS LIKE THE SPECIAL CASE OF MERGING ONE THING WITH ITSELF.. :S") + # endModel=1 + + for modelNumber in range(startModel,endModel): + jointIsFormallyDeclared = getConfigurationJointIsDeclaredInHierarchy(configuration,modelPaths[modelNumber]) + jointPriority = getConfigurationJointPriority(configuration,modelPaths[modelNumber]) + if (skipTrivialModels) and (jointPriority==0): + print(bcolors.FAIL,"Skipping Models IS BUGGY BE CAREFUL ",bcolors.ENDC) + print(bcolors.WARNING,"Skipping Model %u/%u %s/%s!" % (modelNumber,endModel,directory,modelPaths[modelNumber]),bcolors.ENDC) + selectedColumns.append(0) + else: + start = time.time() + #-------------------------------------------------------------------------------------------------------- + print("Loading Model %u/%u %s/%s from disk" % (modelNumber,endModel,directory,modelPaths[modelNumber])) + #-------------------------------------------------------------------------------------------------------- + loaded_model = loadModel("%s/%s/"% (directory,modelPaths[modelNumber]),"model") + allModelList.append(loaded_model(singleInput)) + #-------------------------------------------------------------------------------------------------------- + #print("Inputs : ",allModelList[selectedColumnsCount].inputs) + #print("Outputs : ",allModelList[selectedColumnsCount].outputs) + #for layer in loaded_model.layers: + # if str(layer.name).find("input_")==0: + # print("Input layer is : ",layer.name) + # inModelList.append(layer) + # if str(layer.name).find("output_")==0: + # print("Output layer is : ",layer.name) + # outModelList.append(layer) + #-------------------------------------------------------------------------------------------------------- + includedOutputs.append(modelPaths[modelNumber]) + selectedColumns.append(1) + selectedColumnsCount = selectedColumnsCount + 1 + #-------------------------------------------------------------------------------------------------------- + end = time.time() + thisTime = (end-start) + cumulativeTime+=thisTime + #-------------------------------------------------------------------------------------------------------- + print(bcolors.OKGREEN,"loaded %u/%u @ %0.2f secs / Total %0.2f secs" % (modelNumber,endModel,thisTime,cumulativeTime),bcolors.ENDC) + #----------------------------------------------------------------------------------------------------------------------------------------- + print("Merging ",len(allModelList)," Models..") + if (len(allModelList)>1): + out = concatenate(allModelList[:],name=outputLabel) + elif (len(allModelList)==1): + #One model does not need concatenations + out = allModelList[0] + else: + print("combineModels: No Model found!") + raise ValueError('combineModels: No Model found.') + + mergedModel = Model(inputs=rawInput, outputs=out, name=configuration['OutputDirectory']) + mergedModel.compile(optimizer=activeOptimizer,loss='mse',metrics=['mae', 'acc']) #,jit_compile=True #<- this may cause trouble on non-XLA builds? + + print("Merged Model summary : ") + mergedModel.summary() + + if (doPNGPlot): + try: + keras.utils.plot_model(mergedModel, expand_nested=True) + except: + print("Please install pydot for network graph plot!") + os.system('touch model.png') #<- just make a foo png + + print("Done merging model and saved it to disk") + return mergedModel,includedOutputs,selectedColumns +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def combineAsSingleModel(path,inputFilename,modelInputSize,modelOutputSize,outputFilename,label): + inputLabel = "input_%s" % label + outputLabel = "result_%s" % label + + #We basically want to rename a single model the same way as if we had combined it with others.. + loaded_model = loadModel(path,"%s/model" % inputFilename) + + print("Using TensorFlow ",tf.__version__) + print("Original Single Model was : ") + loaded_model.summary() + + doSimpleWay=0 + + if (doSimpleWay): + loaded_model.layers[0].name=inputLabel + loaded_model.layers[-1].name=outputLabel + loaded_model.compile(optimizer='rmsprop', loss='mse', metrics=['mae', 'acc']) + saveModel("%s/"%path,loaded_model) + print("Merged Single/Combined Model summary : ") + loaded_model.summary() + else: + dummyNetwork = newTrivialModel("DummyPadding",666,modelInputSize,modelOutputSize) + + print("Single model input will have ",modelInputSize," size") + singleInput = Input(shape=(modelInputSize,), name=inputLabel) + outModelList = list() # empty list + outModelList.append(loaded_model(singleInput)) + outModelList.append(dummyNetwork(singleInput)) + out = concatenate(outModelList[:],name=outputLabel) + mergedModel = Model(singleInput,out) + mergedModel.compile(optimizer='rmsprop', loss='mse', metrics=['mae', 'acc']) + saveModel("%s/"%path,mergedModel) + print("Merged Single/Combined Model summary : ") + mergedModel.summary() + + os.system('mv %s/model.h5 %s/%s.h5'%(path,path,outputFilename)) + os.system('mv %s/model.json %s/%s.json'%(path,path,outputFilename)) + return mergedModel +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def visualizeLayer(layerNumber,layerSize,hasSkip=False,dropout=0.3): + print("%02u"%layerNumber,end="") + if (hasSkip) : + print(" HAS SKIP ",end="") + else: + print(" ",end="") + for p in range(0,int(layerSize/10)): + print("█",end="") + print(layerSize," Dropout= %0.2f"%dropout) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def autobuilderBB(inputSize,outputSize,modelName="mnet",outputArrayIndex=0,depth=12,lambdaF=1.0,skip=True,dropOutStart=0.3,dropoutStopNLayersBeforeEnd=4): + print("Auto Builder for ",label," Input ",inputSize," and Output ",outputSize) + print("Depth=",depth," λ=",lambdaF," skip=",skip) + step = int((inputSize*lambdaF)/depth) + dropout = dropOutStart + dropStep = dropOutStart/depth + currentSize = inputSize + for layer in range(0,depth): + if (dropout<0.01): + dropout=0 + elif (layer <= depth-dropoutStopNLayersBeforeEnd): + dropout=0 + + visualizeLayer(layer,currentSize,skip!=0,dropout=dropout) + dropout = dropout - dropStep + currentSize = currentSize - step + + currentSize = outputSize + visualizeLayer(depth,currentSize,dropout=0) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def autobuilder(inputSize,outputSize,modelName="mnet",outputArrayIndex=0,depth=12,lambdaF=1.0,skip=True,dropOutStart=0.3,dropoutStopNLayersBeforeEnd=4,probabilisticMode=0,quantize=False,optimizer="rmsprop"): + #------------------------------------------------------------------------------------ + #------------------------------------------------------------------------------------ + print("Auto Builder for ",modelName," Input ",inputSize," and Output ",outputSize) + print("Depth=",depth," λ=",lambdaF," skip=",skip) + modelName = tensorflowFriendlyModelName(modelName) + print("Model renamed to ",modelName," to make sure it doesn't call tensorflow problems ") + #------------------------------------------------------------------------------------ + #------------------------------------------------------------------------------------ + inputs = Input(shape=(inputSize,)) + act = theActivationMethod + kinit = initializer + layers = list() + layerSizes = list() + layerNumber = int() + totalNumberOfLayers = 0 + #------------------------------------------------------------------------------------ + step = int((inputSize*lambdaF)/depth) + stepDecay = True + dropout = dropOutStart + dropStep = dropOutStart/depth + currentSize = inputSize + previousSize = inputSize + previousLayer = inputs + stop = False + #----------------------------------------------------------------------------------------------------------------------------------- + addLayers = list() + skipLayers = list() + print("Will now try to add skip layers to ",totalNumberOfLayers," layers") + #----------------------------------------------------------------------------------------------------------------------------------- + for layerNumber in range(0,depth): + if (currentSize<=outputSize): + stop = True + currentSize = outputSize + print("λ compression is too aggressive network max depth will be ",layerNumber) + if (dropout<0.01): + dropout=0 + visualizeLayer(layerNumber,currentSize,skip,dropout=dropout) + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + layers.append(Dense(int(currentSize),input_shape=(previousSize,), kernel_initializer=kinit, activation=act, name=tag("layer",modelName,outputArrayIndex,layerNumber))(previousLayer)) + layerSizes.append(int(currentSize)) + previousLayer = layers[len(layers)-1] + if (dropout!=0) and (layerNumber <= depth-dropoutStopNLayersBeforeEnd): + Dropout(dropout,name=tag("dropout",modelName,outputArrayIndex,layerNumber))(layers[len(layers)-1]) + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + if (skip): + for fromLayerMinusOne in range(0,layerNumber): + toSize = layerSizes[len(layerSizes)-1] + toLayer = len(layerSizes)-1 + if (fromLayerMinusOne==0): + fromSize = inputSize + skipLayers.append(Dense(int(toSize),input_shape=(inputSize,),kernel_initializer=kinit, activation=act, name=tag("skip_in_to_%u"%(toLayer),modelName,outputArrayIndex,fromLayerMinusOne))(inputs)) + elif (fromLayerMinusOne>0): + fromLayer = fromLayerMinusOne-1 + fromSize = layerSizes[fromLayer] + skipLayers.append(Dense(int(toSize),kernel_initializer=kinit, activation=act, name=tag("skip_%u_to_%u"%(fromLayer,toLayer),modelName,outputArrayIndex,fromLayer))(layers[fromLayer])) + #----------------------------------------------------------------------------------------------------- + if (dropout!=0) and (layerNumber<= depth - dropoutStopNLayersBeforeEnd): + Dropout(dropout,name=tag("skip_dropout_to_%u"%(toLayer),modelName,outputArrayIndex,layerNumber))(layers[len(layers)-1]) + #----------------------------------------------------------------------------------------------------- + #print("Add Skip ",fromLayer,"->",toLayer) + layers[toLayer] = Add(name=tag("add_%u_to_%u"%(fromLayerMinusOne,toLayer),modelName,outputArrayIndex,fromLayerMinusOne))([skipLayers[len(skipLayers)-1],layers[toLayer]]) + #------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + dropout = dropout - dropStep + currentSize = currentSize - step + if (currentSize < 30): + dropout = 0 + if (stepDecay): + step = max(depth , int(3/6 * step)) + totalNumberOfLayers = totalNumberOfLayers + 1 + if (stop): + break + #----------------------------------------------------------------------------------------------------------------------------------- + currentSize = outputSize + visualizeLayer(depth,currentSize,dropout=0) + #----------------------------------------------------------------------------------------------------------------------------------- + predictions = keras.layers.Dense(int(outputSize),kernel_initializer='normal', activation='linear', name='output_%u_%s'%(outputArrayIndex,modelName))(layers[len(layers)-1]) + #----------------------------------------------------------------------------------------------------------------------------------- + + # the Input layer and three Dense layers + model = Model(name=modelName, inputs=inputs, outputs=predictions) + #------------------------------------------------------------------------------------------------------- + if (optimizer=="adam"): + activeOptimizer=keras.optimizers.Adam(learning_rate=learningRate,epsilon=keras.backend.epsilon()) + else: + activeOptimizer=keras.optimizers.RMSprop(learning_rate=learningRate, rho=0.9, epsilon=keras.backend.epsilon()) # epsilon=1e-6, lr=0.00025 is the old value + #------------------------------------------------------------------------------------------------------- + + + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + if (quantize): + #Careful not all activations are quantization aware + print("Will attempt to quantize model..!") + try: + import tensorflow_model_optimization as tfmot + quantize_model = tfmot.quantization.keras.quantize_model + model = quantize_model(model) + except: + print("Could not quantize model, you are using a non quantization aware activation function..!") + print("https://github.com/tensorflow/model-optimization/blob/master/tensorflow_model_optimization/python/core/quantization/keras/quantize_aware_activation.py") + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + if (probabilisticMode): + model.compile(optimizer=activeOptimizer, loss='categorical_crossentropy', metrics=['accuracy']) + elif (useQuadMetric): + model.compile(optimizer=activeOptimizer, loss=mean_quad_error, metrics=['mae'] ) #Penalize really bad output.. + elif (useSquaredMetric): + model.compile(optimizer=activeOptimizer, loss='mse', metrics=['mae']) + elif (useModuloMetric): + model.compile(optimizer=activeOptimizer, loss=mean_squared_error_modulo_360, metrics=[average_error_modulo_360] ) #With 360 metrics=[rmse_360] + else: + model.compile(optimizer=activeOptimizer, loss='mae', metrics=['mae']) #Without 360 + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------- + model.summary() + return model +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +def newEncoderModelSelector(configuration,modelName,outputArrayIndex,inputDimension,nonNSDMInputSize,numberOfChannelsPerNSDMElement,outputSize,networkCompression,probabilisticMode=0,quantize=False,optimizer="rmsprop"): + if ('autobuilder' in configuration) and (configuration['autobuilder']==1): + depth = int(configuration['neuralNetworkDepth']) + lambdaF = float(configuration['lamda']) + skip = (int(configuration['skipConnections']) == 1) + dropout = float(configuration['dropoutRate']) + return autobuilder( + inputSize=inputDimension, + outputSize=outputSize, + modelName=modelName, + outputArrayIndex=outputArrayIndex, + depth=depth, + lambdaF=lambdaF, + skip=skip, + dropOutStart=dropout, + probabilisticMode=probabilisticMode, + quantize=quantize, + optimizer=optimizer + ) + else: + return newDeepRotModel( + configuration, + modelName, + outputArrayIndex, + inputDimension, + nonNSDMInputSize, + numberOfChannelsPerNSDMElement, + outputSize, + networkCompression, + probabilisticMode=probabilisticMode, + quantize=quantize, + optimizer=optimizer + ) +# return convDeepRotModel(modelName,outputArrayIndex,inputDimension,nonNSDMInputSize,numberOfChannelsPerNSDMElement,outputSize,networkCompression,probabilisticMode=probabilisticMode,quantize=quantize) +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +#=================================================================================================================================================================================== +if __name__ == '__main__': + print("DNNModel.py is a library and cannot be run on it's own") + #------------ + inputSize = 160 + outputSize = 1 + label = "mnet" + depth = 12 + lambdaF = 1.5 + skip = True + + if (len(sys.argv)<=1): + print("Please supply arguments like : ") #33 inputs are 2D + print(bcolors.OKGREEN,"python3 DNNModel.py --inputs 160 --lambda 1.0 --depth 12 --outputs 1 ",bcolors.ENDC) + + if (len(sys.argv)>1): + print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--inputs"): + inputSize =int(sys.argv[i+1]) + if (sys.argv[i]=="--lambda"): + lambdaF =float(sys.argv[i+1]) + if (sys.argv[i]=="--depth"): + depth =int(sys.argv[i+1]) + if (sys.argv[i]=="--outputs"): + outputSize =int(sys.argv[i+1]) + + model = autobuilder(inputSize,outputSize,modelName=label,depth=depth,lambdaF=lambdaF,skip=skip) + try: + keras.utils.plot_model(model,show_shapes=False,rankdir='LR',expand_nested=True) + except: + print("Please install pydot for network graph plot!") + os.system('touch model.png') #<- just make a foo png + #------------ + diff --git a/src/python/mnet4/DNNOptimize.py b/src/python/mnet4/DNNOptimize.py new file mode 100755 index 0000000..bef8fca --- /dev/null +++ b/src/python/mnet4/DNNOptimize.py @@ -0,0 +1,177 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" +#-------------------------------------------------------------- +#-------------------------------------------------------------- +#-------------------------------------------------------------- + +def pruneModel(model,trainIn,trainOut): + print("Will now attempt to optimize model..!") + import tensorflow as tf + import tensorflow_model_optimization as tfmot + initial_sparsity = 0.0 + final_sparsity = 0.75 + begin_step = 1000 + end_step = 5000 + pruning_params = { + 'pruning_schedule': tfmot.sparsity.keras.PolynomialDecay( + initial_sparsity=initial_sparsity, + final_sparsity=final_sparsity, + begin_step=begin_step, + end_step=end_step + ), + 'BatchNormalization': [] + } + model = tfmot.sparsity.keras.prune_low_magnitude(model, **pruning_params) + pruning_callback = tfmot.sparsity.keras.UpdatePruningStep() + + model.compile(optimizer='rmsprop', loss='mse', metrics=['mae', 'acc']) + + model.fit( + trainIn, + trainOut, + epochs=200, + batch_size=1024, + callbacks= pruning_callback, + verbose=1 + ) + return model + +#-------------------------------------------------------------- +#-------------------------------------------------------------- +#-------------------------------------------------------------- + +def clusterModel(model,configuration,trainIn,trainOut): + #https://blog.tensorflow.org/2020/08/tensorflow-model-optimization-toolkit-weight-clustering-api.html + print("Will now attempt to cluster model..!") + import tensorflow as tf + import tensorflow_model_optimization as tfmot + + rmsprop=tf.keras.optimizers.RMSprop(learning_rate=configuration['learningRate'], rho=0.9, epsilon=tf.keras.backend.epsilon()) + model.compile( + optimizer=rmsprop, + loss='mse', + metrics=['mae', 'acc'] + ) + + cluster_weights = tfmot.clustering.keras.cluster_weights + clustering_params = { + 'number_of_clusters': 32, + 'cluster_centroids_init': tfmot.clustering.keras.CentroidInitialization.LINEAR + } + clustered_model = cluster_weights(model, **clustering_params) + clustered_model.compile( + optimizer=rmsprop, + loss='mse', + metrics=['mae', 'acc'] + ) + clustered_model.fit( + trainIn, + trainOut, + epochs=200, + batch_size=1024, + verbose=1 + ) + + + # Prepare model for serving by removing training-only variables. + return tfmot.clustering.keras.strip_clustering(clustered_model) + +#-------------------------------------------------------------- +#-------------------------------------------------------------- +#-------------------------------------------------------------- + +def quantizeModel(model,trainIn,trainOut): + print("Will now attempt to quantize model..!") + import tensorflow as tf + import tensorflow_model_optimization as tfmot + + quantize_model = tfmot.quantization.keras.quantize_model + + # q_aware stands for for quantization aware. + q_aware_model = quantize_model(model) + + # `quantize_model` requires a recompile. + q_aware_model.compile(optimizer='rmsprop', loss='mse', metrics=['mae', 'acc']) + + q_aware_model.fit( + trainIn, + trainOut, + epochs=1, + batch_size=500, + verbose=1, + validation_split=0.1 + ) + + q_aware_model.summary() + return q_aware_model + +#-------------------------------------------------------------- +#-------------------------------------------------------------- +#-------------------------------------------------------------- + +def convertToTensorRT(model,trainIn=0,precision="fp32"): + try: + import os + import sys + import tensorflow as tf + from tensorflow.python.compiler.tensorrt import trt_convert as trt + print("Convert Model to TensorRT / ",precision) + #----------------------------------------------------------------- + os.system("rm -rf tensorRTIntermediateTFModel/") + os.system("rm -rf tensorRTIntermediateTRTModel/") + #----------------------------------------------------------------- + model.save("tensorRTIntermediateTFModel", save_format='tf') #save directory.. + + if (precision=="fp32"): + precision_mode='FP32' + elif (precision=="fp16"): + precision_mode='FP16' + elif (precision=="int8"): + precision_mode='INT8' + else: + print("Unknown precision setting ",precision) + return model + + + # https://www.tensorflow.org/api_docs/python/tf/experimental/tensorrt/Converter + # https://docs.nvidia.com/deeplearning/frameworks/tf-trt-user-guide/index.html#usage-example + print("\nConverting to TensorRT/Tensorflow model") + converter = trt.TrtGraphConverterV2( input_saved_model_dir="tensorRTIntermediateTFModel", precision_mode=precision_mode ) + + #with trt.Builder(TRT_LOGGER) as builder, builder.create_network(network_creation_flag) as network, trt.OnnxParser(network, TRT_LOGGER) as parser, builder.create_builder_config() as config: + # profile = builder.create_optimization_profile() + # profile.set_shape("input_1", (1, 224, 224, 3), (1, 224, 224, 3), (1, 224, 224, 3)) + # config.add_optimization_profile(profile) + # engine = builder.build_engine(network, config) + + + print("\nconverter.convert") + if (trainIn!=0): + converter.convert(calibration_input_fn=trainIn) + converter.build(input_fn=trainIn) + else: + converter.convert() + + + print("\nconverter.save") + converter.save("tensorRTIntermediateTRTModel") + model = tf.keras.models.load_model("tensorRTIntermediateTRTModel") + + os.system("rm -rf tensorRTIntermediateTFModel/") + os.system("rm -rf tensorRTIntermediateTRTModel/") + except: + print("Error while performing TensorRT conversion") + + return model + +#-------------------------------------------------------------- +#-------------------------------------------------------------- +#-------------------------------------------------------------- +if __name__ == '__main__': + print("DNNOptimize.py is a library and cannot be run on it's own") + diff --git a/src/python/mnet4/DNNTraining.py b/src/python/mnet4/DNNTraining.py new file mode 100755 index 0000000..0024cd9 --- /dev/null +++ b/src/python/mnet4/DNNTraining.py @@ -0,0 +1,739 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + + +import os +import sys +import gc +import time +import json + + +import tensorflow as tf +import keras + +#from tensorflow.keras.backend.tensorflow_backend import set_session + +#from tensorflow.keras import backend as K +from keras.layers import Input, Dense +from keras.models import Model +from keras.models import Sequential +from keras.models import model_from_json +from keras.utils import plot_model + +import tensorflow.keras.callbacks +import numpy as np +from numba import njit #Test +from tools import bcolors,checkIfFileExists + +def printTrainingVersion(): + print(""" +███╗ ███╗ ██████╗ ██████╗ █████╗ ██████╗ ███╗ ██╗███████╗████████╗ +████╗ ████║██╔═══██╗██╔════╝██╔══██╗██╔══██╗████╗ ██║██╔════╝╚══██╔══╝ +██╔████╔██║██║ ██║██║ ███████║██████╔╝██╔██╗ ██║█████╗ ██║ +██║╚██╔╝██║██║ ██║██║ ██╔══██║██╔═══╝ ██║╚██╗██║██╔══╝ ██║ +██║ ╚═╝ ██║╚██████╔╝╚██████╗██║ ██║██║ ██║ ╚████║███████╗ ██║ +╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝ + """) + #----------------------------- + #BMVC 21 paper was submitted with Keras 2.2.4/Tensorflow 1.14/Numpy 1.16.2 + #----------------------------- + print("") + print("Tensorflow version : ",tf.__version__) + #print("Keras version : ",keras.__version__) <- no longer available in TF-2.13 + print("Numpy version : ",np.__version__) + #----------------------------- + from tensorflow.python.platform import build_info as tf_build_info + print("TF/CUDA version : ",tf_build_info.build_info['cuda_version']) + print("TF/CUDNN version : ",tf_build_info.build_info['cudnn_version']) + #----------------------------- + print("") + #----------------------------- + +def forceCPU(): + print(bcolors.WARNING,"User selected to use CPU mode",bcolors.ENDC) + os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" # see issue #152 + os.environ["CUDA_VISIBLE_DEVICES"] = "" + +def limitGPUMemory(): + print("Limiting GPU memory usage for multiple instances..\n") + config = tf.compat.v1.ConfigProto() + config.gpu_options.per_process_gpu_memory_fraction = 0.2 + config.gpu_options.visible_device_list = "0" + set_session(tf.compat.v1.Session(config=config)) + +def setTensorflowBackendToHalfFloats(): + dtype='float16' + + #WARNING:tensorflow:Mixed precision compatibility check (mixed_float16): WARNING + #Your GPU may run slowly with dtype policy mixed_float16 because it does not have compute capability + #of at least 7.0. Your GPU: Quadro P6000, compute capability 6.1 + + from tensorflow.keras.mixed_precision import experimental as mixed_precision + policy = mixed_precision.Policy('mixed_float16') + mixed_precision.set_policy(policy) + + keras.backend.set_floatx(dtype) + # default is 1e-7 which is too small for float16. Without adjusting the epsilon, we will get NaN predictions because of divide by zero problems + keras.backend.set_epsilon(1e-4) + + print('Compute dtype: %s' % policy.compute_dtype) + print('Variable dtype: %s' % policy.variable_dtype) + print("\nUsing Half-Floats for training ") + + +def getRSquared(groundTruth, neuralNetworkOutput): + """ + Computes the R-squared metric. + https://en.wikipedia.org/wiki/Coefficient_of_determination + + Parameters: + -- groundTruth (numpy.ndarray): 1D array representing the ground truth. + -- neuralNetworkOutput (numpy.ndarray): 1D array representing the neural network output. + + Returns: + -- float: R-squared metric. + """ + + # Compute the mean of the ground truth + mean_ground_truth = np.mean(groundTruth) + + # Compute the sum of squares of the residuals + ss_residuals = np.sum((groundTruth - neuralNetworkOutput) ** 2) + + # Compute the sum of squares of the total variation + ss_total = np.sum((groundTruth - mean_ground_truth) ** 2) + + # Compute the R-squared metric + r_squared = 1 - (ss_residuals / ss_total) + + return r_squared + + + +@njit +def getRSquaredInPlace(groundTruth,mean_ground_truth,N,neuralNetworkOutput): + """ + Computes the R-squared metric. + https://en.wikipedia.org/wiki/Coefficient_of_determination + + Parameters: + -- groundTruth (numpy.ndarray): 1D array representing the ground truth. + -- neuralNetworkOutput (numpy.ndarray): 1D array representing the neural network output. + + Returns: + -- float: R-squared metric. + """ + + #N = groundTruth.shape[0] + if N == 0: + return np.NAN + + # Compute the mean of the ground truth + #mean_ground_truth = np.mean(groundTruth) + + ss_residuals = np.float32(0.0) + ss_total = np.float32(0.0) + + for i in range(N): + #-------------------------------------- + gt = np.float32(groundTruth[i][0]) #groundTruth is a tensorflow array so each element is also an array -> thus we need [0] + nn_out = np.float32(neuralNetworkOutput[i][0]) #neuralNetworkOutput is a tensorflow array so each element is also an array -> thus we need [0] + #-------------------------------------- + delta = gt - nn_out + ss_residuals += delta ** 2 + #-------------------------------------- + delta = gt - mean_ground_truth + ss_total += delta ** 2 + + # Compute the R-squared metric + r_squared = np.float32(1 - (ss_residuals / ss_total)) + + return r_squared + + + + +def getRSquaredForNeuralNetwork(mocapNETNetwork,networkInput,groundtruthOutput): + import time + startAt = time.time() + #----------------------------------------------------- + try: + print("Extracting R² : Getting neural network response..") + predictions = mocapNETNetwork.predict(networkInput) + #Since the groundtruth is very large and the machines I use don't have + #enough RAM this is an in place R^2 calculation to make sure the computer doesn't enter a page swapping + #loop that makes training grind to a halt..! + print("Extracting R² : Calculating..") + + mean_ground_truth = np.float32(np.mean(groundtruthOutput)) + #print("groundtruthOutput: ",groundtruthOutput) + rSQ = getRSquaredInPlace(groundtruthOutput,mean_ground_truth,groundtruthOutput.shape[0],predictions) + + #This is faster (if you have spare memory..) + #rSQ = getRSquared(groundtruthOutput,predictions) + del predictions + #except: + except Exception as e: + print("Failed to extract R² ..") + print("Error was : ",e) + rSQ = np.NAN + #----------------------------------------------------- + #----------------------------------------------------- + endAt = time.time() + print("R² = ",rSQ," it took ",(endAt-startAt)/60," mins to compute") + return rSQ + + +def logTrainingResults( + filename, + configuration, + outputType, + outputName, + history, + metrics + ): + if (outputType==0): + numberOfModelParametersStr="?" + if 'modelParameters' in configuration: + numberOfModelParametersStr="%u" % int(configuration['modelParameters']) + + #First record - Start of File + inputProcessing="" + if (configuration['eNSRM']): + inputProcessing=inputProcessing+" eNSRM" + if ('NSRMNormalizeAngles' in configuration) and (configuration['NSRMNormalizeAngles']): + if (configuration['NSRMNormalizeAngles']==1): + inputProcessing=inputProcessing+" NSRMNormalizeAngles" #Half + elif (configuration['NSRMNormalizeAngles']==2): + inputProcessing=inputProcessing+" NSRMNormalizeAnglesFull" + else: + inputProcessing=inputProcessing+" NSRMNormalizeAnglesUNKNOWN" + if (configuration['EDM']): + inputProcessing=inputProcessing+" EDM" + if (configuration['eigenPoses']): + inputProcessing=inputProcessing+" eigenPoses" + if (configuration['autobuilder']): + inputProcessing=inputProcessing+" autobuilder" + if ( (configuration['decompositionType']!="") and (configuration['doPCA']!="") and ( int(configuration['PCADimensionsKept'])>0 ) ): + inputProcessing=inputProcessing+" %s(%u)"%(configuration['decompositionType'],configuration['PCADimensionsKept']) + if (configuration['PCAAlsoKeepRawData']): + inputProcessing=inputProcessing+"+PCA_RawData" + inputProcessing=inputProcessing+" act("+configuration['activationFunction']+")" + inputProcessing=inputProcessing+" rand("+configuration['weightRandomizationFunction']+")" + inputProcessing=inputProcessing+" "+configuration['optimizer'] + if (configuration['balanced2DInputs']): + inputProcessing=inputProcessing+" balanced2DInputs" + + if (configuration['useQuadLoss']!=0): + inputProcessing=inputProcessing+" quadLoss" + + if (configuration['setConstantSeedForReproducibleTraining']!=1): + inputProcessing=inputProcessing+" random seed" + + if (configuration['include2DInputVisibilityFlags']==0): + inputProcessing=inputProcessing+" novis2Dflag" + + experimentDescription="" + experimentDescription=experimentDescription+"group %u"%(configuration["groupOutputs"]) + + if (configuration['useRadians']): + experimentDescription=experimentDescription+" radians" + #-------------------------------------------------------------------- + if ('outputValueDistribution' in configuration): + if (configuration['outputValueDistribution']=='balanced'): + experimentDescription=experimentDescription+" balanced offset" + elif (configuration['outputValueDistribution']=='positive'): + experimentDescription=experimentDescription+" positive offset" + elif (configuration['outputValueDistribution']=='negative'): + experimentDescription=experimentDescription+" negative offset" + #-------------------------------------------------------------------- + if ('outputNormalizationStrategy' in configuration): + experimentDescription=experimentDescription+" outputNormalizationStrategy="+configuration['outputNormalizationStrategy'] + #-------------------------------------------------------------------- + if (float(configuration['outputMultiplier'])!=1.0): + experimentDescription=experimentDescription+" outputScaling=%0.2f" % float(configuration['outputMultiplier']) + experimentDescription=experimentDescription+" NNDepth=%u"%configuration['neuralNetworkDepth'] + + #FIRST! RECORD! + with open(filename, 'w') as f: + f.write(""" + + + + Training summary of experiment %s for %s + + + """%(configuration['label'],configuration['hierarchyPartName'])) + + #Add the hostname.. + import socket + f.write("%s
\n"%socket.gethostname()) + + + f.write(""" + + + + + + + + + + + + + + + + + + + + + + + +
DatePartBatchSizeEpochsλRememberOcclusionsInput ProcessingHard Mining
%s%s%u%u/%u/%u%0.2fc %u/p %u%u%s
+ """% ( + configuration['date'], + configuration['hierarchyPartName'], + configuration['defaultBatchSize'], + configuration["defaultNumberOfEpochs"],configuration["highNumberOfEpochs"],configuration["veryHighNumberOfEpochs"], + configuration["lamda"], + configuration["rememberConsecutiveWeights"],configuration["rememberWeights"], + configuration["ignoreOcclusions"]==0, + inputProcessing + ) + ) + + f.write(""" + + + + + + + + + + + + + + + + + + + +
SerialStartEndTraining SamplesModel SizeDropout/L.RDescription
%s%u%u%u%s%0.2f/%0.5f%s
+ + + + + + + + + + + + """% ( + configuration['label'], + configuration['startPosition'], + configuration['endPosition'], + configuration['trainingSamples'], + numberOfModelParametersStr, + configuration['dropoutRate'], + configuration['learningRate'], + experimentDescription + ) + ) + f.close() + #---------------------------------- + + try: + lowestLossAchievedAt = 0 + if ("loss" in history.history) and ("mae" in history.history): + # Initialize variables to store the initial and lowest loss + initial_loss = history.history['loss'][0] + lowest_loss = initial_loss + + initial_mae = history.history['mae'][0] + lowest_mae = initial_mae + + # Iterate through the history object + count = 0 + for i, loss in enumerate(history.history['loss']): + # Update the lowest loss if a lower value is found + if loss < lowest_loss: + lowest_loss = loss + lowestLossAchievedAt = count + count = count + 1 + + lowest_mae = history.history['mae'][lowestLossAchievedAt] + + #By default don't scale + #---------------------------------------------------------------------------- + outputMinimumValue = 0.0 + outputMaximumValue = 0.0 + outputOffsetValue = 0.0 + outputScalarValues = 1.0 + outputScalarFractionValues = 1.0 + #---------------------------------------------------------------------------- + for v in range(0,len(configuration["outputOffsetLabels"])): + if (outputName.lower()==configuration["outputOffsetLabels"][v].lower()): + #we found a rule! + outputMinimumValue = float(configuration["outputOffsetMinima"][v]) + outputMaximumValue = float(configuration["outputOffsetMaxima"][v]) + outputOffsetValue = float(configuration["outputOffsetValues"][v]) + outputScalarValues = float(configuration["outputScalarValues"][v]) + outputScalarFractionValues = float(configuration["outputScalarFractionValues"][v]) + #---------------------------------------------------------------------------- + initialVAL = "" + lowestVAL = "" + if ("val_loss" in history.history) and ("val_mae" in history.history): + initial_VALloss = history.history['val_loss'][0] + lowest_VALloss = history.history['val_loss'][lowestLossAchievedAt] + initial_VALmae = history.history['val_mae'][0] + lowest_VALmae = history.history['val_mae'][lowestLossAchievedAt] + initial_VALmae = initial_VALmae * outputScalarFractionValues + lowest_VALmae = lowest_VALmae * outputScalarFractionValues + #-------------------------------------- + initialVAL = " - " + if ("test_rsquared_start" in metrics): + initialVAL = " - R² %0.2f/"%(metrics["test_rsquared_start"]) + initialVAL = initialVAL + "%0.4f/%0.4f" % (initial_VALloss,initial_VALmae) + #-------------------------------------- + lowestVAL = " - " + if ("test_rsquared_end" in metrics): + lowestVAL = " - R² %0.2f/"%(metrics["test_rsquared_end"]) + lowestVAL = lowestVAL + "%0.4f/%0.4f" % (lowest_VALloss,lowest_VALmae) + + #Every output now gets scaled always + initial_mae = initial_mae * outputScalarFractionValues + lowest_mae = lowest_mae * outputScalarFractionValues + + with open(filename, 'a') as f: + f.write("") + #------------------------------------------------ + #Start Loss + f.write("") + #------------------------------------------------ + #End Loss + f.write("") + #------------------------------------------------ + + #Training Epochs ---------------------------- + f.write("") + #Min/Max ---------------------------- + f.write("") + #Offset ----------------------------- + f.write("") + #Scalar ----------------------------- + f.write("") + f.write("") + #------------------------------------------------ + f.write("") + f.close() + except: + print("An exception occurred in logTrainingResults") + + + +def regularTraining(tensorboardLabel,mocapNETNetwork,numberOfEpochs,batchSize,earlyStoppingPatience,trainInput,trainOutput,testInput,testOutput,minD,modelIsTrivial=False,haveTestSet=False,useHalfFloats=False): + #We use a checkpoint system to return best model.. + if (os.path.isfile("best.weights.h5")): + print(bcolors.WARNING,"Found a forgotten checkpoint file, erasing it to avoid trouble",bcolors.ENDC) + os.system('rm best.weights.h5') + + #whatToMonitor='mean_absolute_error' + #minimumDelta=0.05 + whatToMonitor='loss' + + metrics = dict() + + if (useHalfFloats): + print("Early stopping will use MAE instead of loss due to half floats..") + whatToMonitor='mae' + minimumDelta=minD + + #To see use : + #tensorboard --logdir step0_upperbody_all/tensorboard --bind_all + #------------------------------------------------------------------------ + tensorboard = keras.callbacks.TensorBoard(log_dir=tensorboardLabel,histogram_freq=1) + #------------------------------------------------------------------------ + earlystopper = keras.callbacks.EarlyStopping( + monitor=whatToMonitor, + min_delta=minimumDelta, + patience=earlyStoppingPatience, + verbose=1, + mode='auto' + ) + #------------------------------------------------------------------------- + checkpointer = keras.callbacks.ModelCheckpoint( + filepath="best.weights.h5", + monitor=whatToMonitor, + mode='min', + verbose=1, + save_freq='epoch', + save_best_only=True, + save_weights_only=True + ) + #------------------------------------------------------------------------ + callbacks = [ + earlystopper, + checkpointer, + tensorboard + ] + #------------------------------------------------------------------------ + + print("regularTraining begins -> BatchSize=%u / NumberOfEpochs=%u "%(batchSize,numberOfEpochs)) + #------------------------------------------------------------------------ + if (haveTestSet==False): + #Train without any test data ( probably to conserve memory ) + if (not modelIsTrivial): + metrics["train_rsquared_start"] = getRSquaredForNeuralNetwork(mocapNETNetwork,trainInput,trainOutput) + history = mocapNETNetwork.fit( + trainInput,trainOutput, + epochs=numberOfEpochs, + batch_size=batchSize, + shuffle=True, + #steps_per_epoch=5, #<- Use more steps per epoch? + callbacks=callbacks + ) + if (not modelIsTrivial): + metrics["train_rsquared_end"] = getRSquaredForNeuralNetwork(mocapNETNetwork,trainInput,trainOutput) + else: + #Train with test data + if (not modelIsTrivial): + metrics["train_rsquared_start"] = getRSquaredForNeuralNetwork(mocapNETNetwork,trainInput,trainOutput) + metrics["test_rsquared_start"] = getRSquaredForNeuralNetwork(mocapNETNetwork,testInput,testOutput) + history = mocapNETNetwork.fit( + trainInput,trainOutput, + epochs=numberOfEpochs, + batch_size=batchSize, + shuffle=True, + validation_data=(testInput,testOutput), + #validation_split=0.2, + callbacks=callbacks + ) + if (not modelIsTrivial): + metrics["train_rsquared_end"] = getRSquaredForNeuralNetwork(mocapNETNetwork,trainInput,trainOutput) + metrics["test_rsquared_end"] = getRSquaredForNeuralNetwork(mocapNETNetwork,testInput,testOutput) + #------------------------------------------------------------------------ + if (checkIfFileExists('best.weights.h5')): + print("We use the best possible model from our ModelCheckpoint") + mocapNETNetwork.load_weights('best.weights.h5') + os.system('rm best.weights.h5') #Get rid of the checkpoint as soon as we are done reading it + else: + print("No best.weights.h5 file found, just using last iteration..") + #------------------------------------------------------------------------ + return history,mocapNETNetwork,metrics +#------------------------------------------------------------------------------------------------------------------------------------------------- + +def compareTrainingOutputs(groundTruth,neuralOutput): + if (len(groundTruth)!=len(neuralOutput)): + print("Outputs have a different size") + return np.nan + + values = len(groundTruth) + + losses = (groundTruth[:]-neuralOutput[:])**2 + return losses +#---------------------------------------------------- + +def appendHistory(baseHistory,historyToAppend): + #print("baseHistory -> ",type(baseHistory)) + #print("baseHistory Contents -> ",baseHistory) + #print("historyToAppend -> ",type(historyToAppend)) + #print("historyToAppend Contents -> ",historyToAppend) + #print("baseHistory.history -> ",type(baseHistory.history)) + #print("historyToAppend.history -> ",type(historyToAppend.history)) + #----------------------------------------------------------------------------------------------------------- + if ("mae" in baseHistory.history) and ("mae" in historyToAppend.history): + baseHistory.history['mae'] = baseHistory.history['mae'] + historyToAppend.history['mae'] + if ("loss" in baseHistory.history) and ("loss" in historyToAppend.history): + baseHistory.history['loss'] = baseHistory.history['loss'] + historyToAppend.history['loss'] + if ("val_loss" in baseHistory.history) and ("val_loss" in historyToAppend.history): + baseHistory.history['val_loss'] = baseHistory.history['val_loss'] + historyToAppend.history['val_loss'] + if ("val_mae" in baseHistory.history) and ("val_mae" in historyToAppend.history): + baseHistory.history['val_mae'] = baseHistory.history['val_mae'] + historyToAppend.history['val_mae'] + #----------------------------------------------------------------------------------------------------------- + return baseHistory + + +def getLossManually(mocapNETNetwork,trainInput,trainOutput): + predictions = mocapNETNetwork.predict(trainInput) + losses = compareTrainingOutputs(predictions,trainOutput) + #print(losses) + print("Calculating Loss Statistics :") + #-------------------------- + maximum = np.max(losses) + minimum = np.min(losses) + median = np.median(losses) + mean = np.mean(losses) + from tools import calculateStandardDeviationInPlaceKnowingMean,convertStandardDeviationToVariance + std = calculateStandardDeviationInPlaceKnowingMean(losses,mean) #np.std(losses) + var = convertStandardDeviationToVariance(std) #np.var(losses) + #-------------------------- + titleString="Min=%0.2f,Max=%0.2f,Median=%0.2f,Mean=%0.2f,Std=%0.2f,Var=%0.2f" % (minimum,maximum,median,mean,std,var) + print(bcolors.WARNING," %s " % titleString,bcolors.ENDC) + + #difficultyRating = 1.5 + difficultyRating = 5.0 # 4.0 1.5 2.8 + badScoreAbove = mean + difficultyRating * np.sqrt(std) + print("Threshold for difficult poses is ",badScoreAbove) + + difficultPosesIndexes=list() + numberOfTrainingSamples = len(trainInput) + for z in range(0,numberOfTrainingSamples): + if (np.any(losses[z]>badScoreAbove)): + difficultPosesIndexes.append(z) + del losses + + return difficultPosesIndexes,mean,std + + + +def onlineHardExampleMiningTraining(tensorboardLabel,mocapNETNetwork,numberOfEpochs,numberOfHardEpochs,numberOfNormalEpochsAfterHard,batchSize,earlyStoppingPatience,trainInput,trainOutput,testInput,testOutput,minD,modelIsTrivial=False,haveTestSet=False,useHalfFloats=False): + #First do a regular training.. + halfEpochs=int(numberOfEpochs/2) + if (numberOfEpochs==1): + halfEpochs=numberOfEpochs + print(" Online Hard Example Mining Training session .. ") + + metrics = dict() + if (not modelIsTrivial): + metrics["train_rsquared_start"] = getRSquaredForNeuralNetwork(mocapNETNetwork,trainInput,trainOutput) + + dtypeSelected=np.float32 + if (useHalfFloats): + dtypeSelected=np.float16 + totalHistory,mocapNETNetwork,metrics = regularTraining(tensorboardLabel,mocapNETNetwork,halfEpochs,batchSize,earlyStoppingPatience,trainInput,trainOutput,testInput,testOutput,minD,haveTestSet=haveTestSet,useHalfFloats=useHalfFloats) + + if (numberOfEpochs==1): + print(" Trivial output will not mine etc. ") + return totalHistory,mocapNETNetwork,metrics + + bestTrainingMAE=0.0 + successfulUpdates=0 + + REGULAR_EPOCHS_AFTER_HARD=numberOfNormalEpochsAfterHard + HARD_EPOCHS=numberOfHardEpochs + + miningEpochs = int(halfEpochs/2) + + print("Getting initial state of network") + difficultPosesIndexes,mean,std = getLossManually(mocapNETNetwork,trainInput,trainOutput) + + print("Backing up model with mae ",mean) + mocapNETNetwork.save_weights("modelBackup.h5") + print("Backup complete ") + bestTrainingMAE=mean + + for i in range(0,miningEpochs): + #------------------------------------------------------------------------------------------------------------------------------------------------------- + #------------------------------------------------------------------------------------------------------------------------------------------------------- + #------------------------------------------------------------------------------------------------------------------------------------------------------- + numberOfTrainingSamples = len(trainInput) + inputSize = len (trainInput[1]) + outputSize = len (trainOutput[1]) + + ratioOfDatasetThatIsDifficult = float ( len(difficultPosesIndexes) / numberOfTrainingSamples ) + + #If more than 10% of the dataset is difficult + if (ratioOfDatasetThatIsDifficult>0.5): + print(bcolors.FAIL," More than half of the dataset is hard (%u samples) so we train on all samples as hard %u/%u" % (len(difficultPosesIndexes),i,miningEpochs),bcolors.ENDC) + regularHistory,mocapNETNetwork,metrics = regularTraining(tensorboardLabel,mocapNETNetwork,HARD_EPOCHS,batchSize,5,trainInput,trainOutput,testInput,testOutput,minD,haveTestSet=haveTestSet,useHalfFloats=useHalfFloats) #difficultInput,difficultOutput + totalHistory = appendHistory(totalHistory,regularHistory) + elif (ratioOfDatasetThatIsDifficult>0.1): + difficultInput = np.full([len(difficultPosesIndexes),inputSize],fill_value=0,dtype=dtypeSelected,order='C') + difficultOutput = np.full([len(difficultPosesIndexes),outputSize],fill_value=0,dtype=dtypeSelected,order='C') + for z in range(0,len(difficultPosesIndexes)): + for field in range(0,inputSize): + difficultInput[z,field]=trainInput[difficultPosesIndexes[z],field] + for field in range(0,outputSize): + difficultOutput[z,field]=trainOutput[difficultPosesIndexes[z],field] + + print(bcolors.OKBLUE," Will now train on %u difficult samples %u/%u" % (len(difficultPosesIndexes),i,miningEpochs),bcolors.ENDC) + newHistory,mocapNETNetwork,metrics = regularTraining(tensorboardLabel,mocapNETNetwork,HARD_EPOCHS,batchSize,5,difficultInput,difficultOutput,testInput,testOutput,minD,haveTestSet=haveTestSet,useHalfFloats=useHalfFloats) + totalHistory = appendHistory(totalHistory,newHistory) + del difficultInput + del difficultOutput + + if (meanj): + #----------------------------------------------------------------- + labelJ = getCompositeLabel( + rules['NSDM'][j]['joint'], + rules['NSDM'][j]['halfWayFromThisAnd'], + rules['NSDM'][j]['xOffset'], + rules['NSDM'][j]['yOffset'], + rules['NSDM'][j]['isVirtual'] + ) + #----------------------------------------------------------------- + result.append("EDM-%sY-%sY-Distance"%(labelI,labelJ)) + + #print("EDM matrix will look like this ",result) + return result; + + +def createEDMUsingRules(rules,thisInput): + result=list() + #----------------------------------------------------------------------------------------------------- + if (len(thisInput)==0): + print("createNSDMUsingRules called with no input") + return result + + if (not rules['inputJointMap'].checkJointListDimensions(thisInput)): + print("createNSDMUsingRules called with incorrect input size ") + return thisInput + #----------------------------------------------------------------------------------------------------- + numberOfNSDMRules=len(rules['NSDM']) + for i in range(0,numberOfNSDMRules): + iX,iY,iVisibility,iInvalidPoint = getCompositePoint(rules,i,thisInput) + for j in range(0,numberOfNSDMRules): + if (i>j): + # Ensure that each distance is computed only once since the EDM is a symmetric matrix. + #--------------------------------------------------------------------------- + jX,jY,jVisibility,jInvalidPoint = getCompositePoint(rules,j,thisInput) + if (iInvalidPoint or jInvalidPoint): #Changed to or 17/5/23 <- Why was this AND and not OR ? also C++ EDM.h code + result.append(np.float32(0.0)) + else: + result.append(getJoint2DDistancePoints(iX,iY,jX,jY)) + #--------------------------------------------------------------------------- + return result + + + + +if __name__ == '__main__': + print("EDM.py is a library it cannot be run standalone") diff --git a/src/python/mnet4/EigenPoses.py b/src/python/mnet4/EigenPoses.py new file mode 100755 index 0000000..3338575 --- /dev/null +++ b/src/python/mnet4/EigenPoses.py @@ -0,0 +1,68 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import math +import sys +from enum import Enum +from NSDM import getCompositeLabel,getCompositePoint,getJoint2DDistancePoints + + +def EigenPoseLabels(rules): + result=list() + if ('eigenPoseData' in rules) and ('eigenPoses' in rules) and (int(rules['eigenPoses'])==1): + numberOfEigenPoseRules=len(rules['eigenPoseData']['in']) + print("EigenPoses Rules Number ",numberOfEigenPoseRules) + + for i in range(0,numberOfEigenPoseRules): + #----------------------------------------------------------------- + result.append("EigenPose-%u"%(i)) + #----------------------------------------------------------------- + return result; + + +def computeVectorSimilarity_SAD(vec1,vec2): + loss=float(0.0) + if (len(vec1)==len(vec2)): + #If we are here the vectors have the same size + for i in range(0,len(vec1)): + loss = loss + abs(vec1[i]-vec2[i]) + return loss + +def computeVectorSimilarity_MAD(vec1,vec2): + loss=float(0.0) + if (len(vec1)==len(vec2)): + #If we are here the vectors have the same size + for i in range(0,len(vec1)): + loss = loss + abs(vec1[i]-vec2[i]) + return loss/len(vec1) + +def computeVectorSimilarity(vec1,vec2,mode="MAD"): + if (len(vec1)!=len(vec2)): + print("EigenPoses.py: Asked for similarity on Vectors with Lengths",len(vec1)," vs ",len(vec2)) + sys.exit(0) + return float("nan") + #If we are here the vectors have the same size + if (mode=="SAD"): + return computeVectorSimilarity_SAD(vec1,vec2) + if (mode=="MAD"): + return computeVectorSimilarity_MAD(vec1,vec2) + +def createEigenPosesUsingRules(rules,thisInput): + result=list() + if ('eigenPoseData' in rules) and ('eigenPoses' in rules) and (int(rules['eigenPoses'])==1): + numberOfEigenPoseRules=len(rules['eigenPoseData']['in']) + for i in range(0,numberOfEigenPoseRules): + result.append(computeVectorSimilarity(thisInput,rules['eigenPoseData']['in'][i])) + return result + + + + + +if __name__ == '__main__': + print("EigenPoses.py is a library it cannot be run standalone") diff --git a/src/python/mnet4/MocapNET.py b/src/python/mnet4/MocapNET.py new file mode 100755 index 0000000..5c14b35 --- /dev/null +++ b/src/python/mnet4/MocapNET.py @@ -0,0 +1,1041 @@ +#!/usr/bin/python3 +#test +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +#------------------------------------------------------------------------------------------- +from readCSV import parseConfiguration,parseConfigurationInputJointMap,transformNetworkInput,initializeDecompositionForExecutionEngine,readGroundTruthFile,readCSVFile,parseOutputNormalization +from NSDM import NSDMLabels,createNSDMUsingRules,inputIsEnoughToCreateNSDM,performNSRMAlignment +from EDM import EDMLabels,createEDMUsingRules +from tools import bcolors,checkIfFileExists,readListFromFile,convertListToLowerCase,secondsToHz,getEntryIndexInList,eprint +#------------------------------------------------------------------------------------------- +import sys +sys.path.append("BVH") +from bvhConverter import BVH +#from BVH.bvhConverter import BVH +#------------------------------------------------------------------------------------------- +from principleComponentAnalysis import PCA +#------------------------------------------------------------------------------------------- + +MOCAPNET_VERSION="4.0" + +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +import time +import os +import numpy as np +#------------------------------------------------------------------------------------------- +class MocapNETEnsembleCombination(): + def __init__(self): + self.ensembleNameList = list() + self.ensemblePathList = list() + def addEnsemble(self,name:str,path:str): + self.ensembleNameList.append(name) + self.ensemblePathList.append(path) +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +def checkIfAllListedElementsExistInDict(theList,theDict): + for element in theList: + if not element in theDict: + return False + return True +#------------------------------------------------------------------------------------------- +def checkIfAnyListedElementsExistsInString(theList,theString): + #-------------------------- + if (len(theList)==0): + return False + #-------------------------- + for element in theList: + if element in theString: + return True + return False +#------------------------------------------------------------------------------------------- +def flipHorizontalInput(inputList): + for k in inputList.keys(): + if ("2dx_" in k): + inputList[k]=1.0-inputList[k] + return inputList +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +def getSymmetricLEyeOutputs(): + #I AM NOT AT ALL SURE THE FOLLOWING ARE CORRECT.. + bn=dict() + #--------------------------------------------------- + #These are the actual useful outputs.. that REye has.. + bn["hip_xposition"] = (0.0,"") #0 #ignored + bn["hip_yposition"] = (0.0,"") #1 #ignored + bn["hip_zposition"] = (0.0,"") #2 #ignored + bn["neck1_zrotation"] = (0.0,"") #3 #ignored + bn["neck1_xrotation"] = (0.0,"") #4 #ignored + bn["neck1_yrotation"] = (0.0,"") #5 #ignored + bn["eye.r_zrotation"] = (1.0,"eye.l_zrotation") # + bn["eye.r_xrotation"] = (1.0,"eye.l_xrotation") # + bn["oculi01.r_zrotation"] = (1.0,"oculi01.l_zrotation") # + bn["orbicularis03.r_xrotation"] = (1.0,"orbicularis03.l_xrotation") # + bn["jaw_xrotation"] = (0.0,"") # + bn["jaw_yrotation"] = (0.0,"") # + #--------------------------------------------------- + #The rest should all be ignored ? + #--------------------------------------------------- + bn["oculi01.l_zrotation"] = (0.0,"") #ignored + bn["eye.l_zrotation"] = (0.0,"") # ignored + bn["eye.l_xrotation"] = (0.0,"") # ignored + bn["orbicularis04.r_xrotation"] = (0.0,"") #ignored + bn["orbicularis03.r_yrotation"] = (0.0,"") #ignored + bn["orbicularis04.r_yrotation"] = (0.0,"") #ignored + + bn["levator06.l_xrotation"] = (0.0,"") #ignored + bn["levator06.r_xrotation"] = (0.0,"") #ignored + bn["levator03.l_zrotation"] = (0.0,"") #ignored + bn["levator03.r_zrotation"] = (0.0,"") #ignored + + bn["oris03.l_zrotation"] = (0.0,"") #ignored + bn["oris03.r_zrotation"] = (0.0,"") #ignored + bn["oris07.l_zrotation"] = (0.0,"") #ignored + bn["oris07.r_zrotation"] = (0.0,"") #ignored + + bn["oris04.l_zrotation"] = (0.0,"") #ignored + bn["oris04.r_zrotation"] = (0.0,"") #ignored + bn["oris06.l_zrotation"] = (0.0,"") #ignored + bn["oris06.r_zrotation"] = (0.0,"") #ignored + + bn["orbicularis03.r_yrotation"] = (0.0,"") #ignored + bn["orbicularis04.r_yrotation"] = (0.0,"") #ignored + bn["orbicularis03.l_yrotation"] = (0.0,"") #ignored + bn["orbicularis04.l_yrotation"] = (0.0,"") #ignored + + bn["orbicularis03.l_xrotation"] = (0.0,"") # ignored + bn["orbicularis04.l_xrotation"] = (0.0,"") # ignored + + bn["levator06.l_yrotation"] = (0.0,"") #ignored + bn["levator06.r_yrotation"] = (0.0,"") #ignored + + bn["oris03.l_xrotation"] = (0.0,"") #ignored + bn["oris03.l_yrotation"] = (0.0,"") #ignored + bn["oris07.l_yrotation"] = (0.0,"") #ignored + bn["oris03.r_xrotation"] = (0.0,"") #ignored + bn["oris03.r_yrotation"] = (0.0,"") #ignored + bn["oris07.r_yrotation"] = (0.0,"") #ignored + + bn["oris05_xrotation"] = (0.0,"") #ignored + bn["oris05_yrotation"] = (0.0,"") #ignored + return bn +#--------------------------------------------------- +def getSymmetricLEyeNameList(): + bn=dict() + #--------------------------------------------------- + bn["head_reye_0"] = "head_leye_3" #0 + bn["head_reye_1"] = "head_leye_2" #1 + bn["head_reye_2"] = "head_leye_1" #2 + bn["head_reye_3"] = "head_leye_0" #3 + bn["head_reye_4"] = "head_leye_5" #4 + bn["head_reye_5"] = "head_leye_4" #5 + bn["head_reyebrow_0"] = "head_leyebrow_0" #6 + bn["head_reyebrow_1"] = "head_leyebrow_1" #7 + bn["head_reyebrow_2"] = "head_leyebrow_2" #8 + bn["head_reyebrow_3"] = "head_leyebrow_3" #9 + bn["head_reyebrow_4"] = "head_leyebrow_4" #10 + bn["head_reye"] = "head_leye" #11 + bn["head_rchin_0"] = "head_lchin_0" #12 + bn["head_nostrills_2"]= "head_nostrills_2" #13 + bn["head_chin"] = "head_chin" #14 + return bn +#--------------------------------------------------- +#--------------------------------------------------- +#--------------------------------------------------- + + +def getSymmetricLHandOutputs(): + bn=dict() + #--------------------------------------------------- + bn["lhand_xposition"] = (-1.0,"rhand_xposition") #0 + bn["lhand_yposition"] = (1.0,"rhand_yposition") #1 + bn["lhand_zposition"] = (1.0,"rhand_zposition") #2 + #-------------------------------------------------------------------- + #Flip Quaternion During Symmetric output Calculations + bn["lhand_wrotation"] = (-1.0,"rhand_wrotation") #3 {-w,z,y,x} + bn["lhand_xrotation"] = ( 1.0,"rhand_zrotation") #4 + bn["lhand_yrotation"] = ( 1.0,"rhand_yrotation") #5 + bn["lhand_zrotation"] = ( 1.0,"rhand_xrotation") #6 + #-------------------------------------------------------------------- + bn["finger2-1.l_zrotation"] = (-1.0,"finger2-1.r_zrotation") #7 + bn["finger2-1.l_xrotation"] = (-1.0,"finger2-1.r_xrotation") #8 + bn["finger2-1.l_yrotation"] = (-1.0,"finger2-1.r_yrotation") #9 + bn["finger2-2.l_zrotation"] = (-1.0,"finger2-2.r_zrotation") #10 + bn["finger2-2.l_xrotation"] = (-1.0,"finger2-2.r_xrotation") #11 + bn["finger2-2.l_yrotation"] = (-1.0,"finger2-2.r_yrotation") #12 + bn["finger2-3.l_zrotation"] = (-1.0,"finger2-3.r_zrotation") #13 + bn["finger2-3.l_xrotation"] = (-1.0,"finger2-3.r_xrotation") #14 + bn["finger2-3.l_yrotation"] = (-1.0,"finger2-3.r_yrotation") #15 + #-------------------------------------------------------------------- + bn["finger3-1.l_zrotation"] = (-1.0,"finger3-1.r_zrotation") #16 + bn["finger3-1.l_xrotation"] = (-1.0,"finger3-1.r_xrotation") #17 + bn["finger3-1.l_yrotation"] = (-1.0,"finger3-1.r_yrotation") #18 + bn["finger3-2.l_zrotation"] = (-1.0,"finger3-2.r_zrotation") #19 + bn["finger3-2.l_xrotation"] = (-1.0,"finger3-2.r_xrotation") #20 + bn["finger3-2.l_yrotation"] = (-1.0,"finger3-2.r_yrotation") #21 + bn["finger3-3.l_zrotation"] = (-1.0,"finger3-3.r_zrotation") #22 + bn["finger3-3.l_xrotation"] = (-1.0,"finger3-3.r_xrotation") #23 + bn["finger3-3.l_yrotation"] = (-1.0,"finger3-3.r_yrotation") #24 + #-------------------------------------------------------------------- + bn["finger4-1.l_zrotation"] = (-1.0,"finger4-1.r_zrotation") #25 + bn["finger4-1.l_xrotation"] = (-1.0,"finger4-1.r_xrotation") #26 + bn["finger4-1.l_yrotation"] = (-1.0,"finger4-1.r_yrotation") #27 + bn["finger4-2.l_zrotation"] = (-1.0,"finger4-2.r_zrotation") #28 + bn["finger4-2.l_xrotation"] = (-1.0,"finger4-2.r_xrotation") #29 + bn["finger4-2.l_yrotation"] = (-1.0,"finger4-2.r_yrotation") #30 + bn["finger4-3.l_zrotation"] = (-1.0,"finger4-3.r_zrotation") #31 + bn["finger4-3.l_xrotation"] = (-1.0,"finger4-3.r_xrotation") #32 + bn["finger4-3.l_yrotation"] = (-1.0,"finger4-3.r_yrotation") #33 + #-------------------------------------------------------------------- + bn["finger5-1.l_zrotation"] = (-1.0,"finger5-1.r_zrotation") #34 + bn["finger5-1.l_xrotation"] = (-1.0,"finger5-1.r_xrotation") #35 + bn["finger5-1.l_yrotation"] = (-1.0,"finger5-1.r_yrotation") #36 + bn["finger5-2.l_zrotation"] = (-1.0,"finger5-2.r_zrotation") #37 + bn["finger5-2.l_xrotation"] = (-1.0,"finger5-2.r_xrotation") #38 + bn["finger5-2.l_yrotation"] = (-1.0,"finger5-2.r_yrotation") #39 + bn["finger5-3.l_zrotation"] = (-1.0,"finger5-3.r_zrotation") #40 + bn["finger5-3.l_xrotation"] = (-1.0,"finger5-3.r_xrotation") #41 + bn["finger5-3.l_yrotation"] = (-1.0,"finger5-3.r_yrotation") #42 + #-------------------------------------------------------------------- + bn["lthumbbase_zrotation"] = (-1.0,"rthumbbase_zrotation") #43 +? + bn["lthumbbase_xrotation"] = (-1.0,"rthumbbase_xrotation") #44 +? + bn["lthumbbase_yrotation"] = (-1.0,"rthumbbase_yrotation") #45 + bn["lthumb_zrotation"] = (-1.0,"rthumb_zrotation") #46 + bn["lthumb_xrotation"] = (-1.0,"rthumb_xrotation") #47 + bn["lthumb_yrotation"] = (-1.0,"rthumb_yrotation") #48 + bn["finger1-2.l_zrotation"] = (-1.0,"finger1-2.r_zrotation") #49 + bn["finger1-2.l_xrotation"] = (-1.0,"finger1-2.r_xrotation") #50 + bn["finger1-2.l_yrotation"] = (-1.0,"finger1-2.r_yrotation") #51 + bn["finger1-3.l_zrotation"] = (-1.0,"finger1-3.r_zrotation") #52 + bn["finger1-3.l_xrotation"] = (-1.0,"finger1-3.r_xrotation") #53 + bn["finger1-3.l_yrotation"] = (-1.0,"finger1-3.r_yrotation") #54 + return bn +#--------------------------------------------------- +def getSymmetricLHandNameList(): + bn=dict() + #--------------------------------------------------- + #--------------------------------------------------- + bn["lhand"] = "rhand" #0 - wrist + bn["lthumb"] = "rthumb" #1 - thumb_cmc + bn["lthumbbase"] = "rthumbbase" #1 ? - thumb_cmc + bn["finger1-2.l"] = "finger1-2.r" #2 - thumb_mcp + bn["finger1-3.l"] = "finger1-3.r" #3 - thumb_ip + bn["endsite_finger1-3.l"] = "endsite_finger1-3.r" #4 - thumb_tip + bn["finger2-1.l"] = "finger2-1.r" #5 - index_finger_mcp + bn["finger2-2.l"] = "finger2-2.r" #6 - index_finger_pip + bn["finger2-3.l"] = "finger2-3.r" #7 - index_finger_dip + bn["endsite_finger2-3.l"] = "endsite_finger2-3.r" #8 - index_finger_tip + bn["finger3-1.l"] = "finger3-1.r" #9 - middle_finger_mcp + bn["finger3-2.l"] = "finger3-2.r" #10 - middle_finger_pip + bn["finger3-3.l"] = "finger3-3.r" #11 - middle_finger_dip + bn["endsite_finger3-3.l"] = "endsite_finger3-3.r" #12 - middle_finger_tip + bn["finger4-1.l"] = "finger4-1.r" #13 - ring_finger_mcp + bn["finger4-2.l"] = "finger4-2.r" #14 - ring_finger_pip + bn["finger4-3.l"] = "finger4-3.r" #15 - ring_finger_dip + bn["endsite_finger4-3.l"] = "endsite_finger4-3.r" #16 - ring_tip + bn["finger5-1.l"] = "finger5-1.r" #17 - pinky_mcp + bn["finger5-2.l"] = "finger5-2.r" #18 - pinky_pip + bn["finger5-3.l"] = "finger5-3.r" #19 - pinky_dip + bn["endsite_finger5-3.l"] = "endsite_finger5-3.r" #20 - pinky_tip + return bn +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- + + + +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +class SimulatedMirroredEnsemble(): + def __init__(self, + mirroredModel, + mirroringName, + symmetricNames = list(), + outputOperationsNeeded = list() + ): + self.mirroredModel = mirroredModel + self.partName = mirroringName + self.inputReadyForTF = np.empty([2, 1]) + self.NSRM = np.empty([2, 1]) + self.leftToRightNames = symmetricNames + self.mirroringName = mirroringName + self.outputOperationsNeeded = outputOperationsNeeded + self.serial = mirroredModel.serial + self.outputBVH = dict() + self.outputBVHMinima = dict() + self.outputBVHMaxima = dict() + #------------------------------- + self.simulated = True + #------------------------------- + self.output = dict() + self.outputMinimumValue = dict() + self.outputMaximumValue = dict() + #------------------------------- + self.directInputFlips = dict() + self.flippedInputFlips = dict() + + for key in self.mirroredModel.inputs: + s = key.split("_",1) + if (len(s)>0): + originalName = s[1] + if originalName in self.leftToRightNames: + flippedName = self.leftToRightNames[originalName] + flippedXKey = "2dx_%s" % flippedName + flippedYKey = "2dy_%s" % flippedName + flippedVisibleKey = "visible_%s" % flippedName + self.flippedInputFlips["2dx_%s" % originalName] = flippedXKey #<- this needs flip + self.directInputFlips["2dy_%s" % originalName] = flippedYKey #<- this we copy directly + self.directInputFlips["visible_%s" % originalName] = flippedVisibleKey #<- this we copy directly + + print("\n\n\nInputs that need to be subtracted from one : ",self.flippedInputFlips) + print("\n\n\nInputs that need to be just copied : ",self.directInputFlips) + + + def getModel(self): + return self.mirroredModel.model + + def getModelFlops(self): + return 0 + + def getModelParameters(self): + return 0 + + def test(self): + return 1 + + def prepareInput( + self, + input2D :dict, + configuration : dict + ): + self.inputReadyForTF = self.mirroredModel.predict(input2D=input2D) + self.NSRM = self.mirroredModel.NSRM + return self.inputReadyForTF + + def predict(self,input2D :dict): + #Replicating : https://github.com/FORTH-ModelBasedTracker/MocapNET/blob/mnet3/src/MocapNET2/MocapNETLib2/solutionParts/rightHandSym.cpp + #------------------------------------------------------------ + import copy + flippedInput2D = dict() + #------------------------------------------------------------ + doInputFlips = True # Debug switch should alawys be set to True + doOutputFlips = True # Debug switch should alawys be set to True + #------------------------------------------------------------ + if (doInputFlips): #Do Input flips! + #for key in input2D.keys(): + for keyR in self.mirroredModel.inputs: #<- fix right hand working only if left hand is visible + key = keyR.lower() + if (key in self.directInputFlips): + originalName = key + flippedName = self.directInputFlips[originalName] + if (flippedName in input2D): + flippedInput2D[originalName] = float(input2D[flippedName]) + elif (key in self.flippedInputFlips): + originalName = key + flippedName = self.flippedInputFlips[originalName] + if (flippedName in input2D): + if (float(input2D[flippedName])!=0.0): #<- This should be a check on the visibility channel + flippedInput2D[originalName] = 1.0 - float(input2D[flippedName]) + + #---------------------------------------------------------------------------- + leftHandinputReadyForTF = copy.deepcopy(self.mirroredModel.inputReadyForTF) + leftHandinputNSRM = copy.deepcopy(self.mirroredModel.NSRM) + # =========================================================================== + originalName = self.mirroredModel.partName + self.mirroredModel.partName = self.partName + #--- + self.mirroredOutput = self.mirroredModel.predict(input2D=flippedInput2D) + #--- + self.mirroredModel.partName = originalName + #print("flipped yield ",self.mirroredOutput) #Debug + # =========================================================================== + self.inputReadyForTF = copy.deepcopy(self.mirroredModel.inputReadyForTF) + self.NSRM = copy.deepcopy(self.mirroredModel.NSRM) + #---------------------------------------------------------------------------- + if (doOutputFlips): #Do Output Flips! + for originalKeyRaw in self.mirroredOutput: + originalKey = originalKeyRaw.lower() + #print("OUTPUT 3D FOUND ",originalKey," ",end="") #Debug + if (originalKey in self.outputOperationsNeeded): + flippedKey = self.outputOperationsNeeded[originalKey][1] + flippedFactor = self.outputOperationsNeeded[originalKey][0] + if (flippedFactor!=0.0) and (flippedKey!=""): + #print("USED ",flippedKey) #Debug + self.output[flippedKey] = flippedFactor * float(self.mirroredOutput[originalKey]) + #else: #Debug + # print("IGNORED ",flippedKey) #Debug + + else: + eprint("SYMMETRY: THIS SHOULD NOT HAPPEN / NO RULE FOR ",originalKey,flippedKey) + #------------------------------------------------------------ + #Restore left hand + self.mirroredModel.inputReadyForTF = copy.deepcopy(leftHandinputReadyForTF) + self.mirroredModel.NSRM = copy.deepcopy(leftHandinputNSRM) + #------------------------------------------------------------ + return self.output +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- + + + +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------- +class MocapNET(): + def __init__(self, + #------------------------------------------------- + bvhFilePath:str = "BVH/headerWithHeadAndOneMotion.bvh", + disablePCACode = 0, + disableSmoothingCode = 0, + doPerformanceProfiling = False, + doHCDPostProcessing = 1, + hcdLearningRate = 0.01, + hcdEpochs = 30, + hcdIterations = 15, + langevinDynamics = 0.0, + bvhScale = 1.0, + addNoise = 0.0, + multiThreaded = False, + smoothingSampling = 30.0, + smoothingCutoff = 5.0, + + bvhLibraryPath:str = "BVH/libBVHConverter.so", + smootherLibraryPath:str = "Smooth/libSmoothing.so", + #------------------------------------------------- + engine:str = "onnx", + ensembleToLoad: MocapNETEnsembleCombination = MocapNETEnsembleCombination(), + #------------------------------------------------- + record = False + #------------------------------------------------- + ): + #------------------------------------------------------------------------------- + self.record = record + self.inputHistory = list() + self.history = list() + self.outputHistory = list() + self.ensemble = dict() + #------------------------------------------------------------------------------- + #First initialize the engine.. + self.engine = engine + if (engine=="tensorflow") or (engine=="tf"): + from MocapNETTensorflow import MocapNETTensorflow + self.engineContext = MocapNETTensorflow() + elif (engine=="tflite"): + from MocapNETTFLite import MocapNETTFLite + self.engineContext = MocapNETTFLite() + elif (engine=="onnx"): + from MocapNETONNX import MocapNETONNX + self.engineContext = MocapNETONNX() + else: + print("selectMocapNETClassBasedOnEngine: Unknown engine ",engine) + sys.exit(1) + #------------------------------------------------------------------------------- + for ensembleID in range(0,len(ensembleToLoad.ensembleNameList)): + ensembleName = ensembleToLoad.ensembleNameList[ensembleID] + partName = "%s_all" % ensembleName + ensemblePath = ensembleToLoad.ensemblePathList[ensembleID] + if (ensemblePath!="symmetric"): + print(bcolors.OKGREEN,"Loading ",ensembleName,"..",bcolors.ENDC) + configurationPath = "%s/%s_configuration.json" % (ensemblePath,ensembleName) + if (engine=="tensorflow") or (engine=="tf"): + from MocapNETTensorflow import MocapNETTensorflowSubProblem + modelPath = "%s/" % (ensemblePath) + self.ensemble[ensembleName] = MocapNETTensorflowSubProblem( + context = self.engineContext, + configPath = configurationPath, + modelPath = modelPath, + partName = partName, + completelyDisablePCACode = disablePCACode + ) + elif (engine=="tflite"): + from MocapNETTFLite import MocapNETTFLiteSubProblem + modelPath = "%s/model.tflite" % (ensemblePath) + self.ensemble[ensembleName] = MocapNETTFLiteSubProblem( + context = self.engineContext, + configPath = configurationPath, + modelPath = modelPath, + partName = partName, + completelyDisablePCACode = disablePCACode + ) + elif (engine=="onnx"): + from MocapNETONNX import MocapNETONNXSubProblem + modelPath = "%s/model.onnx" % (ensemblePath) + self.ensemble[ensembleName] = MocapNETONNXSubProblem( + context = self.engineContext, + configPath = configurationPath, + modelPath = modelPath, + partName = partName, + completelyDisablePCACode = disablePCACode + ) + elif (ensemblePath=="symmetric"): + #If we are handling a lhand we get an rhand for free :P + if (ensembleName=="rhand"): + self.ensemble["rhand"] = SimulatedMirroredEnsemble( + mirroredModel = self.ensemble["lhand"], + mirroringName = "rhand", + symmetricNames = getSymmetricLHandNameList(), + outputOperationsNeeded = getSymmetricLHandOutputs() + ) + #If we are handling a reye we get an leye for free :P + if (ensembleName=="leye"): + self.ensemble["leye"] = SimulatedMirroredEnsemble( + mirroredModel = self.ensemble["reye"], + mirroringName = "leye", + symmetricNames = getSymmetricLEyeNameList(), + outputOperationsNeeded = getSymmetricLEyeOutputs() + ) + #------------------------------------------------------------------------------- + print(bcolors.OKGREEN,"Combined network has ",self.getModelParameters()," parameters..",bcolors.ENDC) + #------------------------------------------------------------------------------- + #------------------------------------------------------------------------------- + print(bcolors.OKGREEN,"Loading C/Python libraries..",bcolors.ENDC) + self.multiThreaded = multiThreaded + self.doFineTuning = doHCDPostProcessing + self.addNoise = addNoise + self.smoothingSampling = smoothingSampling + self.smoothingCutoff = smoothingCutoff + self.bvhScale = bvhScale + self.lastMAEErrorInPixels = 0.0 + if (disableSmoothingCode==1): + self.smoothingSampling = 0.0 + self.smoothingCutoff = 0.0 + self.hcdLearningRate = hcdLearningRate + self.hcdEpochs = hcdEpochs + self.hcdIterations = hcdIterations + #------------------------------------------------------------------------------- + self.langevinDynamics = langevinDynamics + self.bvhFilePath = bvhFilePath + self.bvh = BVH(bvhPath = bvhFilePath,libraryPath = bvhLibraryPath) + self.bvh.scale(self.bvhScale) + self.bvhJointList = convertListToLowerCase(self.bvh.getJointList()) + self.bvhJointParentList = self.bvh.getJointParentList() + #------------------------------------------------------------------------------- + self.incompleteUpperbodyInput = 1 + self.incompleteLowerbodyInput = 1 + #------------------------------------------------------------------------------- + self.framesProcessed = 0 + self.currentPrediction = dict() + self.previousPrediction = dict() + self.input2D = dict() + self.output = dict() + self.output2D = dict() + self.outputBVH = dict() + self.outputBVHMinima = dict() + self.outputBVHMaxima = dict() + self.output3D = dict() + + self.perfHistorySize = 30 + #------------------------------------------------------------------------------- + self.history_hz_2DEst = [] + self.hz_2DEst = 0.0 + self.history_hz_NN = [] + self.hz_NN = 0.0 + self.history_hz_HCD = [] + self.hz_HCD = 0.0 + self.history_hz_Vis = [] + self.hz_Vis = 0.0 + #------------------------------------------------------------------------------- + + + #------------------------------------------------------------------------------- + print("Caching networks : ") + self.test() + print(bcolors.OKGREEN,"MocapNET ready for use! ",bcolors.ENDC) + #------------------------------------------------------------------------------- + from tools import checkVersion + checkVersion(MOCAPNET_VERSION) + + def recordBVH(self,val:bool): + self.record=val + return True + + def hasEnsemble(self,name): + if (name in self.ensemble): + return True + else: + return False + + + def getUpperBodyModel(self): + return self.ensemble["upperbody"].getModel() + + def getLowerBodyModel(self): + return self.ensemble["lowerbody"].getModel() + + def getModelFlops(self): + total = 0.0 + for k in self.ensemble.keys(): + total = total + self.ensemble[k].getModelFlops() + return total + + def getModelParameters(self): + total = 0 + for k in self.ensemble.keys(): + total = total + self.ensemble[k].getModelParameters() + return total + + def getEnsembleSerials(self): + description = "" + from datetime import datetime, date, time, timezone + #description = datetime.now().strftime("%Y-%m-%d %H:%M:%S ") + description = datetime.now().strftime("%Y-%m-%d ") + for k in self.ensemble.keys(): + description = description + k + ":" + self.ensemble[k].serial + " " + return description + + + def test(self): + #------------------------------------------- + for k in self.ensemble.keys(): + print("Testing loaded ",k," model ") + self.ensemble[k].test() + #------------------------------------------- + + + def enforceBanlistOnOutput(self,output): + #Banlist ------------------------------------- + if "abdomen_zrotation" in output.keys(): + output["abdomen_zrotation"]=0.0 + if "abdomen_xrotation" in output.keys(): + output["abdomen_xrotation"]=0.0 + if "abdomen_yrotation" in output.keys(): + output["abdomen_yrotation"]=0.0 + #-------------------------------------------- + if "chest_zrotation" in output.keys(): + output["chest_zrotation"]=0.0 + if "chest_xrotation" in output.keys(): + output["chest_xrotation"]=0.0 + if "chest_yrotation" in output.keys(): + output["chest_yrotation"]=0.0 + #-------------------------------------------- + return output + + + def countMissingItemsPercentage(self, inputReadyForMocapNET ): + return 0.0 + + + def perturbInput(self,input2D :dict): + import random + for i in range(5): + print(bcolors.FAIL,"NOISE SCHEDULE IS ACTIVE AND SYNTHETIC NOISE IS ADDED (",self.addNoise,") ..",bcolors.ENDC) + peturbedInput2D = input2D + for coordLabel in peturbedInput2D.keys(): + #print(coordLabel) + coordLabelL = coordLabel.lower() + if ("2dx_" in coordLabelL) or ("2dy_" in coordLabelL): + if (peturbedInput2D[coordLabel]>0.0): + perturbation = random.uniform(float(-self.addNoise/2.0),float(self.addNoise/2.0)) + #print("Perturbing ",peturbedInput2D[coordLabel]," with ",perturbation," -> ",end="") + peturbedInput2D[coordLabel]=peturbedInput2D[coordLabel] + perturbation + #print(" ",peturbedInput2D[coordLabel]) + + return peturbedInput2D + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def predict(self,input2D :dict): + start = time.time() + #-------------------------------------------------------------------------------------- + if (self.addNoise>0.0): + input2D = self.perturbInput(input2D) + #-------------------------------------------------------------------------------------- + self.input2D = input2D + self.output = dict() + self.outputBVHMinima = dict() + self.outputBVHMaxima = dict() + #-------------------------------------------------------------------------------------- + if (self.record): + self.inputHistory.append(input2D) + #-------------------------------------------------------------------------------------- + + #print("INPUT2D : ",input2D) + for k in self.ensemble.keys(): + thisEnsemble = self.ensemble[k] + #--------------------------------------------------------------- + ensembleOutput = thisEnsemble.predict(input2D) + #print("Ensemble ",thisEnsemble.partName," : ",ensembleOutput) #Debug + #--------------------------------------------------------------- + self.output.update(ensembleOutput) + if (not thisEnsemble.simulated): + self.outputBVHMinima.update(thisEnsemble.outputMinimumValue) + self.outputBVHMaxima.update(thisEnsemble.outputMaximumValue) + #--------------------------------------------------------------- + + self.output = self.enforceBanlistOnOutput(self.output) + + self.framesProcessed = self.framesProcessed + 1 + #-------------------------------------------------------------------------------------- + end = time.time() # Time elapsed + self.hz_NN = secondsToHz(end - start) + #--------------------------------------------------------------- + self.history_hz_NN.append(self.hz_NN) + if (len(self.history_hz_NN)>self.perfHistorySize): + self.history_hz_NN.pop(0) #Keep mnet history on limits + #--------------------------------------------------------------- + + + #If we want record, record the raw BVH prediction + #print("RECORD : ",self.output) + if (self.record): + self.outputHistory.append(self.output) #This does not have HCD improvement.. + + #print("\r MocapNET Wrapper NeuralNetwork Framerate : ",round(self.hz_NN,2)," fps \r", end="", flush=True) + #print("\n", end="", flush=True) + + return self.output + + + def printStatus(self): + import sys + sys.stdout.write("\rFrame "+str(self.framesProcessed)+"|"+self.engine+"|MPJPE "+str(round(self.bvh.lastMAEErrorInPixels,1))+" px|2D NN:"+str(round(self.hz_2DEst,1))+"Hz|MocapNET:"+str(round(self.hz_NN,1))+"Hz|HCD:"+str(round(self.hz_HCD,1))+"Hz ") + sys.stdout.flush() + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def predictMultiThreaded(self,input2D :dict): + #---------------------------------- + if (self.multiThreaded): + start = time.time() + #-------------------------------------------------------------------------------------- + if (self.addNoise>0.0): + input2D = self.perturbInput(input2D) + #-------------------------------------------------------------------------------------- + self.input2D = input2D + self.output = dict() + self.outputBVHMinima = dict() + self.outputBVHMaxima = dict() + #-------------------------------------------------------------------------------------- + if (self.record): + self.inputHistory.append(input2D) + #-------------------------------------------------------------------------------------- + + #Create and handle thread initialization if needed.. + self.threads = [] + import threading + for k in self.ensemble.keys(): + #--------------------------------------------------------------- + thisEnsemble = self.ensemble[k] + self.threads.append(threading.Thread(target=thisEnsemble.predict, args=(self.input2D,)) ) + #--------------------------------------------------------------- + #--------------------------------------- + for thread in self.threads: + thread.start() + #--------------------------------------- + #Parallel execution here.. + #--------------------------------------- + for thread in self.threads: + thread.join() + #--------------------------------------- + for k in self.ensemble.keys(): + thisEnsemble = self.ensemble[k] + self.output.update(thisEnsemble.output) + if (k!="rhand") and (k!="lhand") and (k!="leye") : #/Why ? + self.outputBVHMinima.update(thisEnsemble.outputMinimumValue) + self.outputBVHMaxima.update(thisEnsemble.outputMaximumValue) + #--------------------------------------------------------------- + self.output = self.enforceBanlistOnOutput(self.output) + self.framesProcessed = self.framesProcessed + 1 + end = time.time() # Time elapsed + self.hz_NN = secondsToHz(end - start) + #--------------------------------------------------------------- + self.history_hz_NN.append(self.hz_NN) + if (len(self.history_hz_NN)>self.perfHistorySize): + self.history_hz_NN.pop(0) #Keep mnet history on limits + #--------------------------------------------------------------- + + #If we want record, record the raw BVH prediction + #print("RECORD MT : ",self.output) + if (self.record): + self.outputHistory.append(self.output) #This does not have HCD improvement.. + + + #print("\r MocapNET MultiThreaded NeuralNetwork Framerate : ",round(self.hz_NN,2)," fps \r", end="", flush=True) + #print("\n", end="", flush=True) + else: + print("Fallback to single threaded code..") + self.predict(input2D) + return self.output + + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def fineTune(self,input2D :dict, NNOutput:dict): + if (self.bvh.modify(NNOutput)): + self.bvh.processFrame(0) #only have 1 frame ID + + if (self.hcdIterations>0) and (self.doFineTuning==1): + #print(bcolors.OKGREEN,"Running HCD..",bcolors.ENDC) + self.bvh.fineTuneToMatch("body", + input2D, + frameID = 0, + iterations = self.hcdIterations, + epochs = self.hcdEpochs, + lr = self.hcdLearningRate, + fSampling = self.smoothingSampling, + fCutoff = self.smoothingCutoff) + self.bvh.processFrame(0) #This should now be updated with the IK fine tuned prediction..! + + + def predict3DJoints(self,input2D :dict,runNN:bool=True,runHCD:bool=True): + #Extract a BVH dict of BVH motion fields + if ((runNN) or (len(self.previousPrediction)==0)): + if (self.multiThreaded): + rawBVHPrediction = self.predictMultiThreaded(input2D) #If multithreading is disabled this fallbacks to single threaded.. + else: + rawBVHPrediction = self.predict(input2D) + self.previousPrediction = rawBVHPrediction + else: + rawBVHPrediction = self.previousPrediction + + #This spams a lot.. + #print("Predictions from ensemble keys : ",rawBVHPrediction.keys()) + + + # Deal with 3D Mode + #-------------------------------------------------------------------- + if ("upperbody" in self.ensemble) and ("lowerbody" in self.ensemble): + if ('outputMode' in self.ensemble["upperbody"].configuration) and ('outputMode' in self.ensemble["lowerbody"].configuration): + #Running on a recent build with bvh/3d output mode switching + if (self.ensemble["upperbody"].configuration['outputMode']=='3d') and (self.ensemble["lowerbody"].configuration['outputMode']=='3d'): + self.output3D = capitalizeCoordinateTags(self.output) + print(bcolors.OKGREEN,"DIRECT 3D RECOVERY!",bcolors.ENDC) + #print(self.output3D) + return self.output3D + #-------------------------------------------------------------------- + #Modify our BVH armature with the new BVH values + if (self.bvh.modify(rawBVHPrediction)): + + #Remember BVH Pose + self.outputBVH = rawBVHPrediction + + #Render to 2D/3D + self.bvh.processFrame(0) #only have 1 frame ID <- we load our raw prediction + + fineTuningPasses = 0 + if (self.hcdIterations>0) and (self.doFineTuning==1) and (runHCD): + #print(bcolors.OKGREEN,"Running HCD..",bcolors.ENDC) + start = time.time() + if ("upperbody" in self.ensemble) or ("lowerbody" in self.ensemble): + self.bvh.fineTuneToMatch( + "body", + input2D, + frameID = 0, + iterations = self.hcdIterations, + epochs = self.hcdEpochs, + lr = self.hcdLearningRate, + fSampling = self.smoothingSampling, + fCutoff = self.smoothingCutoff, + langevinDynamics = self.langevinDynamics + ) + fineTuningPasses = fineTuningPasses + 1 + self.lastMAEErrorInPixels = self.bvh.lastMAEErrorInPixels + if ("lhand" in self.ensemble): + self.bvh.fineTuneToMatch( + "lhand", + input2D, + frameID = 0, + iterations = self.hcdIterations, + epochs = self.hcdEpochs, + lr = self.hcdLearningRate, + fSampling = self.smoothingSampling, + fCutoff = self.smoothingCutoff, + langevinDynamics = self.langevinDynamics + ) + fineTuningPasses = fineTuningPasses + 1 + if ("rhand" in self.ensemble): + self.bvh.fineTuneToMatch( + "rhand", + input2D, + frameID = 0, + iterations = self.hcdIterations, + epochs = self.hcdEpochs, + lr = self.hcdLearningRate, + fSampling = self.smoothingSampling, + fCutoff = self.smoothingCutoff, + langevinDynamics = self.langevinDynamics + ) + fineTuningPasses = fineTuningPasses + 1 + + #-------------------------------------------------------------------------------------- + if (self.smoothingSampling!=0) or (self.smoothingCutoff!=0): + self.bvh.smooth(frameID=0,fSampling = self.smoothingSampling,fCutoff = self.smoothingCutoff) + else: + print("Smoothing explicitly disabled") + #self.bvh.processFrame(0) # <- this is now done internally to simplify code.. This should now be updated with the IK fine tuned prediction..! + #-------------------------------------------------------------------------------------- + end = time.time() + # Time elapsed + seconds = end - start + if (seconds==0.0): + seconds=1.0 + # Calculate frames per second + self.hz_HCD = 1 / seconds + #------------------------------------------------------------- + self.history_hz_HCD.append(self.hz_HCD) + if (len(self.history_hz_HCD)>self.perfHistorySize): + self.history_hz_HCD.pop(0) #Keep mnet history on limits + #------------------------------------------------------------- + #if (fineTuningPasses>0): + # print("MocapNET HCD Fine tuning Framerate : ",round(self.hz_HCD,2)," fps \n", end="", flush=True) + + else: + print(bcolors.FAIL,"Did not run HCD (",self.hcdIterations,",",self.doFineTuning,",",runHCD,")",bcolors.ENDC) + + + #This block prevents(?) an endless loop of zeros.. + if ( self.bvh.lastMAEErrorInPixels<0.001 ): + print(bcolors.FAIL,"RESET SKELETON (",self.bvh.lastMAEErrorInPixels,") ",bcolors.ENDC) + #rawBVHPrediction["hip_XPosition"]=0.0 + #rawBVHPrediction["hip_YPosition"]=0.0 + #rawBVHPrediction["hip_ZPosition"]=-200.0 + #self.bvh.modify(rawBVHPrediction) + self.bvh.setMotionValueOfFrame(0,2,-200.0) + self.bvh.processFrame(0) #only have 1 frame ID <- we load our raw prediction + self.bvh.lastMAEErrorInPixels = 1000.0 + #print("input2D:",input2D) + #print("rawBVHPrediction:",rawBVHPrediction) + + + + + #If we want record the file + if (self.record): + self.history.append(self.bvh.getAllMotionValuesOfFrame(0)) + + #Retreive 2D/3D Values + self.output2D = dict() + self.output3D = dict() + for jointID in range(0,self.bvh.numberOfJoints): + #------------------------------------------- + jointName = self.bvh.getJointName(jointID).lower() + #------------------------------------------- + x3D,y3D,z3D = self.bvh.getJoint3D(jointID) + self.output3D["3DX_"+jointName]=float(x3D) + self.output3D["3DY_"+jointName]=float(y3D) + self.output3D["3DZ_"+jointName]=float(z3D) + #------------------------------------------- + x2D,y2D = self.bvh.getJoint2D(jointID) + self.output2D["2DX_"+jointName]=float(x2D) + self.output2D["2DY_"+jointName]=float(y2D) + #------------------------------------------- + else: + print(bcolors.FAIL,"We where unable to process the BVH output",bcolors.ENDC) + + + return self.output3D + + def __del__(self): + print(' ') + if (self.record): + print("Write BVH Output!") + self.bvh.saveBVHFileFromList("out.bvh",self.history) + + from tools import saveCSVFileFromListOfDicts + print("Write BVH Output in CSV format!") + saveCSVFileFromListOfDicts("out.csv",self.outputHistory) + print("Write 2D Input!") + saveCSVFileFromListOfDicts("in.csv",self.inputHistory) + else: + print("Did not record output due to --live mode") + + print('Thank you for using MocapNET!') + print('https://github.com/FORTH-ModelBasedTracker/MocapNET') + + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +def easyMocapNETConstructor( + engine = "onnx", + doProfiling = False, + doHCDPostProcessing = 1, + hcdLearningRate = 0.01, + hcdEpochs = 30, + hcdIterations = 15, + smoothingSampling = 30.0, + smoothingCutoff = 5.0, + multiThreaded = False, + bvhScale = 1.0, + doBody = True, + doUpperbody = False, #<- These get auto activated if doBody=True + doLowerbody = False, #<- These get auto activated if doBody=True + doFace = False, + doREye = False, + doMouth = False, + doHands = False, + doSymmetries = True, + addNoise = 0.0 + ): + combo = MocapNETEnsembleCombination() + #-------------------------------------------------------------- + if (doFace): + combo.addEnsemble("face","step1_face_all/") + if (doMouth): + combo.addEnsemble("mouth","step1_mouth_all/") + if (doREye): + combo.addEnsemble("reye","step1_reye_all/") + if(doSymmetries): + combo.addEnsemble("leye","symmetric") #leye will get initialized automatically + if (doHands): + combo.addEnsemble("lhand","step1_lhand_all/") + if(doSymmetries): + combo.addEnsemble("rhand","symmetric") #rhand will get initialized automatically + if (doBody or doLowerbody) : + combo.addEnsemble("lowerbody","step1_lowerbody_all/") + if (doBody or doUpperbody) : + combo.addEnsemble("upperbody","step1_upperbody_all/") + #-------------------------------------------------------------- + mnet = MocapNET( + doPerformanceProfiling = doProfiling, + doHCDPostProcessing = doHCDPostProcessing, + hcdLearningRate = hcdLearningRate, + hcdEpochs = hcdEpochs, + hcdIterations = hcdIterations, + multiThreaded = multiThreaded, + bvhScale = bvhScale, + engine = engine, + ensembleToLoad = combo, + addNoise = addNoise, + smoothingSampling = smoothingSampling, + smoothingCutoff = smoothingCutoff + ) + return mnet +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- + +if __name__ == '__main__': + mnet = MocapNET( + configUpperBodyPath = "step1_upperbody_all/upperbody_configuration.json", + modelUpperBodyPath="step1_upperbody_all", + configLowerBodyPath = "step1_lowerbody_all/lowerbody_configuration.json", + modelLowerBodyPath="step1_lowerbody_all", + bvhFilePath="BVH/headerWithHeadAndOneMotion.bvh" + ) + + mnet.test() + print("Survived Test!") diff --git a/src/python/mnet4/MocapNETONNX.py b/src/python/mnet4/MocapNETONNX.py new file mode 100755 index 0000000..fc675ef --- /dev/null +++ b/src/python/mnet4/MocapNETONNX.py @@ -0,0 +1,472 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import onnxruntime as ort +import onnx +import os +import sys +import time + +#------------------------------------------------------------------------------------------- +from readCSV import parseConfiguration,parseConfigurationInputJointMap,transformNetworkInput,initializeDecompositionForExecutionEngine,readGroundTruthFile,readCSVFile,parseOutputNormalization +from NSDM import NSDMLabels,createNSDMUsingRules,inputIsEnoughToCreateNSDM,performNSRMAlignment +from EDM import EDMLabels,createEDMUsingRules +from tools import bcolors,eprint,checkIfFileExists,readListFromFile,convertListToLowerCase,secondsToHz,capitalizeCoordinateTags,getEntryIndexInList,parseSerialNumberFromSummary +#------------------------------------------------------------------------------------------- +import sys +sys.path.append("BVH") +from bvhConverter import BVH +#from BVH.bvhConverter import BVH +#------------------------------------------------------------------------------------------- +#from Smooth.smoothing import Smooth +#------------------------------------------------------------------------------------------- +from principleComponentAnalysis import PCA +#------------------------------------------------------------------------------------------- + +import numpy as np + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class MocapNETONNXSubProblem(): + def __init__(self, + context, + configPath:str, + modelPath:str, + partName:str, + completelyDisablePCACode = 0 + ): + #self.options = context.sess_options + self.options = ort.SessionOptions() + #------------------------------------------------------------------------------- + self.useOutputLimits = True #Careful, this should always be on! + self.partName = partName + self.configPath = configPath + self.configuration = parseConfiguration(configPath) + self.part = self.configuration["OutputDirectory"] + self.inputName = "input_all" + self.modelPath = modelPath + self.modelDirectory = os.path.dirname(self.modelPath) + self.frameNumber = 0 + #------------------------------------------------------------------------------- + onnxModelForCheck = onnx.load(modelPath) + onnx.checker.check_model(onnxModelForCheck) + print("ONNX devices available : ", ort.get_device()) + providers = ['CPUExecutionProvider'] + #providers = ['CUDAExecutionProvider'] + self.model = ort.InferenceSession(modelPath, providers=providers, sess_options=self.options) + for i in range(0,len(self.model.get_inputs())): + print("ONNX INPUTS ",self.model.get_inputs()[i].name) + self.inputName = self.model.get_inputs()[i].name + + self.model_input_name = self.model.get_inputs() + #------------------------------------------------------------------------------- + self.inputsWithNSRM = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkInputs.list")) + self.inputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkJoints.list")) + self.outputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkOutputs.list")) + self.configuration = parseConfigurationInputJointMap(self.configuration,self.inputs) + self.serial = parseSerialNumberFromSummary(self.modelDirectory+"/summary.html") + #------------------------------------------------------------------------------- + self.inputReadyForTF = np.empty([2, 1]) + self.NSRM = np.empty([2, 2]) + #------------------------------------------------------------------------------- + self.emptyList = [0.0] * len(self.inputsWithNSRM) + self.emptyInput = np.asarray([self.emptyList],dtype=np.float32) + self.emptyList = [0.0] * len(self.outputs) + self.emptyOutput = np.asarray([self.emptyList],dtype=np.float32) + #------------------------------------------------------------------------------- + self.outputScalars = [1.0] * len(self.outputs) + self.outputOffsets = [0.0] * len(self.outputs) + self.outputMinima = [-6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + self.outputMaxima = [6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + #------------------------------------------------------------------------------- + self.outputOffsets = parseOutputNormalization(self.modelDirectory,"/outputOffsets.csv",self.outputs,self.outputOffsets) + self.outputScalars = parseOutputNormalization(self.modelDirectory,"/outputScalarsFraction.csv",self.outputs,self.outputScalars) + self.outputMinima = parseOutputNormalization(self.modelDirectory,"/outputMinima.csv",self.outputs,self.outputMinima) + self.outputMaxima = parseOutputNormalization(self.modelDirectory,"/outputMaxima.csv",self.outputs,self.outputMaxima) + #------------------------------------------------------------------------------- + if (self.outputs[0]=="depth"): + self.outputs[0]="hip_zposition" + #------------------------------------------------------------------------------- + print("Output Mapping :") + for jointID in range(0,len(self.outputs)): + #self.outputScalars[jointID] = 1 / float(self.outputScalars[jointID]) + #print(" - Output ",self.outputs[jointID]," limits [",self.outputMinima[jointID],",",self.outputMaxima[jointID],"] scalar ",self.outputScalars[jointID]," offset ",self.outputOffsets[jointID]) + print("Out %s|Min %0.2f|Max %0.2f|Scalar %0.2f|Offset %0.2f"%(self.outputs[jointID],self.outputMinima[jointID],self.outputMaxima[jointID],self.outputScalars[jointID],self.outputOffsets[jointID])) + #------------------------------------------------------------------------------- + self.incompleteInput = 1 + #------------------------------------------------------------------------------- + self.simulated = False + #------------------------------------------------------------------------------- + self.output = dict() + self.outputMinimumValue = dict() + self.outputMaximumValue = dict() + #------------------------------------------------------------------------------- + self.disablePCACode = completelyDisablePCACode + if (not self.disablePCACode): + self.decompositionEngine = initializeDecompositionForExecutionEngine(self.configuration,self.modelDirectory,self.partName,disablePCACode=self.disablePCACode) + #------------------------------------------------------------------------------- + #The default compatibility setting is the BMVC2019 2channel NSDM, however nowadays we use NSRM + numberOfChannelsPerNSDMElement=2 + if (self.configuration['NSDMAlsoUseAlignmentAngles']==1): + numberOfChannelsPerNSDMElement=1 + print("Number of Channels Per NSDM element ",numberOfChannelsPerNSDMElement) + #------------------------------------------------------------------------------- + if ("eigenPoses" in self.configuration): + if (self.configuration['eigenPoses']==1): + self.configuration['eigenPoseData'] = readGroundTruthFile( + self.configuration, + "Eigenposes", + "%s/2d_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.partName), + "%s/%s_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.configuration['outputMode'],self.partName), #configuration['outputMode'] is either bvh or 3d + 1.0, + numberOfChannelsPerNSDMElement, + int(self.configuration['useRadians']),#useRadians, + 0,#useHalfFloats + externalDecomposition=self.decompositionEngine + ) + #------------------------------------------------------------------------------- + print("\n\n") + print("Inputs :",self.inputs) + print("Outputs :",self.outputs) + #------------------------------------------------------------------------------- + + def getModel(self): + return self.model + + def getModelFlops(self): + print("ONNX has no flops calculator") + return 0 + + def getModelParameters(self): + print("ONNX has no model parameters calculator") + return 0 + + def test(self): + #------------------------------------------- + thisInputONNX = { self.inputName : self.emptyInput} + output_names_onnx = [otp.name for otp in self.model.get_outputs()] + predictions = self.model.run(output_names_onnx,thisInputONNX)[0][0] + #------------------------------------------- + return 1 + + def prepareInput(self,input2D :dict,configuration : dict): + from readCSV import prepareInputG + thisFullInput, self.NSRM, thisInput, angleToRotate, missingRatio = prepareInputG(input2D,configuration,self.inputs,self.inputsWithNSRM,self.part,self.decompositionEngine,self.disablePCACode) + #appendCSVToFile(self.inputName+".csv",thisFullInput,fID=self.frameNumber) # <----------------- + inputReadyForTF = np.asarray([thisFullInput],dtype=np.float32) + return inputReadyForTF,missingRatio + + def logProbabilisticOutput(self,outputFromNN,resolution=60,increment=6.0,numberOfJoints=30): + xs=list() + #---------------------------------- + value = -180.0 + inc = increment + for r in range(0,resolution): + xs.append(value) + value=value+inc + #---------------------------------- + + ys=list() + #---------------------------------- + for j in range(0,numberOfJoints): + rs=list() + for r in range(0,resolution): + rs.append(outputFromNN[(j*resolution) + r]) + ys.append(rs) + #---------------------------------- + + # Importing packages + import matplotlib.pyplot as plt + + #plt.figure(figsize=(80,80)) + plt.clf() + plt.title("Output Distributions %s "%(self.partName)) + + # Define data values + #print("Should plot %u lines"%numberOfJoints) + for j in range(0,numberOfJoints): + plt.plot(xs, ys[j], label='%s (#%u)' %(self.outputs[j],j)) + #print("Plot %u"%j) + + plt.legend() + #plt.show() #<-This blocks + plt.draw() + plt.pause(0.01) + + + + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that may be [it is listed in self.inputs and self.outputs]) + """ + def castProbabilisticOutputToDiscreteOutput(self, outputFromNN): + if ('probabilisticOutput' in self.configuration) and (self.configuration['probabilisticOutput']==1): + print(bcolors.OKGREEN,"DOING PROBABILISTIC OUTPUT",bcolors.ENDC) + + #Resolution incrementation + inc = 10.0 + #------------------------- + minV = -180.0 + maxV = 180.0 + resolution = 0 #<- gets automatically calculated as a function of inc.. + #------------------------- + i = minV + while(ibestValue): + bestValue = outputFromNN[(j*resolution + r)] + bestChoice = value + value=value+inc + pickedOutput.append(bestChoice) + + #pickedOutput[0] = 0 + #pickedOutput[1] = 0 + pickedOutput[2] = -160 + pickedOutput[3] = 0 + pickedOutput[4] = 0 + pickedOutput[5] = 0 + return pickedOutput + return outputFromNN + + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def predict(self, input2D:dict): + #print("Predict ",self.partName) + self.inputReadyForTF,missingRatio = self.prepareInput(input2D,self.configuration) + + if (missingRatio>0.3): + eprint("Not running ",self.partName," due to missing joints ")#,input2D) + return self.output + + + #Turns out on some decompositions like FastICA there are a lot of zeros! + #----------------------------------------------- + #Save cycles by not executing an empty data blob + #----------------------------------------------- + self.incompleteInput = 0 #<- This needs to be set to 0 to mark input is received..! + + #Cast and then run input through MocapNET + thisInputONNX = { self.inputName : self.inputReadyForTF } + output_names_onnx = [otp.name for otp in self.model.get_outputs()] + predictions = self.model.run(output_names_onnx,thisInputONNX)[0][0] + #predictions = self.model(self.inputReadyForTF,training=False) + + #PROBABILISTIC MODE + if ('probabilisticOutput' in self.configuration) and (self.configuration['probabilisticOutput']==1): + predictions = self.castProbabilisticOutputToDiscreteOutput(predictions) + + self.output = dict() + if (len(predictions)!=len(self.outputs)): + print(bcolors.FAIL,"Something bad happened.. the network regressed a different number of parameters (",len(predictions),") than what we expected (",len(self.outputs),") ",bcolors.ENDC) + raise IOError + #Go on with it + return self.output + + #Values to list.. + outputValueList = list() + + for i in range (len(self.outputs)): + outputValueList.append(float(predictions[i])) + + #============================================================================================================== + # THIS SHOULD BE COMMON IN TENSORFLOW/TF-LITE/ONNX + #============================================================================================================== + #Gather our numpy array output in the form of a labeled dictionary + if (self.useOutputLimits): + #Take into account output offsets/scaling + for i in range (len(self.outputs)): + #This should be the exact oposite of the operation in readCSV.py line 550 + recoveredValue = (float(outputValueList[i]) * float(self.outputScalars[i])) + float(self.outputOffsets[i]) + #--------------------------------------------------------------- + if (recoveredValue > self.outputMaxima[i]): + recoveredValue = self.outputMaxima[i] + if (recoveredValue < self.outputMinima[i]): + recoveredValue = self.outputMinima[i] + #--------------------------------------------------------------- + element = self.outputs[i] + self.output[element] = recoveredValue + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #--------------------------------------------------------------- + else: + #Not using limits + for i in range (len(self.outputs)): + element = self.outputs[i] + self.output[element] = float(outputValueList[i]) + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #============================================================================================================== + #============================================================================================================== + + + + self.frameNumber = self.frameNumber + 1 + return self.output +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class PoseNETONNX(): + def __init__( + self, + modelPath:str="movenet/model.onnx", + targetWidth = 192, + targetHeight = 192, + trainingWidth = 1920, + trainingHeight = 1080, + ): + #Tensorflow attempt to be reasonable + #------------------------------------------ + from holisticPartNames import getPoseNETBodyNameList + self.jointNames = getPoseNETBodyNameList() + #------------------------------------------ + import onnxruntime as ort + import onnx + onnxModelForCheck = onnx.load(modelPath) + onnx.checker.check_model(onnxModelForCheck) + print("ONNX devices available : ", ort.get_device()) + providers = ['CPUExecutionProvider'] + #providers = ['CUDAExecutionProvider'] + self.options = ort.SessionOptions() + self.model = ort.InferenceSession(modelPath, providers=providers, sess_options=self.options) + for i in range(0,len(self.model.get_inputs())): + print("ONNX INPUTS ",self.model.get_inputs()[i].name) + self.inputName = self.model.get_inputs()[i].name + + self.model_input_name = self.model.get_inputs() + #------------------------------------------ + self.output = dict() + self.hz = 0.0 + self.targetWidth = targetWidth + self.targetHeight = targetHeight + self.trainingWidth = trainingWidth + self.trainingHeight = trainingHeight + #------------------------------------------ + + def get2DOutput(self): + return self.output + + def convertImageToMocapNETInput(self,image,doFlipX=False,threshold=0.05): + import tensorflow as tf + import numpy as np + import time + import cv2 + sourceWidth = image.shape[1] + sourceHeight = image.shape[0] + currentAspectRatio=self.targetWidth/self.targetHeight + trainedAspectRatio=self.trainingWidth/self.trainingHeight + #Do resize on OpenCV end + #---------------------------------------------------------------- + from tools import img_resizeWithCrop,normalizedCoordinatesAdaptForVerticalImage,normalizedCoordinatesAdaptToResizedCrop + imageTransformed = img_resizeWithCrop(image,self.targetWidth,self.targetHeight) + #imageTransformed = img_resizeWithPadding(image,self.targetWidth,self.targetHeight) + imageTransformed = cv2.cvtColor(imageTransformed,cv2.COLOR_BGR2RGB) + #---------------------------------------------------------------- + + #Hand image to Tensorflow + #---------------------------------------------------------------- + imageONNX = np.expand_dims(imageTransformed, axis=0) + #------------------------------------------------------------------- + + + start = time.time() + #------------------------------------------------------------------- + #------------------------------------------------------------------- + thisInputONNX = { self.inputName : imageONNX.astype('int32')} + #Run input through MocapNET + output_names_onnx = [otp.name for otp in self.model.get_outputs()] + keypoints_with_scores = self.model.run(output_names_onnx,thisInputONNX)[0][0] + predictions = keypoints_with_scores[0] + #------------------------------------------------------------------- + #------------------------------------------------------------------- + seconds = time.time() - start + self.hz = 1 / (seconds+0.0001) + #print("MoveNET ONNX Framerate : ",round(self.hz,2)," fps ") + + + currentAspectRatio=sourceWidth/sourceHeight #We "change" aspect ratio by restoring points + for pointID in range(0,len(predictions)): + #Joints have y,x,acc order + nX,nY = normalizedCoordinatesAdaptToResizedCrop(sourceWidth,sourceHeight,self.targetWidth,self.targetHeight,predictions[pointID][1],predictions[pointID][0]) + nX,nY = normalizedCoordinatesAdaptForVerticalImage(sourceWidth,sourceHeight,self.trainingWidth,self.trainingHeight,nX,nY) + predictions[pointID][1]=nX + predictions[pointID][0]=nY + + from holisticPartNames import processPoseNETLandmarks + self.output = processPoseNETLandmarks(self.jointNames,predictions,currentAspectRatio,trainedAspectRatio,threshold=threshold,doFlipX=doFlipX) + #---------------------------------------------------------------- + from MocapNETVisualization import drawPoseNETLandmarks + self.image = drawPoseNETLandmarks(predictions,image,threshold=threshold,jointLabels=self.jointNames) + #---------------------------------------------------------------- + + return self.output,image +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class MocapNETONNX(): + def __init__(self ): + print(""" + ██████╗ ███╗ ██╗███╗ ██╗██╗ ██╗ +██╔═══██╗████╗ ██║████╗ ██║╚██╗██╔╝ +██║ ██║██╔██╗ ██║██╔██╗ ██║ ╚███╔╝ +██║ ██║██║╚██╗██║██║╚██╗██║ ██╔██╗ +╚██████╔╝██║ ╚████║██║ ╚████║██╔╝ ██╗ + ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═══╝╚═╝ ╚═╝""") + self.sess_options = ort.SessionOptions() + self.sess_options.log_severity_level = 3 #<- log_level + self.sess_options.intra_op_num_threads = 4 + #self.sess_options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + self.sess_options.execution_mode = ort.ExecutionMode.ORT_PARALLEL + self.sess_options.inter_op_num_threads = 4 + self.sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +if __name__ == '__main__': + mnet = MocapNETONNX() + print("Survived Test!") +#------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/python/mnet4/MocapNETTFLite.py b/src/python/mnet4/MocapNETTFLite.py new file mode 100755 index 0000000..cb94bf3 --- /dev/null +++ b/src/python/mnet4/MocapNETTFLite.py @@ -0,0 +1,422 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import tensorflow as tf +import os +import sys +import time + +#------------------------------------------------------------------------------------------- +from readCSV import parseConfiguration,parseConfigurationInputJointMap,transformNetworkInput,initializeDecompositionForExecutionEngine,readGroundTruthFile,readCSVFile,parseOutputNormalization +from NSDM import NSDMLabels,createNSDMUsingRules,inputIsEnoughToCreateNSDM,performNSRMAlignment +from EDM import EDMLabels,createEDMUsingRules +from tools import bcolors,checkIfFileExists,readListFromFile,convertListToLowerCase,secondsToHz,getEntryIndexInList,parseSerialNumberFromSummary +#------------------------------------------------------------------------------------------- +import sys +sys.path.append("BVH") +from bvhConverter import BVH +#from BVH.bvhConverter import BVH +#------------------------------------------------------------------------------------------- +#from Smooth.smoothing import Smooth +#------------------------------------------------------------------------------------------- +from principleComponentAnalysis import PCA +#------------------------------------------------------------------------------------------- + +import numpy as np + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class MocapNETTFLiteSubProblem(): + def __init__(self, + context, + configPath:str, + modelPath:str, + partName:str, + numberOfThreads = 4, + completelyDisablePCACode = 0 + ): + #------------------------------------------------------------------------------- + self.useOutputLimits = True #Careful, this should always be on! + self.partName = partName + self.configPath = configPath + self.configuration = parseConfiguration(configPath) + self.part = self.configuration["OutputDirectory"] + self.inputName = "input_all" + self.modelPath = modelPath + self.modelDirectory= os.path.dirname(self.modelPath) + self.frameNumber = 0 + #------------------------------------------------------------------------------- + #The default compatibility setting is the BMVC2019 2channel NSDM, however nowadays we use NSRM + numberOfChannelsPerNSDMElement=2 + if (self.configuration['NSDMAlsoUseAlignmentAngles']==1): + numberOfChannelsPerNSDMElement=1 + print("Number of Channels Per NSDM element ",numberOfChannelsPerNSDMElement) + if ("eigenPoses" in self.configuration): + if (self.configuration['eigenPoses']==1): + self.configuration['eigenPoseData'] = readGroundTruthFile( + self.configuration, + "Eigenposes", + "%s/2d_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.partName ), + "%s/%s_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.configuration['outputMode'],self.partName), #configuration['outputMode'] is either bvh or 3d + 1.0, + numberOfChannelsPerNSDMElement, + 0,#useRadians, + 0,#useHalfFloats + externalDecomposition=self.decompositionEngine + ) + #------------------------------------------------------------------------------- + self.model = tf.lite.Interpreter( + model_path=self.modelPath, + num_threads=numberOfThreads + ) + + self.model.allocate_tensors() + self.input_details = self.model.get_input_details() + self.output_details = self.model.get_output_details() + + # check the type of the input tensor + self.floating_model = self.input_details[0]['dtype'] == np.float32 + #------------------------------------------------------------------------------- + self.inputsWithNSRM = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkInputs.list")) + self.inputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkJoints.list")) + self.outputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkOutputs.list")) + self.configuration = parseConfigurationInputJointMap(self.configuration,self.inputs) + self.serial = parseSerialNumberFromSummary(self.modelDirectory+"/summary.html") + #------------------------------------------------------------------------------- + self.inputReadyForTF = np.empty([2, 1]) + self.NSRM = np.empty([2, 2]) + #------------------------------------------------------------------------------- + self.outputScalars = [1.0] * len(self.outputs) + self.outputOffsets = [0.0] * len(self.outputs) + self.outputMinima = [-6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + self.outputMaxima = [6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + #------------------------------------------------------------------------------- + self.outputOffsets = parseOutputNormalization(self.modelDirectory,"/outputOffsets.csv",self.outputs,self.outputOffsets) + #self.outputScalars = parseOutputNormalization(self.modelDirectory,"/outputScalars.csv",self.outputs,self.outputScalars) + #for jointID in range(0,len(self.outputs)): + # self.outputScalars[jointID] = 1 / float(self.outputScalars[jointID]) + self.outputScalars = parseOutputNormalization(self.modelDirectory,"/outputScalarsFraction.csv",self.outputs,self.outputScalars) + self.outputMinima = parseOutputNormalization(self.modelDirectory,"/outputMinima.csv",self.outputs,self.outputMinima) + self.outputMaxima = parseOutputNormalization(self.modelDirectory,"/outputMaxima.csv",self.outputs,self.outputMaxima) + #------------------------------------------------------------------------------- + if (self.outputs[0]=="depth"): + self.outputs[0]="hip_zposition" + #------------------------------------------------------------------------------- + print("Output Mapping :") + for jointID in range(0,len(self.outputs)): + #self.outputScalars[jointID] = 1 / float(self.outputScalars[jointID]) + #print("Output ",self.outputs[jointID]," min ",self.outputMinima[jointID]," max ",self.outputMaxima[jointID]," scalar ",self.outputScalars[jointID]," offset ",self.outputOffsets[jointID]) + print("Out %s|Min %0.2f|Max %0.2f|Scalar %0.2f|Offset %0.2f"%(self.outputs[jointID],self.outputMinima[jointID],self.outputMaxima[jointID],self.outputScalars[jointID],self.outputOffsets[jointID])) + #------------------------------------------------------------------------------- + self.incompleteInput = 1 + #------------------------------------------------------------------------------- + self.simulated = False + #------------------------------------------------------------------------------- + self.output = dict() + self.outputMinimumValue = dict() + self.outputMaximumValue = dict() + #------------------------------------------------------------------------------- + self.disablePCACode = completelyDisablePCACode + if (not self.disablePCACode): + self.decompositionEngine = initializeDecompositionForExecutionEngine(self.configuration,self.modelDirectory,self.partName,disablePCACode=self.disablePCACode) + #------------------------------------------------------------------------------- + print("\n\n") + print("Model Dir :",self.modelDirectory) + print("Inputs :",self.inputs) + print("Outputs :",self.outputs) + #------------------------------------------------------------------------------- + + def getModel(self): + return self.model + + def getModelFlops(self): + print("TF-Lite has no flops calculator") + return 0 + + def getModelParameters(self): + model = self.model + #concrete_func = model.signatures["serving_default"] + #print( concrete_func.inputs[0] ) + #print( concrete_func.inputs[0].shape ) + #inputShape = str(concrete_func.inputs[0].shape) + #inputShape = inputShape.strip("() ") + #inputShape = inputShape.replace(",", "x") + #inputShape = inputShape.replace("None", "1") + #inputShape = inputShape.strip(' ') + #print("Input Shape is : ",inputShape) + #------------------------------------------ + totalParameters = 0 + try: + trainableParams = np.sum([np.prod(v.get_shape()) for v in model.trainable_weights]) + totalParameters = int(totalParameters + nonTrainableParams) + except: + print("Could not get model trainable parameters for TF-Lite model..!") + + try: + nonTrainableParams = np.sum([np.prod(v.get_shape()) for v in model.non_trainable_weights]) + totalParameters = int(totalParameters + nonTrainableParams) + except: + print("Could not get model non-trainable parameters for TF-Lite model..!") + + + return totalParameters + + + def test(self): + #------------------------------------------- + emptyList = [0.0] * len(self.inputsWithNSRM) + emptyInput =np.asarray([emptyList],dtype=np.float32) + #------------------------------------------- + #print("Running zeros ") + self.model.set_tensor(self.input_details[0]['index'],emptyInput) + self.model.invoke() + predictions = self.model.get_tensor(self.output_details[0]['index']) + #------------------------------------------- + return 1 + + def prepareInput(self,input2D :dict,configuration : dict): + from readCSV import prepareInputG + thisFullInput, self.NSRM, thisInput, angleToRotate, missingRatio = prepareInputG(input2D,configuration,self.inputs,self.inputsWithNSRM,self.part,self.decompositionEngine,self.disablePCACode) + + inputReadyForTF = np.asarray([thisFullInput],dtype=np.float32) + return inputReadyForTF,missingRatio + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def predict(self,input2D :dict): + + self.inputReadyForTF,missingRatio = self.prepareInput(input2D,self.configuration) + + if (missingRatio>0.3): + print("Not running ",self.partName," due to missing joints ") + return self.output + + + #Turns out on some decompositions like FastICA there are a lot of zeros! + #----------------------------------------------- + #Save cycles by not executing an empty data blob + #----------------------------------------------- + self.incompleteInput = 0 #<- This needs to be set to 0 to mark input is received..! + #totalData = 1 + #zeroData = 0 + #for element in self.inputReadyForTF[0].tolist(): + # #print("ELEMENT ",element) + # totalData = totalData + 1 + # if ( (element<0.005) and (element>-0.005) ): + # zeroData=zeroData + 1 + #missingRatio = zeroData/totalData + #print("Missing Ratio : ",missingRatio) + #if (missingRatio>0.4): + # print(bcolors.FAIL,"Not executing NN with empty data ",bcolors.ENDC) + # #Reset armature..! + # for k in self.output.keys(): + # self.output[k]=0.0 + # self.incompleteInput = 1 + # return self.output + #----------------------------------------------- + + self.model.set_tensor(self.input_details[0]['index'],self.inputReadyForTF) + self.model.invoke() + predictions = self.model.get_tensor(self.output_details[0]['index']) + + self.output = dict() + if (len(predictions[0])!=len(self.outputs)): + print(bcolors.FAIL,"Something bad happened.. the ",self.partName," network regressed a different number of parameters (",len(predictions[0]),") than what we expected (",len(self.outputs),") ",bcolors.ENDC) + raise IOError + return self.output + + + #Values to list.. + outputValueList = list() + + for i in range (len(self.outputs)): + outputValueList.append(float(predictions[0][i])) + + #============================================================================================================== + # THIS SHOULD BE COMMON IN TENSORFLOW/TF-LITE/ONNX + #============================================================================================================== + #Gather our numpy array output in the form of a labeled dictionary + if (self.useOutputLimits): + #Take into account output offsets/scaling + for i in range (len(self.outputs)): + #This should be the exact oposite of the operation in readCSV.py line 550 + recoveredValue = (float(outputValueList[i]) * float(self.outputScalars[i])) + float(self.outputOffsets[i]) + #--------------------------------------------------------------- + if (recoveredValue > self.outputMaxima[i]): + recoveredValue = self.outputMaxima[i] + if (recoveredValue < self.outputMinima[i]): + recoveredValue = self.outputMinima[i] + #--------------------------------------------------------------- + element = self.outputs[i] + self.output[element] = recoveredValue + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #--------------------------------------------------------------- + else: + #Not using limits + for i in range (len(self.outputs)): + element = self.outputs[i] + self.output[element] = float(outputValueList[i]) + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #============================================================================================================== + #============================================================================================================== + + self.frameNumber = self.frameNumber + 1 + return self.output + + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class PoseNETTFLite(): + def __init__( + self, + modelPath:str="movenet/lite-model_movenet_singlepose_lightning_tflite_int8_4.tflite", + targetWidth = 192, + targetHeight = 192, + numberOfThreads = 4, + trainingWidth = 1920, + trainingHeight = 1080, + ): + #Tensorflow attempt to be reasonable + #------------------------------------------ + from holisticPartNames import getPoseNETBodyNameList + self.jointNames = getPoseNETBodyNameList() + #------------------------------------------ + import tensorflow as tf + # Initialize the TFLite interpreter + self.interpreter = tf.lite.Interpreter(model_path=modelPath,num_threads=numberOfThreads) + self.interpreter.allocate_tensors() + #------------------------------------------ + self.output = dict() + self.hz = 0.0 + self.targetWidth = targetWidth + self.targetHeight = targetHeight + self.trainingWidth = trainingWidth + self.trainingHeight = trainingHeight + #------------------------------------------ + + def get2DOutput(self): + return self.output + + def convertImageToMocapNETInput(self,image,doFlipX=False,threshold=0.05): + import tensorflow as tf + import numpy as np + import time + import cv2 + sourceWidth = image.shape[1] + sourceHeight = image.shape[0] + currentAspectRatio=self.targetWidth/self.targetHeight + trainedAspectRatio=self.trainingWidth/self.trainingHeight + + #Do resize on OpenCV end + #---------------------------------------------------------------- + from tools import img_resizeWithCrop,normalizedCoordinatesAdaptForVerticalImage,normalizedCoordinatesAdaptToResizedCrop + imageTransformed = img_resizeWithCrop(image,self.targetWidth,self.targetHeight) + #imageTransformed = img_resizeWithPadding(image,self.targetWidth,self.targetHeight) + imageTransformed = cv2.cvtColor(imageTransformed,cv2.COLOR_BGR2RGB) + #---------------------------------------------------------------- + + #Prepare image for Tensorflow + #---------------------------------------------------------------- + imageTF = np.expand_dims(imageTransformed, axis=0).astype('int32') + #---------------------------------------------------------------- + + # TF Lite format expects tensor type of float32. + input_image = tf.cast(imageTF, dtype=tf.uint8) # tf.float32 + input_details = self.interpreter.get_input_details() + output_details = self.interpreter.get_output_details() + #------------------------------------------------------------------- + + + start = time.time() + #------------------------------------------------------------------- + #------------------------------------------------------------------- + self.interpreter.set_tensor(input_details[0]['index'], input_image.numpy()) + self.interpreter.invoke() + + keypoints_with_scores = self.interpreter.get_tensor(output_details[0]['index']) # Output is a [1, 1, 17, 3] numpy array. + predictions = keypoints_with_scores[0][0] + #------------------------------------------------------------------- + #------------------------------------------------------------------- + seconds = time.time() - start + self.hz = 1 / (seconds+0.0001) + #print("MoveNET TFLite Framerate : ",round(self.hz,2)," fps ") + + + currentAspectRatio=sourceWidth/sourceHeight #We "change" aspect ratio by restoring points + for pointID in range(0,len(predictions)): + #Joints have y,x,acc order + nX,nY = normalizedCoordinatesAdaptToResizedCrop(sourceWidth,sourceHeight,self.targetWidth,self.targetHeight,predictions[pointID][1],predictions[pointID][0]) + nX,nY = normalizedCoordinatesAdaptForVerticalImage(sourceWidth,sourceHeight,self.trainingWidth,self.trainingHeight,nX,nY) + predictions[pointID][1]=nX + predictions[pointID][0]=nY + + from holisticPartNames import processPoseNETLandmarks + self.output = processPoseNETLandmarks(self.jointNames,predictions,currentAspectRatio,trainedAspectRatio,threshold=threshold,doFlipX=doFlipX) + #------------------------------------------------ + from MocapNETVisualization import drawPoseNETLandmarks + self.image = drawPoseNETLandmarks(predictions,image,threshold=threshold,jointLabels=self.jointNames) + #------------------------------------------------ + + return self.output,image +#---------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------- +#---------------------------------------------------------------------------------------------------------------------------- + + + + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- + +class MocapNETTFLite(): + def __init__(self,): + print(""" +████████╗███████╗ ██╗ ██╗████████╗███████╗ +╚══██╔══╝██╔════╝ ██║ ██║╚══██╔══╝██╔════╝ + ██║ █████╗█████╗██║ ██║ ██║ █████╗ + ██║ ██╔══╝╚════╝██║ ██║ ██║ ██╔══╝ + ██║ ██║ ███████╗██║ ██║ ███████╗ + ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚══════╝""") + #------------------------------------------------------------------------------- + #do nothing :P + #------------------------------------------------------------------------------- + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +if __name__ == '__main__': + mnet = MocapNETTFLite() + print("Survived Test!") +#------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/python/mnet4/MocapNETTensorflow.py b/src/python/mnet4/MocapNETTensorflow.py new file mode 100755 index 0000000..b0c7f3e --- /dev/null +++ b/src/python/mnet4/MocapNETTensorflow.py @@ -0,0 +1,533 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +#------------------------------------------------------------------------------------------- +from readCSV import parseConfiguration,parseConfigurationInputJointMap,transformNetworkInput,initializeDecompositionForExecutionEngine,readGroundTruthFile,readCSVFile,parseOutputNormalization +from NSDM import NSDMLabels,createNSDMUsingRules,inputIsEnoughToCreateNSDM,performNSRMAlignment +from EDM import EDMLabels,createEDMUsingRules +from tools import bcolors,checkIfFileExists,readListFromFile,convertListToLowerCase,secondsToHz,getEntryIndexInList,parseSerialNumberFromSummary +#------------------------------------------------------------------------------------------- +import sys +sys.path.append("BVH") +from bvhConverter import BVH +#from BVH.bvhConverter import BVH +#------------------------------------------------------------------------------------------- +#from Smooth.smoothing import Smooth +#------------------------------------------------------------------------------------------- +from principleComponentAnalysis import PCA +#------------------------------------------------------------------------------------------- + +import time +import os +import numpy as np + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class MocapNETTensorflowSubProblem(): + def __init__(self, + context, + configPath:str, + modelPath:str, + partName:str, + device:str="/device:GPU:0", + doPerformanceProfiling = False, + tensorboard = 0, + completelyDisablePCACode = 0 + ): + #------------------------------------------------------------------------------- + self.useOutputLimits = True #Careful, this should always be on! + self.partName = partName + self.configPath = configPath + from readCSV import parseConfiguration + self.configuration = parseConfiguration(configPath) + self.part = partName#self.configuration["OutputDirectory"] + self.modelPath = modelPath + self.modelDirectory= os.path.dirname(self.modelPath) + from DNNModel import loadNewModel + self.model = loadNewModel(modelPath) + self.device = device + self.frameNumber = 0 + #------------------------------------------------------------------------------- + #The default compatibility setting is the BMVC2019 2channel NSDM, however nowadays we use NSRM + numberOfChannelsPerNSDMElement=2 + if (self.configuration['NSDMAlsoUseAlignmentAngles']==1): + numberOfChannelsPerNSDMElement=1 + print("Number of Channels Per NSDM element ",numberOfChannelsPerNSDMElement) + if ("eigenPoses" in self.configuration): + if (self.configuration['eigenPoses']==1): + self.configuration['eigenPoseData'] = readGroundTruthFile( + self.configuration, + "Eigenposes", + "%s/2d_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.partName ), + "%s/%s_%s_eigenposes.csv" % (os.path.dirname(self.modelPath),self.configuration['outputMode'],self.partName), #configuration['outputMode'] is either bvh or 3d + 1.0, + numberOfChannelsPerNSDMElement, + 0,#useRadians, + 0,#useHalfFloats + externalDecomposition=self.decompositionEngine + ) + #------------------------------------------------------------------------------- + import tensorflow as tf + rmsprop=tf.keras.optimizers.RMSprop(learning_rate=0.002, rho=0.9, epsilon=tf.keras.backend.epsilon()) + self.model.compile( + optimizer=rmsprop, + loss='mse', + metrics=['mae', 'acc'], + jit_compile=True #<- this may cause trouble on non-XLA builds? + ) + self.modelKeras = self.model + #print("MocapNET Model for ",partName," has the following signatures ",self.model.signatures) + self.model = self.model.signatures['serving_default'] + + self.profile = doPerformanceProfiling + self.tensorboard = tensorboard + #------------------------------------------------------------------------------- + self.inputsWithNSRM = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkInputs.list")) + self.inputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkJoints.list")) + self.outputs = convertListToLowerCase(readListFromFile(self.modelDirectory+"/neuralNetworkOutputs.list")) + self.configuration = parseConfigurationInputJointMap(self.configuration,self.inputs) + self.serial = parseSerialNumberFromSummary(self.modelDirectory+"/summary.html") + #------------------------------------------------------------------------------- + self.inputReadyForTF = np.empty([2, 1]) + self.NSRM = np.empty([2, 2]) + #------------------------------------------------------------------------------- + self.outputScalars = [1.0] * len(self.outputs) + self.outputOffsets = [0.0] * len(self.outputs) + self.outputMinima = [-6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + self.outputMaxima = [6000.0] * len(self.outputs) #huge limit that essentially doesn't limit anything + #------------------------------------------------------------------------------- + self.outputOffsets = parseOutputNormalization(self.modelDirectory,"/outputOffsets.csv",self.outputs,self.outputOffsets) + #self.outputScalars = parseOutputNormalization(self.modelDirectory,"/outputScalars.csv",self.outputs,self.outputScalars) + #for jointID in range(0,len(self.outputs)): + # self.outputScalars[jointID] = 1 / float(self.outputScalars[jointID]) + self.outputScalars = parseOutputNormalization(self.modelDirectory,"/outputScalarsFraction.csv",self.outputs,self.outputScalars) + self.outputMinima = parseOutputNormalization(self.modelDirectory,"/outputMinima.csv",self.outputs,self.outputMinima) + self.outputMaxima = parseOutputNormalization(self.modelDirectory,"/outputMaxima.csv",self.outputs,self.outputMaxima) + #------------------------------------------------------------------------------- + if (self.outputs[0]=="depth"): + self.outputs[0]="hip_zposition" + #------------------------------------------------------------------------------- + print("Output Mapping :") + for jointID in range(0,len(self.outputs)): + #self.outputScalars[jointID] = 1 / float(self.outputScalars[jointID]) + #print("Output ",self.outputs[jointID]," min ",self.outputMinima[jointID]," max ",self.outputMaxima[jointID]," scalar ",self.outputScalars[jointID]," offset ",self.outputOffsets[jointID]) + print("Out %s|Min %0.2f|Max %0.2f|Scalar %0.2f|Offset %0.2f"%(self.outputs[jointID],self.outputMinima[jointID],self.outputMaxima[jointID],self.outputScalars[jointID],self.outputOffsets[jointID])) + #------------------------------------------------------------------------------- + self.networkInputList = [0.0] * len(self.inputsWithNSRM) + self.networkInput = np.asarray([self.networkInputList],dtype=np.float32) + #------------------------------------------------------------------------------- + self.incompleteInput = 1 + #------------------------------------------------------------------------------- + self.simulated = False + #------------------------------------------------------------------------------- + self.output = dict() + self.outputMinimumValue = dict() + self.outputMaximumValue = dict() + #------------------------------------------------------------------------------- + self.disablePCACode = completelyDisablePCACode + if (not self.disablePCACode): + self.decompositionEngine = initializeDecompositionForExecutionEngine(self.configuration,self.modelDirectory,self.partName,disablePCACode=self.disablePCACode) + #------------------------------------------------------------------------------- + print("\n\n") + print("Inputs :",self.inputs) + print("Outputs :",self.outputs) + #------------------------------------------------------------------------------- + + def getModel(self): + return self.modelKeras + + def getModelFlops(self): + import tensorflow as tf + from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2_as_graph + model = self.modelKeras + concrete = tf.function(lambda inputs: model(inputs)) + concrete_func = concrete.get_concrete_function( + [tf.TensorSpec([1, *inputs.shape[1:]]) for inputs in model.inputs]) + frozen_func, graph_def = convert_variables_to_constants_v2_as_graph(concrete_func) + with tf.Graph().as_default() as graph: + tf.graph_util.import_graph_def(graph_def, name='') + run_meta = tf.compat.v1.RunMetadata() + opts = tf.compat.v1.profiler.ProfileOptionBuilder.float_operation() + flops = tf.compat.v1.profiler.profile(graph=graph, run_meta=run_meta, cmd="op", options=opts) + + totalFLOPS = int(flops.total_float_ops) + return totalFLOPS + return 0 + + def getModelParameters(self): + model = self.modelKeras + #------------------------------------------------------------------------------------------ + trainableParams = np.sum([np.prod(v.get_shape()) for v in model.trainable_weights]) + nonTrainableParams = np.sum([np.prod(v.get_shape()) for v in model.non_trainable_weights]) + totalParameters = int(trainableParams + nonTrainableParams) + return totalParameters + + def test(self): + #------------------------------------------- + emptyList = [0.0] * len(self.inputsWithNSRM) + emptyInput =np.asarray([emptyList],dtype=np.float32) + #------------------------------------------- + #print(bcolors.FAIL,"Running zeros ",bcolors.ENDC) + import tensorflow as tf + outputs = self.model(tf.cast(emptyInput,dtype=tf.float32)) #,training=False + #print("Outputs : ",outputs) + #print("Output Keys", outputs.keys()) + outKey = list(outputs.keys())[0] + outputsRaw = outputs[outKey] + predictions = outputsRaw + + #predictions = self.model(emptyInput,training=False) + #print("MocapNET result = ",predictions) + #------------------------------------------- + return 1 + + def prepareInput(self,input2D :dict,configuration : dict): + from readCSV import prepareInputG + thisFullInput, self.NSRM, thisInput, angleToRotate, missingRatio = prepareInputG(input2D,configuration,self.inputs,self.inputsWithNSRM,self.part,self.decompositionEngine,self.disablePCACode) + + #i=0 + #for value in thisFullInput: + # self.networkInput[i]=value + # i=i+1 + #import tensorflow as tf + #self.networkInput = tf.convert_to_tensor(self.networkInputNumpy) + self.networkInput = np.asarray([thisFullInput],dtype=np.float32) + return self.networkInput,missingRatio + #----------------------------- + #inputReadyForTF = np.asarray([thisFullInput],dtype=np.float32) + #return inputReadyForTF + + + """ + Convert a dictionary of 2D inputs to MocapNET output + (Whatever that maybe [it is listed in self.inputs and self.outputs]) + """ + def predict(self,input2D :dict): + + #This call works @ 400Hz + #-------------------------------------------------------------------------------------- + self.inputReadyForTF,missingRatio = self.prepareInput(input2D,self.configuration) + + + if (missingRatio>0.3): + print("Not running ",self.partName," due to missing joints ") + return self.output + + #Turns out on some decompositions like FastICA there are a lot of zeros! + #----------------------------------------------- + #Save cycles by not executing an empty data blob + #----------------------------------------------- + self.incompleteInput = 0 #<- This needs to be set to 0 to mark input is received..! + #totalData = 1 + #zeroData = 0 + #for element in self.inputReadyForTF[0].tolist(): + # #print("ELEMENT ",element) + # totalData = totalData + 1 + # if ( (element<0.005) and (element>-0.005) ): + # zeroData=zeroData + 1 + #missingRatio = zeroData/totalData + #print("Missing Ratio : ",missingRatio) + #if (missingRatio>0.4): + # print(bcolors.FAIL,"Not executing NN with empty data ",bcolors.ENDC) + # #Reset armature..! + # for k in self.output.keys(): + # self.output[k]=0.0 + # + # self.incompleteInput = 1 + # return self.output + #-------------------------------------------------------------------------------------- + predictions = list() + self.output = dict() + + + #-------------------------------------------------------------------------------------- + if (self.profile): + #Run input through MocapNET and Profile code (slower) + print(bcolors.WARNING,"WARNING: Profiling NN enabled, execution will be slower",bcolors.ENDC) + predictions = self.modelKeras.predict(self.inputReadyForTF,callbacks = [self.tensorboard]) + else: + #As stated in https://github.com/keras-team/keras/blob/v2.8.0/keras/engine/training.py#L1825-L2012 : + # and https://keras.io/getting_started/faq/#whats-the-difference-between-model-methods-predict-and-call + import tensorflow as tf + with tf.device(self.device): + #with tf.device('/device:CPU:0'): + inferenceOutputs = self.model(tf.cast(self.inputReadyForTF,dtype=tf.float32)) # ,training=False We shouldn't run predict to get as fast results as possible + #print("Outputs : ",outputs) + #print("Output Keys", outputs.keys()) + outKey = list(inferenceOutputs.keys())[0] + outputsRaw = inferenceOutputs[outKey] + #outputsRaw = outputs['result_all'] + predictions = outputsRaw + #-------------------------------------------------------------------------------------- + + + if (len(predictions)>0): + if(len(predictions[0])!=len(self.outputs)): + print(bcolors.FAIL,"Something bad happened.. the network regressed a different number of parameters than what we expected",bcolors.ENDC) + raise IOError + return self.output + + + #Values to list.. + outputValueList = list() + + for i in range (len(self.outputs)): + outputValueList.append(float(predictions[0][i])) + + #============================================================================================================== + # THIS SHOULD BE COMMON IN TENSORFLOW/TF-LITE/ONNX + #============================================================================================================== + #Gather our numpy array output in the form of a labeled dictionary + if (self.useOutputLimits): + #Take into account output offsets/scaling + for i in range (len(self.outputs)): + #This should be the exact oposite of the operation in readCSV.py line 550 + recoveredValue = (float(outputValueList[i]) * float(self.outputScalars[i])) + float(self.outputOffsets[i]) + #--------------------------------------------------------------- + if (recoveredValue > self.outputMaxima[i]): + recoveredValue = self.outputMaxima[i] + if (recoveredValue < self.outputMinima[i]): + recoveredValue = self.outputMinima[i] + #--------------------------------------------------------------- + element = self.outputs[i] + self.output[element] = recoveredValue + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #--------------------------------------------------------------- + else: + #Not using limits + for i in range (len(self.outputs)): + element = self.outputs[i] + self.output[element] = float(outputValueList[i]) + self.outputMinimumValue[element] = float(self.outputMinima[i]) + self.outputMaximumValue[element] = float(self.outputMaxima[i]) + #============================================================================================================== + #============================================================================================================== + + self.frameNumber = self.frameNumber + 1 + return self.output + + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +class PoseNET(): + def __init__( + self, + modelPath:str="movenet/", + targetWidth = 192, + targetHeight = 192, + trainingWidth = 1920, + trainingHeight = 1080, + ): + #Tensorflow attempt to be reasonable + #------------------------------------------ + from holisticPartNames import getPoseNETBodyNameList + self.jointNames = getPoseNETBodyNameList() + import tensorflow as tf + self.model = tf.saved_model.load(modelPath) + self.movenet = self.model.signatures['serving_default'] + #------------------------------------------ + self.output = dict() + self.hz = 0.0 + self.targetWidth = targetWidth + self.targetHeight = targetHeight + self.trainingWidth = trainingWidth + self.trainingHeight = trainingHeight + #------------------------------------------ + def get2DOutput(self): + return self.output + + def convertImageToMocapNETInput(self,image,doFlipX=False,threshold=0.05): + import tensorflow as tf + import numpy as np + import time + import cv2 + sourceWidth = image.shape[1] + sourceHeight = image.shape[0] + currentAspectRatio=self.targetWidth/self.targetHeight + trainedAspectRatio=self.trainingWidth/self.trainingHeight + #Do resize on OpenCV end + #---------------------------------------------------------------- + from tools import img_resizeWithCrop,normalizedCoordinatesAdaptForVerticalImage,normalizedCoordinatesAdaptToResizedCrop + imageTransformed = img_resizeWithCrop(image,self.targetWidth,self.targetHeight) + #imageTransformed = img_resizeWithPadding(image,self.targetWidth,self.targetHeight) + imageTransformed = cv2.cvtColor(imageTransformed,cv2.COLOR_BGR2RGB) + #---------------------------------------------------------------- + + #Prepare image for Tensorflow + #---------------------------------------------------------------- + imageTF = np.expand_dims(imageTransformed, axis=0).astype('int32') + #---------------------------------------------------------------- + + + start = time.time() + # TF Lite format expects tensor type of float32. + #------------------------------------------------------------------- + #------------------------------------------------------------------- + outputs = self.movenet(tf.cast(imageTF, dtype=tf.int32)) + keypoints_with_scores = outputs['output_0'] + predictionsRaw = keypoints_with_scores[0][0] + #------------------------------------------------------------------- + #------------------------------------------------------------------- + seconds = time.time() - start + self.hz = 1 / (seconds+0.0001) + #print("MoveNET Framerate : ",round(self.hz,2)," fps ") + + + currentAspectRatio=sourceWidth/sourceHeight #We "change" aspect ratio by restoring points + predictions=list() + for pointID in range(0,len(predictionsRaw)): + #Joints have y,x,acc order + thisPoint = list() + thisPoint.append(float(predictionsRaw[pointID][0])) #y + thisPoint.append(float(predictionsRaw[pointID][1])) #x + thisPoint.append(float(predictionsRaw[pointID][2])) #score + nX,nY = normalizedCoordinatesAdaptToResizedCrop(sourceWidth,sourceHeight,self.targetWidth,self.targetHeight,thisPoint[1],thisPoint[0]) + nX,nY = normalizedCoordinatesAdaptForVerticalImage(sourceWidth,sourceHeight,self.trainingWidth,self.trainingHeight,nX,nY) + thisPoint[0]= nY #Just update coords + thisPoint[1]= nX #Just update coords + predictions.append(thisPoint) + #------------------------------------------------------------------- + + from holisticPartNames import processPoseNETLandmarks + self.output = processPoseNETLandmarks(self.jointNames,predictions,currentAspectRatio,trainedAspectRatio,threshold=threshold,doFlipX=doFlipX) + + from MocapNETVisualization import drawPoseNETLandmarks + self.image = drawPoseNETLandmarks(predictions,image,threshold=threshold,jointLabels=self.jointNames) + #------------------------------------------------ + + return self.output,image +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- + +def get_available_devices(): + from tensorflow.python.client import device_lib + local_device_protos = device_lib.list_local_devices() + return [x.name for x in local_device_protos if x.device_type == 'GPU' or x.device_type == 'CPU'] + +class MocapNETTensorflow(): + def __init__(self, + doPerformanceProfiling = False, + ): + print(""" +████████╗███████╗███╗ ██╗███████╗ ██████╗ ██████╗ ███████╗██╗ ██████╗ ██╗ ██╗ +╚══██╔══╝██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝██║ ██╔═══██╗██║ ██║ + ██║ █████╗ ██╔██╗ ██║███████╗██║ ██║██████╔╝█████╗ ██║ ██║ ██║██║ █╗ ██║ + ██║ ██╔══╝ ██║╚██╗██║╚════██║██║ ██║██╔══██╗██╔══╝ ██║ ██║ ██║██║███╗██║ + ██║ ███████╗██║ ╚████║███████║╚██████╔╝██║ ██║██║ ███████╗╚██████╔╝╚███╔███╔╝ + ╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝""") + self.doPerformanceProfiling = doPerformanceProfiling + + #Tensorflow attempt to be reasonable + #------------------------------------------ + import gc + gc.collect() #Do garbage collection before allocating TF stuff + import os + os.environ['TF_ENABLE_GPU_GARBAGE_COLLECTION']='false' + #Make sure CUDA cache is not disabled! + os.environ['CUDA_CACHE_DISABLE'] = '0' + #Try to presist cudnn + os.environ['TF_USE_CUDNN_BATCHNORM_SPATIAL_PERSISTENT'] = '1' + #Try to allocate as little memory as possible + os.environ['TF_FORCE_GPU_ALLOW_GROWTH'] = 'true' + #Use seperate threads so execution is not throttled by CPU + os.environ['TF_GPU_THREAD_MODE'] = 'gpu_private' + #0 = all messages are logged (default behavior) + #1 = INFO messages are not printed + #2 = INFO and WARNING messages are not printed + #3 = INFO, WARNING, and ERROR messages are not printed + os.environ['TF_CPP_MIN_LOG_LEVEL'] = '0' + #improve the stability of the auto-tuning process used to select the fastest convolution algorithms + os.environ['TF_AUTOTUNE_THRESHOLD'] = '1' + #------------------------------------------ + import tensorflow as tf + devices = get_available_devices() + print("Available Tensorflow devices are : ",devices) + self.device = '/device:CPU:0' + for device in devices: + if (device.find("GPU")!=-1): + self.device = device + print("Selecting device : ",self.device) + + #If enabled, an op will be placed on CPU if any of the following are true + #1 - there's no GPU implementation for the OP + #2 - no GPU devices are known or registered + #3 - need to co-locate with reftype input(s) which are from CPU + tf.config.set_soft_device_placement(True) + + #Only give the warning when not profiling otherwise we will get an error! + if (not doPerformanceProfiling): + tf.config.experimental.set_device_policy('explicit') + + try: + tf.config.run_functions_eagerly(True) + tf.config.experimental.set_synchronous_execution(False) + except: + #Invalid device or cannot modify virtual devices once initialized. + pass + + try: + physical_devices = tf.config.list_physical_devices('CPU') + tf.config.experimental.set_memory_growth(physical_devices[0], True) + physical_devices = tf.config.list_physical_devices('GPU') + tf.config.experimental.set_memory_growth(physical_devices[0], True) + except: + #Invalid device or cannot modify virtual devices once initialized. + pass + + try: + tf.config.threading.set_intra_op_parallelism_threads(8) + tf.config.threading.set_inter_op_parallelism_threads(8) + except: + #Most probably : RuntimeError: Intra op parallelism cannot be modified after initialization + pass + + if (doPerformanceProfiling): + import tensorflow as tf + self.tensorboard = tf.keras.callbacks.TensorBoard(log_dir = "profiling",histogram_freq = 1) + #tensorboard --bind_all --logdir profiling + from DNNModel import startProfiling + startProfiling() + else: + self.tensorboard=0 + + + def __del__(self): + if (self.doPerformanceProfiling): + from DNNModel import stopProfiling + stopProfiling() + print("To see profile results \nUse :\n tensorboard --logdir profiling ") + print('TFLite stopped.') + +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +#------------------------------------------------------------------------------------------------------------------------------------------------------- +if __name__ == '__main__': + mnet = MocapNETTensorflow() + print("Survived Test!") +#------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/python/mnet4/MocapNETVisualization.py b/src/python/mnet4/MocapNETVisualization.py new file mode 100755 index 0000000..cd0f947 --- /dev/null +++ b/src/python/mnet4/MocapNETVisualization.py @@ -0,0 +1,1017 @@ +#!/usr/bin/python3 +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +def getColor(i): + if i>107: + i = i % 108 + #--------------------- + if (i==0): + return (247,252,253) + elif (i==1): + return (224,236,244) + elif (i==2): + return (191,211,230) + elif (i==3): + return (158,188,218) + elif (i==4): + return (140,150,198) + elif (i==5): + return (140,107,177) + elif (i==6): + return (136,65,157) + elif (i==7): + return (129,15,124) + elif (i==8): + return (77,0,75) + elif (i==9): + return (247,252,253) + elif (i==10): + return (229,245,249) + elif (i==11): + return (204,236,230) + elif (i==12): + return (153,216,201) + elif (i==13): + return (102,194,164) + elif (i==14): + return (65,174,118) + elif (i==15): + return (35,139,69) + elif (i==16): + return (0,109,44) + elif (i==17): + return (0,68,27) + elif (i==18): + return (247,252,240) + elif (i==19): + return (224,243,219) + elif (i==20): + return (204,235,197) + elif (i==21): + return (168,221,181) + elif (i==22): + return (123,204,196) + elif (i==23): + return (78,179,211) + elif (i==24): + return (43,140,190) + elif (i==25): + return (8,104,172) + elif (i==26): + return (8,64,129) + elif (i==27): + return (255,247,236) + elif (i==28): + return (254,232,200) + elif (i==29): + return (253,212,158) + elif (i==30): + return (253,187,132) + elif (i==31): + return (252,141,89) + elif (i==32): + return (239,101,72) + elif (i==33): + return (215,48,31) + elif (i==34): + return (179,0,0) + elif (i==35): + return (127,0,0) + elif (i==36): + return (255,247,251) + elif (i==37): + return (236,231,242) + elif (i==38): + return (208,209,230) + elif (i==39): + return (166,189,219) + elif (i==40): + return (116,169,207) + elif (i==41): + return (54,144,192) + elif (i==42): + return (5,112,176) + elif (i==43): + return (4,90,141) + elif (i==44): + return (2,56,88) + elif (i==45): + return (255,247,251) + elif (i==46): + return (236,226,240) + elif (i==47): + return (208,209,230) + elif (i==48): + return (166,189,219) + elif (i==49): + return (103,169,207) + elif (i==50): + return (54,144,192) + elif (i==51): + return (2,129,138) + elif (i==52): + return (1,108,89) + elif (i==53): + return (1,70,54) + elif (i==54): + return (247,244,249) + elif (i==55): + return (231,225,239) + elif (i==56): + return (212,185,218) + elif (i==57): + return (201,148,199) + elif (i==58): + return (223,101,176) + elif (i==59): + return (231,41,138) + elif (i==60): + return (206,18,86) + elif (i==61): + return (152,0,67) + elif (i==62): + return (103,0,31) + elif (i==63): + return (255,247,243) + elif (i==64): + return (253,224,221) + elif (i==65): + return (252,197,192) + elif (i==66): + return (250,159,181) + elif (i==67): + return (247,104,161) + elif (i==68): + return (221,52,151) + elif (i==69): + return (174,1,126) + elif (i==70): + return (122,1,119) + elif (i==71): + return (73,0,106) + elif (i==72): + return (255,255,229) + elif (i==73): + return (247,252,185) + elif (i==74): + return (217,240,163) + elif (i==75): + return (173,221,142) + elif (i==76): + return (120,198,121) + elif (i==77): + return (65,171,93) + elif (i==78): + return (35,132,67) + elif (i==79): + return (0,104,55) + elif (i==80): + return (0,69,41) + elif (i==81): + return (255,255,217) + elif (i==82): + return (237,248,177) + elif (i==83): + return (199,233,180) + elif (i==84): + return (127,205,187) + elif (i==85): + return (65,182,196) + elif (i==86): + return (29,145,192) + elif (i==87): + return (34,94,168) + elif (i==88): + return (37,52,148) + elif (i==89): + return (8,29,88) + elif (i==90): + return (255,255,229) + elif (i==91): + return (255,247,188) + elif (i==92): + return (254,227,145) + elif (i==93): + return (254,196,79) + elif (i==94): + return (254,153,41) + elif (i==95): + return (236,112,20) + elif (i==96): + return (204,76,2) + elif (i==97): + return (153,52,4) + elif (i==98): + return (102,37,6) + elif (i==99): + return (255,255,204) + elif (i==100): + return (255,237,160) + elif (i==101): + return (254,217,118) + elif (i==102): + return (254,178,76) + elif (i==103): + return (253,141,60) + elif (i==104): + return (252,78,42) + elif (i==105): + return (227,26,28) + elif (i==106): + return (189,0,38) + elif (i==107): + return (128,0,38) + + return (255,255,255) + +def drawMissingInput(image): + import cv2 + width = image.shape[1] + height = image.shape[0] + color = (0,0,255) + cv2.line(image, pt1=(0,0), pt2=(width,height), color=color, thickness=12) + cv2.line(image, pt1=(0,0+height), pt2=(width,0), color=color, thickness=12) + font = cv2.FONT_HERSHEY_SIMPLEX + org = (int(width/2)-300,int(height/2)) + fontScale = 2 + color = (0,0,0) + thickness = 2 + message = 'Incomplete Input' + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (int(width/2)+2-300,int(height/2)+2) + color = (255,255,255) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + return image + + + +def drawPoseNETLandmarks(predictions,image,threshold=0.25,jointLabels=dict()): + import cv2 + sourceWidth = image.shape[1] + sourceHeight = image.shape[0] + width = image.shape[1] + height = image.shape[0] + #jointLabels = getPoseNETBodyNameList() # getBody25NameList() + jID = 0 + for joint in predictions: + #print("Joint ",joint) + y2D = int(joint[0]*sourceHeight) + x2D = int(joint[1]*sourceWidth) + vis2D = float(joint[2]) + color=(0,255,255) + if (threshold>vis2D): + color=(0,0,255) + + cv2.circle(image,(x2D,y2D),2,color) + + font = cv2.FONT_HERSHEY_SIMPLEX + org = (x2D,y2D) + fontScale = 0.4 + thickness = 1 + message = '%s|%0.4f' % (jointLabels[jID],vis2D) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + jID += 1 + return image + + + +def resolveXY(input2D,joint,width,height,flipX=False): + x2D=0 + y2D=0 + jointName2DX = "2dx_"+joint + jointName2DY = "2dy_"+joint + if ( jointName2DX in input2D ) and ( jointName2DY in input2D ): + if (flipX): + x2D = int((1.0-input2D[jointName2DX])*width) + else: + x2D = int(input2D[jointName2DX]*width) + y2D = int(input2D[jointName2DY]*height) + else: + print("Cannot resolve ",joint) + #print(joint," resolved to ",x2D,",",y2D) + return x2D,y2D + + + +def drawMocapNETInput(input2D,image,flipX=False,doLines=True): + import cv2 + if (type(image)==type(None)): + print("Invalid Image given, can't do anything with it") + return image + width = image.shape[1] + height = image.shape[0] + #print("Drawing output to ",width,"x",height," cvmat") + + if (doLines): + #Draw lines + #==================================================================== + t=8 + x1,y1=resolveXY(input2D,"rshoulder",width,height,flipX=flipX) + x2,y2=resolveXY(input2D,"relbow",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x1,y1=resolveXY(input2D,"rhand",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x1,y1=resolveXY(input2D,"rshoulder",width,height,flipX=flipX) + x2,y2=resolveXY(input2D,"neck",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x2,y2=resolveXY(input2D,"hip",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x1,y1=resolveXY(input2D,"rhip",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x2,y2=resolveXY(input2D,"rknee",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + x1,y1=resolveXY(input2D,"rfoot",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,0), thickness=t) + + x1,y1=resolveXY(input2D,"lshoulder",width,height,flipX=flipX) + x2,y2=resolveXY(input2D,"lelbow",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x1,y1=resolveXY(input2D,"lhand",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x1,y1=resolveXY(input2D,"lshoulder",width,height,flipX=flipX) + x2,y2=resolveXY(input2D,"neck",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x2,y2=resolveXY(input2D,"hip",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x1,y1=resolveXY(input2D,"lhip",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x2,y2=resolveXY(input2D,"lknee",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + x1,y1=resolveXY(input2D,"lfoot",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,0,255), thickness=t) + + x1,y1=resolveXY(input2D,"neck",width,height,flipX=flipX) + x2,y2=resolveXY(input2D,"head",width,height,flipX=flipX) + cv2.line(image, pt1=(x1,y1), pt2=(x2,y2), color=(0,255,255), thickness=t) + #==================================================================== + + + font = cv2.FONT_HERSHEY_SIMPLEX + fontScale = 0.4 + thickness = 1 + color = (0,0,0) + + for jointRaw in input2D: + #print("Joint ",jointRaw) + jointSplit = jointRaw.lower().split("_",1) + if (len(jointSplit)>1): + joint = jointSplit[1].lower() + jointName2DX = "2dx_"+joint + jointName2DY = "2dy_"+joint + if ( jointName2DX in input2D ) and ( jointName2DY in input2D ): + if (flipX): + x2D = int((1.0-input2D[jointName2DX])*width) + else: + x2D = int(input2D[jointName2DX]*width) + y2D = int(input2D[jointName2DY]*height) + #print("IS Joint ",joint,x2D,y2D) + color=(0,255,255) + circleSize = 2 + if (len(joint)>0): + #We have a joint Name + if not 'head' in joint: + circleSize = 4 #body joints are bigger + + + if (len(joint)>1): + if (joint[len(joint)-2]=='.') and (joint[len(joint)-1]=='r'): #Right Joint + color=(0,255,0) #GREEN COLOR + if (joint[len(joint)-2]=='.') and (joint[len(joint)-1]=='l'): #Left Joint + color=(0,0,255) #RED COLOR + + if (joint[0]=='r'): #Right Joint + color=(0,255,0) #GREEN COLOR + elif (joint[0]=='l'): #Left Joint + color=(0,0,255) #RED COLOR + elif ("head_l" in joint): + color=(0,0,255) #RED COLOR + #image = cv2.putText(image, "%s" % (joint.replace("head_","")) , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + elif ("head_r" in joint): + color=(0,255,0) #GREEN COLOR + #image = cv2.putText(image, "%s" % (joint.replace("head_","")) , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + #else: + # image = cv2.putText(image, "%s" % (joint) , (x2D+2,y2D), font, fontScale+0.7, color, thickness, cv2.LINE_AA) + cv2.circle(image,(x2D,y2D),circleSize,color,cv2.FILLED) + #if ("lshoulder"==joint) or ("lelbow"==joint) or ("lhand"==joint): + # image = cv2.putText(image, "%s" % (joint) , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + if ("head_reye"==joint) or ("head_leye"==joint) or ("reye"==joint) or ("leye"==joint): + color=(255,0,0) + circleSize = 4 + image = cv2.putText(image, "%s" % (joint) , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + cv2.circle(image,(x2D,y2D),circleSize,color,cv2.FILLED) + + #Post Visualization score + #jointNameVis = "visible_"+joint + #image = cv2.putText(image, "%0.2f" % (input2D[jointNameVis]) , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + + #if ('__' in joint): #Print __temporalis joint + # image = cv2.putText(image, joint , (x2D+2,y2D), font, fontScale, color, thickness, cv2.LINE_AA) + return image + +def drawMocapNETOutput(mnet,image,xOffset=0): #set xOffset to -400 to make visualization more clean by seperating 2D/3D + import cv2 + if (type(image)==type(None)): + print("Invalid Image given, can't do anything with it") + return image + width = image.shape[1] + height = image.shape[0] + #print("Drawing output to ",width,"x",height," cvmat") + + jointID = 0 + for joint in mnet.bvhJointList: + #---------------------------------------------------------------------------------------------- + jointParentID = mnet.bvhJointParentList[jointID] + jointParent = mnet.bvhJointList[jointParentID] + #print("Joint ",joint) + #print("Joint Parent",jointParent) + + #Enforce Joint LowerCase + joint = joint.lower() + jointParent = mnet.bvhJointList[jointParentID].lower() + + doThisDraw = 1 + #---------------------------------------------------------------------------------------------- + jointName2DX = "2DX_"+joint + jointName2DY = "2DY_"+joint + if ( not jointName2DX in mnet.output2D ) or ( not jointName2DY in mnet.output2D ): + doThisDraw = 0 + elif (mnet.output2D[jointName2DX]==0.0) and (mnet.output2D[jointName2DY]==0.0): + doThisDraw = 0 + else: + xA2D = xOffset + int((1.0-mnet.output2D[jointName2DX])*width) + yA2D = int(mnet.output2D[jointName2DY]*height) + #---------------------------------------------------------------------------------------------- + jointParentName2DX = "2DX_"+jointParent + jointParentName2DY = "2DY_"+jointParent + if ( not jointParentName2DX in mnet.output2D ) or ( not jointParentName2DY in mnet.output2D ): + doThisDraw = 0 + elif (mnet.output2D[jointParentName2DX]==0.0) and (mnet.output2D[jointParentName2DY]==0.0): + doThisDraw = 0 + else: + xB2D = xOffset + int((1.0-mnet.output2D[jointParentName2DX])*width) + yB2D = int(mnet.output2D[jointParentName2DY]*height) + #---------------------------------------------------------------------------------------------- + if (doThisDraw): + color=(255,0,0) #BLUE COLOR + if (joint[0]=='l'): + color=(0,0,255) #RED COLOR + if (joint[0]=='r'): + color=(0,255,0) #GREEN COLOR + cv2.line(image, pt1=(xA2D,yA2D), pt2=(xB2D,yB2D), color=color, thickness=12) + #---------------------------------------------------------------------------------------------- + jointID = jointID + 1 + #---------------------------------------------------------------------------------------------- + + for joint in mnet.bvhJointList: + #print("Joint ",joint) + joint = joint.lower() + jointName2DX = "2DX_"+joint + jointName2DY = "2DY_"+joint + if ( jointName2DX in mnet.output2D ) and ( jointName2DY in mnet.output2D ): + if (mnet.output2D[jointName2DX]!=0.0) or (mnet.output2D[jointName2DY]!=0.0): + x2D = xOffset + int((1.0-mnet.output2D["2DX_"+joint])*width) + y2D = int(mnet.output2D["2DY_"+joint]*height) + color=(0,255,255) + cv2.circle(image,(x2D,y2D),2,color) + #---------------------------------------------------------------------------------------------- + return image + + + + +def drawDescriptor(name,elements,image,x,y,w,h): + #------------------------------------ + if (elements.shape[1]==0): + return image + #------------------------------------ + import cv2 + block = int(w / elements.shape[1]) + #------------------------------------ + if (block==0): + return image + #------------------------------------ + #print("WIDTH ",w," BLOCK",block," ELEMENTS ",elements.shape[1]) + eI = 0 + for xI in range(x,x+w-block,block): + xA2D=xI + yA2D=y + xB2D=xI+block + yB2D=y+h + #---------------------------------------------- + val = elements[0][eI] + #---------------------------------------------- + greenValue = 0.0 + blueValue = 0.0 + #---------------------------------------------- + if (val<0.0): + blueValue=abs(val) + else: + greenValue=val + #---------------------------------------------- + color=( + min(255,int(255.0 * blueValue)), + min(255,int(255.0 * greenValue)), + min(255,int(25.5 * greenValue)) + ) + #---------------------------------------------- + if (xA2D!=0.0) and (yA2D!=0.0) and (xB2D!=0.0) and (yB2D!=0.0): + cv2.line(image, pt1=(xA2D,yA2D), pt2=(xB2D,yB2D), color=color, thickness=12) + eI +=1 + eI = min(eI,elements.shape[1]-1) + #---------------------------------------------- + font = cv2.FONT_HERSHEY_SIMPLEX + fontScale = 0.5 + thickness = 1 + org = (x+10,y+int(h/2)+5) + color = (0,0,0) + image = cv2.putText(image, name , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (x+8,y+int(h/2)+3) + color = (255,255,255) + image = cv2.putText(image, name , org, font, fontScale, color, thickness, cv2.LINE_AA) + + return image + + + +def printNSDM(nsdm): + import math + from tools import bcolors + NSRMDimension = int(math.sqrt(len(nsdm))) + eI = 0 + for yI in range(0,NSRMDimension): + for xI in range(0,NSRMDimension): + #------------------------- + val = nsdm[eI] + eI +=1 + if (val==0.0): + print(bcolors.FAIL,end=" ") + elif (val<0.0): + print(bcolors.OKBLUE,end="") + else: + print(bcolors.OKGREEN,end=" ") + print("%0.2f " % val,end="") + print(bcolors.ENDC,end="") + print(" ") + + +def drawNSRM(name,elements,image,x,y,w,h): + import cv2 + import math + #print("Draw NSRM with ",len(elements)," elements ") + NSRMDimension = int(math.sqrt(len(elements))) + blockX = int(w/NSRMDimension) + blockY = int(h/NSRMDimension) + + #print("WIDTH ",w," BLOCK",block," ELEMENTS ",elements.shape[1]) + if (NSRMDimension<4): + print("drawNSRM not drawing matrix with len(elements) = ",len(elements)) + return image + + eI = 0 + for yI in range(0,NSRMDimension): + for xI in range(0,NSRMDimension): + xA2D=x + xI*blockX + yA2D=y + yI*blockY + xB2D=xA2D+blockX + yB2D=yA2D+blockY + #------------------------- + val = elements[eI] + eI +=1 + #------------------------- + redValue = 0 + greenValue = 0 + blueValue = 0 + #------------------------- + if (val==0.0): + redValue = 1 + elif (val<0.0): + blueValue = abs(val)#/2 + else: + greenValue = val#/2 + #------------------------- + color=( + int(255.0 * blueValue), #B + int(255.0 * greenValue), #G + int(255.0 * redValue) #R + ) + #------------------------- + cv2.rectangle(image, pt1=(xA2D,yA2D), pt2=(xB2D,yB2D), color=color, thickness=-1) + #----------------------------------- + font = cv2.FONT_HERSHEY_SIMPLEX + fontScale = 0.5 + thickness = 1 + org = (x,y-10) + color = (0,0,0) + image = cv2.putText(image, name , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (x-2,y-8) + color = (255,255,255) + image = cv2.putText(image, name , org, font, fontScale, color, thickness, cv2.LINE_AA) + + return image + + + + +def drawMAE2DError(name,mae,image,x,y,w,h): + if (mae<=0.0): + return + import cv2 + #----------------------------------------- + color = (123,123,123) + if (mae<127): + color = (0,255-(mae*2),0) #B G R + elif (mae<255): + color = (0,mae,mae) #B G R + else: + color = (0,0,min(255,mae-255)) #B G R + #----------------------------------------- + xA2D=x + yA2D=y + xB2D=x+w + yB2D=y+h + cv2.rectangle(image, pt1=(xA2D,yA2D), pt2=(xB2D,yB2D), color=color, thickness=-1) + #----------------------------------------- + font = cv2.FONT_HERSHEY_SIMPLEX + fontScale = 0.4 + thickness = 1 + #----------------------------------------- + yOffset=15 + message = '%s ' % (name) + org = (x+2,y+2+yOffset) + color = (0,0,0) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (x,y+yOffset) + color = (255,255,255) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + #----------------------------------------- + message = '%0.2f' % (mae) + color = (0,0,0) + org = (x,y+20+yOffset) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + color = (255,255,255) + org = (x+2,y+22+yOffset) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + #----------------------------------------- + + +def calculateRelativeValue(y,h,value,minimum,maximum): + if (maximum==minimum): + return int(y + (h/2)) + #------------------------------------------------- + #TODO IMPROVE THIS! + vRange = (maximum - minimum) + return int( y + (h/2) - ( value / vRange ) * (h/2) ) + + +def drawMocapNETSinglePlot(history,plotNumber,itemName,image,x,y,w,h,minimumValue,maximumValue): + import cv2 + color=getColor(plotNumber) + if (minimumValue==maximumValue): + color = (40,40,40) + + cv2.line(image, pt1=(x,y+h), pt2=(x+w,y+h), color=color, thickness=1) + cv2.line(image, pt1=(x,y), pt2=(x,y+h), color=color, thickness=1) + + font = cv2.FONT_HERSHEY_SIMPLEX + org = (x,y) + fontScale = 0.3 + tColor = (123,123,123) + thickness = 1 + message = '%s #%u ' % (itemName,plotNumber) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + message = 'Max %0.2f ' % (maximumValue) + org = (x,y+10) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + message = 'Min %0.2f ' % (minimumValue) + org = (x,y+h+10) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + + + for frameID in range(1,len(history)): + #Old code + #previousValue = int(y+history[frameID-1][itemName] + h/2) + #nextValue = int(y+history[frameID][itemName] + h/2) + #------------------------------------------------------------------------------------- + previousValue = calculateRelativeValue(y,h,history[frameID-1][itemName],minimumValue,maximumValue) + nextValue = calculateRelativeValue(y,h,history[frameID][itemName],minimumValue,maximumValue) + #------------------------------------------------------------------------------------- + jointPointPrev = (int(x+ frameID-1), previousValue ) + jointPointNext = (int(x+ frameID), nextValue ) + #cv::Scalar usedColor = getColorFromIndex(joint); + if (itemName=="hip_yrotation"): + color=(0,0,255) + + cv2.line(image, pt1=jointPointPrev, pt2=jointPointNext, color=color, thickness=1) + + #old code + #org = (int(x+len(history)),int(y+history[len(history)-1][itemName] + h/2)) + org = (int(x+len(history)), calculateRelativeValue(y,h,history[len(history)-1][itemName],minimumValue,maximumValue) ) + message = '%0.2f' % (history[len(history)-1][itemName]) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) +#--------------------------------------------------------------------------------------------- + + +def drawMocapNETSinglePlotValueList(valueList,plotNumber,itemName,image,x,y,w,h,minimumValue,maximumValue): + import cv2 + import numpy as np + color=getColor(plotNumber) + if (minimumValue==maximumValue): + color = (40,40,40) #Dead plot + + listMaxValue = np.max(valueList) + if (listMaxValue>maximumValue): + maximumValue=listMaxValue*2 #Adapt to maximum + + #------------------------------------------------------------------ + cv2.line(image, pt1=(x,y+h), pt2=(x+w,y+h), color=color, thickness=1) + cv2.line(image, pt1=(x,y), pt2=(x,y+h), color=color, thickness=1) + + font = cv2.FONT_HERSHEY_SIMPLEX + org = (x,y) + fontScale = 0.3 + tColor = (123,123,123) + thickness = 1 + message = '%s #%u ' % (itemName,plotNumber) + image = cv2.putText(image, message , (x-1,y-1), font, fontScale, (0,0,0) , thickness, cv2.LINE_AA) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + message = 'Max %0.2f ' % (maximumValue) + org = (x,y+10) + image = cv2.putText(image, message , (x-1,y+10-1), font, fontScale, (0,0,0) , thickness, cv2.LINE_AA) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + message = 'Min %0.2f ' % (minimumValue) + org = (x,y+h+10) + image = cv2.putText(image, message , (x-1,y+h+10-1), font, fontScale, (0,0,0) , thickness, cv2.LINE_AA) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + + + for frameID in range(1,len(valueList)): + #Old code + #previousValue = int(y+history[frameID-1][itemName] + h/2) + #nextValue = int(y+history[frameID][itemName] + h/2) + #------------------------------------------------------------------------------------- + previousValue = calculateRelativeValue(y,h,valueList[frameID-1],minimumValue,maximumValue) + nextValue = calculateRelativeValue(y,h,valueList[frameID],minimumValue,maximumValue) + #------------------------------------------------------------------------------------- + jointPointPrev = (int(x+ frameID-1), previousValue ) + jointPointNext = (int(x+ frameID), nextValue ) + #cv::Scalar usedColor = getColorFromIndex(joint); + if (itemName=="hip_yrotation"): + color=(0,0,255) + + cv2.line(image, pt1=jointPointPrev, pt2=jointPointNext, color=color, thickness=1) + + #old code + #org = (int(x+len(valueList)),int(y+valueList[len(valueList)-1] + h/2)) + org = (int(x+len(valueList)), calculateRelativeValue(y,h,valueList[len(valueList)-1],minimumValue,maximumValue) ) + message = '%0.2f' % (valueList[len(valueList)-1]) + image = cv2.putText(image, message , org, font, fontScale, (0,0,0), thickness, cv2.LINE_AA) + + org = (1+int(x+len(valueList)), 1+calculateRelativeValue(y,h,valueList[len(valueList)-1],minimumValue,maximumValue) ) + image = cv2.putText(image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + +#--------------------------------------------------------------------------------------------- + + + +def drawMocapNETAllPlots(history,width,height,minimumLimits=dict(),maximumLimits=dict()): + import cv2 + import numpy as np +#------------------------------------------------- + imageForPlot = np.zeros([height,width,3],dtype=np.uint8) +#------------------------------------------------- + margin = 25 + x = 0 + y = margin + widthOfGraphs = 80 + heightOfGraphs = 80 +#------------------------------------------------- + plotNumber = 0 + if (len(history)>0): + for itemName in history[0].keys(): + minimumValue=-180.0 + maximumValue= 180.0 + if (itemName in minimumLimits) and (itemName in maximumLimits): + minimumValue=float(minimumLimits[itemName]) + maximumValue=float(maximumLimits[itemName]) + + if (minimumValue!=maximumValue): + drawMocapNETSinglePlot(history,plotNumber,itemName,imageForPlot,x,y,widthOfGraphs,heightOfGraphs,minimumValue,maximumValue) + plotNumber=plotNumber+1 + y = y + heightOfGraphs + margin + if (y + heightOfGraphs > height - heightOfGraphs): + y = margin + x = x + widthOfGraphs + margin + #cv2.imshow('Motion History',imageForPlot) + return imageForPlot +#------------------------------------------------- + + + +def drawMocapNETFrequencyPlots(history): + import numpy as np + import matplotlib.pyplot as plt +#------------------------------------------------- + if (len(history)>0): + for itemName in history[0].keys(): + output="freq_%s.png" % itemName + + data=list() + for frameID in range(1,len(history)): + data.append(float(history[frameID][itemName])) + plt.cla() + plt.hist(data, bins=250) + # Add labels and title + plt.xlabel('Value') + plt.ylabel('Frequency') + plt.title('Histogram of %s of %u values'%(itemName,len(history))) + # Save figure as PNG file + plt.savefig(output) + #fig.savefig(output) +#------------------------------------------------- + + + +def drawValueLineInRange(history, minimumValues, maximumValues, label, result_img, x, y, w, h): + # Calculate the position of the value within the specified range + + if (h>20) and (len(history)>0): + h = h-20 + lastItem = len(history)-1 + if (label in history[lastItem]) and (label in minimumValues) and (label in maximumValues) : + minimumValue = minimumValues[label] + maximumValue = maximumValues[label] + import cv2 + #print("Items to select ",history[lastItem].keys()) + #print("Items to select ",len(history[lastItem])) + value = history[lastItem][label] + #print("DRAW ",value," between ",minimumValue," and ",maximumValue) + normalized_value = (value - minimumValue) / (maximumValue - minimumValue) + x_pos = int(x + normalized_value * w) + + # Draw a horizontal line to represent the value + #print("cv2.line") + color = (255,255,255) + colorB = (123,123,123) + + cv2.putText(result_img, label, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) + cv2.putText(result_img, "%0.2f"%value, (x, y + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) + + cv2.line(result_img, (x, y+h), (x+w,y+h), colorB, 2) #Horizontal line + cv2.line(result_img, (x, y+5), (x,y+h), colorB, 2) #Vertical line start + cv2.line(result_img, (x+w,y+5), (x+w,y+h), colorB, 2) #Vertical line end + + #Draw labels l/r + cv2.putText(result_img, "%0.1f"% minimumValue , (x,y+h+20), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1) + cv2.putText(result_img, "%0.1f"% maximumValue , (x+w,y+h+20), cv2.FONT_HERSHEY_SIMPLEX, 0.3, color, 1) + + #Draw Arrow + cv2.line(result_img, (x_pos, y), (x_pos, y + h), color, 2) + cv2.line(result_img, (x_pos-10, y+h-10), (x_pos, y + h), color, 2) + cv2.line(result_img, (x_pos+10, y+h-10), (x_pos, y + h), color, 2) + else: + print("Failed visualizing ",label," in range [",minimumValue,",",maximumValue,"]") + + + +def drawMNETSerials(mnet,image,x,y): + import cv2 + #----------------------------------------- + font = cv2.FONT_HERSHEY_SIMPLEX + fontScale = 0.4 + thickness = 1 + #----------------------------------------- + #print("MNET Serials ",mnet.getEnsembleSerials()) + #----------------------------------------- + color = (0,0,0) + org = (x+2,y+2) + image = cv2.putText(image, mnet.getEnsembleSerials() , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (x,y) + color = (255,255,255) + image = cv2.putText(image, mnet.getEnsembleSerials() , org, font, fontScale, color, thickness, cv2.LINE_AA) + #----------------------------------------- + + +def registerVisualizationTime(mnet,startTime): + import time + end = time.time() # Time elapsed + from tools import secondsToHz + mnet.hz_Vis = secondsToHz(end - startTime) + mnet.history_hz_Vis.append(mnet.hz_Vis) + if (len(mnet.history_hz_Vis)>mnet.perfHistorySize): + mnet.history_hz_Vis.pop(0) #Keep mnet history on limits + + +def visualizeMocapNETEnsemble(mnet,annotated_image,plotBVHChannels=0,bvhAnglesForPlotting=list(),economic=False,drawOutput=True): + import time + start = time.time() # Time elapsed + try: + #from MocapNETVisualization import drawMocapNETOutput,drawMocapNETAllPlots,drawMissingInput,drawDescriptor,drawNSRM,drawMAE2DError + #------------------------------------------------------------------------------------ + if (drawOutput): + if ("upperbody" in mnet.ensemble): + drawMocapNETOutput(mnet,annotated_image) #only draw 3D ouput if upperbody is loaded and working.. + + drawMocapNETInput(mnet.input2D,annotated_image,doLines=(drawOutput==False)) + if (economic): + registerVisualizationTime(mnet,start) + return annotated_image,annotated_image + #------------------------------------------------------------------------------------ + + width = annotated_image.shape[1] + height = annotated_image.shape[0] + + locY = 10 + if ("upperbody" in mnet.ensemble) and (mnet.ensemble["upperbody"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["upperbody"].configuration["decompositionType"] + drawDescriptor("%s upperbody" % dcmp,mnet.ensemble["upperbody"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + if ("lowerbody" in mnet.ensemble) and (mnet.ensemble["lowerbody"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["lowerbody"].configuration["decompositionType"] + drawDescriptor("%s lowerbody"% dcmp,mnet.ensemble["lowerbody"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + if ("face" in mnet.ensemble) and (mnet.ensemble["face"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["face"].configuration["decompositionType"] + drawDescriptor("%s face"% dcmp,mnet.ensemble["face"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + if ("reye" in mnet.ensemble) and (mnet.ensemble["reye"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["reye"].configuration["decompositionType"] + drawDescriptor("%s reye"% dcmp,mnet.ensemble["reye"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + dcmp = mnet.ensemble["leye"].configuration["decompositionType"] + drawDescriptor("%s leye"% dcmp,mnet.ensemble["leye"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + if ("mouth" in mnet.ensemble) and (mnet.ensemble["mouth"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["mouth"].configuration["decompositionType"] + drawDescriptor("%s mouth"% dcmp,mnet.ensemble["mouth"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + if ("lhand" in mnet.ensemble) and (mnet.ensemble["lhand"].configuration["decompositionType"]!=""): + dcmp = mnet.ensemble["lhand"].configuration["decompositionType"] + drawDescriptor("%s lhand"% dcmp,mnet.ensemble["lhand"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + dcmp = mnet.ensemble["rhand"].configuration["decompositionType"] + drawDescriptor("%s rhand"% dcmp,mnet.ensemble["rhand"].inputReadyForTF,annotated_image,10,locY,annotated_image.shape[1]-20,5); locY+=15 + #-------------------------------------------------------------------------------------------------------------- + NSRM_Y = 120 + NSRM_Body_Y = NSRM_Y + if ("upperbody" in mnet.ensemble): + drawNSRM("NSRM Up",mnet.ensemble["upperbody"].NSRM,annotated_image,10,NSRM_Y,100,100); NSRM_Y+=130 + if ("lowerbody" in mnet.ensemble): + drawNSRM("NSRM Down",mnet.ensemble["lowerbody"].NSRM,annotated_image,120,NSRM_Body_Y,100,100);# NSRM_Y+=130 + if ("face" in mnet.ensemble): + drawNSRM("NSRM Face",mnet.ensemble["face"].NSRM,annotated_image,10,NSRM_Y,100,100); NSRM_Y+=130 + if ("leye" in mnet.ensemble): + drawNSRM("NSRM LEye",mnet.ensemble["leye"].NSRM,annotated_image,120,NSRM_Y,100,100); + if ("reye" in mnet.ensemble): + drawNSRM("NSRM REye",mnet.ensemble["reye"].NSRM,annotated_image,10,NSRM_Y,100,100); NSRM_Y+=130 + if ("mouth" in mnet.ensemble): + drawNSRM("NSRM Mouth",mnet.ensemble["mouth"].NSRM,annotated_image,10,NSRM_Y,100,100); NSRM_Y+=130 + if ("lhand" in mnet.ensemble): + drawNSRM("NSRM LHand",mnet.ensemble["lhand"].NSRM,annotated_image,10,NSRM_Y,100,100); + drawNSRM("NSRM RHand",mnet.ensemble["rhand"].NSRM,annotated_image,120,NSRM_Y,100,100); NSRM_Y+=130 + + + #drawValueLineInRange(value, minimumValue, maximumValue, label, result_img, x, y, w, h): + #These cause the "failed visualizing" error to be emitted from drawValueLineInRange in google collab (why though?) + drawValueLineInRange(bvhAnglesForPlotting,mnet.outputBVHMinima,mnet.outputBVHMaxima,"hip_xposition",annotated_image,10,NSRM_Y,100,50); NSRM_Y+=90 + drawValueLineInRange(bvhAnglesForPlotting,mnet.outputBVHMinima,mnet.outputBVHMaxima,"hip_yposition",annotated_image,10,NSRM_Y,100,50); NSRM_Y+=90 + drawValueLineInRange(bvhAnglesForPlotting,mnet.outputBVHMinima,mnet.outputBVHMaxima,"hip_zposition",annotated_image,10,NSRM_Y,100,50) + + #-------------------------------------------------------------------------------------------------------------- + drawMAE2DError("2D M.A.E.",mnet.lastMAEErrorInPixels,annotated_image,width-70,height-120,width-10,height-90) + #-------------------------------------------------------------------------------------------------------------- + + perfWidgetY = 120 + if (len(mnet.history_hz_2DEst)>0): + drawMocapNETSinglePlotValueList(mnet.history_hz_2DEst,1,"RGB->2D FPS",annotated_image,width-70,perfWidgetY,70,70,0.0,30.0) + perfWidgetY += 100 + + if (len(mnet.history_hz_NN)>0): + drawMocapNETSinglePlotValueList(mnet.history_hz_NN,1,"NN FPS",annotated_image,width-70,perfWidgetY,70,70,0.0,30.0) + perfWidgetY += 100 + + if (len(mnet.history_hz_HCD)>0): + drawMocapNETSinglePlotValueList(mnet.history_hz_HCD,1,"HCD FPS",annotated_image,width-70,perfWidgetY,70,70,0.0,30.0) + perfWidgetY += 100 + + if (len(mnet.history_hz_Vis)>0): + drawMocapNETSinglePlotValueList(mnet.history_hz_Vis,1,"Visualization",annotated_image,width-70,perfWidgetY,70,70,0.0,30.0) + perfWidgetY += 100 + + drawMNETSerials(mnet,annotated_image,10,30) + + + + #if (mnet.incompleteUpperbodyInput and mnet.incompleteLowerbodyInput): + # drawMissingInput(annotated_image) + if (plotBVHChannels==1): + plotImage = drawMocapNETAllPlots(bvhAnglesForPlotting,1920,920,minimumLimits=mnet.outputBVHMinima,maximumLimits=mnet.outputBVHMaxima) + registerVisualizationTime(mnet,start) + return annotated_image,plotImage + except Exception as e: + print("\n\n\n\nFAILED: Exception while visualizing : ",e,"\n\n\n\n") + #Fall-through + registerVisualizationTime(mnet,start) + return annotated_image,annotated_image + + + + +if __name__ == '__main__': + print("MocapNETVisualization.py is a library and cannot run standalone") diff --git a/src/python/mnet4/NSDM.py b/src/python/mnet4/NSDM.py new file mode 100755 index 0000000..2c8efa0 --- /dev/null +++ b/src/python/mnet4/NSDM.py @@ -0,0 +1,521 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import numpy as np +from enum import Enum + +goFromDegreesToRad=np.float32(np.pi/180.0) +goFromRadToDegrees=np.float32(180.0/np.pi) + +def getJoint2DDistancePoints(aX,aY,bX,bY): + xDistance=np.float32(bX-aX) + yDistance=np.float32(bY-aY) + return np.float32(np.sqrt( (xDistance*xDistance) + (yDistance*yDistance) )) + +def getNumberOfSquaredCompressedSquaredJoints(): + countList=len(getBodyNoHandsList()) + numberOfSquaredCompressedJoints=countList*countList*2 + return numberOfSquaredCompressedJoints + + +def getAngleToAlignToZero(iX,iY,jX,jY,NSRMNormalizeAngles=0): + #--------------------------------------------- + if ( (iX==jX) and (iY==jY) ): + return np.float32(0.0) + #--------------------------------------------- + #We have points a, b and c and we want to calculate angle b + aX= iX*100 + aY= iY*100 + #--------------------------------------------- + bX= jX*100 + bY= jY*100 + #--------------------------------------------- + lengthBetweenAAndB = getJoint2DDistancePoints(aX,aY,bX,bY) + #--------------------------------------------- + cX= bX + cY= bY - lengthBetweenAAndB + #--------------------------------------------- + #fprintf(stderr,"We want to align A(%0.2f,%0.2f) to C(%0.2f,%0.2f) with pivot B(%0.2f,%0.2f)\n",aX,aY,cX,cY,bX,bY) + #fprintf(stderr,"length AB = %0.2f\n",lengthBetweenAAndB); + #fprintf(stderr,"bY = %0.2f\n",bY); + #fprintf(stderr,"cY = %0.2f = %0.2f - %0.2f\n",cY,bY,lengthBetweenAAndB); + #Calulate vector a->b + abX = bX - aX + abY = bY - aY + #calculate vector c->b + cbX = bX - cX + cbY = bY - cY + #--------------------------------------------- + dot = np.float32( (abX * cbX) + (abY * cbY) ) # dot product + cross = np.float32( (abX * cbY) - (abY * cbX) ) # cross product + #--------------------------------------------- + alpha = np.arctan2(cross,dot) #arctan2 returns a value in the range [-pi, pi] + #--------------------------------------------- + #fprintf(stderr,"Angle is %0.2f rad or %0.2f degrees \n",alpha,alpha*goFromRadToDegrees); + if (NSRMNormalizeAngles==2): + return np.float32( (2.0*alpha) / np.pi) # Normalize output in range [-1..1] + if (NSRMNormalizeAngles==1): + #This should actually be (2*alpha) / np.pi + #arctan(R) -> [ -pi/2 , pi/2] + return np.float32(alpha / np.pi) # Normalize output in range [-1/2..1/2] + else: + return np.float32(alpha) # Output in range [-pi..pi] + + +""" + This is the new function to make resolving 2D coordinates on a vector easier and safer (but slower :P) +""" +def getJoint2DXYV(rules,positions,jointName): + #------------------------------------------------------------------------------------------ + x = np.float32(0.0) + y = np.float32(0.0) + visibility = np.float32(0.0) + #------------------------------------------------------------------------------------------ + positionDataLength = len(positions) + if (positionDataLength==0): + print("getJoint2DXYV cannot resolve positions for joint ",jointName," with no vector data") + return x,y,visibility + + if (not rules['inputJointMap'].checkJointListDimensions(positions)): + print("getJoint2DXYV cannot resolve positions for joint ",jointName," input joint map has a different dimension size..") + return x,y,visibility + + + + #Get correct indexes for jointName + #-------------------------------------------------------- + jID_X = rules['inputJointMap'].getJointID_2DX(jointName) + jID_Y = rules['inputJointMap'].getJointID_2DY(jointName) + jID_Vis = rules['inputJointMap'].getJointID_Visibility(jointName) + #-------------------------------------------------------- + if ( (positionDataLength<=jID_X) or (positionDataLength<=jID_Y) or (positionDataLength<=jID_Vis) ): + print("getJoint2DXYV cannot get positions for joint ",jointName," with a vector data of only ",positionDataLength) + return x,y,visibility + #-------------------------------------------------------- + if ( (jID_X==-1) or (jID_Y==-1) or (jID_Vis==-1) ): + print("getJoint2DXYV could not resolve joint ",jointName," with vector data of ",positionDataLength) + return x,y,visibility + #-------------------------------------------------------- + #print("getJoint2DXYV(%s->%u/%u/%u) length %u"%(jointName,jID_X,jID_Y,jID_Vis,positionDataLength)) + #Pedantic behavior on missing data + if (positions[jID_X]==0.0) or (positions[jID_Y]==0.0) or (positions[jID_Vis]==0.0): + return x,y,visibility + #------------------------------------ + x = np.float32(positions[jID_X]) + y = np.float32(positions[jID_Y]) + visibility = np.float32(positions[jID_Vis]) + #------------------------------------ + return x,y,visibility + + + +def rotate2DPointsTest(cx,cy,jX,jY,angleToRotateInRadians): + #----------------------------------------------- + s = np.float32( np.sin(angleToRotateInRadians) ) + c = np.float32( np.cos(angleToRotateInRadians) ) + #----------------------------------------------- + jX = np.float32(jX - cx) + jY = np.float32(jY - cy) + #----------------------------------------------- + xnew = np.float32( (jX * c) - (jY * s) ) + ynew = np.float32( (jX * s) + (jY * c) ) + #----------------------------------------------- + return xnew + cx , ynew + cy + + +def rotate2DPointsBasedOnJointAsCenter(rules,positions,angleToRotateInRadians,jointNameCenter): + if (len(positions)==0): + print("rotate2DPointsBasedOnJointAsCenter cannot work without input.. \n") + return positions + + s = np.float32( np.sin(angleToRotateInRadians) ) + c = np.float32( np.cos(angleToRotateInRadians) ) + + #-------------------------------------------------------- + cx,cy,cVisibility = getJoint2DXYV(rules,positions,jointNameCenter) + #-------------------------------------------------------- + + if (cVisibility==0.0): + print("rotate2DPointsBasedOnJointAsCenter: cannot work without pivot joint .. \n") + return positions + + + result = positions + + for jID in range(0,int(len(rules['NSDM'])) ): + #-------------------------------------------------------- + jointName=rules['NSDM'][jID]['joint'] + jX,jY,jVisibility = getJoint2DXYV(rules,positions,jointName) + #-------------------------------------------------------- + #printf("Rotating point %0.2f,%0.2f using pivot %0.2f,%0.2f by %0.2f deg -> "%(jX,jY,cx,cy,angle)) + + #Translate point back to origin: + jX = np.float32(jX - cx) + jY = np.float32(jY - cy) + + #Rotate point + xnew = np.float32( (jX * c) - (jY * s) ) + ynew = np.float32( (jX * s) + (jY * c) ) + + #Translate point back: + jID_X = rules['inputJointMap'].getJointID_2DX(jointName) + jID_Y = rules['inputJointMap'].getJointID_2DY(jointName) + jID_Vis = rules['inputJointMap'].getJointID_Visibility(jointName) + #---------------------------------------------------------------- + result[jID_X] = np.float32(xnew + cx) + result[jID_Y] = np.float32(ynew + cy) + result[jID_Vis] = np.float32(jVisibility) + + #printf("%0.2f,%0.2f\n"%(result[jID*3+0],result[jID*3+1])); + + return result + + + + +def performNSRMAlignment(thisInput,configuration): + angleToRotateInRadians = 0.0 + NSRMUseAlignmentToPivot = configuration['eNSRM'] + NSRMNormalizeAngles = 0 + if ("NSRMNormalizeAngles" in configuration) and (configuration["NSRMNormalizeAngles"]==1): + NSRMNormalizeAngles = 1 + if (NSRMUseAlignmentToPivot==1): + pivotPoint = configuration['Alignment'][0]['jointStart'] + referencePoint = configuration['Alignment'][0]['jointEnd'] + #----------------------------------------------------------------------------------------------- + if (pivotPoint!=referencePoint): + pivotX,pivotY,pivotVisibility = getJoint2DXYV(configuration,thisInput,pivotPoint) + #-------------------------------------------------------------------------------------------- + referenceX,referenceY,referenceVisibility = getJoint2DXYV(configuration,thisInput,referencePoint) + #-------------------------------------------------------------------------------------------- + if ((pivotVisibility!=0) and (referenceVisibility!=0)): + angleToRotateInRadians = getAngleToAlignToZero(pivotX,pivotY,referenceX,referenceY,NSRMNormalizeAngles) + rotatedInput = rotate2DPointsBasedOnJointAsCenter(configuration,thisInput,angleToRotateInRadians,pivotPoint) + return angleToRotateInRadians,rotatedInput + else: + print("Pivot Point ",pivotPoint," and Reference Point ",referencePoint," are the same\n") + #----------------------------------------------------------------------------------------------- + return angleToRotateInRadians,thisInput + + + + +def getCompositeLabel(jointA,jointB,xOffset,yOffset,virtualPointType): + labelI=jointA + labelIX="" + labelIY="" + if (virtualPointType==1): + if (xOffset<0): + labelIX="minus" + elif (xOffset>0): + labelIX="plus" + #---------------------------------- + if (yOffset<0): + labelIY="minus" + elif (yOffset>0): + labelIY="plus" + #---------------------------------- + labelI="virtual_"+jointA+"_x_"+labelIX+str(xOffset).replace('.','_').replace('-','_')+"_y_"+labelIY+str(yOffset).replace('.','_').replace('-','_') + elif (virtualPointType==2): + #---------------------------------- + labelI="halfway_"+jointA+"_and_"+jointB + return labelI + + + + + + +def NSDMLabels(rules): + result=list() + + useXY=1 + useAngles=0 + if (rules['NSDMAlsoUseAlignmentAngles']==1): + useXY=0 + useAngles=1 + + numberOfNSDMRules=len(rules['NSDM']) + print("Rules Number ",numberOfNSDMRules) + + for i in range(0,numberOfNSDMRules): + for j in range(0,numberOfNSDMRules): + if (i==j): + if (i==0): + result.append("NSRM-angleUsedFor2DRotation_%u"%(i)) + else: + result.append("NSRM-scaleBetween_"+rules['NSDM'][i]['joint']+"_and_"+rules['NSDM'][i]['joint']) + else: + #----------------------------------------------------------------- + labelI = getCompositeLabel( + rules['NSDM'][i]['joint'], + rules['NSDM'][i]['halfWayFromThisAnd'], + rules['NSDM'][i]['xOffset'], + rules['NSDM'][i]['yOffset'], + rules['NSDM'][i]['isVirtual'] + ) + #----------------------------------------------------------------- + labelJ = getCompositeLabel( + rules['NSDM'][j]['joint'], + rules['NSDM'][j]['halfWayFromThisAnd'], + rules['NSDM'][j]['xOffset'], + rules['NSDM'][j]['yOffset'], + rules['NSDM'][j]['isVirtual'] + ) + #----------------------------------------------------------------- + if (useXY): + result.append("NSDM-%sX-%sX"%(labelI,labelJ)) + result.append("NSDM-%sY-%sY"%(labelI,labelJ)) + if (useAngles): + result.append("NSRM-%sY-%sY-Angle"%(labelI,labelJ)) + #----------------------------------------------------------------- + + #print("NSDM matrix will look like this ",result) + return result; + +def inputIsEnoughToCreateNSDM(rules,thisInput): + numberOfNSDMRules=len(rules['NSDM']) + numberOfJointIDs =len(thisInput) + for i in range(0,numberOfNSDMRules): + jointName=rules['NSDM'][i]['joint'] + if (not rules['inputJointMap'].getJointID_Exists(jointName)): + return False + return True + +def getListOfMissingNSRMJoints(rules,thisInput): + missingList=list() + numberOfNSDMRules=len(rules['NSDM']) + numberOfJointIDs =len(thisInput) + for i in range(0,numberOfNSDMRules): + jointName=rules['NSDM'][i]['joint'] + if (not rules['inputJointMap'].getJointID_Exists(jointName)): + missingList.append(jointName) + else: + iX,iY,iVisibility = getJoint2DXYV(rules,thisInput,jointName) + if (iVisibility==0.0): + missingList.append(jointName) + return missingList + + + +def getCompositePoint(rules,i,thisInput): + #----------------------------------------------------------- + if (len(thisInput)==0): + print("getCompositePoint called with no input for element ",i) + return np.float32(0.0),np.float32(0.0),np.float32(0.0),1 + #-------------------------------------------------------- + invalidPoint = 0 + jointName = rules['NSDM'][i]['joint'] + #----------------------------------------------------------- + iX,iY,iVisibility = getJoint2DXYV(rules,thisInput,jointName) + #----------------------------------------------------------- + if ( iX!=0.0 and iY!=0.0 and iVisibility!=0.0 ): + #--------------------------------------------------------------------------- + # Synthetic Points + #--------------------------------------------------------------------------- + if (rules['NSDM'][i]['isVirtual']==1): + iX=iX+rules['NSDM'][i]['xOffset'] + iY=iY+rules['NSDM'][i]['yOffset'] + elif (rules['NSDM'][i]['isVirtual']==2): + secondTargetJointName=rules['NSDM'][i]['halfWayFromThisAnd'] + secondTargetX,secondTargetY,secondTargetVisibility = getJoint2DXYV(rules,thisInput,secondTargetJointName) + if ((secondTargetX!=0.0) or (secondTargetY!=0.0)): + iX=np.float32((iX+secondTargetX)/2) + iY=np.float32((iY+secondTargetY)/2) + else: + invalidPoint = 1 + iX = np.float32(0.0) + iY = np.float32(0.0) + iVisibility = np.float32(0.0) + #--------------------------------------------------------------------------- + else: + #Added : Fixed bug! 11/5/2023 + #If either of X,Y is zero we treat the point as completely invisible + invalidPoint = 1 + iX = np.float32(0.0) + iY = np.float32(0.0) + iVisibility = np.float32(0.0) + #----------------------------------------------------------- + return iX,iY,iVisibility,invalidPoint + + + + +def createNSDMUsingRules(rules,thisInput,angleUsedToRotateInput): + result=list() + #----------------------------------------------------------------------------------------------------- + if (len(thisInput)==0): + print("createNSDMUsingRules called with no input") + return result + + + if (not rules['inputJointMap'].checkJointListDimensions(thisInput)): + print("createNSDMUsingRules called with incorrect input size ") + return thisInput + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + NSRMNormalizeAngles = 0 + if ("NSRMNormalizeAngles" in rules) and (rules["NSRMNormalizeAngles"]==1): + NSRMNormalizeAngles = 1 + + doNormalization = (rules['NSDMNormalizationMasterSwitch']==1) + useXY = True + useAngles = False + if (rules['eNSRM']==1) or (rules['NSDMAlsoUseAlignmentAngles']==1): + useXY = False + useAngles = True + doNormalization = False + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + + #----------------------------------------------------------------------------------------------------- + # ..Main NSRM parameters .. + #----------------------------------------------------------------------------------------------------- + numberOfNSDMRules=len(rules['NSDM']) + for i in range(0,numberOfNSDMRules): + #--------------------------------------------------------------------------- + iX,iY,iVisibility,iInvalidPoint = getCompositePoint(rules,i,thisInput) + #--------------------------------------------------------------------------- + for j in range(0,numberOfNSDMRules): + #--------------------------------------------------------------------------- + jX,jY,jVisibility,jInvalidPoint = getCompositePoint(rules,j,thisInput) + #--------------------------------------------------------------------------- + if (iInvalidPoint or jInvalidPoint): + #If any of the two joints is invalid, invalidate all output + if (useXY): + result.append(np.float32(0.0)) + result.append(np.float32(0.0)) + if (useAngles): + result.append(np.float32(0.0)) + else: + if (useXY): + result.append(np.float32(iX-jX)) + result.append(np.float32(iY-jY)) + if (useAngles): + result.append(getAngleToAlignToZero(iX,iY,jX,jY,NSRMNormalizeAngles)) + #--------------------------------------------------------------------------- + + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + # ..New eNSRM diagonal parameters .. + #----------------------------------------------------------------------------------------------------- + if (rules['eNSRM']==1): + elementID=0 + #------------------------------------------------------------ + iJointName=rules['NSDM'][0]['joint'] #Pivot point + iX,iY,iVisibility = getJoint2DXYV(rules,thisInput,iJointName) + #------------------------------------------------------------ + for j in range(0,numberOfNSDMRules): + elementID = j * numberOfNSDMRules + j # Calculate diagonal elementID based on j + #--------------------------------------------------------------- + jJointName = rules['NSDM'][j]['joint'] + jX,jY,jVisibility = getJoint2DXYV(rules,thisInput,jJointName) + #--------------------------------------------------------------- + if (jVisibility!=0.0) and (iVisibility!=0.0): + #Populate diagonal elements with distance from our pivot point + result[elementID] = getJoint2DDistancePoints(iX,iY,jX,jY) + else: + result[elementID] = np.float32(0.0) + #--------------------------------------------------------------- + #Overwrite first (null) element of NSRM matrix with the angle used to rotate the input + result[0]=np.float32(angleUsedToRotateInput); + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + + if (doNormalization): + #normalization is made for NSDM not xNSRM + #Normalizing results.. --------------------------------------- + numberOfNSDMScalingRules=len(rules['NormalizeNSDMBasedOn']) + if (numberOfNSDMScalingRules>0): + numberOfDistanceSamples=0 + sumOfDistanceSamples=0.0 + for i in range(0,numberOfNSDMScalingRules): + #------------------------------------------------------------------------ + iJointName=rules['NormalizeNSDMBasedOn'][i]['jointStart'] + iX,iY,iVisibility = getJoint2DXYV(rules,thisInput,iJointName) + #------------------------------------------------------------------------ + jJointName=rules['NormalizeNSDMBasedOn'][i]['jointEnd'] + jX,jY,jVisibility = getJoint2DXYV(rules,thisInput,jJointName) + #------------------------------------------------------------------------ + if (iJointName==jJointName): + print("Error: Normalization Rule ",i," points to same start/end joint ",iJointID," == ",jJointID) + distance = getJoint2DDistancePoints(iX,iY,jX,jY) + if (distance>0.0): + numberOfDistanceSamples=numberOfDistanceSamples+1 + sumOfDistanceSamples=sumOfDistanceSamples+distance + + #------------------------------------------------------------------------------------------------- + scaleDistance=1.0 + #------------------------------------------------------------------------------------------------- + if (numberOfDistanceSamples>0): + scaleDistance=sumOfDistanceSamples/numberOfDistanceSamples + #------------------------------------------------------------------------------------------------- + #print("NSDM Scale = ",scaleDistance," \n") + if (scaleDistance!=1.0): + for i in range(0,len(result)): + result[i]=np.float32(result[i]/scaleDistance) + #------------------------------------------------------------------------------------------------- + + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------- + if (useXY): + for i in range(0,len(result)): + if (result[i]!=0.0): + result[i]=np.float32(0.5+result[i]) + #------------------------------------------------------------------------------------------------- + #print(result) + #print("Result has ", len(result), " elements " ) + #print("Result should have ", int( 2 * len(bodyWithoutHands) * len(bodyWithoutHands) ), " elements " ) + return result; + + + +if __name__ == '__main__': + print("NSDM.py is a library it cannot be run standalone") + + sign = 1.0 #This is positive +1.0 + + iX=0.5; iY=0.5; jX=1.0; jY=1.0 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + + iX=0.5; iY=0.5; jX=0.5; jY=1.0 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + + iX=0.5; iY=0.5; jX=0.0; jY=1.0 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + + iX=0.5; iY=0.5; jX=1.0; jY=0.5 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + + iX=0.5; iY=0.5; jX=1.0; jY=0.0 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + + iX=0.5; iY=0.5; jX=0.0; jY=0.0 + a = getAngleToAlignToZero(iX,iY,jX,jY) + print("Points A(%0.2f,%0.2f) -> B(%0.2f,%0.2f) => angle %0.4f" %(iX,iY,jX,jY,a)) + jX,jY = rotate2DPointsTest(iX,iY,jX,jY,sign * a) + print("Rotated it goes to -> B(%0.2f,%0.2f) => angle to align %0.4f" %(jX,jY,getAngleToAlignToZero(iX,iY,jX,jY))) + diff --git a/src/python/mnet4/PoseNET.py b/src/python/mnet4/PoseNET.py new file mode 100755 index 0000000..b3f7751 --- /dev/null +++ b/src/python/mnet4/PoseNET.py @@ -0,0 +1,455 @@ +#!/usr/bin/python3 + +""" +Author : "Ammar Qammaz" +Copyright : "2022 Foundation of Research and Technology, Computer Science Department Greece, See license.txt" +License : "FORTH" +""" + +import os +from tools import secondsToHz,eprint + +#https://tfhub.dev/google/movenet/singlepose/lightning/4 +#https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/int8/4 +#wget -q -O lite-model_movenet_singlepose_lightning_tflite_int8_4.tflite https://storage.googleapis.com/tfhub-lite-models/google/lite-model/movenet/singlepose/lightning/tflite/int8/4.tflite + +#zip movenet.zip movenet/* movenet/*/* + +trainingWidth = 1920 +trainingHeight = 1080 + +def getCaptureDeviceFromPath(videoFilePath,videoWidth,videoHeight): + import cv2 + #------------------------------------------ + if (videoFilePath=="esp"): + from espStream import ESP32CamStreamer + cap = ESP32CamStreamer() + elif (videoFilePath=="webcam"): + cap = cv2.VideoCapture(0) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, videoWidth) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, videoHeight) + elif (videoFilePath=="/dev/video0"): + cap = cv2.VideoCapture(0) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, videoWidth) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, videoHeight) + elif (videoFilePath=="/dev/video1"): + cap = cv2.VideoCapture(1) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, videoWidth) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, videoHeight) + elif (videoFilePath=="/dev/video2"): + cap = cv2.VideoCapture(2) + cap.set(cv2.CAP_PROP_FRAME_WIDTH, videoWidth) + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, videoHeight) + else: + from tools import checkIfPathIsDirectory + if (checkIfPathIsDirectory(videoFilePath) and (not "/dev/" in videoFilePath) ): + from folderStream import FolderStreamer + cap = FolderStreamer(path=videoFilePath,width=videoWidth,height=videoHeight) + mnet.bvh.configureRendererFromFile("%s/color.calib"%videoFilePath) + else: + cap = cv2.VideoCapture(videoFilePath) + return cap + + + +def runPoseNETSerial(): + #Parse command line arguments + #----------------------------------------- + import sys + import cv2 + import time + headless = False + economicVisualization= False + saveVideo = False + videoFilePath = "webcam" + videoWidth = 1280 + videoHeight = 720 + doProfiling = False + doFlipX = False + engine = "onnx" + doNNEveryNFrames = 1 # 3 + bvhScale = 1.0 + doHCDPostProcessing = 1 + hcdLearningRate = 0.001 + hcdEpochs = 15 + hcdIterations = 30 + smoothingSampling = 30.0 + smoothingCutoff = 5.0 + threshold = 0.05 + calibrationFile = "" + plotBVHChannels = False + bvhAnglesForPlotting = list() + bvhAllAnglesForPlotting = list() + + + if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--save"): + saveVideo=True + if (sys.argv[i]=="--nnsubsample"): + doNNEveryNFrames = int(sys.argv[i+1]) + if (sys.argv[i]=="--headless"): + headless = True + if (sys.argv[i]=="--flipx"): + doFlipX = True + if (sys.argv[i]=="--plot"): + plotBVHChannels=True + if (sys.argv[i]=="--nonn"): + doNNEveryNFrames = 1000 + if (sys.argv[i]=="--calib"): + calibrationFile = sys.argv[i+1] + if (sys.argv[i]=="--ik"): + hcdLearningRate = float(sys.argv[i+1]) + hcdEpochs = int(sys.argv[i+2]) + hcdIterations = int(sys.argv[i+3]) + if (sys.argv[i]=="--smooth"): + smoothingSampling = float(sys.argv[i+1]) + smoothingCutoff = float(sys.argv[i+2]) + if (sys.argv[i]=="--noik"): + doHCDPostProcessing = 0 + doNNEveryNFrames = 1 + if (sys.argv[i]=="--scale"): + bvhScale=float(sys.argv[i+1]) + if (sys.argv[i]=="--from"): + videoFilePath=sys.argv[i+1] + if (sys.argv[i]=="--engine"): + engine=sys.argv[i+1] + if (sys.argv[i]=="--plot"): + plotBVHChannels=True + + # For webcam input: + frameNumber = 0 + #------------------------------------------ + cap = getCaptureDeviceFromPath(videoFilePath,videoWidth,videoHeight) + + #----------------------------------------- + #python3 -m tf2onnx.convert --saved-model movenet --opset 14 --output movenet/model.onnx + #zip movenet.zip movenet/* movenet/*/* + #----------------------------------------- + + bvhAnglesForPlotting = list() + + # Initialize the PoseNET + if (engine=="tensorflow"): + from MocapNETTensorflow import PoseNET + poseNET = PoseNET(modelPath="movenet/",trainingWidth=trainingWidth,trainingHeight=trainingHeight) + elif (engine=="onnx"): + from MocapNETONNX import PoseNETONNX + poseNET = PoseNETONNX(modelPath="movenet/model.onnx",trainingWidth=trainingWidth,trainingHeight=trainingHeight) + elif (engine=="tflite"): + from MocapNETTFLite import PoseNETTFLite + poseNET = PoseNETTFLite(trainingWidth=trainingWidth,trainingHeight=trainingHeight) + else: + print("Unknown engine (",engine,") for MoveNET") + sys.exit(1) + + + #Select a MocapNET class from tensorflow/tensorrt/onnx/tf-lite engines + from MocapNET import easyMocapNETConstructor + mnet = easyMocapNETConstructor( + engine, + doProfiling = doProfiling, + doBody = False, #<- override whole body + doUpperbody = True, + doLowerbody = True, + doHCDPostProcessing = doHCDPostProcessing, + hcdLearningRate = hcdLearningRate, + hcdEpochs = hcdEpochs, + hcdIterations = hcdIterations, + smoothingSampling = smoothingSampling, + smoothingCutoff = smoothingCutoff, + doFace = False, + doREye = False, + doMouth = False, + doHands = False, + bvhScale=bvhScale + ) + + + if (calibrationFile!=""): + print("Enforcing Calibration file : ",calibrationFile) + mnet.bvh.configureRendererFromFile(calibrationFile) + + mnet.test() + + + #------------------------------------------------ + #------------------------------------------------ + #------------------------------------------------ + print("\n\n\n\nStarting MocapNET in Singlethreaded mode using BlazePose 2D Input") + print("Please wait until input processing finishes!") + while cap.isOpened(): + success, annotated_image = cap.read() + if not success: + print("Ignoring empty camera frame.") + break + # If loading a video, use 'break' instead of 'continue'. + #continue + #print(image.type) + + start = time.time() + #Our 2D Joint Estimation + #------------------------------------------------------------------------------------ + mocapNETInput,annotated_image = poseNET.convertImageToMocapNETInput(annotated_image,doFlipX=doFlipX,threshold=threshold) + #------------------------------------------------------------------------------------ + end = time.time() # Time elapsed + mnet.hz_2DEst = secondsToHz(end - start) + mnet.history_hz_2DEst.append(mnet.hz_2DEst) + if (len(mnet.history_hz_2DEst)>mnet.perfHistorySize): + mnet.history_hz_2DEst.pop(0) #Keep mnet history on limits + + + + #Our 3D Joint Estimation + #------------------------------------------------------------------------------------ + doNN = (frameNumber%doNNEveryNFrames)==0 + mocapNET3DOutput = mnet.predict3DJoints(mocapNETInput,runNN=doNN,runHCD=True) + mocapNETBVHOutput = mnet.outputBVH + bvhAnglesForPlotting.append(mocapNETBVHOutput) + bvhAllAnglesForPlotting.append(mocapNETBVHOutput) + if (len(bvhAnglesForPlotting)>100): + bvhAnglesForPlotting.pop(0) + #------------------------------------------------------------------------------------ + from MocapNETVisualization import visualizeMocapNETEnsemble + image,plotImage = visualizeMocapNETEnsemble(mnet,annotated_image,plotBVHChannels=plotBVHChannels,bvhAnglesForPlotting=bvhAnglesForPlotting,economic=economicVisualization) + #------------------------------------------------------------------------------------ + + + seconds = time.time() - start + fps = 1 / (seconds+0.0001) + #print("\r PoseNET+MocepNET aggregate Framerate : ",round(fps,2)," fps \r", end="", flush=True) + #print("\n", end="", flush=True) + + font = cv2.FONT_HERSHEY_SIMPLEX + org = (50, 50) + fontScale = 1 + color = (0,0,0) + thickness = 2 + + message = 'MNET4+ ST/%s/NN:%u/%0.2f fps (2DNN %0.2f/3DNN %0.2f/3DHCD %0.2f)' % (engine,doNN,fps,poseNET.hz,mnet.hz_NN,mnet.hz_HCD) + annotated_image = cv2.putText(annotated_image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (52, 52) + color = (255,255,255) + annotated_image = cv2.putText(annotated_image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + + #cv2.imwrite('mediapipe_%05u.jpg'%frameNumber, annotated_image) + frameNumber = frameNumber + 1 + + + if (saveVideo): + cv2.imwrite('colorFrame_0_%05u.jpg'%(frameNumber), annotated_image) + if (plotBVHChannels): + cv2.imwrite('plotFrame_0_%05u.jpg'%(frameNumber), plotImage) + + if not headless: + cv2.imshow('MocapNET 4 using PoseNET Holistic 2D Joints', annotated_image) + if (plotBVHChannels): + cv2.imshow('MocapNET 4 using PoseNET Holistic Motion History',plotImage) + + if cv2.waitKey(1) & 0xFF == 27: + break + + + + cap.release() + + if (saveVideo): # 1280x720 by default + os.system("ffmpeg -framerate 30 -i colorFrame_0_%%05d.jpg -s %ux%u -y -r 30 -pix_fmt yuv420p -threads 8 livelastRun3DHiRes.mp4 && rm colorFrame_0_*.jpg " % (videoWidth,videoHeight)) # + if (plotBVHChannels): + os.system("ffmpeg -framerate 30 -i plotFrame_0_%05d.jpg -s 1200x720 -y -r 30 -pix_fmt yuv420p -threads 8 livelastPlot3DHiRes.mp4 && rm plotFrame_0_*.jpg") + + + + + +def runPoseNETParallel(): + #Parse command line arguments + #----------------------------------------- + import sys + import cv2 + import time + import threading + + headless = False + videoFilePath = "webcam" + videoWidth = 1280 + videoHeight = 720 + saveVideo = False + plotBVHChannels = False + doProfiling = False + doFlipX = False + engine = "onnx" + doNNEveryNFrames = 1 + bvhScale = 1.0 + threshold = 0.05 + + + if (len(sys.argv)>1): + #print('Argument List:', str(sys.argv)) + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--headless"): + headless = True + if (sys.argv[i]=="--flipx"): + doFlipX = True + if (sys.argv[i]=="--plot"): + plotBVHChannels=True + if (sys.argv[i]=="--scale"): + bvhScale=float(sys.argv[i+1]) + if (sys.argv[i]=="--noplot"): + plotBVHChannels=0 + if (sys.argv[i]=="--from"): + videoFilePath=sys.argv[i+1] + if (sys.argv[i]=="--engine"): + engine=sys.argv[i+1] + if (sys.argv[i]=="--dump"): + global doFrameDumpingForTiles + doFrameDumpingForTiles=1 + + + # For webcam input: + #----------------------------------------- + frameNumber = 0 + cap = getCaptureDeviceFromPath(videoFilePath,videoWidth,videoHeight) + #----------------------------------------- + #python3 -m tf2onnx.convert --saved-model movenet --opset 14 --output movenet/model.onnx + #zip movenet.zip movenet/* movenet/*/* + #----------------------------------------- + + from MocapNETVisualization import drawMocapNETOutput,drawMocapNETAllPlots,drawMissingInput + #It is important for MocapNET to be the first to be initialized! + #So the tensorflow configuration will be set by it ( if tensorflow engine is selected ) + #Select a MocapNET class from tensorflow/tensorrt/onnx/tf-lite engines + from MocapNET import easyMocapNETConstructor + mnet = easyMocapNETConstructor(engine,doProfiling=doProfiling,bvhScale=bvhScale) + mnet.test() + + bvhAnglesForPlotting = list() + + + # Initialize the PoseNET + if (engine=="tensorflow"): + from MocapNETTensorflow import PoseNET + poseNET = PoseNET(modelPath="movenet/",trainingWidth=trainingWidth,trainingHeight=trainingHeight) + elif (engine=="onnx"): + from MocapNETONNX import PoseNETONNX + poseNET = PoseNETONNX(modelPath="movenet/model.onnx",trainingWidth=trainingWidth,trainingHeight=trainingHeight) + elif (engine=="tflite"): + from MocapNETTFLite import PoseNETTFLite + poseNET = PoseNETTFLite(trainingWidth=trainingWidth,trainingHeight=trainingHeight) + else: + print("Unknown engine (",engine,") for MoveNET") + sys.exit(1) + + + + if cap.isOpened(): + success, previous_image = cap.read() + if not success: + print("Could not grab first frame!.") + sys.exit(0) + mocapNETInput,previous_image = poseNET.convertImageToMocapNETInput(previous_image,doFlipX=doFlipX,threshold=threshold) + + #------------------------------------------------ + #------------------------------------------------ + #------------------------------------------------ + print("Starting MocapNET in Multithreaded mode using BlazePose 2D Input") + while cap.isOpened(): + success, next_image = cap.read() + if not success: + print("Ignoring empty camera frame.") + break + # If loading a video, use 'break' instead of 'continue'. + #continue + #print(image.type) + + start = time.time() + #Our 2D Joint Estimation AND 3D Joint Estimation happening in parallel + #------------------------------------------------------------------------------------ + doNN = (frameNumber%doNNEveryNFrames)==0 + t1 = threading.Thread(name='predict3DJoints', target=mnet.predict3DJoints, args=(mocapNETInput,),kwargs={'runNN': doNN , 'runHCD' : True}) + t2 = threading.Thread(name='convertImageToMocapNETInput', target=poseNET.convertImageToMocapNETInput, args=(next_image,)) + #------------------------------------------------------------------------------------ + t1.start() + t2.start() + # All threads running in parallel, now we wait + # ... + t1.join() + t2.join() + #------------------------------------------------------------------------------------ + mocapNET3DOutput = mnet.output3D + mocapNETBVHOutput = mnet.outputBVH + bvhAnglesForPlotting.append(mocapNETBVHOutput) + if (len(bvhAnglesForPlotting)>100): + bvhAnglesForPlotting.pop(0) + + #------------------------------------------------------------------------------------ + from MocapNETVisualization import visualizeMocapNETEnsemble + image,plotImage = visualizeMocapNETEnsemble(mnet,previous_image,plotBVHChannels=plotBVHChannels,bvhAnglesForPlotting=bvhAnglesForPlotting,economic=True) + #------------------------------------------------------------------------------------ + + mocapNETInput = poseNET.output + next_image = poseNET.image + #------------------------------------------------------------------------------------ + seconds = time.time() - start + fps = 1 / (seconds+0.0001) + #print("\r MoveNET+MocepNET MT aggregate Framerate : ",round(fps,2)," fps \r", end="", flush=True) + #print("\n", end="", flush=True) + + + frameNumber = frameNumber + 1 + + + #drawMocapNETOutput(mnet,previous_image) + font = cv2.FONT_HERSHEY_SIMPLEX + org = (50, 50) + fontScale = 1 + color = (0,0,0) + thickness = 2 + + message = 'MNET4+ MT/%s/NN:%u/%0.2f fps (2DNN %0.2f/3DNN %0.2f/3DHCD %0.2f)' % (engine,doNN,fps,poseNET.hz,mnet.hz_NN,mnet.hz_HCD) + previous_image = cv2.putText(previous_image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + org = (52, 52) + color = (255,255,255) + previous_image = cv2.putText(previous_image, message , org, font, fontScale, color, thickness, cv2.LINE_AA) + #------------------------------------------------------------------------------------------------------------ + + if (saveVideo): + cv2.imwrite('colorFrame_0_%05u.jpg'%(frameNumber), annotated_image) + if (plotBVHChannels): + cv2.imwrite('plotFrame_0_%05u.jpg'%(frameNumber), plotImage) + + if not headless: + cv2.imshow('MocapNET 4 using MoveNET Holistic 2D Joints', previous_image) + if (plotBVHChannels): + cv2.imshow('MocapNET 4 using MoveNET Holistic Motion History',plotImage) + + if cv2.waitKey(1) & 0xFF == 27: + break + + previous_image = next_image + + cap.release() + + if (saveVideo): # + # 1280x720 by default + os.system("ffmpeg -framerate 30 -i colorFrame_0_%%05d.jpg -s %ux%u -y -r 30 -pix_fmt yuv420p -threads 8 livelastRun3DHiRes.mp4 && rm colorFrame_0_*.jpg " % (videoWidth,videoHeight)) # + if (plotBVHChannels): + os.system("ffmpeg -framerate 30 -i plotFrame_0_%05d.jpg -s 1200x720 -y -r 30 -pix_fmt yuv420p -threads 8 livelastPlot3DHiRes.mp4 && rm plotFrame_0_*.jpg") + + + + + +if __name__ == '__main__': + doSerialRun = True + import sys + if (len(sys.argv)>1): + for i in range(0, len(sys.argv)): + if (sys.argv[i]=="--mt"): + doSerialRun = False + runPoseNETParallel() + + if (doSerialRun): + runPoseNETSerial() + diff --git a/src/python/mnet4/PoseNETServer.py b/src/python/mnet4/PoseNETServer.py new file mode 100755 index 0000000..eba5864 --- /dev/null +++ b/src/python/mnet4/PoseNETServer.py @@ -0,0 +1,232 @@ +import argparse +import asyncio +import json +import logging +import os +import ssl +import uuid + +import cv2 +from aiohttp import web +from av import VideoFrame + +from aiortc import MediaStreamTrack, RTCPeerConnection, RTCSessionDescription +from aiortc.contrib.media import MediaBlackhole, MediaPlayer, MediaRecorder, MediaRelay + +ROOT = os.path.dirname(__file__) + +logger = logging.getLogger("pc") +pcs = set() +relay = MediaRelay() + +requests = 0 + +# Initialize the PoseNET +from PoseNET import PoseNET,PoseNETONNX +#poseNET = PoseNET(modelPath="movenet/") +poseNET = PoseNETONNX(modelPath="movenet/model.onnx") + +from MocapNETVisualization import drawMocapNETOutput,drawMocapNETAllPlots,drawMissingInput + +#Select a MocapNET class from tensorflow/tensorrt/onnx/tf-lite engines +doProfiling = False +engine = "onnx" +from MocapNET import easyMocapNETConstructor +mnet = easyMocapNETConstructor(engine,doProfiling=doProfiling) +mnet.test() + + +class VideoTransformTrack(MediaStreamTrack): + """ + A video stream track that transforms frames from an another track. + """ + + kind = "video" + + def __init__(self, track, transform): + super().__init__() # don't forget this! + self.track = track + self.transform = transform + + async def recv(self): + frame = await self.track.recv() + + if self.transform == "cartoon": + img = frame.to_ndarray(format="bgr24") + + # prepare color + img_color = cv2.pyrDown(cv2.pyrDown(img)) + for _ in range(6): + img_color = cv2.bilateralFilter(img_color, 9, 9, 7) + img_color = cv2.pyrUp(cv2.pyrUp(img_color)) + + # prepare edges + img_edges = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + img_edges = cv2.adaptiveThreshold( + cv2.medianBlur(img_edges, 7), + 255, + cv2.ADAPTIVE_THRESH_MEAN_C, + cv2.THRESH_BINARY, + 9, + 2, + ) + img_edges = cv2.cvtColor(img_edges, cv2.COLOR_GRAY2RGB) + + # combine color and edges + img = cv2.bitwise_and(img_color, img_edges) + + # rebuild a VideoFrame, preserving timing information + new_frame = VideoFrame.from_ndarray(img, format="bgr24") + new_frame.pts = frame.pts + new_frame.time_base = frame.time_base + return new_frame + elif self.transform == "edges": + # perform edge detection + img = frame.to_ndarray(format="bgr24") + img = cv2.cvtColor(cv2.Canny(img, 100, 200), cv2.COLOR_GRAY2BGR) + + # rebuild a VideoFrame, preserving timing information + new_frame = VideoFrame.from_ndarray(img, format="bgr24") + new_frame.pts = frame.pts + new_frame.time_base = frame.time_base + return new_frame + elif self.transform == "mocapnet": + # rotate image + img = frame.to_ndarray(format="bgr24") + + mocapNETInput,annotated_image = poseNET.convertImageToMocapNETInput(img) + mocapNET3DOutput = mnet.predict3DJoints(mocapNETInput,runNN=1,runHCD=True) + drawMocapNETOutput(mnet,annotated_image) + + + # rebuild a VideoFrame, preserving timing information + new_frame = VideoFrame.from_ndarray(annotated_image, format="bgr24") + new_frame.pts = frame.pts + new_frame.time_base = frame.time_base + return new_frame + else: + return frame + + +async def index(request): + content = open(os.path.join(ROOT, "PoseNETServer.html"), "r").read() + return web.Response(content_type="text/html", text=content) + + +async def javascript(request): + content = open(os.path.join(ROOT, "client.js"), "r").read() + return web.Response(content_type="application/javascript", text=content) + + +async def offer(request): + global requests + requests = requests + 1 + params = await request.json() + offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) + + pc = RTCPeerConnection() + pc_id = "PeerConnection(%s)" % uuid.uuid4() + pcs.add(pc) + + def log_info(msg, *args): + logger.info(pc_id + " " + msg, *args) + + log_info("Created for %s", request.remote) + + # prepare local media + player = MediaPlayer(os.path.join(ROOT, "demo-instruct.wav")) + if args.record_to: + recorder = MediaRecorder("%s_%u.mp4"%(args.record_to,requests)) + else: + recorder = MediaBlackhole() + + @pc.on("datachannel") + def on_datachannel(channel): + @channel.on("message") + def on_message(message): + if isinstance(message, str) and message.startswith("ping"): + channel.send("pong" + message[4:]) + + @pc.on("connectionstatechange") + async def on_connectionstatechange(): + log_info("Connection state is %s", pc.connectionState) + if pc.connectionState == "failed": + await pc.close() + pcs.discard(pc) + + @pc.on("track") + def on_track(track): + log_info("Track %s received", track.kind) + + if track.kind == "audio": + pc.addTrack(player.audio) + recorder.addTrack(track) + elif track.kind == "video": + pc.addTrack( + VideoTransformTrack( + relay.subscribe(track), transform=params["video_transform"] + ) + ) + if args.record_to: + recorder.addTrack(relay.subscribe(track)) + + @track.on("ended") + async def on_ended(): + log_info("Track %s ended", track.kind) + await recorder.stop() + + # handle offer + await pc.setRemoteDescription(offer) + await recorder.start() + + # send answer + answer = await pc.createAnswer() + await pc.setLocalDescription(answer) + + return web.Response( + content_type="application/json", + text=json.dumps( + {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} + ), + ) + + +async def on_shutdown(app): + # close peer connections + coros = [pc.close() for pc in pcs] + await asyncio.gather(*coros) + pcs.clear() + + + +if __name__ == "__main__": + #openssl req --new --newkey rsa:4096 -x509 -sha256 --nodes --keyout apache.key --out apache-certificate.crt + parser = argparse.ArgumentParser(description="WebRTC audio / video / data-channels demo") + parser.add_argument("--cert-file", help="SSL certificate file (for HTTPS)") + parser.add_argument("--key-file", help="SSL key file (for HTTPS)") + parser.add_argument("--host", default="0.0.0.0", help="Host for HTTP server (default: 0.0.0.0)") + parser.add_argument("--port", type=int, default=8080, help="Port for HTTP server (default: 8080)") + parser.add_argument("--record-to", help="Write received media to a file."), + parser.add_argument("--verbose", "-v", action="count") + args = parser.parse_args() + + if args.verbose: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + + ssl_context = ssl.SSLContext() + ssl_context.load_cert_chain("apache-certificate.crt","apache.key") + #if args.cert_file: + # ssl_context = ssl.SSLContext() + # ssl_context.load_cert_chain(args.cert_file, args.key_file) + #else: + # ssl_context = None + + app = web.Application() + app.on_shutdown.append(on_shutdown) + app.router.add_get("/", index) + app.router.add_get("/client.js", javascript) + app.router.add_post("/offer", offer) + web.run_app(app, access_log=None, host=args.host, port=args.port, ssl_context=ssl_context) diff --git a/src/python/mnet4/align2DPoints.py b/src/python/mnet4/align2DPoints.py new file mode 100755 index 0000000..755c67b --- /dev/null +++ b/src/python/mnet4/align2DPoints.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +import h5py +import numpy as np +import csv +import os +import sys + +from scipy.spatial import procrustes +from scipy.linalg import orthogonal_procrustes + +import matplotlib +import matplotlib.pyplot as plt +import matplotlib.animation as animation + +#Taken from https://github.com/una-dinosauria/3d-pose-baseline/blob/master/src/procrustes.py +def compute_similarity_transform(X, Y, compute_optimal_scale=False): + """ + A port of MATLAB's `procrustes` function to Numpy. + Adapted from http://stackoverflow.com/a/18927641/1884420 + Args + X: array NxM of targets, with N number of points and M point dimensionality + Y: array NxM of inputs + compute_optimal_scale: whether we compute optimal scale or force it to be 1 + Returns: + d: squared error after transformation + Z: transformed Y + T: computed rotation + b: scaling + c: translation + """ + + muX = X.mean(0) + muY = Y.mean(0) + + X0 = X - muX + Y0 = Y - muY + + ssX = (X0**2.).sum() + ssY = (Y0**2.).sum() + + # centred Frobenius norm + normX = np.sqrt(ssX) + normY = np.sqrt(ssY) + + # scale to equal (unit) norm + X0 = X0 / normX + Y0 = Y0 / normY + + # optimum rotation matrix of Y + A = np.dot(X0.T, Y0) + U,s,Vt = np.linalg.svd(A,full_matrices=False) + V = Vt.T + T = np.dot(V, U.T) + + # Make sure we have a rotation + detT = np.linalg.det(T) + V[:,-1] *= np.sign( detT ) + s[-1] *= np.sign( detT ) + T = np.dot(V, U.T) + + traceTA = s.sum() + + if compute_optimal_scale: # Compute optimum scaling of Y. + b = traceTA * normX / normY + d = 1 - traceTA**2 + Z = normX*traceTA*np.dot(Y0, T) + muX + else: # If no scaling allowed + b = 1 + d = 1 + ssY/ssX - 2 * traceTA * normY / normX + Z = normY*np.dot(Y0, T) + muX + + c = muX - b*np.dot(muY, T) + + return d, Z, T, b, c + + +def pointListReturnXYZListForScatterPlot(A): + numberOfPoints=A.shape[0] + xs=list() + ys=list() + zs=list() + for i in range(0,numberOfPoints): + xs.append(A[i][0]) + ys.append(A[i][1]) + zs.append(A[i][2]) + return xs,ys,zs + + + +def pointListsReturnAvgDistance(A,B): + numberOfPoints=A.shape[0] + if (A.shape[0]!=B.shape[0]): + print("Error comparing point lists of different length") + return inf + + distance=0 + for i in range(0,numberOfPoints): + #--------- + xA=A[i][0] + yA=A[i][1] + zA=A[i][2] + #--------- + xB=B[i][0] + yB=B[i][1] + zB=B[i][2] + #--------- + xAB=xA-xB + yAB=yA-yB + zAB=zA-zB + + #Pythagorean theorem for 3 dimensions + #distance = squareRoot( xAB^2 + yAB^2 + zAB^2 ) + distance+=np.sqrt(xAB*xAB+yAB*yAB+zAB*zAB) + return distance/numberOfPoints + + + + + diff --git a/src/python/mnet4/align3DPoints.py b/src/python/mnet4/align3DPoints.py new file mode 100755 index 0000000..5212750 --- /dev/null +++ b/src/python/mnet4/align3DPoints.py @@ -0,0 +1,280 @@ +#!/usr/bin/env python3 +#Written by Ammar Qammaz a.k.a AmmarkoV - 2020 + +import h5py +import numpy as np +import csv +import os +import sys + + +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + + +#Taken from https://github.com/una-dinosauria/3d-pose-baseline/blob/master/src/procrustes.py +def compute_similarity_transform(X, Y, compute_optimal_scale=False): + """ + A port of MATLAB's `procrustes` function to Numpy. + Adapted from http://stackoverflow.com/a/18927641/1884420 + Args + X: array NxM of targets, with N number of points and M point dimensionality + Y: array NxM of inputs + compute_optimal_scale: whether we compute optimal scale or force it to be 1 + Returns: + d: squared error after transformation + Z: transformed Y + T: computed rotation + b: scaling + c: translation + """ + + #We create a normalized version of X and Y called X0,Y0 + #that is centered on 0 + muX = X.mean(0) + muY = Y.mean(0) + + X0 = X - muX + Y0 = Y - muY + + ssX = (X0**2.).sum() + ssY = (Y0**2.).sum() + + # centred Frobenius norm + normX = np.sqrt(ssX) + normY = np.sqrt(ssY) + + # scale to equal (unit) norm + X0 = X0 / normX + Y0 = Y0 / normY + + #For reference this is the SciPy version : + #https://github.com/scipy/scipy/blob/v1.9.3/scipy/spatial/_procrustes.py#L15-L130 + #https://github.com/scipy/scipy/blob/v1.9.3/scipy/linalg/_procrustes.py#L12-L89 + + # optimum rotation matrix of Y + A = np.dot(X0.T, Y0) + + #full_matrices => bool, optional + #If True (default), u and vh have the shapes (..., M, M) and (..., N, N), respectively. + #Otherwise, the shapes are (..., M, K) and (..., K, N), respectively, where K = min(M, N). + U,s,Vt = np.linalg.svd(A,full_matrices=False) + V = Vt.T + T = np.dot(V, U.T) + + # Make sure we have a rotation + detT = np.linalg.det(T) + V[:,-1] *= np.sign( detT ) + s[-1] *= np.sign( detT ) + T = np.dot(V, U.T) + #------------------------- + traceTA = s.sum() + #------------------------- + if compute_optimal_scale: # Compute optimum scaling of Y. + b = traceTA * normX / normY + d = 1 - traceTA**2 + Z = normX*traceTA*np.dot(Y0, T) + muX + else: # If no scaling allowed + b = 1 + d = 1 + ssY/ssX - 2 * traceTA * normY / normX + Z = normY*np.dot(Y0, T) + muX + #------------------------- + c = muX - b*np.dot(muY, T) + #------------------------- + return d, Z, T, b, c + + + + +""" +Calculate Area Under Curve (AUC) +""" +def AUC(values,minValue,maxValue): + underCurve=0 + samples=len(values) + for value in values: + if ((minValue<=value) and (value<=maxValue) ): + underCurve=underCurve+1 + return 100*(underCurve/samples) + + +""" +Pythagorean theorem, get the 3D distance between two 3D points given their X,Y,Z coordinates +""" +def get3DDistance(jX,jY,jZ,pX,pY,pZ): + return np.sqrt( ((jX-pX)*(jX-pX)) + ((jY-pY)*(jY-pY)) + ((jZ-pZ)*(jZ-pZ)) ) + + +""" +Given two lists of 3D points A,B calculate their average distance +""" +def calculateAverageDistanceOf3DPoints(A,B): + numberOfPoints=A.shape[0] + + if (numberOfPoints==0): + print(bcolors.FAIL,"calculateAverageDistanceOf3DPoints(A,B), A has no points!",bcolors.ENDC) + return np.inf + elif (A.shape[0]!=B.shape[0]): + print(bcolors.FAIL,"Error comparing point lists of different length",bcolors.ENDC) + return np.inf + + distance=0.0 + for i in range(0,numberOfPoints): + #--------- + xA=A[i][0] + yA=A[i][1] + zA=A[i][2] + #--------- + xB=B[i][0] + yB=B[i][1] + zB=B[i][2] + #---------------------------------------------- + distance += get3DDistance(xA,yA,zA,xB,yB,zB) + #---------------------------------------------- + + return distance/numberOfPoints + + +""" +Return the jointID of a jointName in a list of labels without being case sensitive +""" +def findJointID(jointName,labels): + jointNameStreamlined=jointName.lower().strip() + for i in range(0,len(labels)): + labelStreamlined=labels[i].lower().strip() + if (jointNameStreamlined==labelStreamlined): + return i + print(bcolors.FAIL,"Cannot find joint `%s` between %u labels !"%(jointNameStreamlined,len(labels)),bcolors.ENDC) + #print(labels) + return -1 + + + +def compareGroundTruthToPrediction(configuration,groundTruth,prediction,doProcrustes=1,allowProcrustesToChangeScale=1,jointsToCompare=list(),useSciKitImplementation=False): + #print("GroundTruth : ",groundTruth) + #print("Prediction : ",prediction) + + #Automatically fill joints to compare if it is empty with whatever currently + #exists in configuration + numberOfJoints = len(configuration["hierarchy"]) + if (len(jointsToCompare)==0): + for jID in range (0,numberOfJoints): + jointsToCompare.append(configuration["hierarchy"][jID]["joint"].lower()) + else: + numberOfJoints = len(jointsToCompare) + #-------------------------------------------------------------------------- + + #Initialize our variables + #------------------------------- + comparedJoints = list() + groundTruth3DPoints = list() + mnet3DPoints = list() + #------------------------------- + scale = 1.0 + outputScale = 10.0 # We go from Centimeters to Millimeters! + numberOfJointsToCompare = 0 + #------------------------------- + + for jointName in jointsToCompare: + jointName = jointName.lower() #Make double sure if supplied as argument + + labelX = '3DX_%s' % jointName + labelY = '3DY_%s' % jointName + labelZ = '3DZ_%s' % jointName + + if ( + ( labelX in groundTruth ) and + ( labelY in groundTruth ) and + ( labelZ in groundTruth ) and + ( labelX in prediction ) and + ( labelY in prediction ) and + ( labelZ in prediction ) + ): + comparedJoints.append(jointName) + numberOfJointsToCompare = numberOfJointsToCompare + 1 + #-------------------------------------------- + # Grab ground truth for point + #-------------------------------------------- + x3DGT=scale*groundTruth[labelX] + y3DGT=scale*groundTruth[labelY] + z3DGT=scale*groundTruth[labelZ] + #-------------------------------------------- + groundTruth3DPoints.append([x3DGT,y3DGT,z3DGT]) + #-------------------------------------------- + + #-------------------------------------------- + # Grab MocapNET point + #-------------------------------------------- + x3DMNET=scale*prediction[labelX] + y3DMNET=scale*prediction[labelY] + z3DMNET=scale*prediction[labelZ] + #-------------------------------------------- + mnet3DPoints.append([x3DMNET,y3DMNET,z3DMNET]) + #-------------------------------------------- + else: + print(bcolors.WARNING,"Joint ",jointName," was not found this will influence results ") + + if (numberOfJointsToCompare==0): + print(bcolors.FAIL,"No joints where found ..",bcolors.ENDC) + + #We package our lists in numpy to be able to easily manipulate them + #------------------------------------------------------------------ + np_GTPointCloud = np.asarray(groundTruth3DPoints,dtype=np.float32) + np_OURPointCloud = np.asarray(mnet3DPoints,dtype=np.float32) + #------------------------------------------------------------------ + + #This is the main comparison after using procrustes and transforming the pointcloud + #to align it or when just doing plain old euclidean distance + #-------------------------------------------------------------------------------- + if (useSciKitImplementation): + from scipy.spatial import procrustes + mtx1, mtx2, disparity = procrustes(np_GTPointCloud,np_OURPointCloud) + np_GTPointCloud=mtx1 + np_OURPointCloud=mtx2 + elif (doProcrustes): + d, Z, T, b, c = compute_similarity_transform(np_GTPointCloud,np_OURPointCloud,compute_optimal_scale=allowProcrustesToChangeScale) + #disparity=np.sqrt(d) #d: squared error after transformation + #print("compute_similarity_transform : ",disparity) + + #Our point cloud is brought to the same translation and rotation as h36 point cloud + np_OURPointCloud = (b*np_OURPointCloud.dot(T))+c + disparity = outputScale * calculateAverageDistanceOf3DPoints(np_OURPointCloud,np_GTPointCloud) + else: + disparity = outputScale * calculateAverageDistanceOf3DPoints(np_OURPointCloud,np_GTPointCloud) + #-------------------------------------------------------------------------------- + #Here for each element in ground truth we want to get the same point from prediction.. + + #We want to calculate Mean Per Joint Position Error (MPJPE) + #to do so we have to calculate the position error of each of the joints in our point cloud + #sum it up and then divide it through the number of samples + totalError = 0.0 + totalSamples = 0 + #alljointDistances = list() + jointDistance = dict() + for jID in range(0,numberOfJointsToCompare): + #------------------------------------------------------------------ + #We use the np_ourPointCloud and np_h36PointCloud so that if procrustes analysis is enabled it will be used.. + perJointDisparity= outputScale * get3DDistance( + np_OURPointCloud[jID][0],np_OURPointCloud[jID][1],np_OURPointCloud[jID][2], + np_GTPointCloud[jID][0] ,np_GTPointCloud[jID][1] ,np_GTPointCloud[jID][2] + ) + totalError+=perJointDisparity + totalSamples+=1 + #We also keep every sample on a list to do an analysis in the end + #alljointDistances.append(perJointDisparity) + jointDistance[comparedJoints[jID]]=perJointDisparity + #------------------------------------------------------------------ + + jointDistance["meanAverageError"] = disparity + #print("Frame %u / Disparity %f " % (frameID,disparity)) + return jointDistance + + + diff --git a/src/python/mnet4/colabStream.py b/src/python/mnet4/colabStream.py new file mode 100644 index 0000000..f16d693 --- /dev/null +++ b/src/python/mnet4/colabStream.py @@ -0,0 +1,104 @@ +# +# based on: https://colab.research.google.com/notebooks/snippets/advanced_outputs.ipynb#scrollTo=2viqYx97hPMi +# + +from IPython.display import display, Javascript +from google.colab.output import eval_js +from base64 import b64decode, b64encode +import numpy as np +import cv2 + +def init_camera(): + """Create objects and functions in HTML/JavaScript to access local web camera""" + + js = Javascript(''' + + // global variables to use in both functions + var div = null; + var video = null; // \s*
JointR² / Start Loss / M.A.ER² / End Loss / M.A.ETr.EpochsMin/MaxOffsetScalarScalar Fr.
") + f.write(outputName) + f.write("") + if ("train_rsquared_start" in metrics): + f.write("R² %0.2f/"%(metrics["train_rsquared_start"])) + f.write("%0.4f/%0.4f%s"%(initial_loss,initial_mae,initialVAL)) + f.write("") + if ("train_rsquared_end" in metrics): + f.write("R² %0.2f/"%(metrics["train_rsquared_end"])) + f.write("%0.4f/%0.4f%s"%(lowest_loss,lowest_mae,lowestVAL)) + f.write("") + f.write("%u"%(lowestLossAchievedAt)) + f.write("") + f.write("%0.4f/%0.4f"%(outputMinimumValue,outputMaximumValue)) + f.write("") + f.write("%0.4f"%(outputOffsetValue)) + f.write("") + f.write("%0.4f"%(outputScalarValues)) + f.write("") + f.write("%0.4f"%(outputScalarFractionValues)) + f.write("
Description<\/td>\s*<\/tr>\s*
\s*([A-Za-z0-9]+)' + + # Search for the pattern in the HTML content + match = re.search(pattern, content, re.DOTALL) + + if match: + serial_number = match.group(1) + return serial_number + + return "?" + +def filterListOfStringsByRegex(string_list, regex_pattern): + import re + #Usage : + #input_list = ["apple", "banana", "cherry", "date", "elderberry"] + #pattern = r"^[a-c].*" # Matches strings starting with letters a, b, or c + #result = filterListOfStringsByRegex(input_list, pattern) + + matched_strings = [] + + for string in string_list: + if re.match(regex_pattern, string): + matched_strings.append(string) + + return matched_strings + + +def convertListOfRegexToListOfLists(master_string_list,regex_list): + string_list_output = [] + for regex_pattern in regex_list: + string_list_output.append(filterListOfStringsByRegex(master_string_list,regex_pattern)) + return string_list_output + + +""" +Check if an entry is part of a given list +""" +def getEntryIndexInList(theList,theEntry): + i=0 + for listItem in theList: + if(theEntry.lower()==listItem.lower()): + return i + i=i+1 + return -1 + + +""" +Check if an entry is in a sublist of our configuration +""" +def checkIfEntryIsInConfigurationKey(configuration,theKey,theEntry): + for listItem in configuration[theKey]: + if(theEntry==listItem): return 1 + return 0 + +""" +Check if a joint is declared in the configuration hierarchy +""" +def getConfigurationJointIsDeclaredInHierarchy(configuration,theEntry): + #------------------------------------------------------------------------------------------- + if (theEntry=="everything"): + print(bcolors.WARNING,"EVERYTHING.. is declared always.. ",bcolors.ENDC) + return 1 + #------------------------------------------------------------------------------------------- + try: + out = theEntry.split('_') + theEntry=out[0] + except: + print("getConfigurationJointIsDeclaredInHierarchy could not split ",theEntry) + #------------------------------------------------------------------------------------------- + if 'banned' in configuration: + for listItem in configuration['banned']: + if(theEntry.lower()==listItem['output'].lower()): + print(bcolors.WARNING," Joint ",theEntry," is declared in banlist! ",bcolors.ENDC) + return 1 + + if 'hierarchy' in configuration: + for listItem in configuration['hierarchy']: + #print("Check ",theEntry," vs ",listItem['joint']) + if(theEntry.lower()==listItem['joint'].lower()): + print("The Joint ",theEntry," is : declared in hierarchy") + return 1 + + print("Joint is not declared in hierarchy..") + #------------------------------------------------------------------------------------------- + return 0 + + +""" +Retrieve the configuration joint priority of a joint is declared in the configuration hierarchy +""" +def getConfigurationJointPriority(configuration,theEntry): + #------------------------------------------------------------------------------------------- + if (theEntry=="everything"): + print("EVERYTHING.. has a high priority always.. ") + return 1 + #------------------------------------------------------------------------------------------- + try: + out = theEntry.split('_') + theEntry=out[0] + except: + print("getConfigurationJointPriority could not split ",theEntry) + #------------------------------------------------------------------------------------------- + if 'outputMode' in configuration: + if (configuration['outputMode'] == "3d"): + print(bcolors.WARNING,"We treat all joints as terribly important in 3D point mode!",bcolors.ENDC) + return 1 + + if 'banned' in configuration: + for listItem in configuration['banned']: + if(theEntry==listItem['output']): + print(bcolors.WARNING," Joint ",theEntry," is in banlist! ",bcolors.ENDC) + return 0 + + if 'hierarchy' in configuration: + for listItem in configuration['hierarchy']: + if(theEntry==listItem['joint']): + print("The Importance of Joint ",theEntry," is : ",listItem['importance']) + return listItem['importance'] + + print("The Importance of Joint ",theEntry," is : 0 ") + #------------------------------------------------------------------------------------------- + return 0 + + +""" +Get the parent network from our configuration joint hierarchy +""" +def getParentNetwork(configuration,theEntry): + #------------------------------------------------------------------------------------------- + if (theEntry=="everything"): + print("EVERYTHING.. has no parent.. ") + return "none" + #------------------------------------------------------------------------------------------- + try: + out = theEntry.split('_') + theJoint=out[0] + theChannel=out[1] + except: + print("getParentNetwork could not split ",theEntry) + theJoint=theEntry + theChannel=0 + #------------------------------------------------------------------------------------------- + print("Checking the parent of Joint(",theJoint,")/Channel(",theChannel,")") + for listItem in configuration['hierarchy']: + if(theJoint==listItem['joint']): + if (listItem['inheritNetwork']=="none"): + return "none" + parentNetwork="%s_%s" % (listItem['inheritNetwork'],theChannel) + if (parentNetwork==theEntry): + return "none" + else: + return parentNetwork + #------------------------------------------------------------------------------------------- + return "none" + + +if __name__ == '__main__': + print("Tools.py is a library!") + splitTextBasedOnGroupNumber(3,"tmp.tmp","tmpF.tmp") + diff --git a/src/python/mnet4/writeCSV.py b/src/python/mnet4/writeCSV.py new file mode 100755 index 0000000..5494fd3 --- /dev/null +++ b/src/python/mnet4/writeCSV.py @@ -0,0 +1,33 @@ +#!/usr/bin/python3 +import numpy as np +import csv +import gc +import time +import array +import sys + + +def writeCSVFileHeader(filenameOutput,inputListLabels,inputStart,inputEnd): + inputNumber=0 + fileCSV = open(filenameOutput,"w") + for entry in inputListLabels[inputStart:inputEnd]: + #if (inputNumber>=inputStart) and (inputNumber=inputStart) and (inputNumber 1: + for i in range(0, len(sys.argv)): + if sys.argv[i] == "--headless": + headless = True + if sys.argv[i] == "--live": + liveDemo = True + if sys.argv[i] == "--mt": + multiThreaded = True + if sys.argv[i] == "--calib": + calibrationFile = sys.argv[i+1] + if sys.argv[i] == "--focalLength": + fX = float(sys.argv[i+1]) + fY = float(sys.argv[i+2]) + alterFocalLength = True + if sys.argv[i] == "--frameskip": + frameSkip = int(sys.argv[i+1]) + if sys.argv[i] == "--nnsubsample": + doNNEveryNFrames = int(sys.argv[i+1]) + if sys.argv[i] == "--ik": + hcdLearningRate = float(sys.argv[i+1]) + hcdEpochs = int(sys.argv[i+2]) + hcdIterations = int(sys.argv[i+3]) + if sys.argv[i] == "--smooth": + smoothingSampling = float(sys.argv[i+1]) + smoothingCutoff = float(sys.argv[i+2]) + if sys.argv[i] == "--noik": + doHCDPostProcessing = 0 + if sys.argv[i] == "--aspectCorrection": + aspectCorrection = float(sys.argv[i+1]) + if sys.argv[i] == "--noise": + addNoise = float(sys.argv[i+1]) + if sys.argv[i] == "--size": + videoWidth = int(sys.argv[i+1]) + videoHeight = int(sys.argv[i+2]) + if sys.argv[i] == "--scale": + scale = float(sys.argv[i+1]) + if sys.argv[i] == "--all": + doBody = True + doFace = True + doREye = True + doMouth = True + doHands = True + if sys.argv[i] == "--centercrop": + centerCrop = True + if sys.argv[i] == "--nobody": + doBody = False + if sys.argv[i] == "--face": + doFace = True + if sys.argv[i] == "--eyes" or sys.argv[i] == "--reye": + doREye = True + if sys.argv[i] == "--mouth": + doMouth = True + if sys.argv[i] == "--hands": + doHands = True + if sys.argv[i] == "--save": + saveVideo = True + if sys.argv[i] == "--engine": + engine = sys.argv[i+1] + print("Selecting MocapNET engine:", engine) + if sys.argv[i] == "--from": + videoFilePath = sys.argv[i+1] + if sys.argv[i] == "--profile": + doProfiling = True + + if "/dev/video" in videoFilePath: + smoothingSampling = 20.0 + smoothingCutoff = 8.0 + print("Special low smoothing setup for live camera") + + # --------------------------------------------------------------------- + # Initialize MocapNET + # --------------------------------------------------------------------- + print("\nInitializing MocapNET...") + mnet = easyMocapNETConstructor( + engine, + doProfiling = doProfiling, + multiThreaded = multiThreaded, + doHCDPostProcessing = doHCDPostProcessing, + hcdLearningRate = hcdLearningRate, + hcdEpochs = hcdEpochs, + hcdIterations = hcdIterations, + smoothingSampling = smoothingSampling, + smoothingCutoff = smoothingCutoff, + bvhScale = scale, + doBody = doBody, + doFace = doFace, + doREye = doREye, + doMouth = doMouth, + doHands = doHands, + addNoise = addNoise + ) + + if calibrationFile != "": + print("Enforcing Calibration file :", calibrationFile) + mnet.bvh.configureRendererFromFile(calibrationFile) + + if alterFocalLength and fX is not None and fY is not None: + commands = dict() + commands["fX"] = fX + commands["fY"] = fY + mnet.bvh.configureRenderer(commands) + + mnet.test() + # Record BVH to out.bvh (if not liveDemo, will be closed at end) + mnet.recordBVH(not liveDemo) + + # --------------------------------------------------------------------- + # Initialize YMAPNet 2D Pose Estimator + # --------------------------------------------------------------------- + print("\nInitializing YMAPNet 2D pose estimator (PoseEstimator2D)...") + # These defaults match runYMAPNet.py + pose_threshold = 84 + pose_keypoint_thresh = 60.0 + pose_engine = "tensorflow" # YMAPNet backend + profiling_2d = False + + estimator = PoseEstimator2D( + modelPath='2d_pose_estimation', + threshold=pose_threshold, + keypoint_threshold=pose_keypoint_thresh, + engine=pose_engine, + profiling=profiling_2d, + illustrate=False, + monitor=list(), + window_arrangement=list() + ) + + # --------------------------------------------------------------------- + # Capture device + # --------------------------------------------------------------------- + cap = getCaptureDeviceFromPath(videoFilePath, videoWidth, videoHeight) + + # --------------------------------------------------------------------- + # Main loop + # --------------------------------------------------------------------- + print("\nStarting MocapNET using YMAPNet 2D Joint Estimator Input") + print("Press ESC or q to quit.\n") + + frameNumber = 0 + maxBrokenFrames = 100 + brokenFrames = 0 + totalProcessingTime = 0.0 + totalProcessingSamples = 0 + + # For optional performance history plotting inside mnet + bvhAnglesForPlotting = list() + + while cap.isOpened(): + success, image = cap.read() + + #Crop input to square before YMAPNet + if (centerCrop): + image = extract_centered_rectangle(image) #<- aspect ratio is not correct .. + + # Frame skipping + if frameSkip > 0: + for _ in range(0, frameSkip): + frameNumber += 1 + success, image = cap.read() + + if not success or image is None: + brokenFrames += 1 + eprint("Ignoring empty camera frame:", brokenFrames, "/", maxBrokenFrames) + if brokenFrames > maxBrokenFrames: + break + continue + + # Reset broken frame counter on success + brokenFrames = 0 + + # Aspect ratio correction (optional) + if aspectCorrection != 1.0: + width = int(image.shape[1] * aspectCorrection) + height = int(image.shape[0]) + image = cv2.resize(image, (width, height)) + + imageClean = image.copy() + + # ----------------------------------------------------------------- + # 2D Pose Estimation via YMAPNet (PoseEstimator2D) + # ----------------------------------------------------------------- + start_2d = time.time() + + # Run YMAPNet on current frame + estimator.process(image) + + # If no skeletons detected, skip frame + if len(estimator.skeletons) == 0: + frameNumber += 1 + continue + + # This returns the exact format MocapNET expects: + # {'2dx_nose': ..., '2dy_nose': ..., '2dv_nose': ..., ... } + mocapNETInput = estimator.encodeSkeletonAsDict(0, denormalize=True, keypoint_names=mnet_names) + #print("mocapNETInput Before",mocapNETInput) + mocapNETInput = guessLandmarks(mocapNETInput) + mocapNETInput = fakeFeet(mocapNETInput) + #print("\n\nmocapNETInput ",mocapNETInput) + + # ----------------------------------------------------------------- + # IMPORTANT: match MocapNET's training aspect ratio, just like the + # MediaPipe-based pipeline used to do. + # ----------------------------------------------------------------- + height, width = image.shape[:2] + currentAspectRatio = width / float(height) + + for key in list(mocapNETInput.keys()): + if key.startswith("2dx_"): + part_name = key[4:] # strip "2dx_" + y_key = "2dy_" + part_name + if y_key in mocapNETInput: + x = mocapNETInput[key] + y = mocapNETInput[y_key] + # Apply the same normalization MediaPipe path used + x2, y2 = normalize2DPointWhileAlsoMatchingTrainingAspectRatio( + x, y, currentAspectRatio + ) + mocapNETInput[key] = x2 + mocapNETInput[y_key] = y2 + # ----------------------------------------------------------------- + + # For visualization, draw YMAPNet annotations on a copy + annotated_image = image.copy() + # Do not open extra windows from PoseEstimator2D; MocapNET will handle showing + estimator.visualize(annotated_image, show=True, save=False) + + end_2d = time.time() + mnet.hz_2DEst = secondsToHz(end_2d - start_2d) + mnet.history_hz_2DEst.append(mnet.hz_2DEst) + if len(mnet.history_hz_2DEst) > mnet.perfHistorySize: + mnet.history_hz_2DEst.pop(0) + + # ----------------------------------------------------------------- + # MocapNET 3D prediction + # ----------------------------------------------------------------- + doNN = 1 + if doNNEveryNFrames > 0: + doNN = (frameNumber % doNNEveryNFrames) == 0 + + start_total = time.time() + mocapNET3DOutput = mnet.predict3DJoints(mocapNETInput, runNN=doNN, runHCD=True) + mocapNETBVHOutput = mnet.outputBVH + bvhAnglesForPlotting.append(mocapNETBVHOutput) + if len(bvhAnglesForPlotting) > 100: + bvhAnglesForPlotting.pop(0) + + # Update timing stats + totalProcessingTime += (time.time() - start_total) + totalProcessingSamples += 1 + + # ----------------------------------------------------------------- + # Visualization + # ----------------------------------------------------------------- + # visualizeMocapNETEnsemble returns (image, plotImage) + imageOut, plotImage = visualizeMocapNETEnsemble( + mnet, + annotated_image, + plotBVHChannels=False, + bvhAnglesForPlotting=bvhAnglesForPlotting + ) + + frameNumber += 1 + + # Print status line in terminal + mnet.printStatus() + + # ----------------------------------------------------------------- + # Optional saving of frames + # ----------------------------------------------------------------- + if saveVideo: + cv2.imwrite('colorFrame_0_%05u.jpg' % (frameNumber), imageOut) + + # ----------------------------------------------------------------- + # Show window (unless headless) + # ----------------------------------------------------------------- + if not headless: + cv2.imshow('MocapNET 4 using YMAPNet 2D Joints', imageOut) + key = cv2.waitKey(1) & 0xFF + if key == 27 or key == ord('q') or key == ord('Q'): + print("Terminating after receiving keyboard request") + break + + # --------------------------------------------------------------------- + # Cleanup + # --------------------------------------------------------------------- + cap.release() + if not headless: + cv2.destroyAllWindows() + + if totalProcessingSamples > 0: + avg_ms = 1000.0 * (totalProcessingTime / totalProcessingSamples) + avg_hz = secondsToHz(totalProcessingTime / totalProcessingSamples) + print("Average processing time over", totalProcessingSamples, "frames:") + print(" %0.1f ms (%0.1f Hz)" % (avg_ms, avg_hz)) + else: + print("No processing time statistics (no valid frames processed).") + + # Save video if requested + if saveVideo: + os.system( + "ffmpeg -framerate 30 -i colorFrame_0_%%05d.jpg " + "-s %ux%u -y -r 30 -pix_fmt yuv420p -threads 8 " + "livelastRun3DHiRes.mp4" % (videoWidth, videoHeight) + ) + + # Destroy MocapNET object so that out.bvh is finalized + del mnet + + +# ------------------------------------------------------------------------- +# Entry point +# ------------------------------------------------------------------------- +if __name__ == '__main__': + streamPosesFromCameraToMocapNET() +