From f4237dca411b60b3146152a61fe2acbb8f62dea7 Mon Sep 17 00:00:00 2001 From: gineshidalgo99 Date: Thu, 24 May 2018 20:59:24 -0400 Subject: [PATCH] Add flag frame_keep_distortion, improved calib doc --- doc/Chessboard_in_PDF/pattern.eps | Bin 0 -> 168166 bytes doc/Chessboard_in_PDF/pattern.pdf | 1 + doc/calibration_demo.md | 16 +- doc/demo_overview.md | 2 +- doc/installation.md | 25 +- doc/release_notes.md | 18 +- examples/openpose/openpose.cpp | 5 +- .../1_custom_post_processing.cpp | 5 +- .../1_openpose_read_and_display.cpp | 5 +- .../2_user_processing_function.cpp | 5 +- .../1_user_synchronous_postprocessing.cpp | 5 +- .../2_user_synchronous_input.cpp | 5 +- .../3_user_synchronous_output.cpp | 5 +- .../6_user_asynchronous_output.cpp | 5 +- include/openpose/producer/flirReader.hpp | 5 +- .../openpose/producer/spinnakerWrapper.hpp | 6 +- .../openpose/utilities/flagsToOpenPose.hpp | 1 + src/openpose/producer/flirReader.cpp | 17 +- src/openpose/producer/spinnakerWrapper.cpp | 318 +++++++++++------- src/openpose/utilities/flagsToOpenPose.cpp | 8 +- 20 files changed, 286 insertions(+), 171 deletions(-) create mode 100644 doc/Chessboard_in_PDF/pattern.eps create mode 100644 doc/Chessboard_in_PDF/pattern.pdf diff --git a/doc/Chessboard_in_PDF/pattern.eps b/doc/Chessboard_in_PDF/pattern.eps new file mode 100644 index 0000000000000000000000000000000000000000..bcdd8af7ccb7075fde860303981b66f119690414 GIT binary patch literal 168166 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 N{I0!xoj_Zy{{?laZG->- literal 0 HcmV?d00001 diff --git a/doc/Chessboard_in_PDF/pattern.pdf b/doc/Chessboard_in_PDF/pattern.pdf new file mode 100644 index 00000000..d310f3ec --- /dev/null +++ b/doc/Chessboard_in_PDF/pattern.pdf @@ -0,0 +1 @@ +%PDF-1.2 1 0 obj << /Creator (Adobe Illustrator_TM_ 7.0) /Producer (Acrobat PDF File Format 1.1 for Macintosh) >> endobj 2 0 obj << /Pages 4 0 R /Type /Catalog /Outlines 7 0 R >> endobj 3 0 obj << /Type /Page /Parent 4 0 R /Resources << /ProcSet [ /PDF /Text ] >> /Contents 5 0 R >> endobj 4 0 obj << /Kids [ 3 0 R ] /Count 1 /Type /Pages /MediaBox [ 0 0 612 792 ] >> endobj 5 0 obj << /Length 6 0 R >> stream endstream endobj 6 0 obj 0 endobj 7 0 obj << /Count 0 /Type /Outlines >> endobj xref 0 8 0000000000 65535 f 0000000009 00000 n 0000000123 00000 n 0000000193 00000 n 0000000303 00000 n 0000000394 00000 n 0000000448 00000 n 0000000466 00000 n trailer << /Size 8 /Info 1 0 R /Root 2 0 R >> startxref 516 %%EOF 8 0 obj << /Length 9 0 R >> stream 1 0 0 1 0 -1 cm BX /Layer << /Title (Layer 1) /Visible true /Preview true /Editable true /Printed true /Dimmed false /Color [20224 32768 65535 ] >> BDC 0 0 0 1 k 0 0 0 1 K 0 w [ ] 0 d 0 J 0 j 4 M 0 i %%Note: 93.5508 269.7693 m 8.5254 269.7693 l 8.5254 184.7419 l 93.5508 184.7419 l 93.5508 269.7693 l b 93.5508 439.8240 m 8.5254 439.8240 l 8.5254 354.7967 l 93.5508 354.7967 l 93.5508 439.8240 l b 178.5585 184.7508 m 93.5332 184.7508 l 93.5332 99.7235 l 178.5585 99.7235 l 178.5585 184.7508 l b 263.6016 269.7959 m 178.5762 269.7959 l 178.5762 184.7685 l 263.6016 184.7685 l 263.6016 269.7959 l b 178.5940 354.7701 m 93.5685 354.7701 l 93.5685 269.7427 l 178.5940 269.7427 l 178.5940 354.7701 l b 263.6016 439.8507 m 178.5762 439.8507 l 178.5762 354.8232 l 263.6016 354.8232 l 263.6016 439.8507 l b 93.5863 609.9053 m 8.5608 609.9053 l 8.5608 524.8779 l 93.5863 524.8779 l 93.5863 609.9053 l b 93.5863 779.9600 m 8.5608 779.9600 l 8.5608 694.9327 l 93.5863 694.9327 l 93.5863 779.9600 l b 178.5940 524.8868 m 93.5685 524.8868 l 93.5685 439.8594 l 178.5940 439.8594 l 178.5940 524.8868 l b 263.6371 609.9319 m 178.6116 609.9319 l 178.6116 524.9045 l 263.6371 524.9045 l 263.6371 609.9319 l b 178.6293 694.9062 m 93.6040 694.9062 l 93.6040 609.8788 l 178.6293 609.8788 l 178.6293 694.9062 l b 263.6371 779.9866 m 178.6116 779.9866 l 178.6116 694.9592 l 263.6371 694.9592 l 263.6371 779.9866 l b 348.6624 184.7375 m 263.6371 184.7375 l 263.6371 99.7102 l 348.6624 99.7102 l 348.6624 184.7375 l b 433.7055 269.7826 m 348.6802 269.7826 l 348.6802 184.7552 l 433.7055 184.7552 l 433.7055 269.7826 l b 348.6447 354.8100 m 263.6193 354.8100 l 263.6193 269.7826 l 348.6447 269.7826 l 348.6447 354.8100 l b 433.7055 439.8374 m 348.6802 439.8374 l 348.6802 354.8100 l 433.7055 354.8100 l 433.7055 439.8374 l b 518.7132 184.7641 m 433.6879 184.7641 l 433.6879 99.7368 l 518.7132 99.7368 l 518.7132 184.7641 l b 603.7563 269.8092 m 518.7310 269.8092 l 518.7310 184.7818 l 603.7563 184.7818 l 603.7563 269.8092 l b 518.7487 354.7834 m 433.7232 354.7834 l 433.7232 269.7560 l 518.7487 269.7560 l 518.7487 354.7834 l b 603.7563 439.8639 m 518.7310 439.8639 l 518.7310 354.8365 l 603.7563 354.8365 l 603.7563 439.8639 l b 348.6979 524.8735 m 263.6725 524.8735 l 263.6725 439.8462 l 348.6979 439.8462 l 348.6979 524.8735 l b 433.7410 609.9185 m 348.7156 609.9185 l 348.7156 524.8913 l 433.7410 524.8913 l 433.7410 609.9185 l b 348.6802 694.9460 m 263.6548 694.9460 l 263.6548 609.9185 l 348.6802 609.9185 l 348.6802 694.9460 l b 433.7410 779.9733 m 348.7156 779.9733 l 348.7156 694.9460 l 433.7410 694.9460 l 433.7410 779.9733 l b 518.7487 524.9001 m 433.7232 524.9001 l 433.7232 439.8727 l 518.7487 439.8727 l 518.7487 524.9001 l b 603.7918 609.9452 m 518.7664 609.9452 l 518.7664 524.9178 l 603.7918 524.9178 l 603.7918 609.9452 l b 518.7841 694.9194 m 433.7587 694.9194 l 433.7587 609.8920 l 518.7841 609.8920 l 518.7841 694.9194 l b 603.7918 779.9999 m 518.7664 779.9999 l 518.7664 694.9725 l 603.7918 694.9725 l 603.7918 779.9999 l b 93.5608 99.6839 m 8.5355 99.6839 l 8.5355 14.6566 l 93.5608 14.6566 l 93.5608 99.6839 l b 263.6116 99.7106 m 178.5862 99.7106 l 178.5862 14.6832 l 263.6116 14.6832 l 263.6116 99.7106 l b 433.7155 99.6972 m 348.6902 99.6972 l 348.6902 14.6699 l 433.7155 14.6699 l 433.7155 99.6972 l b 603.7663 99.7239 m 518.7410 99.7239 l 518.7410 14.6965 l 603.7663 14.6965 l 603.7663 99.7239 l b EMC EX endstream endobj 9 0 obj 3422 endobj 3 0 obj << /Type /Page /Parent 4 0 R /Contents 8 0 R /Resources << /ProcSet [ /PDF ] >> /Rotate 0 /MediaBox [ 0 0 612 792 ] /CropBox [ 0 0 612 792 ] /PieceInfo <> >> >> endobj 1 0 obj << /CreationDate (D:19990908151621) /ModDate (D:19990908151621) /Creator (Adobe Illustrator 7.0) /Producer (Acrobat PDF File Format 1.1 for Windows) /Author (Unknown) >> endobj xref 0 1 0000000000 65535 f 1 1 0000004478 00000 n 3 1 0000004250 00000 n 8 2 0000000754 00000 n 0000004229 00000 n trailer << /Size 10 /Prev 516 /Root 2 0 R /Info 1 0 R >> startxref 4663 %%EOF \ No newline at end of file diff --git a/doc/calibration_demo.md b/doc/calibration_demo.md index f68d2051..d1499510 100644 --- a/doc/calibration_demo.md +++ b/doc/calibration_demo.md @@ -5,18 +5,19 @@ OpenPose Calibration Module and Demo 1. [Introduction](#introduction) 2. [Installing the Calibration Module](#installing-the-calibration-module) 3. [Quick Start](#quick-start) +4. [Using a Different Camera Brand](#using-a-different-camera-brand) ## Introduction This experimental module performs camera calibration (distortion, intrinsic, and extrinsic camera parameter extraction). It computes and saves the intrinsics parameters of the input images. It is built on top of OpenCV, but aimed to simplify the process for people with no calibration or computer vision background at all (or for lazy people like myself). +Note: We are not aiming to have the best calibration toolbox, but the simplest one. If very high quality calibration is required, I am sure there must exist many other toolboxs with better extrinsic parameter estimation tools. + ## Installing the OpenPose 3-D Reconstruction Module -Install OpenPose following [doc/installation.md](./installation.md#3d-reconstruction-module). In addition, you must enable the `WITH_EIGEN` flag when running CMake. You can either set it to: - 1. `APT_GET` if you download it with `sudo apt-get install libeigen3-dev` (Ubuntu only). - 2. `BUILD` if you want to let OpenPose download it and keep it inside the OpenPose folder (no installation nor extra steps required). +Check [doc/installation.md#calibration-module](./installation.md#calibration-module) for installation steps. @@ -25,7 +26,7 @@ Note: This example will assume that the target are 3 Flir/Point Grey cameras, bu 1. Distortion and intrinsic parameter calibration: 1. Run OpenPose and save images for your desired camera. Use a grid (chessboard) pattern and move around all the image area. I.e., cover several distances, and within each distance, cover all parts of the image view (all corners and center). You should get at least 100-150 images for a good calibration. Note: we recommend saving the images in PNG format (default behavior) in order to improve calibration quality. - 1. Webcam calibration: `./build/examples/openpose/openpose.bin --num_gpu 0 --write_images {intrinsic_images_folder_path}`. + 1. Webcam calibration: `./build/examples/openpose/openpose.bin --num_gpu 0 --frame_keep_distortion --write_images {intrinsic_images_folder_path}`. 2. Flir camera calibration: Add the flags `--flir_camera --flir_camera_index 0` (or the desired flir camera index) to the webcam command. 3. Calibration from video sequence: Add the flag `--video {video_path}` to the webcam command. 4. Any other camera brand: Simply save your images in {intrinsic_images_folder_path}, file names are not relevant. @@ -39,6 +40,13 @@ Note: This example will assume that the target are 3 Flir/Point Grey cameras, bu ``` 4. In this case, the intrinsic parameters would have been generated as {intrinsic_images_folder_path}/18079958.xml. 5. Run steps 1-4 for each one of your cameras. + 6. After you calibrate the camera intrinsics, when you run OpenPose with those cameras, you should see the lines in real-life to be (almost) perfect lines in the image. Otherwise, the calibration was not good. Try checking straight patterns such us wall corners or ceilings: +``` +# With distortion (lines might seem with a more circular shape) +./build/examples/openpose/openpose.bin --num_gpu 0 --flir_camera --flir_camera_index 0 --frame_keep_distortion +# Without distortion (lines should look as lines) +./build/examples/openpose/openpose.bin --num_gpu 0 --flir_camera --flir_camera_index 0 +``` 2. Extrinsic parameter calibration: 1. We are still implementing this part. Documentation will be available after completing it. diff --git a/doc/demo_overview.md b/doc/demo_overview.md index 4fb15b5d..a2b37d1b 100644 --- a/doc/demo_overview.md +++ b/doc/demo_overview.md @@ -150,7 +150,7 @@ Each flag is divided into flag name, default value, and description. - DEFINE_bool(frames_repeat, false, "Repeat frames when finished."); - DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is too long, it will skip frames. If it is too fast, it will slow it down."); - DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); - +- DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e., it will leave it as it is."); 3. OpenPose - DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); - DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the input image resolution."); diff --git a/doc/installation.md b/doc/installation.md index e650801f..fdd1da63 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -15,11 +15,12 @@ OpenPose - Installation 2. [MPI Model](#mpi-model) 3. [CPU Version](#cpu-version) 4. [3D Reconstruction Module](#3d-reconstruction-module) - 5. [Compiling without cuDNN](#compiling-without-cudnn) - 6. [Custom Caffe (Ubuntu Only)](#custom-caffe-ubuntu-only) - 7. [Custom OpenCV (Ubuntu Only)](#custom-opencv-ubuntu-only) - 8. [Doxygen Documentation Autogeneration (Ubuntu Only)](#doxygen-documentation-autogeneration-ubuntu-only) - 9. [CMake Command Line Configuration (Ubuntu Only)](#cmake-command-line-configuration-ubuntu-only) + 5. [Calibration Module](#calibration-module) + 6. [Compiling without cuDNN](#compiling-without-cudnn) + 7. [Custom Caffe (Ubuntu Only)](#custom-caffe-ubuntu-only) + 8. [Custom OpenCV (Ubuntu Only)](#custom-opencv-ubuntu-only) + 9. [Doxygen Documentation Autogeneration (Ubuntu Only)](#doxygen-documentation-autogeneration-ubuntu-only) + 10. [CMake Command Line Configuration (Ubuntu Only)](#cmake-command-line-configuration-ubuntu-only) @@ -119,6 +120,13 @@ The instructions in this section describe the steps to build OpenPose using CMak - [Caffe](http://posefs1.perception.cs.cmu.edu/OpenPose/3rdparty/windows/caffe_2018_01_18.zip): Unzip as `3rdparty/windows/caffe/`. - [Caffe dependencies](http://posefs1.perception.cs.cmu.edu/OpenPose/3rdparty/windows/caffe3rdparty_2017_07_14.zip): Unzip as `3rdparty/windows/caffe3rdparty/`. - [OpenCV 3.1](http://posefs1.perception.cs.cmu.edu/OpenPose/3rdparty/windows/opencv_310.zip): Unzip as `3rdparty/windows/opencv/`. +5. **Eigen prerequisite**: + - Note: This step is optional, only required for some specific extra functionality, such as extrinsic camera calibration. + - If you enable the `WITH_EIGEN` flag when running CMake. You can either: + 1. Do not do anything if you set the `WITH_EIGEN` flag to `BUILD`, CMake will automatically download Eigen. Alternatively, you might prefer to download it manually: + - [Eigen3](http://posefs1.perception.cs.cmu.edu/OpenPose/3rdparty/eigen_2018_05_23.zip): Unzip as `3rdparty/eigen/`. + 2. Run `sudo apt-get install libeigen3-dev` (Ubuntu only) if you prefer to set `WITH_EIGEN` to `APT_GET` (Ubuntu only). + 3. Use your own version of Eigen by setting `WITH_EIGEN` to `BUILD`, run CMake so that OpenPose downloads the zip file, and then replace the contents of `3rdparty/eigen/` by your own version. @@ -276,6 +284,13 @@ After installation, check the [doc/3d_reconstruction_demo.md](./3d_reconstructio +#### Calibration Module +The calibration module is included by default, but you must also enable `WITH_EIGEN` if you intend to use the extrinsic camera parameter estimation tool. You can set that flag to 2 different values: `APT_GET` or `BUILD`, check [Requirements](#requirements) for more information. + +After installation, check the [doc/calibration_demo.md](./calibration_demo.md) instructions. + + + #### Compiling without cuDNN The [cuDNN](https://developer.nvidia.com/cudnn) library is not mandatory, but required for full keypoint detection accuracy. In case your graphics card is not compatible with cuDNN, you can disable it by unchecking `USE_CUDNN` in CMake. diff --git a/doc/release_notes.md b/doc/release_notes.md index 278d25a8..657df72c 100644 --- a/doc/release_notes.md +++ b/doc/release_notes.md @@ -217,14 +217,15 @@ OpenPose Library - Release Notes 2. Flir cameras: Added software trigger and a dedicated thread to keep reading images so latency is removed and runtime is faster (analogously to webcamReader). 3. Flir cameras: Undistortion of the images is x3.5 faster per camera, i.e., x3.5 Flir camera producer reading (it was multi-threaded). 4. Flir cameras: Added flag `flir_camera_index` to allow running on all the cameras at once, or only on 1 camera at the time. - 5. 3-D reconstruction: Added non-linear minimization to further improve 3-D triangulation accuracy by ~5% (Ubuntu only). - 6. CMake: All libraries as single variable (simpler to add/remove libraries). - 7. Datum includes extrinsic and intrinsic camera parameters. - 8. Function `scaleKeypoints(Array& keypoints, const float scale)` also accepts 3D keypoints. - 9. 3D keypoints and camera parameters in meters (instead of millimeters) in order to reduce numerical errors. - 10. New `PoseExtractor` class to contain future ID and tracking algorithms as well as the current OpenPose keypoint detection algorithm. - 11. Added initial alpha versions of the `tracking` and `identification` modules (for now disabled but available in the source code), including `PersonIdExtractor` and `PersonTracker`. `PersonIdExtractor` includes greedy matrix OP-LK matching. - 12. Added catchs to all demos for higher debug information. + 5. Flir cameras: Added flag `frame_keep_distortion` not to undistort the images. E.g., useful when recording images for camera calibration. + 6. 3-D reconstruction: Added non-linear minimization to further improve 3-D triangulation accuracy by ~5% (Ubuntu only). + 7. CMake: All libraries as single variable (simpler to add/remove libraries). + 8. Datum includes extrinsic and intrinsic camera parameters. + 9. Function `scaleKeypoints(Array& keypoints, const float scale)` also accepts 3D keypoints. + 10. 3D keypoints and camera parameters in meters (instead of millimeters) in order to reduce numerical errors. + 11. New `PoseExtractor` class to contain future ID and tracking algorithms as well as the current OpenPose keypoint detection algorithm. + 12. Added initial alpha versions of the `tracking` and `identification` modules (for now disabled but available in the source code), including `PersonIdExtractor` and `PersonTracker`. `PersonIdExtractor` includes greedy matrix OP-LK matching. + 13. Added catchs to all demos for higher debug information. 2. Functions or parameters renamed: 1. Removed scale parameter from hand and face rectangle extractor (causing wrong results if custom `--output_resolution`). 2. Functions `scaleKeypoints`, other than `scaleKeypoints(Array& keypoints, const float scale)`, renamed as `scaleKeypoints2d`. @@ -234,6 +235,7 @@ OpenPose Library - Release Notes 1. Fixed hand and face extraction and rendering scaling issues when `--output_resolution` is not the default one. 2. Part candidates (`--part_candidates`) are saved with the same scale than the final keypoints itself. 3. Fixed bug in keepTopNPeople.hpp (`--number_people_max`) that provoked core dumped if lots of values equal to the threshold. + 4. Flir cameras: Cameras sorted by serial number. Video and images recorded from flir cameras were (and are) assigned the camera parameters based on serial number order, so it would fail if the cameras order was not the same than if sorted by serial number. diff --git a/examples/openpose/openpose.cpp b/examples/openpose/openpose.cpp index 48240f36..50d90083 100755 --- a/examples/openpose/openpose.cpp +++ b/examples/openpose/openpose.cpp @@ -68,6 +68,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -253,7 +256,7 @@ int openPoseDemo() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/examples/tutorial_add_module/1_custom_post_processing.cpp b/examples/tutorial_add_module/1_custom_post_processing.cpp index 44e15abb..2769aa43 100644 --- a/examples/tutorial_add_module/1_custom_post_processing.cpp +++ b/examples/tutorial_add_module/1_custom_post_processing.cpp @@ -77,6 +77,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -251,7 +254,7 @@ int openPoseTutorialWrapper4() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/examples/tutorial_thread/1_openpose_read_and_display.cpp b/examples/tutorial_thread/1_openpose_read_and_display.cpp index fcf9656a..753b6acd 100644 --- a/examples/tutorial_thread/1_openpose_read_and_display.cpp +++ b/examples/tutorial_thread/1_openpose_read_and_display.cpp @@ -48,6 +48,9 @@ DEFINE_string(ip_camera, "", "String with the IP came DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" " input image resolution."); @@ -78,7 +81,7 @@ int openPoseTutorialThread1() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); const auto displayProducerFpsMode = (FLAGS_process_real_time ? op::ProducerFpsMode::OriginalFps : op::ProducerFpsMode::RetrievalFps); diff --git a/examples/tutorial_thread/2_user_processing_function.cpp b/examples/tutorial_thread/2_user_processing_function.cpp index 201e2f6d..9c131e34 100644 --- a/examples/tutorial_thread/2_user_processing_function.cpp +++ b/examples/tutorial_thread/2_user_processing_function.cpp @@ -49,6 +49,9 @@ DEFINE_string(ip_camera, "", "String with the IP came DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" " input image resolution."); @@ -112,7 +115,7 @@ int openPoseTutorialThread2() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); const auto displayProducerFpsMode = (FLAGS_process_real_time ? op::ProducerFpsMode::OriginalFps : op::ProducerFpsMode::RetrievalFps); diff --git a/examples/tutorial_wrapper/1_user_synchronous_postprocessing.cpp b/examples/tutorial_wrapper/1_user_synchronous_postprocessing.cpp index 9c0c5eb4..26bdc899 100644 --- a/examples/tutorial_wrapper/1_user_synchronous_postprocessing.cpp +++ b/examples/tutorial_wrapper/1_user_synchronous_postprocessing.cpp @@ -68,6 +68,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -296,7 +299,7 @@ int openPoseDemo() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/examples/tutorial_wrapper/2_user_synchronous_input.cpp b/examples/tutorial_wrapper/2_user_synchronous_input.cpp index d5fb8235..77cb423d 100644 --- a/examples/tutorial_wrapper/2_user_synchronous_input.cpp +++ b/examples/tutorial_wrapper/2_user_synchronous_input.cpp @@ -68,6 +68,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -331,7 +334,7 @@ int openPoseDemo() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/examples/tutorial_wrapper/3_user_synchronous_output.cpp b/examples/tutorial_wrapper/3_user_synchronous_output.cpp index a2f1aaa6..37aaf17d 100644 --- a/examples/tutorial_wrapper/3_user_synchronous_output.cpp +++ b/examples/tutorial_wrapper/3_user_synchronous_output.cpp @@ -68,6 +68,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -343,7 +346,7 @@ int openPoseDemo() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/examples/tutorial_wrapper/6_user_asynchronous_output.cpp b/examples/tutorial_wrapper/6_user_asynchronous_output.cpp index dae6a3c9..da4dbe40 100644 --- a/examples/tutorial_wrapper/6_user_asynchronous_output.cpp +++ b/examples/tutorial_wrapper/6_user_asynchronous_output.cpp @@ -67,6 +67,9 @@ DEFINE_bool(frames_repeat, false, "Repeat frames when fini DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is" " too long, it will skip frames. If it is too fast, it will slow it down."); DEFINE_string(camera_parameter_folder, "models/cameraParameters/flir/", "String with the folder where the camera parameters are located."); +DEFINE_bool(frame_keep_distortion, false, "If false (default), it will undistortionate the image based on the" + " `camera_parameter_folder` camera parameters; if true, it will not undistortionate, i.e.," + " it will leave it as it is."); // OpenPose DEFINE_string(model_folder, "models/", "Folder path (absolute or relative) where the models (pose, face, ...) are located."); DEFINE_string(output_resolution, "-1x-1", "The image resolution (display and output). Use \"-1x-1\" to force the program to use the" @@ -326,7 +329,7 @@ int openPoseTutorialWrapper1() // producerType const auto producerSharedPtr = op::flagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_camera_resolution, FLAGS_camera_fps, - FLAGS_camera_parameter_folder, + FLAGS_camera_parameter_folder, !FLAGS_frame_keep_distortion, (unsigned int) FLAGS_3d_views, FLAGS_flir_camera_index); // poseModel const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose); diff --git a/include/openpose/producer/flirReader.hpp b/include/openpose/producer/flirReader.hpp index 7fdb9379..62ba9173 100644 --- a/include/openpose/producer/flirReader.hpp +++ b/include/openpose/producer/flirReader.hpp @@ -18,12 +18,11 @@ namespace op /** * Constructor of FlirReader. It opens all the available FLIR cameras */ - explicit FlirReader(const std::string& cameraParametersPath, const Point& cameraResolution); + explicit FlirReader(const std::string& cameraParametersPath, const Point& cameraResolution, + const bool undistortImage = true, const int cameraIndex = -1); ~FlirReader(); - void setCameraIndex(const int cameraIndex); - std::vector getCameraMatrices(); std::vector getCameraExtrinsics(); diff --git a/include/openpose/producer/spinnakerWrapper.hpp b/include/openpose/producer/spinnakerWrapper.hpp index 43122e17..1645b430 100644 --- a/include/openpose/producer/spinnakerWrapper.hpp +++ b/include/openpose/producer/spinnakerWrapper.hpp @@ -14,13 +14,13 @@ namespace op public: /** * Constructor of SpinnakerWrapper. It opens all the available FLIR cameras + * cameraIndex = -1 means that all cameras are taken */ - explicit SpinnakerWrapper(const std::string& cameraParameterPath, const Point& cameraResolution); + explicit SpinnakerWrapper(const std::string& cameraParameterPath, const Point& cameraResolution, + const bool undistortImage, const int cameraIndex = -1); ~SpinnakerWrapper(); - void setCameraIndex(const int cameraIndex); - std::vector getRawFrames(); std::vector getCameraMatrices() const; diff --git a/include/openpose/utilities/flagsToOpenPose.hpp b/include/openpose/utilities/flagsToOpenPose.hpp index 16251e16..c7f2118d 100644 --- a/include/openpose/utilities/flagsToOpenPose.hpp +++ b/include/openpose/utilities/flagsToOpenPose.hpp @@ -26,6 +26,7 @@ namespace op const std::string& cameraResolution = "-1x-1", const double webcamFps = 30., const std::string& cameraParameterPath = "models/cameraParameters/", + const bool undistortImage = true, const unsigned int imageDirectoryStereo = 1, const int flirCameraIndex = -1); diff --git a/src/openpose/producer/flirReader.cpp b/src/openpose/producer/flirReader.cpp index 64ad2bf5..aaacb19f 100644 --- a/src/openpose/producer/flirReader.cpp +++ b/src/openpose/producer/flirReader.cpp @@ -4,9 +4,10 @@ namespace op { - FlirReader::FlirReader(const std::string& cameraParametersPath, const Point& cameraResolution) : + FlirReader::FlirReader(const std::string& cameraParametersPath, const Point& cameraResolution, + const bool undistortImage, const int cameraIndex) : Producer{ProducerType::FlirCamera}, - mSpinnakerWrapper{cameraParametersPath, cameraResolution}, + mSpinnakerWrapper{cameraParametersPath, cameraResolution, undistortImage, cameraIndex}, mFrameNameCounter{0} { try @@ -35,18 +36,6 @@ namespace op } } - void FlirReader::setCameraIndex(const int cameraIndex) - { - try - { - return mSpinnakerWrapper.setCameraIndex(cameraIndex); - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - std::vector FlirReader::getCameraMatrices() { try diff --git a/src/openpose/producer/spinnakerWrapper.cpp b/src/openpose/producer/spinnakerWrapper.cpp index b7de767c..a0f18dfe 100644 --- a/src/openpose/producer/spinnakerWrapper.cpp +++ b/src/openpose/producer/spinnakerWrapper.cpp @@ -11,35 +11,97 @@ namespace op { #ifdef WITH_FLIR_CAMERA + std::vector getSerialNumbers(const Spinnaker::CameraList& cameraList, + const bool sorted) + { + try + { + // Get strSerialNumbers + std::vector strSerialNumbers(cameraList.GetSize()); + for (auto i = 0u; i < strSerialNumbers.size(); i++) + { + // Select camera + auto cameraPtr = cameraList.GetByIndex(i); + + strSerialNumbers[i] = ""; + + Spinnaker::GenApi::CStringPtr ptrStringSerial = cameraPtr->GetTLDeviceNodeMap().GetNode( + "DeviceSerialNumber" + ); + + if (Spinnaker::GenApi::IsAvailable(ptrStringSerial) + && Spinnaker::GenApi::IsReadable(ptrStringSerial)) + { + strSerialNumbers[i] = ptrStringSerial->GetValue(); + } + } + + // Spinnaker::GenICam::gcstring to std::string + std::vector serialNumbers(strSerialNumbers.size()); + for (auto i = 0u ; i < serialNumbers.size() ; i++) + serialNumbers[i] = strSerialNumbers[i]; + + // Sort serial numbers + if (sorted) + std::sort(serialNumbers.begin(), serialNumbers.end()); + + // Return result + return serialNumbers; + } + catch (Spinnaker::Exception &e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return {}; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return {}; + } + } + // This function prints the device information of the camera from the transport // layer; please see NodeMapInfo example for more in-depth comments on printing // device information from the nodemap. int printDeviceInfo(Spinnaker::GenApi::INodeMap &iNodeMap, const unsigned int camNum) { - int result = 0; - - log("Printing device information for camera " + std::to_string(camNum) + "...\n", Priority::High); - - Spinnaker::GenApi::FeatureList_t features; - Spinnaker::GenApi::CCategoryPtr cCategoryPtr = iNodeMap.GetNode("DeviceInformation"); - if (Spinnaker::GenApi::IsAvailable(cCategoryPtr) && Spinnaker::GenApi::IsReadable(cCategoryPtr)) + try { - cCategoryPtr->GetFeatures(features); + int result = 0; - Spinnaker::GenApi::FeatureList_t::const_iterator it; - for (it = features.begin(); it != features.end(); ++it) + log("Printing device information for camera " + std::to_string(camNum) + "...\n", Priority::High); + + Spinnaker::GenApi::FeatureList_t features; + Spinnaker::GenApi::CCategoryPtr cCategoryPtr = iNodeMap.GetNode("DeviceInformation"); + if (Spinnaker::GenApi::IsAvailable(cCategoryPtr) && Spinnaker::GenApi::IsReadable(cCategoryPtr)) { - Spinnaker::GenApi::CNodePtr pfeatureNode = *it; - const auto cValuePtr = (Spinnaker::GenApi::CValuePtr)pfeatureNode; - log(pfeatureNode->GetName() + " : " + - (IsReadable(cValuePtr) ? cValuePtr->ToString() : "Node not readable"), Priority::High); - } - } - else - log("Device control information not available.", Priority::High); - log(" ", Priority::High); + cCategoryPtr->GetFeatures(features); - return result; + Spinnaker::GenApi::FeatureList_t::const_iterator it; + for (it = features.begin(); it != features.end(); ++it) + { + Spinnaker::GenApi::CNodePtr pfeatureNode = *it; + const auto cValuePtr = (Spinnaker::GenApi::CValuePtr)pfeatureNode; + log(pfeatureNode->GetName() + " : " + + (IsReadable(cValuePtr) ? cValuePtr->ToString() : "Node not readable"), Priority::High); + } + } + else + log("Device control information not available.", Priority::High); + log(" ", Priority::High); + + return result; + } + catch (Spinnaker::Exception &e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return -1; + } + catch (const std::exception& e) + { + error(e.what(), __LINE__, __FUNCTION__, __FILE__); + return -1; + } } // This function returns the camera to a normal state by turning off trigger @@ -287,9 +349,11 @@ namespace op Spinnaker::CameraList mCameraList; Spinnaker::SystemPtr mSystemPtr; std::vector mCvMats; + std::vector mSerialNumbers; // Camera index - int mCameraIndex; + const int mCameraIndex; // Undistortion + const bool mUndistortImage; std::vector mRemoveDistortionMaps1; std::vector mRemoveDistortionMaps2; // Thread @@ -299,14 +363,16 @@ namespace op std::atomic mCloseThread; std::thread mThread; - ImplSpinnakerWrapper() : + ImplSpinnakerWrapper(const bool undistortImage, const int cameraIndex) : mInitialized{false}, - mCameraIndex{-1} + mCameraIndex{cameraIndex}, + mUndistortImage{undistortImage} { } - void undistortImage(const int i, const Spinnaker::ImagePtr& imagePtr, - const cv::Mat& cameraIntrinsics, const cv::Mat& cameraDistorsions) + void readAndUndistortImage(const int i, const Spinnaker::ImagePtr& imagePtr, + const cv::Mat& cameraIntrinsics = cv::Mat(), + const cv::Mat& cameraDistorsions = cv::Mat()) { try { @@ -315,42 +381,49 @@ namespace op // Spinnaker to cv::Mat const auto cvMatDistorted = spinnakerWrapperToCvMat(imagePtrColor); // const auto cvMatDistorted = spinnakerWrapperToCvMat(imagePtr); - // Baseline - // mCvMats[i] = cvMatDistorted.clone(); // Undistort - // // Option a - 80 ms / 3 images - // // http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html#undistort - // cv::undistort(cvMatDistorted, mCvMats[i], cameraIntrinsics, cameraDistorsions); - // // In OpenCV 2.4, cv::undistort is exactly equal than cv::initUndistortRectifyMap - // (with CV_16SC2) + cv::remap (with LINEAR). I.e., log(cv::norm(cvMatMethod1-cvMatMethod2)) = 0. - // Option b - 15 ms / 3 images (LINEAR) or 25 ms (CUBIC) - // Distorsion removal - not required and more expensive (applied to the whole image instead of - // only to our interest points) - if (mRemoveDistortionMaps1[i].empty() || mRemoveDistortionMaps2[i].empty()) + if (mUndistortImage) { - const auto imageSize = cvMatDistorted.size(); - cv::initUndistortRectifyMap(cameraIntrinsics, - cameraDistorsions, - cv::Mat(), - // cameraIntrinsics instead of cv::getOptimalNewCameraMatrix to - // avoid black borders - cameraIntrinsics, - // cv::getOptimalNewCameraMatrix(cameraIntrinsics, - // cameraDistorsions, - // imageSize, 1, - // imageSize, 0), - imageSize, - CV_16SC2, // Faster, less memory - // CV_32FC1, // More accurate - mRemoveDistortionMaps1[i], - mRemoveDistortionMaps2[i]); + // Security check + if (cameraIntrinsics.empty() || cameraDistorsions.empty()) + error("Camera intrinsics/distortions were empty.", __LINE__, __FUNCTION__, __FILE__); + // // Option a - 80 ms / 3 images + // // http://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html#undistort + // cv::undistort(cvMatDistorted, mCvMats[i], cameraIntrinsics, cameraDistorsions); + // // In OpenCV 2.4, cv::undistort is exactly equal than cv::initUndistortRectifyMap + // (with CV_16SC2) + cv::remap (with LINEAR). I.e., log(cv::norm(cvMatMethod1-cvMatMethod2)) = 0. + // Option b - 15 ms / 3 images (LINEAR) or 25 ms (CUBIC) + // Distorsion removal - not required and more expensive (applied to the whole image instead of + // only to our interest points) + if (mRemoveDistortionMaps1[i].empty() || mRemoveDistortionMaps2[i].empty()) + { + const auto imageSize = cvMatDistorted.size(); + cv::initUndistortRectifyMap(cameraIntrinsics, + cameraDistorsions, + cv::Mat(), + // cameraIntrinsics instead of cv::getOptimalNewCameraMatrix to + // avoid black borders + cameraIntrinsics, + // cv::getOptimalNewCameraMatrix(cameraIntrinsics, + // cameraDistorsions, + // imageSize, 1, + // imageSize, 0), + imageSize, + CV_16SC2, // Faster, less memory + // CV_32FC1, // More accurate + mRemoveDistortionMaps1[i], + mRemoveDistortionMaps2[i]); + } + cv::remap(cvMatDistorted, mCvMats[i], + mRemoveDistortionMaps1[i], mRemoveDistortionMaps2[i], + // cv::INTER_NEAREST); + cv::INTER_LINEAR); + // cv::INTER_CUBIC); + // cv::INTER_LANCZOS4); // Smoother, but we do not need this quality & its >>expensive } - cv::remap(cvMatDistorted, mCvMats[i], - mRemoveDistortionMaps1[i], mRemoveDistortionMaps2[i], - // cv::INTER_NEAREST); - cv::INTER_LINEAR); - // cv::INTER_CUBIC); - // cv::INTER_LANCZOS4); // Smoother, but we do not need this quality & its >>expensive + // Baseline (do not undistort) + else + mCvMats[i] = cvMatDistorted.clone(); } catch (const std::exception& e) { @@ -367,7 +440,8 @@ namespace op // Get cameras - ~0.005 ms (3 cameras) std::vector cameraPtrs(mCameraList.GetSize()); for (auto i = 0u; i < cameraPtrs.size(); i++) - cameraPtrs.at(i) = mCameraList.GetByIndex(i); + cameraPtrs.at(i) = mCameraList.GetBySerial(mSerialNumbers.at(i)); // Sorted by Serial Number + // cameraPtrs.at(i) = mCameraList.GetByIndex(i); // Sorted by however Spinnaker decided while (!mCloseThread) { // Trigger @@ -495,22 +569,32 @@ namespace op // All cameras if (cameraIndex < 0) { - std::vector threads(imagePtrs.size()-1); - for (auto i = 0u; i < imagePtrs.size()-1; i++) + // Undistort image + if (mUndistortImage) { - // Multi-thread option - threads.at(i) = std::thread{&ImplSpinnakerWrapper::undistortImage, this, i, - imagePtrs.at(i), cameraIntrinsics.at(i), - cameraDistorsions.at(i)}; - // // Single-thread option - // undistortImage(i, imagePtrs.at(i), cameraIntrinsics.at(i), cameraDistorsions.at(i)); + std::vector threads(imagePtrs.size()-1); + for (auto i = 0u; i < imagePtrs.size()-1; i++) + { + // Multi-thread option + threads.at(i) = std::thread{&ImplSpinnakerWrapper::readAndUndistortImage, this, i, + imagePtrs.at(i), cameraIntrinsics.at(i), + cameraDistorsions.at(i)}; + // // Single-thread option + // readAndUndistortImage(i, imagePtrs.at(i), cameraIntrinsics.at(i), cameraDistorsions.at(i)); + } + readAndUndistortImage(imagePtrs.size()-1, imagePtrs.back(), cameraIntrinsics.back(), + cameraDistorsions.back()); + // Close threads + for (auto& thread : threads) + if (thread.joinable()) + thread.join(); + } + // Do not undistort image + else + { + for (auto i = 0u; i < imagePtrs.size(); i++) + readAndUndistortImage(i, imagePtrs.at(i)); } - undistortImage(imagePtrs.size()-1, imagePtrs.back(), cameraIntrinsics.back(), - cameraDistorsions.back()); - // Close threads - for (auto& thread : threads) - if (thread.joinable()) - thread.join(); } // Only 1 camera else @@ -523,8 +607,12 @@ namespace op + std::to_string(cameraIndex) +"`), which doesn't exist. Note that the index is" + " 0-based.", __LINE__, __FUNCTION__, __FILE__); // Undistort image - undistortImage(cameraIndex, imagePtrs.at(cameraIndex), cameraIntrinsics.at(cameraIndex), - cameraDistorsions.at(cameraIndex)); + if (mUndistortImage) + readAndUndistortImage(cameraIndex, imagePtrs.at(cameraIndex), cameraIntrinsics.at(cameraIndex), + cameraDistorsions.at(cameraIndex)); + // Do not undistort image + else + readAndUndistortImage(cameraIndex, imagePtrs.at(cameraIndex)); mCvMats = std::vector{mCvMats[cameraIndex]}; } } @@ -544,9 +632,10 @@ namespace op #endif }; - SpinnakerWrapper::SpinnakerWrapper(const std::string& cameraParameterPath, const Point& resolution) + SpinnakerWrapper::SpinnakerWrapper(const std::string& cameraParameterPath, const Point& resolution, + const bool undistortImage, const int cameraIndex) #ifdef WITH_FLIR_CAMERA - : upImpl{new ImplSpinnakerWrapper{}} + : upImpl{new ImplSpinnakerWrapper{undistortImage, cameraIndex}} #endif { #ifdef WITH_FLIR_CAMERA @@ -566,7 +655,7 @@ namespace op // Retrieve list of cameras from the upImpl->mSystemPtr upImpl->mCameraList = upImpl->mSystemPtr->GetCameras(); - unsigned int numCameras = upImpl->mCameraList.GetSize(); + const unsigned int numCameras = upImpl->mCameraList.GetSize(); log("Number of cameras detected: " + std::to_string(numCameras), Priority::High); @@ -579,8 +668,8 @@ namespace op // Release upImpl->mSystemPtr upImpl->mSystemPtr->ReleaseInstance(); - log("Not enough cameras!\nPress Enter to exit...", Priority::High); - getchar(); + // log("Not enough cameras!\nPress Enter to exit...", Priority::High); + // getchar(); error("No cameras detected.", __LINE__, __FUNCTION__, __FILE__); } @@ -671,8 +760,7 @@ namespace op // // Serial numbers are the only persistent objects we gather in this // example, which is why a std::vector is created. - std::vector strSerialNumbers(upImpl->mCameraList.GetSize()); - for (auto i = 0u; i < strSerialNumbers.size(); i++) + for (auto i = 0; i < upImpl->mCameraList.GetSize(); i++) { // Select camera auto cameraPtr = upImpl->mCameraList.GetByIndex(i); @@ -692,7 +780,7 @@ namespace op error("Unable to set acquisition mode to continuous (entry 'continuous' retrieval " + std::to_string(i) + "). Aborting...", __LINE__, __FUNCTION__, __FILE__); - int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue(); + const int64_t acquisitionModeContinuous = ptrAcquisitionModeContinuous->GetValue(); ptrAcquisitionMode->SetIntValue(acquisitionModeContinuous); @@ -740,29 +828,31 @@ namespace op cameraPtr->BeginAcquisition(); log("Camera " + std::to_string(i) + " started acquiring images...", Priority::High); - - // Retrieve device serial number for filename - strSerialNumbers[i] = ""; - - Spinnaker::GenApi::CStringPtr ptrStringSerial = cameraPtr->GetTLDeviceNodeMap().GetNode( - "DeviceSerialNumber" - ); - - if (Spinnaker::GenApi::IsAvailable(ptrStringSerial) - && Spinnaker::GenApi::IsReadable(ptrStringSerial)) - { - strSerialNumbers[i] = ptrStringSerial->GetValue(); - log("Camera " + std::to_string(i) + " serial number set to " - + strSerialNumbers[i].c_str() + "...", Priority::High); - } - log(" ", Priority::High); } + // Retrieve device serial number for filename + log("\nReading (and sorting by) serial numbers...", Priority::High); + const bool sorted = true; + upImpl->mSerialNumbers = getSerialNumbers(upImpl->mCameraList, sorted); + const auto& serialNumbers = upImpl->mSerialNumbers; + for (auto i = 0u; i < serialNumbers.size(); i++) + log("Camera " + std::to_string(i) + " serial number set to " + + serialNumbers[i] + "...", Priority::High); + // Read camera parameters from SN - std::vector serialNumbers(strSerialNumbers.size()); - for (auto i = 0u ; i < serialNumbers.size() ; i++) - serialNumbers[i] = strSerialNumbers[i]; - upImpl->mCameraParameterReader.readParameters(cameraParameterPath, serialNumbers); + if (upImpl->mUndistortImage) + { + // If all images required + if (upImpl->mCameraIndex < 0) + upImpl->mCameraParameterReader.readParameters(cameraParameterPath, serialNumbers); + // If only one required + else + { + upImpl->mCameraParameterReader.readParameters( + cameraParameterPath, + std::vector(serialNumbers.size(), serialNumbers.at(upImpl->mCameraIndex))); + } + } // Start buffering thread upImpl->mThreadOpened = true; @@ -790,6 +880,8 @@ namespace op #else UNUSED(cameraParameterPath); UNUSED(resolution); + UNUSED(undistortImage); + UNUSED(cameraIndex); error(WITH_FLIR_CAMERA_ERROR, __LINE__, __FUNCTION__, __FILE__); #endif } @@ -806,23 +898,6 @@ namespace op } } - void SpinnakerWrapper::setCameraIndex(const int cameraIndex) - { - try - { - #ifdef WITH_FLIR_CAMERA - upImpl->mCameraIndex = cameraIndex; - #else - UNUSED(cameraIndex); - error(WITH_FLIR_CAMERA_ERROR, __LINE__, __FUNCTION__, __FILE__); - #endif - } - catch (const std::exception& e) - { - error(e.what(), __LINE__, __FUNCTION__, __FILE__); - } - } - std::vector SpinnakerWrapper::getRawFrames() { try @@ -831,7 +906,8 @@ namespace op try { // Security checks - if ((unsigned long long) upImpl->mCameraList.GetSize() + if (upImpl->mUndistortImage && + (unsigned long long) upImpl->mCameraList.GetSize() != upImpl->mCameraParameterReader.getNumberCameras()) error("The number of cameras must be the same as the INTRINSICS vector size.", __LINE__, __FUNCTION__, __FILE__); diff --git a/src/openpose/utilities/flagsToOpenPose.cpp b/src/openpose/utilities/flagsToOpenPose.cpp index e801ae46..977b57ee 100644 --- a/src/openpose/utilities/flagsToOpenPose.cpp +++ b/src/openpose/utilities/flagsToOpenPose.cpp @@ -142,7 +142,8 @@ namespace op const std::string& ipCameraPath, const int webcamIndex, const bool flirCamera, const std::string& cameraResolution, const double webcamFps, const std::string& cameraParameterPath, - const unsigned int imageDirectoryStereo, const int flirCameraIndex) + const bool undistortImage, const unsigned int imageDirectoryStereo, + const int flirCameraIndex) { try { @@ -161,9 +162,8 @@ namespace op { // cameraFrameSize const auto cameraFrameSize = flagsToPoint(cameraResolution, "-1x-1"); - auto flirReader = std::make_shared(cameraParameterPath, cameraFrameSize); - flirReader->setCameraIndex(flirCameraIndex); - return flirReader; + return std::make_shared(cameraParameterPath, cameraFrameSize, undistortImage, + flirCameraIndex); } // Webcam if (type == ProducerType::Webcam)