├── .gitignore ├── tests ├── data │ ├── vignetting_model_empty.vgn │ ├── radiometric_response_empty.crf │ ├── radiometric_response_invalid.crf │ ├── .gitattributes │ ├── radiometric_response_scaling.crf │ ├── radiometric_response_constant.crf │ ├── radiometric_response_identity.crf │ ├── polynomial_vignetting_model_identity.vgn │ ├── nonparametric_vignetting_model_identity.vgn │ └── radiometric_response_calibration │ │ ├── 000001_000.mat │ │ ├── 000002_000.mat │ │ ├── 000003_000.mat │ │ ├── 000004_000.mat │ │ ├── 000005_000.mat │ │ ├── 000006_000.mat │ │ ├── 000008_000.mat │ │ ├── 000010_000.mat │ │ ├── 000012_000.mat │ │ ├── 000015_000.mat │ │ ├── 000018_000.mat │ │ ├── 000022_000.mat │ │ ├── 000026_000.mat │ │ ├── 000031_000.mat │ │ ├── 000037_000.mat │ │ ├── 000044_000.mat │ │ ├── 000052_000.mat │ │ ├── 000062_000.mat │ │ ├── 000074_000.mat │ │ ├── 000088_000.mat │ │ ├── 000104_000.mat │ │ ├── 000123_000.mat │ │ └── 000146_000.mat ├── test_calibrate_radiometric_response.sh ├── CMakeLists.txt ├── test.h.in ├── test_polynomial_vignetting_model.cpp ├── test_nonparametric_vignetting_model.cpp └── test_vignetting_response.cpp ├── doc ├── .gitattributes ├── radiometric-response-calibration-setup.jpg ├── pioneer.gif ├── vignetting-responses.png ├── radiometric-response-calibration-dataset.gif ├── radiometric-response-calibration-result.png ├── radiometric-response-calibration-computation.gif ├── calibrate-vignetting-response.md └── calibrate-radiometric-response.md ├── src ├── apps │ ├── remove_vignetting │ │ └── CMakeLists.txt │ ├── display_vignetting_response │ │ └── CMakeLists.txt │ ├── display_radiometric_response │ │ ├── CMakeLists.txt │ │ └── display_radiometric_response.cpp │ ├── calibrate_radiometric_response │ │ ├── CMakeLists.txt │ │ ├── engel_calibration.h │ │ ├── dataset_collection.h │ │ ├── debevec_calibration.h │ │ ├── calibration.h │ │ ├── dataset.h │ │ ├── dataset_collection.cpp │ │ ├── calibration.cpp │ │ └── engel_calibration.cpp │ ├── calibrate_vignetting_response │ │ ├── CMakeLists.txt │ │ ├── model_fitting.h │ │ ├── blob_tracker.h │ │ ├── blob_tracker.cpp │ │ └── model_fitting.cpp │ └── CMakeLists.txt ├── utils │ ├── CMakeLists.txt │ ├── colors.cpp │ ├── key_code.cpp │ ├── plot_radiometric_response.cpp │ └── plot_histogram.cpp ├── grabbers │ ├── CMakeLists.txt │ ├── grabber.cpp │ └── realsense_grabber.cpp └── radical │ ├── vignetting_model.cpp │ ├── check.cpp │ ├── mat_io.cpp │ ├── nonparametric_vignetting_model.cpp │ ├── polynomial_vignetting_model.cpp │ ├── radiometric_response.cpp │ └── vignetting_response.cpp ├── cmake ├── Config.cmake.in ├── Uninstall.cmake.in ├── Utils.cmake └── modules │ ├── FindRealSense.cmake │ ├── FindPylon.cmake │ └── FindOpenNI2.cmake ├── .appveyor.yml ├── .clang-format ├── .travis.yml ├── LICENSE.md ├── include ├── utils │ ├── colors.h │ ├── key_code.h │ ├── plot_radiometric_response.h │ ├── plot_histogram.h │ ├── mask_saturated_pixels.h │ ├── program_options.h │ ├── plot_polynomial_vignetting_model.h │ ├── arrange_images_in_grid.h │ └── mean_image.h ├── radical │ ├── mat_io.h │ ├── check.h │ ├── nonparametric_vignetting_model.h │ ├── vignetting_response.h │ ├── vignetting_model.h │ ├── polynomial_vignetting_model.h │ ├── radiometric_response.h │ └── exceptions.h └── grabbers │ ├── pylon_grabber.h │ ├── openni2_grabber.h │ ├── realsense_grabber.h │ └── grabber.h └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /tests/data/vignetting_model_empty.vgn: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_empty.crf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_invalid.crf: -------------------------------------------------------------------------------- 1 | 1 2 3 4 5.0 6,1 2 | -------------------------------------------------------------------------------- /tests/data/.gitattributes: -------------------------------------------------------------------------------- 1 | radiometric_response_calibration/** filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /doc/.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | *.gif filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_scaling.crf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/tests/data/radiometric_response_scaling.crf -------------------------------------------------------------------------------- /doc/radiometric-response-calibration-setup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/doc/radiometric-response-calibration-setup.jpg -------------------------------------------------------------------------------- /tests/data/radiometric_response_constant.crf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/tests/data/radiometric_response_constant.crf -------------------------------------------------------------------------------- /tests/data/radiometric_response_identity.crf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/tests/data/radiometric_response_identity.crf -------------------------------------------------------------------------------- /tests/data/polynomial_vignetting_model_identity.vgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/tests/data/polynomial_vignetting_model_identity.vgn -------------------------------------------------------------------------------- /doc/pioneer.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:438ac6d574a76530ae72d7f3c2de2b1dea617f682aec470f12cc2cd264b556da 3 | size 2043598 4 | -------------------------------------------------------------------------------- /src/apps/remove_vignetting/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | APP_ADD(remove_vignetting 2 | OPENCV2 highgui 3 | OPENCV3 highgui imgcodecs 4 | LINK_WITH grabbers utils 5 | ) 6 | -------------------------------------------------------------------------------- /tests/data/nonparametric_vignetting_model_identity.vgn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taketwo/radical/HEAD/tests/data/nonparametric_vignetting_model_identity.vgn -------------------------------------------------------------------------------- /src/apps/display_vignetting_response/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | APP_ADD(display_vignetting_response 2 | OPENCV2 highgui imgproc 3 | OPENCV3 highgui imgproc imgcodecs 4 | ) 5 | -------------------------------------------------------------------------------- /doc/vignetting-responses.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c12d0e95ee12b6b5d314a442ac23f8eadf31441266bbeb64dabfdfa4bc7595dd 3 | size 462784 4 | -------------------------------------------------------------------------------- /src/apps/display_radiometric_response/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | APP_ADD(display_radiometric_response 2 | OPENCV2 highgui 3 | OPENCV3 highgui imgcodecs 4 | LINK_WITH utils 5 | ) 6 | -------------------------------------------------------------------------------- /doc/radiometric-response-calibration-dataset.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d4bc2fdbc9567a20780b50050fe0db215459aec78c55127f12abca820bf80a81 3 | size 924670 4 | -------------------------------------------------------------------------------- /doc/radiometric-response-calibration-result.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8625a88ca289ab3445e8fc393faad369582ee0d65c9a235d31747d2f65e48b03 3 | size 14707 4 | -------------------------------------------------------------------------------- /doc/radiometric-response-calibration-computation.gif: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:14809b19270fd533b9b185313b5b7c0b1365665046e6bbbff775e7b11e03c434 3 | size 253825 4 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | APP_ADD(calibrate_radiometric_response 2 | OPENCV2 highgui imgproc 3 | OPENCV3 highgui imgproc 4 | LINK_WITH grabbers utils ceres 5 | ) 6 | -------------------------------------------------------------------------------- /src/apps/calibrate_vignetting_response/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | APP_ADD(calibrate_vignetting_response 2 | OPENCV2 highgui imgproc 3 | OPENCV3 highgui imgproc 4 | LINK_WITH grabbers utils ceres 5 | ) 6 | 7 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000001_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c38caf1fed0bc07c7cf5dee961e724fdb24867f3edb29746e56ceb847c805c56 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000002_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bd5964be00a4de8c0df9b7f14cd7858f1f00c2a1e4b536b27ee7346ebd1cbf81 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000003_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0b1213a90f1e67b623bc7951745434fc78bce8113d4c9c15d7dcc72ecd680cf5 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000004_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b6489a4c86959c125edcadc9fd80f3581a1a5251b09774ec3774a2f051b781c6 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000005_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b329b6f46e0154e3a9000d54bbd6e599f5f4b526e74063ac8feed9ba7da9be73 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000006_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f0c46102cb975bb2732d3f23e1b3ca4a055ccf990c2904cd4aaaabf8599bccda 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000008_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:014785b682379a16fa9ddd57ebf900a68bef0be16b7c05aacad64685be49a619 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000010_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:58a7ba75c6e8ad36854c675c17e274c8d8093d02f60c5b17ed86b1edc584c521 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000012_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:50822347ccd4664b0af496b04f2f5dd9c03e928c7f605368f6f99aa501d66847 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000015_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0a4c0719815c608e4b2a647a80ae238bc1dfacf60ceae12f8800d3dd4bca899d 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000018_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:fa7e0438580ebf63bf124e834b39d5c45086bc1ac0020b8312cc12a39c6767d7 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000022_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5569e3233b71d7a6e04db645220d1e3e27453a59afc0f5f513b056c226064570 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000026_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ea997526c9141f987f519e33a92d906015bfc8975b7f2df1070716ae553f06e6 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000031_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:91006806af15ee6ee1adc7aa43c4a85eafe8af5b062e4c5ec77c7b95bb818046 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000037_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:23716fd048bd9a0d9ef9356e3233ac429e903017cf8bbbea590e1318261ba8fc 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000044_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8d06664da11968f32666e80a3329af7ca74c2d3bea41922439448e92ace42f41 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000052_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6f353c446e932308fbd56002b6df4e158bd4d8388f156f785f807796cb2e8db5 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000062_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0b557f58092b466e5eee6b96fd64fd060f39f2a0483658cfcbfa1657e1cac365 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000074_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:1aa6eed179f1247cead0b4128a1726590507190b1f58d99d16419becafaedd0d 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000088_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:870be8b711453f15ab5b2ce208881653f5af8fad153ac413ebe9070776d53f63 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000104_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0544ccef0e33a9b4f76e5fe90811fc8afd74b43eab33c00ee81964309c2d5851 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000123_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4e7503037aa295c62b29f300fb67083c63fd2b704295f4f7cb816d20b828918e 3 | size 921620 4 | -------------------------------------------------------------------------------- /tests/data/radiometric_response_calibration/000146_000.mat: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:dfcbb46968517eedca9459f46c62893a69a87783f386ffd4a9a021829e6ffcc8 3 | size 921620 4 | -------------------------------------------------------------------------------- /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | find_package(OpenCV @OpenCV_VERSION@ REQUIRED) 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@_targets_export_name@.cmake") 6 | check_required_components("@PROJECT_NAME@") 7 | -------------------------------------------------------------------------------- /src/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(UTILS_SRC mean_image.cpp key_code.cpp colors.cpp plot_radiometric_response.cpp plot_histogram.cpp) 2 | add_library(utils ${UTILS_SRC}) 3 | target_link_libraries(utils ${LIB_NAME} opencv_core opencv_imgproc) 4 | set_property(TARGET utils PROPERTY POSITION_INDEPENDENT_CODE ON) 5 | -------------------------------------------------------------------------------- /.appveyor.yml: -------------------------------------------------------------------------------- 1 | shallow_clone: true 2 | 3 | cache: 4 | - c:\tools\vcpkg\installed\ 5 | 6 | install: 7 | - cd c:\tools\vcpkg 8 | - git pull >$null 9 | - .\bootstrap-vcpkg.bat >$null 10 | - vcpkg update 11 | - vcpkg install opencv boost 12 | 13 | configuration: Release 14 | 15 | build: 16 | parallel: true 17 | 18 | build_script: 19 | - cd c:\projects\radical 20 | - mkdir build 21 | - cd build 22 | - cmake -DCMAKE_TOOLCHAIN_FILE=c:\tools\vcpkg\scripts\buildsystems\vcpkg.cmake 23 | -DBUILD_TESTS=ON -DBUILD_APPS=ON 24 | .. 25 | - cmake --build . --config Release 26 | 27 | test_script: 28 | - ctest -VV -C Release 29 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language : Cpp 3 | AllowShortIfStatementsOnASingleLine : false 4 | AllowShortLoopsOnASingleLine : false 5 | AllowShortFunctionsOnASingleLine : Empty 6 | BasedOnStyle : Google 7 | BreakConstructorInitializers : BeforeComma 8 | ConstructorInitializerAllOnOneLineOrOnePerLine : false 9 | ConstructorInitializerIndentWidth : 0 10 | ColumnLimit : 120 11 | Standard : Cpp11 12 | ReflowComments : false 13 | ... 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: cpp 4 | compiler: gcc 5 | git: 6 | depth: 1 7 | lfs_skip_smudge: true 8 | env: 9 | - OPENCV_VERSION=2.4.8 10 | - OPENCV_VERSION=3.1.0 11 | - OPENCV_VERSION=3.4.0 12 | addons: 13 | apt: 14 | sources: 15 | - boost-latest 16 | packages: 17 | - libboost1.55-dev libboost-filesystem1.55-dev libboost-program-options1.55-dev libboost-system1.55-dev libboost-test1.55-dev libsuitesparse-dev bc 18 | cache: 19 | directories: 20 | - $HOME/opencv 21 | - $HOME/cmake 22 | - $HOME/eigen 23 | - $HOME/glog 24 | - $HOME/ceres 25 | - $HOME/git-lfs 26 | script: 27 | - bash .travis.sh 28 | -------------------------------------------------------------------------------- /cmake/Uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 3 | endif() 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program("@CMAKE_COMMAND@" 11 | ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif() 18 | else() 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist") 20 | endif() 21 | endforeach() 22 | -------------------------------------------------------------------------------- /src/grabbers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(GRABBERS_SRC grabber.cpp) 2 | 3 | if(WITH_OPENNI2) 4 | find_package(OpenNI2) 5 | if(OPENNI2_FOUND) 6 | list(APPEND GRABBERS_SRC openni2_grabber.cpp) 7 | list(APPEND GRABBERS_DEPS openni2) 8 | endif() 9 | endif() 10 | 11 | if(WITH_REALSENSE) 12 | find_package(RealSense 1.9.7) 13 | if(REALSENSE_FOUND) 14 | list(APPEND GRABBERS_SRC realsense_grabber.cpp) 15 | list(APPEND GRABBERS_DEPS realsense) 16 | endif() 17 | endif() 18 | 19 | if(WITH_PYLON) 20 | find_package(Pylon) 21 | if(PYLON_FOUND) 22 | list(APPEND GRABBERS_SRC pylon_grabber.cpp) 23 | list(APPEND GRABBERS_DEPS ${PYLON_LIBRARIES}) 24 | endif() 25 | endif() 26 | 27 | add_library(grabbers ${GRABBERS_SRC}) 28 | target_include_directories(grabbers PUBLIC ${Boost_INCLUDE_DIRS}) 29 | target_link_libraries(grabbers opencv_core ${GRABBERS_DEPS}) 30 | 31 | foreach(grabber openni2 realsense pylon) 32 | string(TOUPPER ${grabber} GRABBER) 33 | if(${GRABBER}_FOUND) 34 | target_compile_definitions(grabbers PRIVATE "-DHAVE_${GRABBER}") 35 | endif() 36 | endforeach() 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) 2016 Sergey Alexandrov 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 10 | of the Software, and to permit persons to whom the Software is furnished to do 11 | so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /tests/test_calibrate_radiometric_response.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function feq() { 4 | echo $1'=='$2 | bc -l 5 | } 6 | 7 | function fle() { 8 | echo $1'<='$2 | bc -l 9 | } 10 | 11 | exe="$1" 12 | dir="$2/radiometric_response_calibration" 13 | 14 | methods=("debevec" "engel") 15 | for method in "${methods[@]}"; do 16 | crf=$($exe $dir --verbosity 0 --no-visualization --print --method $method -o /tmp/crf) 17 | if [[ $? -ne 0 ]]; then 18 | echo "Calibration app returned non-zero status" 19 | echo "NOTE: this test requires calibration data to be downloaded from Git LFS." 20 | echo " Please make sure to run: git lfs pull" 21 | exit 1 22 | fi 23 | while read -r line; do 24 | values=($(echo $line)) 25 | if [[ ${#values[@]} != 256 ]]; then 26 | echo "CRF should have 256 values ($method)" 27 | exit 1 28 | fi 29 | if [[ $(feq ${values[0]} 0) == 0 ]]; then 30 | echo "CRF should map 0 to 0 ($method)" 31 | exit 1 32 | fi 33 | if [[ $(feq ${values[255]} 1) == 0 ]]; then 34 | echo "CRF should map 255 to 1 ($method)" 35 | exit 1 36 | fi 37 | for i in "${!values[@]-1}"; do 38 | if [[ $(fle ${values[i]} ${values[i+1]}) == 0 ]]; then 39 | echo "CRF should be nondecreasing ($method, element $i)" 40 | exit 1 41 | fi 42 | done 43 | done <<< "$crf" 44 | done 45 | 46 | exit 0 47 | -------------------------------------------------------------------------------- /doc/calibrate-vignetting-response.md: -------------------------------------------------------------------------------- 1 | # `calibrate_vignetting_response` 2 | 3 | This app allows you to calibrate the vignetting response of the camera. 4 | 5 | Get a sheet of white paper and fix it on a table so it is uniformly lit by the 6 | light sources in your office. If your table has a bright color, it is better to 7 | make a contour around the paper with black tape to make sure that the blob 8 | detector is able to distinguish between the white paper and the table. 9 | 10 | Point the camera at the paper and start the app. Adjust the exposure time with 11 | up and down arrows. We want the exposure time to be as large as possible, 12 | however there should be no saturated pixels. For your convenience, overexposed 13 | pixels will be highlighted with red color. After adjusting the exposure time so 14 | that there are no red pixels on the calibration target, press Enter to start 15 | data collection. Move the camera around so that the paper is observed through 16 | every camera pixel. Take care to move the camera so that it does not cast 17 | shadows onto the paper. In the bottom-left corner the amount of collected 18 | samples per pixels is visualized. By default, we aim to collect 100 samples per 19 | pixel. Once this amount is collected, the corresponding pixel will turn green. 20 | After the required number of samples is collected for every pixel, the 21 | vignetting response will be calibrated and stored in a file named after the 22 | camera (model type + serial number). Run with `--help` to see different options. 23 | -------------------------------------------------------------------------------- /src/utils/colors.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "utils/colors.h" 24 | 25 | namespace utils { 26 | 27 | namespace colors { 28 | 29 | const cv::Scalar BGR[3] = {{180, 120, 31}, {44, 160, 51}, {28, 26, 227}}; 30 | const cv::Scalar BGR_LIGHT[3] = {{227, 206, 166}, {138, 223, 178}, {153, 154, 251}}; 31 | } // namespace colors 32 | } // namespace utils 33 | -------------------------------------------------------------------------------- /include/utils/colors.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace utils { 28 | 29 | namespace colors { 30 | 31 | // Colors from the "Paired" palette of Cynthia Brewer 32 | extern const cv::Scalar BGR[3]; 33 | extern const cv::Scalar BGR_LIGHT[3]; 34 | } // namespace colors 35 | } // namespace utils 36 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | get_filename_component(TEST_DATA_DIR "data" ABSOLUTE) 2 | configure_file(test.h.in test.h) 3 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 4 | 5 | add_custom_target(tests "${CMAKE_CTEST_COMMAND}" "-V" VERBATIM) 6 | 7 | macro(TEST_ADD _name) 8 | set(options) 9 | set(one_value_args) 10 | set(multi_value_args LINK_WITH) 11 | cmake_parse_arguments(TEST_ADD "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) 12 | 13 | set(_executable test_${_name}) 14 | add_executable(${_executable} ${_executable}.cpp) 15 | target_include_directories(${_executable} PRIVATE ${Boost_INCLUDE_DIRS}) 16 | target_compile_definitions(${_executable} PRIVATE "-DBOOST_TEST_MODULE=${_name}") 17 | if(LIB_TYPE STREQUAL SHARED) 18 | target_compile_definitions(${_executable} PRIVATE "-DBOOST_TEST_DYN_LINK") 19 | endif() 20 | target_link_libraries(${_executable} 21 | ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} 22 | ${TEST_ADD_LINK_WITH} 23 | ) 24 | add_test(NAME ${_name} COMMAND ${_executable}) 25 | add_dependencies(tests ${_executable}) 26 | endmacro() 27 | 28 | TEST_ADD(radiometric_response LINK_WITH radical) 29 | TEST_ADD(vignetting_response LINK_WITH radical) 30 | TEST_ADD(nonparametric_vignetting_model LINK_WITH radical) 31 | TEST_ADD(polynomial_vignetting_model LINK_WITH radical) 32 | 33 | if(BUILD_APPS) 34 | macro(APP_TEST_ADD _name) 35 | if(NOT WIN32) 36 | set(_script "${CMAKE_CURRENT_SOURCE_DIR}/test_${_name}.sh") 37 | add_test(NAME ${_name} COMMAND ${_script} $ ${TEST_DATA_DIR}) 38 | add_dependencies(tests ${_name}) 39 | endif() 40 | endmacro() 41 | 42 | TEST_ADD(mean_image LINK_WITH radical utils) 43 | APP_TEST_ADD(calibrate_radiometric_response) 44 | endif() 45 | -------------------------------------------------------------------------------- /src/apps/calibrate_vignetting_response/model_fitting.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | radical::PolynomialVignettingModel<3>::Ptr fitPolynomialModel(cv::InputArray data, bool fixed_center = false, 30 | unsigned int max_num_iterations = 50, 31 | bool verbose = false); 32 | -------------------------------------------------------------------------------- /include/utils/key_code.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | namespace utils { 26 | 27 | class KeyCode { 28 | public: 29 | KeyCode(int code); 30 | 31 | enum Key { 32 | ENTER, 33 | ESC, 34 | PLUS, 35 | MINUS, 36 | ARROW_UP, 37 | ARROW_DOWN, 38 | ARROW_RIGHT, 39 | ARROW_LEFT, 40 | NO_KEY, 41 | }; 42 | 43 | bool operator==(Key key) const; 44 | 45 | bool operator!=(Key key) const; 46 | 47 | private: 48 | unsigned char code_lsb_; 49 | }; 50 | } // namespace utils 51 | -------------------------------------------------------------------------------- /src/apps/calibrate_vignetting_response/blob_tracker.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | /** Trivial flood-fill blob tracker. 28 | * Input is an RGB image, output is a mask of the blob. */ 29 | class BlobTracker { 30 | public: 31 | BlobTracker(); 32 | 33 | void operator()(cv::InputArray image, cv::OutputArray mask); 34 | 35 | private: 36 | bool tracking_ = false; 37 | cv::Point position_; 38 | cv::Mat dilate_element_; 39 | cv::Mat erode_element_; 40 | }; 41 | -------------------------------------------------------------------------------- /include/radical/mat_io.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace radical { 31 | 32 | /** Write a given cv::Mat to a file in binary format. 33 | * The cv::Mat is expected to: 34 | * - not be empty; 35 | * - be continuous; 36 | * - be 1- or 2-dimensional. */ 37 | void writeMat(const std::string& filename, const cv::Mat& mat); 38 | 39 | void writeMat(std::ofstream& file, const cv::Mat& mat); 40 | 41 | /** Read a cv::Mat from a file. */ 42 | cv::Mat readMat(const std::string& filename); 43 | 44 | cv::Mat readMat(std::ifstream& file); 45 | 46 | } // namespace radical 47 | -------------------------------------------------------------------------------- /src/apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(OpenCV_VERSION VERSION_LESS 3.0.0) 2 | set(OpenCV_COMPONENTS highgui) 3 | else() 4 | set(OpenCV_COMPONENTS imgcodecs highgui) 5 | endif() 6 | 7 | find_package(OpenCV COMPONENTS ${OpenCV_COMPONENTS} REQUIRED) 8 | 9 | if(WITH_CERES) 10 | find_package(Ceres CONFIG) 11 | if(Ceres_FOUND) 12 | set_target_properties(ceres 13 | PROPERTIES 14 | INTERFACE_INCLUDE_DIRECTORIES "${CERES_INCLUDE_DIRS}" 15 | INTERFACE_COMPILE_DEFINITIONS "HAVE_CERES") 16 | endif() 17 | endif() 18 | 19 | include_directories(${CMAKE_CURRENT_LIST_DIR}) 20 | 21 | macro(APP_ADD _name) 22 | set(options) 23 | set(one_value_args) 24 | set(multi_value_args OPENCV2 OPENCV3 LINK_WITH) 25 | cmake_parse_arguments(APP_ADD "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) 26 | set(_enabled 0) 27 | if(NOT APP_ADD_OPENCV2 AND NOT APP_ADD_OPENCV3) 28 | set(_enabled 1) 29 | endif() 30 | if(OpenCV_VERSION VERSION_LESS 3.0.0 AND APP_ADD_OPENCV2) 31 | set(_enabled 1) 32 | set(_opencv_link ${APP_ADD_OPENCV2}) 33 | elseif(NOT OpenCV_VERSION VERSION_LESS 3.0.0 AND APP_ADD_OPENCV3) 34 | set(_enabled 1) 35 | set(_opencv_link ${APP_ADD_OPENCV3}) 36 | endif() 37 | if(_enabled) 38 | set(_executable ${_name}) 39 | foreach(_lib ${APP_ADD_LINK_WITH}) 40 | if(TARGET ${_lib}) 41 | list(APPEND _link_with ${_lib}) 42 | endif() 43 | endforeach() 44 | foreach(_lib ${_opencv_link}) 45 | list(APPEND _link_with "opencv_${_lib}") 46 | endforeach() 47 | file(GLOB _sources *.cpp) 48 | add_executable(${_executable} ${_sources}) 49 | target_include_directories(${_executable} PUBLIC ${Boost_INCLUDE_DIRS}) 50 | target_link_libraries(${_executable} 51 | ${LIB_NAME} 52 | ${_link_with} 53 | ${Boost_LIBRARIES} 54 | ) 55 | endif() 56 | endmacro(APP_ADD) 57 | 58 | add_subdirectory(calibrate_radiometric_response) 59 | add_subdirectory(calibrate_vignetting_response) 60 | add_subdirectory(display_radiometric_response) 61 | add_subdirectory(display_vignetting_response) 62 | add_subdirectory(remove_vignetting) 63 | -------------------------------------------------------------------------------- /src/radical/vignetting_model.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | namespace radical { 29 | 30 | #define TRY_LOAD(Model) \ 31 | if (!model) \ 32 | try { \ 33 | model.reset(new Model(filename)); \ 34 | } catch (SerializationException&) { \ 35 | } 36 | 37 | VignettingModel::Ptr VignettingModel::load(const std::string& filename) { 38 | VignettingModel::Ptr model = nullptr; 39 | 40 | TRY_LOAD(NonparametricVignettingModel); 41 | TRY_LOAD(PolynomialVignettingModel<3>); 42 | 43 | return model; 44 | } 45 | 46 | } // namespace radical 47 | -------------------------------------------------------------------------------- /include/radical/check.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace radical { 31 | 32 | class Check { 33 | public: 34 | Check(const std::string& name, cv::InputArray m); 35 | 36 | const Check& hasChannels(int channels) const; 37 | 38 | const Check& hasDepth(int depth) const; 39 | 40 | const Check& hasMaxDimensions(int max_dims) const; 41 | 42 | const Check& hasSize(cv::Size size) const; 43 | 44 | const Check& hasSize(int width, int height) const; 45 | 46 | const Check& hasSize(int total) const; 47 | 48 | const Check& hasType(int type) const; 49 | 50 | const Check& isContinuous() const; 51 | 52 | const Check& notEmpty() const; 53 | 54 | private: 55 | std::string name_; 56 | std::reference_wrapper m_; 57 | }; 58 | 59 | } // namespace radical 60 | -------------------------------------------------------------------------------- /include/utils/plot_radiometric_response.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | namespace utils { 30 | 31 | /** Plot (inverse) radiometric response (specified as a LUT) to a given canvas. 32 | * 33 | * If the response has a single channel, then \arg color should specify the color of the plotted curve. If the response 34 | * has three channels, then \arg color will be ignored and the curves will be plotted with Brue, Green, Red colors 35 | * respectively. */ 36 | void plotRadiometricResponse(const cv::Mat& response, cv::Mat& canvas, const cv::Scalar& color = {0, 0, 0, 0}); 37 | 38 | cv::Mat plotRadiometricResponse(const cv::Mat& response, const cv::Size& size = {512, 512}, 39 | const cv::Scalar& color = {0, 0, 0, 0}); 40 | 41 | cv::Mat plotRadiometricResponse(const radical::RadiometricResponse& rr, cv::Size size = {512, 512}); 42 | } // namespace utils 43 | -------------------------------------------------------------------------------- /cmake/Utils.cmake: -------------------------------------------------------------------------------- 1 | # Provides an option that the user can enable/disable. 2 | # Positional arguments: 3 | # variable : variable where to store user choice 4 | # description : help string describing the option 5 | # value : initial value or boolean expression 6 | # 7 | # ~ Simplified version of corresponding OCV_OPTION macro from OpenCV 8 | macro(RADICAL_OPTION variable description value) 9 | set(__value ${value}) 10 | if(__value MATCHES ";") 11 | if(${__value}) 12 | option(${variable} "${description}" ON) 13 | else() 14 | option(${variable} "${description}" OFF) 15 | endif() 16 | elseif(DEFINED ${__value}) 17 | if(${__value}) 18 | option(${variable} "${description}" ON) 19 | else() 20 | option(${variable} "${description}" OFF) 21 | endif() 22 | else() 23 | option(${variable} "${description}" ${__value}) 24 | endif() 25 | unset(__value) 26 | endmacro() 27 | 28 | 29 | # Read set of version defines from a header file. 30 | # 31 | # ~ Source code copied from OpenCV 32 | macro(parse_header FILENAME FILE_VAR) 33 | set(vars_regex "") 34 | set(__parnet_scope OFF) 35 | set(__add_cache OFF) 36 | foreach(name ${ARGN}) 37 | if("${name}" STREQUAL "PARENT_SCOPE") 38 | set(__parnet_scope ON) 39 | elseif("${name}" STREQUAL "CACHE") 40 | set(__add_cache ON) 41 | elseif(vars_regex) 42 | set(vars_regex "${vars_regex}|${name}") 43 | else() 44 | set(vars_regex "${name}") 45 | endif() 46 | endforeach() 47 | if(EXISTS "${FILENAME}") 48 | file(STRINGS "${FILENAME}" ${FILE_VAR} REGEX "#define[ \t]+(${vars_regex})[ \t]+[0-9]+" ) 49 | else() 50 | unset(${FILE_VAR}) 51 | endif() 52 | foreach(name ${ARGN}) 53 | if(NOT "${name}" STREQUAL "PARENT_SCOPE" AND NOT "${name}" STREQUAL "CACHE") 54 | if(${FILE_VAR}) 55 | if(${FILE_VAR} MATCHES ".+[ \t]${name}[ \t]+([0-9]+).*") 56 | string(REGEX REPLACE ".+[ \t]${name}[ \t]+([0-9]+).*" "\\1" ${name} "${${FILE_VAR}}") 57 | else() 58 | set(${name} "") 59 | endif() 60 | if(__add_cache) 61 | set(${name} ${${name}} CACHE INTERNAL "${name} parsed from ${FILENAME}" FORCE) 62 | elseif(__parnet_scope) 63 | set(${name} "${${name}}" PARENT_SCOPE) 64 | endif() 65 | else() 66 | unset(${name} CACHE) 67 | endif() 68 | endif() 69 | endforeach() 70 | endmacro() 71 | -------------------------------------------------------------------------------- /include/radical/nonparametric_vignetting_model.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace radical { 28 | 29 | /** Nonparametric (dense) model of vignetting response. 30 | * 31 | * Attenuation factor of every color channel at every image location is stored directly. */ 32 | class NonparametricVignettingModel : public VignettingModel { 33 | public: 34 | using Ptr = std::shared_ptr; 35 | 36 | NonparametricVignettingModel(cv::InputArray coefficients); 37 | 38 | NonparametricVignettingModel(const std::string& filename); 39 | 40 | virtual std::string getName() const override; 41 | 42 | virtual void save(const std::string& filename) const override; 43 | 44 | virtual cv::Vec3f operator()(const cv::Vec2f& p) const override; 45 | 46 | using VignettingModel::operator(); 47 | 48 | virtual cv::Size getImageSize() const override; 49 | 50 | virtual cv::Mat getModelCoefficients() const override; 51 | 52 | private: 53 | cv::Mat coefficients_; 54 | }; 55 | 56 | } // namespace radical 57 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/engel_calibration.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include "calibration.h" 28 | 29 | class EngelCalibration : public Calibration { 30 | public: 31 | void setConvergenceThreshold(double threshold); 32 | 33 | virtual std::string getMethodName() const override { 34 | return "Engel"; 35 | } 36 | 37 | protected: 38 | virtual cv::Mat calibrateChannel(const Dataset& dataset) override; 39 | 40 | private: 41 | void optimizeInverseResponse(); 42 | 43 | void optimizeIrradiance(); 44 | 45 | double computeEnergy(); 46 | 47 | void rescale(); 48 | 49 | void visualizeProgress(); 50 | 51 | const Dataset* dataset_ = nullptr; 52 | 53 | bool converged_; 54 | cv::Mat B_; // irradiance 55 | cv::Mat U_; // inverse response 56 | 57 | double energy_, delta_; 58 | double convergence_threshold_ = 1e-5; 59 | double scale_; 60 | 61 | // Storage for temporary matrices to avoid re-allocation 62 | cv::Mat_ sum_t2_i_; 63 | std::array sum_omega_k_; 64 | std::array size_omega_k_; 65 | }; 66 | -------------------------------------------------------------------------------- /src/apps/calibrate_vignetting_response/blob_tracker.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include "blob_tracker.h" 26 | 27 | BlobTracker::BlobTracker() { 28 | dilate_element_ = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5)); 29 | erode_element_ = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(18, 18)); 30 | } 31 | 32 | void BlobTracker::operator()(cv::InputArray _image, cv::OutputArray _mask) { 33 | cv::Mat image = _image.getMat(); 34 | if (!tracking_) { 35 | position_ = cv::Point(image.cols / 2, image.rows / 2); 36 | tracking_ = true; 37 | } 38 | cv::Mat mask(image.rows + 2, image.cols + 2, CV_8UC1); 39 | mask.setTo(0); 40 | cv::Rect box; 41 | cv::floodFill(image, mask, position_, cv::Scalar(255, 40, 20), &box, cv::Scalar(5, 5, 5), cv::Scalar(5, 5, 5), 42 | 8 | (255 << 8) | cv::FLOODFILL_MASK_ONLY); 43 | cv::dilate(mask, mask, dilate_element_); 44 | cv::erode(mask, mask, erode_element_); 45 | position_ = cv::Point(box.x + box.width / 2, box.y + box.height / 2); 46 | mask(cv::Rect(1, 1, image.cols, image.rows)).copyTo(_mask); 47 | } 48 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/dataset_collection.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "grabbers/grabber.h" 24 | 25 | #include "utils/mean_image.h" 26 | 27 | #include "dataset.h" 28 | 29 | class DatasetCollection { 30 | public: 31 | struct Parameters { 32 | int exposure_min; 33 | int exposure_max; 34 | float exposure_factor; 35 | unsigned int exposure_control_lag = 10; 36 | unsigned int num_average_frames = 25; 37 | unsigned int num_images = 1; 38 | unsigned int valid_intensity_min = 1; 39 | unsigned int valid_intensity_max = 254; 40 | unsigned int bloom_radius = 25; 41 | }; 42 | 43 | DatasetCollection(grabbers::Grabber::Ptr grabber, const Parameters& params); 44 | 45 | bool addFrame(const cv::Mat& frame); 46 | 47 | Dataset::Ptr getDataset() const; 48 | 49 | private: 50 | cv::Mat computeSaturationMask(const cv::Mat& image); 51 | 52 | grabbers::Grabber::Ptr grabber_; 53 | Parameters params_; 54 | 55 | Dataset::Ptr dataset_; 56 | utils::MeanImage mean_; 57 | utils::MeanImage mean_mask_; 58 | int exposure_; 59 | int skip_frames_; 60 | int images_to_accumulate_; 61 | const cv::Mat morph_; 62 | }; 63 | -------------------------------------------------------------------------------- /include/grabbers/pylon_grabber.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Tim Caselitz 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace grabbers { 28 | 29 | class PylonGrabber : public Grabber { 30 | public: 31 | using Ptr = std::shared_ptr; 32 | 33 | PylonGrabber(); 34 | 35 | virtual ~PylonGrabber(); 36 | 37 | virtual bool hasMoreFrames() const override; 38 | 39 | virtual bool grabFrame(cv::OutputArray color) override; 40 | 41 | virtual void setAutoWhiteBalanceEnabled(bool state = true) override; 42 | 43 | virtual void setAutoExposureEnabled(bool state = true) override; 44 | 45 | virtual void setExposure(int exposure) override; 46 | 47 | virtual int getExposure() const override; 48 | 49 | virtual std::pair getExposureRange() const override; 50 | 51 | virtual void setGain(int gain) override; 52 | 53 | virtual int getGain() const override; 54 | 55 | virtual std::pair getGainRange() const override; 56 | 57 | virtual std::string getCameraModelName() const override; 58 | 59 | virtual std::string getCameraSerialNumber() const override; 60 | 61 | private: 62 | struct Impl; 63 | std::unique_ptr p; 64 | }; 65 | 66 | } // namespace grabbers 67 | -------------------------------------------------------------------------------- /include/grabbers/openni2_grabber.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace grabbers { 28 | 29 | class OpenNI2Grabber : public Grabber { 30 | public: 31 | using Ptr = std::shared_ptr; 32 | 33 | OpenNI2Grabber(const std::string& device_uri = ""); 34 | 35 | virtual ~OpenNI2Grabber(); 36 | 37 | virtual bool hasMoreFrames() const override; 38 | 39 | virtual bool grabFrame(cv::OutputArray color) override; 40 | 41 | virtual void setAutoWhiteBalanceEnabled(bool state = true) override; 42 | 43 | virtual void setAutoExposureEnabled(bool state = true) override; 44 | 45 | virtual void setExposure(int exposure) override; 46 | 47 | virtual int getExposure() const override; 48 | 49 | virtual std::pair getExposureRange() const override; 50 | 51 | virtual void setGain(int gain) override; 52 | 53 | virtual int getGain() const override; 54 | 55 | virtual std::pair getGainRange() const override; 56 | 57 | virtual std::string getCameraModelName() const override; 58 | 59 | virtual std::string getCameraSerialNumber() const override; 60 | 61 | private: 62 | struct Impl; 63 | std::unique_ptr p; 64 | }; 65 | 66 | } // namespace grabbers 67 | -------------------------------------------------------------------------------- /include/grabbers/realsense_grabber.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace grabbers { 28 | 29 | class RealSenseGrabber : public Grabber { 30 | public: 31 | using Ptr = std::shared_ptr; 32 | 33 | RealSenseGrabber(const std::string& device_uri = ""); 34 | 35 | virtual ~RealSenseGrabber(); 36 | 37 | virtual bool hasMoreFrames() const override; 38 | 39 | virtual bool grabFrame(cv::OutputArray color) override; 40 | 41 | virtual void setAutoWhiteBalanceEnabled(bool state = true) override; 42 | 43 | virtual void setAutoExposureEnabled(bool state = true) override; 44 | 45 | virtual void setExposure(int exposure) override; 46 | 47 | virtual int getExposure() const override; 48 | 49 | virtual std::pair getExposureRange() const override; 50 | 51 | virtual void setGain(int gain) override; 52 | 53 | virtual int getGain() const override; 54 | 55 | virtual std::pair getGainRange() const override; 56 | 57 | virtual std::string getCameraModelName() const override; 58 | 59 | virtual std::string getCameraSerialNumber() const override; 60 | 61 | private: 62 | struct Impl; 63 | std::unique_ptr p; 64 | }; 65 | 66 | } // namespace grabbers 67 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/debevec_calibration.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include "calibration.h" 26 | 27 | struct CeresIterationCallback; 28 | 29 | class DebevecCalibration : public Calibration { 30 | public: 31 | /** Set the minimum number of samples per each intensity level used for optimization. 32 | * A subset of all image pixels is used in the optimization. The sampling procedure ensures that at least this many 33 | * observations per each intensity level (from min to max valid) is taken. */ 34 | void setMinSamplesPerIntensityLevel(unsigned int min_samples); 35 | 36 | void setSmoothingLambda(double lambda); 37 | 38 | virtual std::string getMethodName() const override { 39 | return "Debevec"; 40 | } 41 | 42 | protected: 43 | virtual cv::Mat calibrateChannel(const Dataset& data) override; 44 | 45 | private: 46 | void selectPixels(); 47 | 48 | void visualizeProgress(); 49 | 50 | unsigned int min_samples_ = 5; 51 | double lambda_ = 20; 52 | 53 | const Dataset* dataset_ = nullptr; 54 | std::vector locations_; 55 | 56 | // Irradiances to optimize 57 | std::vector X_; 58 | // Model parameters to optimize 59 | cv::Mat_ U_; 60 | 61 | friend struct CeresIterationCallback; 62 | }; 63 | -------------------------------------------------------------------------------- /src/utils/key_code.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "utils/key_code.h" 24 | 25 | namespace utils { 26 | 27 | KeyCode::KeyCode(int code) 28 | : code_lsb_(code % 256) {} 29 | 30 | bool KeyCode::operator==(Key key) const { 31 | switch (key) { 32 | case Key::ENTER: 33 | return code_lsb_ == 13 // Enter, Numpad (Ubuntu + OpenCV 3) 34 | || code_lsb_ == 10 // Enter (Ubuntu + OpenCV 2) 35 | || code_lsb_ == 141; // Numpad (Ubuntu + OpenCV 2) 36 | case Key::ESC: 37 | return code_lsb_ == 27; 38 | case Key::ARROW_UP: 39 | return code_lsb_ == 82; 40 | case Key::ARROW_DOWN: 41 | return code_lsb_ == 84; 42 | case Key::ARROW_RIGHT: 43 | return code_lsb_ == 83; 44 | case Key::ARROW_LEFT: 45 | return code_lsb_ == 81; 46 | case Key::PLUS: 47 | return code_lsb_ == 171 // Numpad (Ubuntu + OpenCV 2) 48 | || code_lsb_ == 43; // Numpad (Ubuntu + OpenCV 3) 49 | case Key::MINUS: 50 | return code_lsb_ == 173 // Numpad (Ubuntu + OpenCV 2) 51 | || code_lsb_ == 45; // Numpad (Ubuntu + OpenCV 3) 52 | case Key::NO_KEY: 53 | return code_lsb_ == 255; 54 | } 55 | return false; 56 | } 57 | 58 | bool KeyCode::operator!=(Key key) const { 59 | return !this->operator==(key); 60 | } 61 | } // namespace utils 62 | -------------------------------------------------------------------------------- /src/grabbers/grabber.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace grabbers { 31 | 32 | Grabber::~Grabber() = default; 33 | 34 | std::string Grabber::getCameraUID() const { 35 | return getCameraModelName() + "." + getCameraSerialNumber(); 36 | } 37 | 38 | Grabber::Ptr createGrabber(const std::string& uri) { 39 | #if HAVE_REALSENSE 40 | if (uri == "rs" || uri == "realsense" || uri == "intel" || uri == "") 41 | try { 42 | return Grabber::Ptr(new RealSenseGrabber); 43 | } catch (GrabberException&) { 44 | } 45 | #endif 46 | #if HAVE_OPENNI2 47 | try { 48 | if (uri == "openni" || uri == "openni2" || uri == "kinect" || uri == "asus" || uri == "") 49 | return Grabber::Ptr(new OpenNI2Grabber); 50 | else 51 | return Grabber::Ptr(new OpenNI2Grabber(uri)); 52 | } catch (GrabberException&) { 53 | } 54 | #endif 55 | #if HAVE_PYLON 56 | try { 57 | return Grabber::Ptr(new PylonGrabber); 58 | } catch (GrabberException&) { 59 | } 60 | #endif 61 | BOOST_THROW_EXCEPTION(GrabberException("Failed to create a grabber") << GrabberException::URI(uri)); 62 | return nullptr; 63 | } 64 | 65 | } // namespace grabbers 66 | -------------------------------------------------------------------------------- /cmake/modules/FindRealSense.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Find librealsense 3 | # 4 | # find_package(RealSense) 5 | # 6 | # Variables defined by this module: 7 | # 8 | # REALSENSE_FOUND True if librealsense was found 9 | # REALSENSE_API_VERSION The API version 10 | # REALSENSE_INCLUDE_DIR The location of librealsense headers 11 | # REALSENSE_LIBRARY Libraries needed to use librealsense 12 | # 13 | # Imported targets defined by this module: 14 | # 15 | # realsense Library to link against 16 | 17 | find_path(_REALSENSE_INCLUDE_DIR NAMES rs.h 18 | HINTS /usr/local/include/librealsense 19 | /usr/include/librealsense 20 | /usr/local/include 21 | /usr/local 22 | DOC "librealsense include directory" 23 | ) 24 | 25 | if(_REALSENSE_INCLUDE_DIR) 26 | 27 | # Include directory 28 | get_filename_component(REALSENSE_INCLUDE_DIR ${_REALSENSE_INCLUDE_DIR} DIRECTORY) 29 | mark_as_advanced(REALSENSE_INCLUDE_DIR) 30 | 31 | ## Library 32 | find_library(REALSENSE_LIBRARY NAMES realsense) 33 | if(REALSENSE_LIBRARY) 34 | add_library(realsense SHARED IMPORTED) 35 | set_property(TARGET realsense PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${REALSENSE_INCLUDE_DIR}) 36 | set_property(TARGET realsense PROPERTY IMPORTED_LOCATION ${REALSENSE_LIBRARY}) 37 | set(HAVE_REALSENSE TRUE) 38 | endif() 39 | 40 | # Version 41 | set(REALSENSE_VERSION 0) 42 | file(READ "${_REALSENSE_INCLUDE_DIR}/rs.h" _rs_H_CONTENTS) 43 | string(REGEX MATCH "define[ \t]+RS_API_MAJOR_VERSION[ \t]+([0-9]+)" _major_version_match "${_rs_H_CONTENTS}") 44 | set(REALSENSE_API_MAJOR_VERSION "${CMAKE_MATCH_1}") 45 | string(REGEX MATCH "define[ \t]+RS_API_MINOR_VERSION[ \t]+([0-9]+)" _minor_version_match "${_rs_H_CONTENTS}") 46 | set(REALSENSE_API_MINOR_VERSION "${CMAKE_MATCH_1}") 47 | string(REGEX MATCH "define[ \t]+RS_API_PATCH_VERSION[ \t]+([0-9]+)" _patch_version_match "${_rs_H_CONTENTS}") 48 | set(REALSENSE_API_PATCH_VERSION "${CMAKE_MATCH_1}") 49 | set(REALSENSE_API_VERSION "${REALSENSE_API_MAJOR_VERSION}.${REALSENSE_API_MINOR_VERSION}.${REALSENSE_API_PATCH_VERSION}") 50 | unset(_rs_H_CONTENTS) 51 | 52 | unset(_REALSENSE_INCLUDE_DIR CACHE) 53 | 54 | endif() 55 | 56 | include(FindPackageHandleStandardArgs) 57 | find_package_handle_standard_args(RealSense 58 | FOUND_VAR REALSENSE_FOUND 59 | REQUIRED_VARS REALSENSE_LIBRARY REALSENSE_INCLUDE_DIR 60 | VERSION_VAR REALSENSE_API_VERSION 61 | ) 62 | 63 | if(REALSENSE_FOUND AND NOT RealSense_FIND_QUIETLY) 64 | message(STATUS "Found RealSense (version: ${REALSENSE_API_VERSION}, location: ${REALSENSE_LIBRARY})") 65 | endif() 66 | -------------------------------------------------------------------------------- /include/utils/plot_histogram.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace utils { 28 | 29 | /** Plot a one-dimensional histogram on a given canvas. 30 | * The histogram bar width is computed automatically such that it is as wide as possible while the histogram still 31 | * entirely fits on the canvas. 32 | * Single-channel and three-channel histograms are supported. In the latter case 33 | * - per-channel histograms will be plotted one on top of another in reverse order; 34 | * - \arg color will be ignored and the histograms will be plotted with Blue, Green, and Red colors respectively. */ 35 | void plotHistogram(const cv::Mat& histogram, cv::Mat& canvas, const cv::Scalar& color = {0, 0, 0, 0}); 36 | 37 | /** Plot a one-dimensional histogram. 38 | * Canvas is created automatically to fit the histogram. 39 | * \param[in] histogram 40 | * \param[in] bar_width width in pixels of each histogram bin bar 41 | * \param[in] height height of the histogram of each channel, thus total height of the output plot will be \arg height 42 | * multiplied by the number of channels 43 | * \param[in] color color of the histogram bars, ignored in the case of multi-channel histogram */ 44 | cv::Mat plotHistogram(const cv::Mat& histogram, unsigned int bar_width, unsigned int height, 45 | const cv::Scalar& color = {0, 0, 0, 0}); 46 | } // namespace utils 47 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/calibration.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include "dataset.h" 29 | 30 | class Calibration { 31 | public: 32 | using Ptr = std::shared_ptr; 33 | 34 | virtual ~Calibration() {} 35 | 36 | cv::Mat calibrate(const Dataset& dataset); 37 | 38 | void setMaxNumIterations(unsigned int max_num_iterations); 39 | 40 | void setVerbosity(unsigned int level); 41 | 42 | void setValidPixelRange(unsigned char min_valid, unsigned char max_valid); 43 | 44 | /** Visualize calibration progress. 45 | * 46 | * \param[i] imshow a function that can display a given cv::Mat */ 47 | void setVisualizeProgress(const std::function& imshow); 48 | 49 | virtual std::string getMethodName() const = 0; 50 | 51 | protected: 52 | /** Calibrate a single color channel. 53 | * This should be implemented by deriving classes. The dataset is guaranteed to have a single color channel. */ 54 | virtual cv::Mat calibrateChannel(const Dataset& dataset) = 0; 55 | 56 | bool isPixelValid(unsigned char pixel) const; 57 | 58 | bool isPixelValid(const cv::Vec3b& pixel) const; 59 | 60 | void printHeader() const; 61 | 62 | void printFooter() const; 63 | 64 | void printIteration(unsigned int iteration, double residual, double delta, const char extra = ' '); 65 | 66 | unsigned int max_num_iterations_ = 30; 67 | unsigned int verbosity_ = 0; 68 | unsigned char min_valid_ = 1; 69 | unsigned char max_valid_ = 254; 70 | std::function imshow_; 71 | 72 | int channel_; 73 | }; 74 | -------------------------------------------------------------------------------- /include/utils/mask_saturated_pixels.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | /** Mask pixels where any of the color channels is under/over exposed. 28 | * If an existing mask of matching size is provided, it will be updated. 29 | * \param[i] mask_value value to assign to saturated pixels, if it is ≥ 1, then non-masked values will be zeros; if it 30 | * is = 0, then non-masked values will be 255 */ 31 | void maskSaturatedPixels(cv::InputArray _image, cv::InputOutputArray _mask, uint8_t mask_value = 0, uint8_t min = 5, 32 | uint8_t max = 250) { 33 | cv::Mat image, mask; 34 | CV_Assert(_image.type() == CV_8UC3); 35 | if (_mask.empty() || _mask.size() != _image.size() || _mask.type() != CV_8U) { 36 | _mask.create(_image.size(), CV_8U); 37 | mask = _mask.getMat(); 38 | mask.setTo(mask_value == 0 ? 255 : 0); // OpenCV 2 does not support setTo() on OutputArray 39 | } else { 40 | mask = _mask.getMat(); 41 | } 42 | image = _image.getMat(); 43 | 44 | #if CV_MAJOR_VERSION > 2 45 | image.forEach([&mask, mask_value, min, max](const cv::Vec3b& v, const int* p) { 46 | for (int c = 0; c < 3; ++c) 47 | if (v[c] < min || v[c] > max) { 48 | mask.at(p) = mask_value; 49 | break; 50 | } 51 | }); 52 | #else 53 | for (int i = 0; i < image.rows; i++) 54 | for (int j = 0; j < image.cols; j++) { 55 | const auto& v = image.at(i, j); 56 | for (size_t c = 0; c < 3; ++c) 57 | if (v[c] < min || v[c] > max) { 58 | mask.at(i, j) = mask_value; 59 | break; 60 | } 61 | } 62 | #endif 63 | } 64 | -------------------------------------------------------------------------------- /include/radical/vignetting_response.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace radical { 31 | 32 | class VignettingModel; 33 | 34 | class VignettingResponse { 35 | public: 36 | using Ptr = std::shared_ptr; 37 | 38 | VignettingResponse(const std::string& filename); 39 | 40 | virtual ~VignettingResponse(); 41 | 42 | std::shared_ptr getModel() const; 43 | 44 | cv::Mat getResponse() const; 45 | 46 | cv::Mat getResponse(cv::Size image_size) const; 47 | 48 | cv::Mat getLogResponse() const; 49 | 50 | cv::Mat getLogResponse(cv::Size image_size) const; 51 | 52 | /** Remove vignetting effects from a given image. 53 | * \param[in] E image irradiance 54 | * \param[out] L scene radiance */ 55 | void remove(cv::InputArray E, cv::OutputArray L) const; 56 | 57 | /** Remove vignetting effects from a given log image. 58 | * \param[in] E logarithm of image irradiance 59 | * \param[out] L logarithm of scene radiance */ 60 | void removeLog(cv::InputArray E, cv::OutputArray L) const; 61 | 62 | /** Add vignetting effects to a given image. 63 | * \param[in] L scene radiance 64 | * \param[out] E image irradiance */ 65 | void add(cv::InputArray L, cv::OutputArray E) const; 66 | 67 | /** Add vignetting effects to a given log image. 68 | * \param[in] L logarithm of scene radiance 69 | * \param[out] E logarithm of image irradiance */ 70 | void addLog(cv::InputArray L, cv::OutputArray E) const; 71 | 72 | private: 73 | std::shared_ptr model_; 74 | 75 | struct ResponseCache; 76 | mutable std::unique_ptr response_cache_; 77 | }; 78 | 79 | } // namespace radical 80 | -------------------------------------------------------------------------------- /include/radical/vignetting_model.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | namespace radical { 31 | 32 | class VignettingModel { 33 | public: 34 | using Ptr = std::shared_ptr; 35 | using ConstPtr = std::shared_ptr; 36 | 37 | virtual ~VignettingModel() {} 38 | 39 | /** Get the name of the vignetting model. */ 40 | virtual std::string getName() const = 0; 41 | 42 | /** Write vignetting model to a file. */ 43 | virtual void save(const std::string& filename) const = 0; 44 | 45 | /** Evaluate the model at a given image location. 46 | * 47 | * Pixel coordinates do not need to be integer. Some vignetting models may 48 | * be able to interpolate between pixels. 49 | * 50 | * Note: it is the responsibility of the user to make sure that the location 51 | * is within valid range (\sa getImageSize()). */ 52 | virtual cv::Vec3f operator()(const cv::Vec2f& p) const = 0; 53 | 54 | /** Evaluate the model at a given pixel location. */ 55 | cv::Vec3f operator()(float x, float y) const { 56 | return operator()({x, y}); 57 | } 58 | 59 | /** Get image size for which the model is valid. */ 60 | virtual cv::Size getImageSize() const = 0; 61 | 62 | /** Get model coefficients. */ 63 | virtual cv::Mat getModelCoefficients() const = 0; 64 | 65 | /** Load the vignetting model stored in a given file. 66 | * This function will try to load every implemented vignetting model from the given file. The first one that succeeds 67 | * in loading will be returned. If the file does not contain any valid vignetting model, \c nullptr is returned. */ 68 | static Ptr load(const std::string& filename); 69 | }; 70 | 71 | } // namespace radical 72 | -------------------------------------------------------------------------------- /src/utils/plot_radiometric_response.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include "utils/colors.h" 30 | #include "utils/plot_radiometric_response.h" 31 | 32 | namespace utils { 33 | 34 | using radical::Check; 35 | 36 | void plotRadiometricResponse(const cv::Mat& response, cv::Mat& canvas, const cv::Scalar& color) { 37 | Check("Radiometric response", response).hasDepth(CV_32F).hasSize(256); 38 | Check("Canvas", canvas).notEmpty().hasType(CV_8UC3); 39 | 40 | auto x_scale = static_cast(canvas.cols) / 256.0f; 41 | double min, max; 42 | cv::minMaxIdx(response, &min, &max); 43 | // auto y_scale = static_cast(canvas.rows) / 1.2; 44 | auto y_scale = static_cast(canvas.rows) / max; 45 | 46 | auto circle = [&canvas, x_scale, y_scale](float x, float y, const cv::Scalar& color) { 47 | cv::circle(canvas, cv::Point(x * x_scale, canvas.size().height - y * y_scale), std::ceil(x_scale), color, -1); 48 | }; 49 | 50 | for (int i = 0; i < 256; ++i) { 51 | if (response.channels() == 1) 52 | circle(i, response.at(i), color); 53 | else 54 | for (int c = 0; c < response.channels(); ++c) 55 | circle(i, response.at(i)[c], utils::colors::BGR[c]); 56 | } 57 | } 58 | 59 | cv::Mat plotRadiometricResponse(const cv::Mat& response, const cv::Size& size, const cv::Scalar& color) { 60 | assert(size.area() > 0); 61 | cv::Mat canvas(size, CV_8UC3); 62 | canvas.setTo(255); 63 | plotRadiometricResponse(response, canvas, color); 64 | return canvas; 65 | } 66 | 67 | cv::Mat plotRadiometricResponse(const radical::RadiometricResponse& rr, cv::Size size) { 68 | return plotRadiometricResponse(rr.getInverseResponse(), size); 69 | } 70 | } // namespace utils 71 | -------------------------------------------------------------------------------- /include/radical/polynomial_vignetting_model.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace radical { 28 | 29 | /** This model parameterizes vignetting response using an even order polynomial. 30 | * 31 | * Specifically, vignetting response at a given image location x is computed as: 32 | * \f[ 33 | * V(\mathbf{x}) = 1 + \sum_{n=1}^{k}\beta_{n}(\mathbf{x} - \mathbf{c})^{2n}, 34 | * \f] 35 | * 36 | * where c is the center of symmetry of the vignetting response. 37 | * Template argument \c Degree is the order of the polynomial divided by two (k from the formula). 38 | * Each color channel has its own model coefficients. The number of coefficients per channel is \c Degree + 2. First 39 | * two numbers define c, and the remaining are betas. 40 | * 41 | * \note The implementation is generic and supports polynomials of different degree. However, the model is explicitly 42 | * instantiated only with \c Degree = 3. */ 43 | template 44 | class PolynomialVignettingModel : public VignettingModel { 45 | public: 46 | using Ptr = std::shared_ptr>; 47 | 48 | PolynomialVignettingModel(cv::InputArray coefficients, cv::Size image_size); 49 | 50 | PolynomialVignettingModel(const std::string& filename); 51 | 52 | virtual std::string getName() const override; 53 | 54 | virtual void save(const std::string& filename) const override; 55 | 56 | virtual cv::Vec3f operator()(const cv::Vec2f& p) const override; 57 | 58 | using VignettingModel::operator(); 59 | 60 | virtual cv::Size getImageSize() const override; 61 | 62 | virtual cv::Mat getModelCoefficients() const override; 63 | 64 | private: 65 | cv::Mat coefficients_; 66 | cv::Size image_size_; 67 | }; 68 | 69 | } // namespace radical 70 | -------------------------------------------------------------------------------- /cmake/modules/FindPylon.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Find Pylon 3 | # 4 | # find_package(Pylon) 5 | # 6 | # Variables defined by this module: 7 | # 8 | # PYLON_FOUND 9 | # PYLON_VERSION 10 | # PYLON_INCLUDE_DIR 11 | # PYLON_LIBRARIES 12 | 13 | find_path(_CONFIG_DIR NAMES pylon-config 14 | HINTS $ENV{PYLON_ROOT}/bin 15 | /opt/pylon5/bin 16 | ) 17 | 18 | if(_CONFIG_DIR) 19 | set(_CONFIG "${_CONFIG_DIR}/pylon-config") 20 | 21 | set(PYLON_FOUND TRUE) 22 | 23 | execute_process(COMMAND ${_CONFIG} --version OUTPUT_VARIABLE PYLON_VERSION) 24 | string(REPLACE "\n" "" PYLON_VERSION "${PYLON_VERSION}") 25 | execute_process(COMMAND ${_CONFIG} --version-major OUTPUT_VARIABLE PYLON_VERSION_MAJOR) 26 | string(REPLACE "\n" "" PYLON_VERSION_MAJOR "${PYLON_VERSION_MAJOR}") 27 | execute_process(COMMAND ${_CONFIG} --version-minor OUTPUT_VARIABLE PYLON_VERSION_MINOR) 28 | string(REPLACE "\n" "" PYLON_VERSION_MINOR "${PYLON_VERSION_MINOR}") 29 | execute_process(COMMAND ${_CONFIG} --version-subminor OUTPUT_VARIABLE PYLON_VERSION_PATCH) 30 | string(REPLACE "\n" "" PYLON_VERSION_PATCH "${PYLON_VERSION_PATCH}") 31 | execute_process(COMMAND ${_CONFIG} --version-build OUTPUT_VARIABLE PYLON_VERSION_TWEAK) 32 | string(REPLACE "\n" "" PYLON_VERSION_TWEAK "${PYLON_VERSION_TWEAK}") 33 | 34 | execute_process(COMMAND ${_CONFIG} --cflags-only-I OUTPUT_VARIABLE PYLON_INCLUDE_DIR) 35 | string(REPLACE " " ";" PYLON_INCLUDE_DIR "${PYLON_INCLUDE_DIR}") 36 | string(REPLACE "-I" "" PYLON_INCLUDE_DIR "${PYLON_INCLUDE_DIR}") 37 | string(REPLACE "\n" "" PYLON_INCLUDE_DIR "${PYLON_INCLUDE_DIR}") 38 | 39 | execute_process(COMMAND ${_CONFIG} --libs-only-l OUTPUT_VARIABLE _LIBS) 40 | string(REPLACE " " ";" _LIBS "${_LIBS}") 41 | string(REPLACE "-l" "" _LIBS "${_LIBS}") 42 | string(REPLACE "\n" "" _LIBS "${_LIBS}") 43 | execute_process(COMMAND ${_CONFIG} --libdir OUTPUT_VARIABLE _LIBDIR) 44 | string(REPLACE "\n" "" _LIBDIR "${_LIBDIR}") 45 | unset(PYLON_LIBRARIES CACHE) 46 | foreach (_LIB ${_LIBS}) 47 | find_library(_LIBRARY NAMES ${_LIB} HINTS ${_LIBDIR}) 48 | if(_LIBRARY) 49 | list(APPEND PYLON_LIBRARIES ${_LIB}) 50 | add_library(${_LIB} SHARED IMPORTED) 51 | set_property(TARGET ${_LIB} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYLON_INCLUDE_DIR}) 52 | set_property(TARGET ${_LIB} PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES ${PYLON_INCLUDE_DIR}) 53 | set_property(TARGET ${_LIB} PROPERTY IMPORTED_LOCATION ${_LIBRARY}) 54 | endif() 55 | unset(_LIBRARY CACHE) 56 | endforeach() 57 | 58 | set(HAVE_PYLON TRUE) 59 | 60 | unset(_LIBS CACHE) 61 | unset(_LIBDIR CACHE) 62 | unset(_CONFIG CACHE) 63 | else() 64 | set(PYLON_FOUND FALSE) 65 | endif() 66 | unset(_CONFIG_DIR CACHE) 67 | 68 | include(FindPackageHandleStandardArgs) 69 | find_package_handle_standard_args(Pylon 70 | FOUND_VAR PYLON_FOUND 71 | REQUIRED_VARS PYLON_LIBRARIES PYLON_INCLUDE_DIR 72 | VERSION_VAR PYLON_VERSION 73 | ) 74 | 75 | if(PYLON_FOUND AND NOT Pylon_FIND_QUIETLY) 76 | message(STATUS "Found Pylon (version: ${PYLON_VERSION}, location: ${_LIBRARY})") 77 | endif() 78 | -------------------------------------------------------------------------------- /src/radical/check.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | 26 | namespace radical { 27 | 28 | Check::Check(const std::string& name, cv::InputArray m) 29 | : name_(name) 30 | , m_(std::cref(m)) {} 31 | 32 | const Check& Check::hasChannels(int channels) const { 33 | if (m_.get().channels() != channels) 34 | throw MatChannelsException(name_, channels, m_.get().channels()); 35 | return *this; 36 | } 37 | 38 | const Check& Check::hasDepth(int depth) const { 39 | if (m_.get().depth() != depth) 40 | throw MatDepthException(name_, depth, m_.get().depth()); 41 | return *this; 42 | } 43 | 44 | const Check& Check::hasMaxDimensions(int max_dims) const { 45 | if (m_.get().getMat().dims > max_dims) 46 | throw MatMaxDimensionsException(name_, max_dims, m_.get().getMat().dims); 47 | return *this; 48 | } 49 | 50 | const Check& Check::hasSize(cv::Size size) const { 51 | if (m_.get().size() != size) 52 | throw MatSizeException(name_, size, m_.get().size()); 53 | return *this; 54 | } 55 | 56 | const Check& Check::hasSize(int width, int height) const { 57 | return hasSize({width, height}); 58 | } 59 | 60 | const Check& Check::hasSize(int total) const { 61 | if (static_cast(m_.get().total()) != total) 62 | throw MatSizeException(name_, {total, 1}, m_.get().size()); 63 | return *this; 64 | } 65 | 66 | const Check& Check::hasType(int type) const { 67 | if (m_.get().type() != type) 68 | throw MatTypeException(name_, type, m_.get().type()); 69 | return *this; 70 | } 71 | 72 | const Check& Check::isContinuous() const { 73 | if (!m_.get().getMat().isContinuous()) 74 | throw MatException(name_ + " is not continuous"); 75 | return *this; 76 | } 77 | 78 | const Check& Check::notEmpty() const { 79 | if (m_.get().empty()) 80 | throw MatException(name_ + " is empty"); 81 | return *this; 82 | } 83 | 84 | } // namespace radical 85 | -------------------------------------------------------------------------------- /src/utils/plot_histogram.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include "utils/colors.h" 28 | #include "utils/plot_histogram.h" 29 | 30 | namespace utils { 31 | 32 | using radical::Check; 33 | 34 | void plotHistogram(const cv::Mat& histogram, cv::Mat& canvas, const cv::Scalar& color) { 35 | Check("Histogram", histogram).notEmpty().hasDepth(CV_32F); 36 | Check("Canvas", canvas).notEmpty().hasType(CV_8UC3); 37 | 38 | const auto N = histogram.total(); 39 | const auto bar_width = canvas.cols / N; 40 | const auto bar_height = canvas.rows / histogram.channels(); 41 | assert(bar_width > 0); 42 | assert(bar_height > 0); 43 | 44 | double max = 0; 45 | cv::minMaxLoc(histogram, nullptr, &max, nullptr, nullptr); 46 | 47 | std::vector histogram_channels; 48 | cv::split(histogram, histogram_channels); 49 | 50 | const cv::Scalar* COLORS = histogram.channels() > 1 ? utils::colors::BGR : &color; 51 | for (int c = 0; c < histogram.channels(); ++c) { 52 | cv::Mat roi(canvas, cv::Rect(0, (histogram.channels() - 1 - c) * bar_height, N * bar_width, bar_height)); 53 | roi.setTo(255); 54 | for (unsigned int b = 0; b < N; b++) { 55 | auto value = histogram_channels[c].at(b); 56 | auto height = std::round(value * bar_height / max); 57 | if (height > 0) 58 | cv::rectangle(roi, cv::Point(b * bar_width, bar_height - 1), 59 | cv::Point((b + 1) * bar_width - 1, bar_height - 1 - height), COLORS[c], -1); 60 | } 61 | } 62 | } 63 | 64 | cv::Mat plotHistogram(const cv::Mat& histogram, unsigned int bar_width, unsigned int height, const cv::Scalar& color) { 65 | assert(bar_width > 0); 66 | assert(height > 0); 67 | 68 | cv::Mat canvas(height * histogram.channels(), bar_width * histogram.total(), CV_8UC3); 69 | canvas.setTo(0); 70 | 71 | plotHistogram(histogram, canvas, color); 72 | return canvas; 73 | } 74 | } // namespace utils 75 | -------------------------------------------------------------------------------- /include/utils/program_options.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | class OptionsBase { 30 | public: 31 | virtual ~OptionsBase() {} 32 | 33 | bool parse(int argc, const char** argv) { 34 | namespace po = boost::program_options; 35 | po::options_description general("Options"); 36 | po::options_description hidden; 37 | po::positional_options_description positional; 38 | general.add_options()("help,h", "Print this help message"); 39 | addOptions(general); 40 | addPositional(hidden, positional); 41 | po::options_description all; 42 | all.add(general).add(hidden); 43 | try { 44 | auto parser = po::command_line_parser(argc, argv).options(all).positional(positional); 45 | po::store(parser.run(), vm); 46 | if (vm.count("help")) { 47 | printHelp(); 48 | std::cout << general << std::endl; 49 | return false; 50 | } 51 | po::notify(vm); 52 | validate(); 53 | } catch (boost::program_options::error& e) { 54 | std::cerr << "Option parsing error: " << e.what() << std::endl; 55 | return false; 56 | } 57 | return true; 58 | } 59 | 60 | bool operator()(const std::string& name) const { 61 | return vm.count(name) != 0; 62 | } 63 | 64 | protected: 65 | virtual void addOptions(boost::program_options::options_description& /* desc */) {} 66 | 67 | virtual void addPositional(boost::program_options::options_description& /* desc */, 68 | boost::program_options::positional_options_description& /* positional */) {} 69 | 70 | virtual void printHelp() { 71 | std::cout << "Help" << std::endl; 72 | } 73 | 74 | /** Validate supplied options. 75 | * This function is called directly after parsing command line. 76 | * Should throw boost::program_options::error if validation fails. */ 77 | virtual void validate() {} 78 | 79 | boost::program_options::variables_map vm; 80 | }; 81 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/dataset.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | class Dataset { 32 | public: 33 | using Ptr = std::shared_ptr; 34 | 35 | Dataset(); 36 | 37 | /** Insert an image taken at a given exposure time in the dataset. */ 38 | void insert(int exposure_time, const cv::Mat& image); 39 | 40 | /** Get the size of images in the dataset. */ 41 | cv::Size getImageSize() const; 42 | 43 | /** Get the total number of images in the dataset. */ 44 | size_t getNumImages() const; 45 | 46 | /** Get the number of images at a given exposure time in the dataset. */ 47 | size_t getNumImages(int exposure_time) const; 48 | 49 | /** Get all images taken at a given exposure time. */ 50 | std::vector getImages(int exposure_time) const; 51 | 52 | /** Get all exposure times present in the dataset, sorted in ascending order. */ 53 | std::vector getExposureTimes() const; 54 | 55 | /** Split a dataset with multi-channel images into multiple single-channel datasets. */ 56 | std::vector splitChannels() const; 57 | 58 | /** Get the contents of the dataset as a flat vector of images and a flat vector of their corresponding exposure 59 | * times. This format is compatible with OpenCV built-in CRF calibration algorithms. */ 60 | void asImageAndExposureTimeVectors(std::vector& images, std::vector& exposure_times) const; 61 | 62 | /** Compute per-channel histogram of intensities of all images in the dataset. 63 | * Returns a matrix of size 1 x 256 with as many channels as the dataset images have. */ 64 | cv::Mat computeIntensityHistogram() const; 65 | 66 | /** Save the dataset to the disk. */ 67 | void save(const std::string& path) const; 68 | 69 | /** Load a dataset from the disk. */ 70 | static Ptr load(const std::string& path); 71 | 72 | private: 73 | size_t num_images_; 74 | cv::Size image_size_; 75 | std::unordered_map> data_; 76 | }; 77 | -------------------------------------------------------------------------------- /include/utils/plot_polynomial_vignetting_model.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include "utils/colors.h" 32 | 33 | template 34 | inline cv::Mat plotPolynomialVignettingModel(const radical::PolynomialVignettingModel& pvm, 35 | cv::Size plot_size) { 36 | assert(plot_size.area() > 0); 37 | 38 | cv::Mat plot(plot_size, CV_8UC3); 39 | plot.setTo(255); 40 | 41 | auto image_size = pvm.getImageSize(); 42 | const auto scale_x = 1.0 * plot_size.width / image_size.width; 43 | const auto scale_y = 1.0 * plot_size.height / image_size.height; 44 | 45 | cv::line(plot, cv::Point(plot_size.width / 2, 0), cv::Point(plot_size.width / 2, plot_size.height - 1), 46 | cv::Scalar(100, 100, 100), 2); 47 | cv::line(plot, cv::Point(0, plot_size.height / 2), cv::Point(plot_size.width - 1, plot_size.height / 2), 48 | cv::Scalar(100, 100, 100), 2); 49 | 50 | auto max_radius = 0.5 * std::sqrt(image_size.width * image_size.width + image_size.height * image_size.height); 51 | auto radius_step = max_radius / plot_size.width; 52 | auto radius_scale = 1.0 * plot_size.width / max_radius; 53 | auto coeff = pvm.getModelCoefficients().template ptr(); 54 | 55 | for (size_t i = 0; i < 3; ++i) { 56 | auto cx = coeff[0][i]; 57 | auto cy = coeff[1][i]; 58 | cv::circle(plot, cv::Point(cx * scale_x, cy * scale_y), 7, utils::colors::BGR[i], -1); 59 | auto radius = 0.0; 60 | while (radius < max_radius) { 61 | auto v = pvm(cx + radius, cy)[i]; 62 | cv::circle(plot, cv::Point(radius * radius_scale, (1.0 - v) * plot_size.height), 2, utils::colors::BGR[i], -1); 63 | radius += radius_step; 64 | } 65 | } 66 | 67 | return plot; 68 | } 69 | 70 | template 71 | inline cv::Mat plotPolynomialVignettingModel(const radical::PolynomialVignettingModel& pvm) { 72 | return plotPolynomialVignettingModel(pvm, pvm.getImageSize()); 73 | } 74 | -------------------------------------------------------------------------------- /src/radical/mat_io.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace radical { 31 | 32 | static const uint32_t MAGIC = 0xC4A1FDD9; 33 | 34 | void writeMat(const std::string& filename, const cv::Mat& mat) { 35 | std::ofstream file(filename, std::ios::out | std::ios::binary); 36 | if (!file.is_open()) 37 | throw SerializationException("Failed to open file for writing cv::Mat", filename); 38 | 39 | writeMat(file, mat); 40 | } 41 | 42 | void writeMat(std::ofstream& file, const cv::Mat& mat) { 43 | Check("Serialized mat", mat).notEmpty().isContinuous().hasMaxDimensions(2); 44 | 45 | uint32_t type = mat.type(); 46 | file.write((const char*)(&MAGIC), sizeof(uint32_t)); 47 | file.write((const char*)(&type), sizeof(uint32_t)); 48 | file.write((const char*)(&mat.dims), sizeof(uint32_t)); 49 | file.write((const char*)(&mat.rows), sizeof(uint32_t)); 50 | file.write((const char*)(&mat.cols), sizeof(uint32_t)); 51 | file.write((const char*)(mat.data), mat.elemSize() * mat.total()); 52 | } 53 | 54 | cv::Mat readMat(const std::string& filename) { 55 | std::ifstream file(filename, std::ios::in | std::ios::binary); 56 | if (!file.is_open()) 57 | throw SerializationException("Failed to open file for reading cv::Mat", filename); 58 | return readMat(file); 59 | } 60 | 61 | cv::Mat readMat(std::ifstream& file) { 62 | uint32_t magic, type, dims, rows, cols; 63 | file.read((char*)(&magic), sizeof(uint32_t)); 64 | if (magic != MAGIC) 65 | throw SerializationException("File does not contain a cv::Mat"); 66 | file.read((char*)(&type), sizeof(uint32_t)); 67 | file.read((char*)(&dims), sizeof(uint32_t)); 68 | if (dims > 2) 69 | throw SerializationException("File contains a cv::Mat that is not 1- or 2-dimensional"); 70 | file.read((char*)(&rows), sizeof(uint32_t)); 71 | file.read((char*)(&cols), sizeof(uint32_t)); 72 | cv::Mat mat(rows, cols, type); 73 | assert(mat.isContinuous()); 74 | file.read((char*)(mat.data), mat.elemSize() * mat.total()); 75 | file.close(); 76 | return mat; 77 | } 78 | 79 | } // namespace radical 80 | -------------------------------------------------------------------------------- /src/radical/nonparametric_vignetting_model.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | namespace radical { 33 | 34 | NonparametricVignettingModel::NonparametricVignettingModel(cv::InputArray _coefficients) { 35 | Check("Nonparametric vignetting model", _coefficients).notEmpty().hasType(CV_32FC3); 36 | coefficients_ = _coefficients.getMat(); 37 | } 38 | 39 | NonparametricVignettingModel::NonparametricVignettingModel(const std::string& filename) { 40 | std::ifstream file(filename, std::ios::in | std::ios::binary); 41 | if (file.is_open()) { 42 | std::string line, name; 43 | { 44 | std::getline(file, line); 45 | std::stringstream(line) >> name; 46 | if (name != "NonparametricVignettingModel") 47 | throw SerializationException("Vignetting model stored in the file is not nonparametric", filename); 48 | } 49 | coefficients_ = readMat(file); 50 | file.close(); 51 | } else { 52 | throw SerializationException("Unable to open vignetting model file", filename); 53 | } 54 | } 55 | 56 | std::string NonparametricVignettingModel::getName() const { 57 | return "nonparametric"; 58 | } 59 | 60 | void NonparametricVignettingModel::save(const std::string& filename) const { 61 | std::ofstream file(filename, std::ios::out | std::ios::binary); 62 | if (file.is_open()) { 63 | file << "NonparametricVignettingModel\n"; 64 | writeMat(file, coefficients_); 65 | file.close(); 66 | } else { 67 | throw SerializationException("Unable to open file to save vignetting model", filename); 68 | } 69 | } 70 | 71 | cv::Vec3f NonparametricVignettingModel::operator()(const cv::Vec2f& p) const { 72 | return coefficients_.at(static_cast(std::floor(p[1])), static_cast(std::floor(p[0]))); 73 | } 74 | 75 | cv::Size NonparametricVignettingModel::getImageSize() const { 76 | return coefficients_.size(); 77 | } 78 | 79 | cv::Mat NonparametricVignettingModel::getModelCoefficients() const { 80 | return coefficients_; 81 | } 82 | 83 | } // namespace radical 84 | -------------------------------------------------------------------------------- /include/grabbers/grabber.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | namespace grabbers { 35 | 36 | class GrabberException : public boost::exception, public std::runtime_error { 37 | public: 38 | GrabberException(const std::string& message) 39 | : std::runtime_error(message) {} 40 | using ErrorInfo = boost::error_info; 41 | using URI = boost::error_info; 42 | }; 43 | 44 | class Grabber { 45 | public: 46 | using Ptr = std::shared_ptr; 47 | 48 | virtual ~Grabber(); 49 | 50 | virtual bool hasMoreFrames() const = 0; 51 | 52 | virtual bool grabFrame(cv::OutputArray color) = 0; 53 | 54 | virtual void setAutoWhiteBalanceEnabled(bool state = true) = 0; 55 | 56 | virtual void setAutoExposureEnabled(bool state = true) = 0; 57 | 58 | virtual void setExposure(int exposure) = 0; 59 | 60 | virtual int getExposure() const = 0; 61 | 62 | virtual std::pair getExposureRange() const = 0; 63 | 64 | virtual void setGain(int gain) = 0; 65 | 66 | virtual int getGain() const = 0; 67 | 68 | virtual std::pair getGainRange() const = 0; 69 | 70 | virtual std::string getCameraModelName() const = 0; 71 | 72 | virtual std::string getCameraSerialNumber() const = 0; 73 | 74 | /** Get a unique id for the camera (concatenation of model name and serial number). */ 75 | std::string getCameraUID() const; 76 | }; 77 | 78 | /** Create a grabber from URI. 79 | * 80 | * Supported URI types: 81 | * 82 | * - "rs", "realsense", "intel" → RealSenseGrabber with first available device 83 | * - "openni", "openni2", "kinect", "asus" → OpenNI2Grabber with first available device 84 | * - path to an *.oni file → OpenNIGrabber with file 85 | * - openni device uri → OpenNIGrabber for that device 86 | * - "" (empty string) → first available device with any grabber */ 87 | Grabber::Ptr createGrabber(const std::string& uri = ""); 88 | 89 | } // namespace grabbers 90 | -------------------------------------------------------------------------------- /tests/test.h.in: -------------------------------------------------------------------------------- 1 | #define TEST_DATA_DIR "@TEST_DATA_DIR@" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | inline std::string getTestFilename(const std::string& relative_path) { 9 | boost::filesystem::path p(std::string(TEST_DATA_DIR)); 10 | return (p / relative_path).make_preferred().string(); 11 | } 12 | 13 | inline std::string getTemporaryFilename() { 14 | boost::filesystem::path p(boost::filesystem::temp_directory_path()); 15 | return (p / boost::filesystem::unique_path()).make_preferred().string(); 16 | // TODO: return an object which deletes the file upon destruction 17 | } 18 | 19 | inline void setRNGSeed(int seed) { 20 | #if CV_MAJOR_VERSION > 3 || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION > 1) 21 | cv::setRNGSeed(seed); // this function was added in 3.2 22 | #else 23 | cv::theRNG().state = seed; 24 | #endif 25 | } 26 | 27 | inline cv::Mat_ generateRandomImage(int height, int width) { 28 | cv::Mat_ image(height, width); 29 | cv::randu(image, cv::Scalar(0, 0, 0), cv::Scalar(255, 255, 255)); 30 | return image; 31 | } 32 | 33 | inline cv::Mat_ generateRandomImage(const cv::Size& size) { 34 | return generateRandomImage(size.height, size.width); 35 | } 36 | 37 | template std::vector generateRandomVector(size_t size, T min, T max) { 38 | std::vector vector(size); 39 | cv::randu(vector, min, max); 40 | return vector; 41 | } 42 | 43 | template T generateRandomNumber(T min, T max) { 44 | std::vector vector(1); 45 | cv::randu(vector, min, max); 46 | return vector[0]; 47 | } 48 | 49 | /** Test two cv::Mat for equality (same size, type, and elements). */ 50 | template 51 | boost::test_tools::predicate_result compareMat(const cv::Mat& m1, const cv::Mat& m2) { 52 | if (m1.size() != m2.size()) { 53 | boost::test_tools::predicate_result result(false); 54 | result.message() << "Different sizes [" << m1.size() << " != " << m2.size() << "]"; 55 | return result; 56 | } 57 | if (m1.type() != m2.type()) { 58 | boost::test_tools::predicate_result result(false); 59 | result.message() << "Different types [" << m1.type() << " != " << m2.type() << "]"; 60 | return result; 61 | } 62 | for (int row = 0; row < m1.rows; ++row) { 63 | for (int col = 0; col < m1.cols; ++col) { 64 | const auto& v1 = m1.at(row, col); 65 | const auto& v2 = m2.at(row, col); 66 | if (v1 != v2) { 67 | boost::test_tools::predicate_result result(false); 68 | result.message() << "Elements at position [" << row << ", " << col << "] are different [" << v1 << " != " << v2 69 | << "]"; 70 | return result; 71 | } 72 | } 73 | } 74 | return true; 75 | } 76 | 77 | /** Test each element of a cv::Mat with a given element for equality. */ 78 | template 79 | boost::test_tools::predicate_result compareMat(const cv::Mat& m, const T& element) { 80 | T e(element); 81 | for (int row = 0; row < m.rows; ++row) { 82 | for (int col = 0; col < m.cols; ++col) { 83 | const auto& v = m.at(row, col); 84 | if (v != e) { 85 | boost::test_tools::predicate_result result(false); 86 | result.message() << "Element at position [" << row << ", " << col << "] is different [" << v << " != " << e 87 | << "]"; 88 | return result; 89 | } 90 | } 91 | } 92 | return true; 93 | } 94 | 95 | #define BOOST_CHECK_EQUAL_MAT(M1, M2, T) BOOST_CHECK((compareMat(M1, M2))) 96 | #define BOOST_REQUIRE_EQUAL_MAT(M1, M2, T) BOOST_REQUIRE((compareMat(M1, M2))) 97 | -------------------------------------------------------------------------------- /include/utils/arrange_images_in_grid.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | // To preserve compatibility with OpenCV 2.x 32 | #if CV_MAJOR_VERSION >= 4 33 | #define CV_BGR2GRAY cv::COLOR_BGR2GRAY 34 | #define CV_GRAY2BGR cv::COLOR_GRAY2BGR 35 | #endif 36 | 37 | /** Arrange a collection of images into a grid and return as a single cv::Mat. 38 | * The input images may have different depth and number of channels, but should have equal size. */ 39 | cv::Mat arrangeImagesInGrid(const std::vector& images, cv::Size grid, int depth = -1, int channels = -1) { 40 | cv::Mat out; 41 | if (images.empty()) 42 | return out; 43 | 44 | std::vector homogenized_images(images.size()); 45 | 46 | // All images should have same size, depth, and number of channels 47 | cv::Size size = images.front().size(); 48 | depth = (depth == -1 ? images.front().depth() : depth); 49 | channels = (channels == -1 ? images.front().channels() : channels); 50 | int type = CV_MAKETYPE(depth, channels); 51 | for (size_t i = 0; i < images.size(); ++i) { 52 | BOOST_ASSERT(images[i].size() == size); 53 | if (images[i].depth() != depth) 54 | images[i].convertTo(homogenized_images[i], depth); 55 | else 56 | homogenized_images[i] = images[i]; 57 | if (homogenized_images[i].channels() == 3 && channels == 1) 58 | cv::cvtColor(homogenized_images[i], homogenized_images[i], CV_BGR2GRAY); 59 | else if (homogenized_images[i].channels() == 1 && channels == 3) 60 | cv::cvtColor(homogenized_images[i], homogenized_images[i], CV_GRAY2BGR); 61 | else if (homogenized_images[i].channels() != channels) 62 | BOOST_ASSERT_MSG(false, "Number of channels does not match and can not be corrected"); 63 | } 64 | 65 | // Grid dimensions should be positive and no less than the number of images 66 | BOOST_ASSERT(grid.width > 0 && grid.height > 0); 67 | BOOST_ASSERT(static_cast(grid.area()) >= homogenized_images.size()); 68 | 69 | int width = grid.width * size.width, height = grid.height * size.height; 70 | out.create(height, width, type); 71 | out.setTo(0); 72 | for (int i = 0; i < static_cast(homogenized_images.size()); ++i) { 73 | int y = i / grid.width; 74 | int x = i % grid.width; 75 | cv::Mat roi(out, cv::Rect(x * size.width, y * size.height, size.width, size.height)); 76 | homogenized_images[i].copyTo(roi); 77 | } 78 | return out; 79 | } 80 | -------------------------------------------------------------------------------- /include/radical/radiometric_response.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | namespace radical { 32 | 33 | /** The RadiometricResponse class models camera response function (CRF) and 34 | * allows to map from pixel brightness to pixel irradiance and vice versa. */ 35 | class RadiometricResponse { 36 | public: 37 | using Ptr = std::shared_ptr; 38 | 39 | /** Construct RadiometricResponse from a cv::Mat with inverse CRF. 40 | * \param[in] response inverse CRF (CV_32FC3, 256 elements) */ 41 | RadiometricResponse(cv::InputArray response); 42 | 43 | RadiometricResponse(const std::string& filename); 44 | 45 | virtual ~RadiometricResponse(); 46 | 47 | /** Get inverse of the response function (a lookup table that maps pixel brightness to irradiance). */ 48 | cv::Mat getInverseResponse() const; 49 | 50 | /** Write radiometric response to a file. */ 51 | void save(const std::string& filename) const; 52 | 53 | /** Compute pixel brightness from pixel irradiance (direct mapping). 54 | * \param[in] E pixel irradiance 55 | * \returns pixel brightness */ 56 | cv::Vec3b directMap(const cv::Vec3f& E) const; 57 | 58 | /** Compute image brightness from image irradiance (direct mapping). 59 | * \param[in] E image irradiance 60 | * \param[out] I image brightness */ 61 | void directMap(cv::InputArray E, cv::OutputArray I) const; 62 | 63 | /** Compute pixel irradiance from pixel brightness (inverse mapping). 64 | * \param[in] I pixel brightness 65 | * \returns pixel irradiance */ 66 | cv::Vec3f inverseMap(const cv::Vec3b& I) const; 67 | 68 | /** Compute image irradiance from image brightness (inverse mapping). 69 | * \param[in] I image brightness 70 | * \param[out] E image irradiance */ 71 | void inverseMap(cv::InputArray I, cv::OutputArray E) const; 72 | 73 | /** Compute logarithm of pixel irradiance from pixel brightness (inverse mapping). 74 | * \param[in] I pixel brightness 75 | * \returns logarithm of pixel irradiance */ 76 | cv::Vec3f inverseLogMap(const cv::Vec3b& I) const; 77 | 78 | /** Compute logarithm of image irradiance from image brightness (inverse mapping). 79 | * \param[in] I image brightness 80 | * \param[out] E logarithm of image irradiance */ 81 | void inverseLogMap(cv::InputArray I, cv::OutputArray E) const; 82 | 83 | private: 84 | cv::Mat response_; 85 | cv::Mat log_response_; 86 | std::vector response_channels_; 87 | }; 88 | 89 | } // namespace radical 90 | -------------------------------------------------------------------------------- /doc/calibrate-radiometric-response.md: -------------------------------------------------------------------------------- 1 | # `calibrate_radiometric_response` 2 | 3 | This app allows you to calibrate the radiometric response of the camera. Two 4 | methods are implemented: 5 | 6 | * Debevec and Malik (Recovering High Dynamic Range Radiance Maps from Photographs) 7 | * Engel et al. (A Photometrically Calibrated Benchmark For Monocular Visual Odometry) 8 | 9 | The first method requires _radical_ to be compiled with _Ceres_ support. If you 10 | do not have _Ceres_ installed on your system (as will often be the case for 11 | Windows users), then you are limited to the second option. In our experience, 12 | the first methods gives better results, so it is recommended to use it when 13 | possible. 14 | 15 | To see the list of available command-line options, run 16 | `calibrate_radiometric_response --help`. 17 | 18 | ## Preparation 19 | 20 | Fix the camera in front of a static scene, i.e. a scene without moving objects 21 | and with stable lighting conditions. Note that even slight variations in 22 | lighting during the calibration process may negatively affect the results. Be 23 | especially careful if you scene is lit by a natural light source. For example, 24 | on partly cloudy days the light intensity will fluctuate as the clouds move 25 | through the sky. 26 | 27 | During the calibration process the camera will take images at multiple exposure 28 | times, starting from the shortest and ending at the longest. The goal is to 29 | observe all possible intensity values in every color channel. Therefore, choose 30 | the scene such that with the shortest exposure time the image is mostly black 31 | and with the longest exposure time it is very bright. A good choice is to put a 32 | colorful picture in front of the camera (Suprematist art works fairly well). 33 | Here is a possible calibration setup: 34 | 35 |

36 | 37 | ## Data collection 38 | 39 | After starting the app, a window with the current camera image will pop up. The 40 | app will adjust the white balance and exposure time, and then begin data 41 | capture. The window will also display a histogram of intensity distribution in 42 | the collected dataset: 43 | 44 |

45 | 46 | Once again, it is important that all possible intensities in all color channels 47 | are present in the dataset. Use the histogram to assess the quality of collected 48 | data and strive to get something similar to what is shown above. If some 49 | intensities are missing, choose another scene and re-run the app. 50 | 51 | ## Response computation 52 | 53 | Once the dataset is collected, the app will proceed to compute the radiometric 54 | response for each color channel. The process will be visualized in the window: 55 | 56 |

57 | 58 | Note that above is the visualization for `debevec` method. For `engel` it will 59 | look slightly different. 60 | 61 | ## Results 62 | 63 | After the response curves are computed, they will be saved to a file and 64 | presented for visual evaluation on the screen: 65 | 66 |

67 | 68 | In our experience, the curves should be smooth and very similar between 69 | different color channels. If this is not the case, double-check that there is no 70 | lighting changes during data collection. Also try to change the scene or tweak 71 | app parameters. 72 | 73 | ## Hints 74 | 75 | The app can store the collected dataset if you run it with the `--save` option. 76 | Goes without saying, the app can calibrate radiometric response from a stored 77 | dataset. Leverage this to save time on re-collecting the data if you want to 78 | experiment with different calibration methods and their parameters. 79 | -------------------------------------------------------------------------------- /src/apps/display_radiometric_response/display_radiometric_response.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | // In OpenCV 3 imwrite has been moved to imgcodecs module 28 | #if CV_MAJOR_VERSION >= 3 29 | #include 30 | #endif 31 | 32 | #include 33 | 34 | #include "utils/plot_radiometric_response.h" 35 | #include "utils/program_options.h" 36 | 37 | class Options : public OptionsBase { 38 | public: 39 | std::string r_response; 40 | bool save = false; 41 | 42 | protected: 43 | void addOptions(boost::program_options::options_description& desc) override { 44 | namespace po = boost::program_options; 45 | desc.add_options()("save,s", po::bool_switch(&save), "Save to PNG file and exit"); 46 | } 47 | 48 | void addPositional(boost::program_options::options_description& desc, 49 | boost::program_options::positional_options_description& positional) override { 50 | namespace po = boost::program_options; 51 | desc.add_options()("crf", po::value(&r_response), "Calibration file with radiometric response"); 52 | positional.add("crf", 1); 53 | } 54 | 55 | void printHelp() override { 56 | std::cout << "Usage: display_radiometric_response [options] " << std::endl; 57 | std::cout << "" << std::endl; 58 | std::cout << "Plots radiometric response stored in a calibration file and displays it on the screen." << std::endl; 59 | std::cout << "" << std::endl; 60 | std::cout << "With the --save option the plot will be written to the disk instead of showing on the " << std::endl; 61 | std::cout << "screen. Output file name is constructed by appending \".png\" to the input file path." << std::endl; 62 | std::cout << "" << std::endl; 63 | } 64 | }; 65 | 66 | int main(int argc, const char** argv) { 67 | Options options; 68 | if (!options.parse(argc, argv)) 69 | return 1; 70 | 71 | radical::RadiometricResponse rr(options.r_response); 72 | auto min_radiance = rr.inverseMap(cv::Vec3b(0, 0, 0)); 73 | auto max_radiance = rr.inverseMap(cv::Vec3b(255, 255, 255)); 74 | 75 | std::cout << "Loaded radiometric response from file \"" << options.r_response << "\"" << std::endl; 76 | std::cout << "Irradiance range: " << min_radiance << " - " << max_radiance << std::endl; 77 | 78 | auto plot = utils::plotRadiometricResponse(rr); 79 | 80 | if (options.save) { 81 | auto output = options.r_response + ".png"; 82 | cv::imwrite(output, plot); 83 | std::cout << "Saved radiometric response visualization to file \"" << output << "\"" << std::endl; 84 | } else { 85 | cv::imshow("Radiometric response", plot); 86 | cv::waitKey(-1); 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /cmake/modules/FindOpenNI2.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Find OpenNI2 3 | # 4 | # find_package(OpenNI2) 5 | # 6 | # Variables defined by this module: 7 | # 8 | # OPENNI2_FOUND True if OpenNI2 was found 9 | # OPENNI2_VERSION The version of found OpenNI2 library 10 | # OPENNI2_INCLUDE_DIR The location of OpenNI2 headers 11 | # OPENNI2_LIBRARY Libraries needed to use OpenNI2 12 | # 13 | # Imported targets defined by this module: 14 | # 15 | # openni2 Library to link against 16 | # 17 | # Based on the CMake script from OpenCV 18 | 19 | if(NOT "${OPENNI2_LIB_DIR}" STREQUAL "${OPENNI2_LIB_DIR_INTERNAL}") 20 | unset(OPENNI2_LIBRARY CACHE) 21 | unset(OPENNI2_LIB_DIR CACHE) 22 | endif() 23 | 24 | if(NOT "${OPENNI2_INCLUDE_DIR}" STREQUAL "${OPENNI2_INCLUDE_DIR_INTERNAL}") 25 | unset(OPENNI2_INCLUDES CACHE) 26 | unset(OPENNI2_INCLUDE_DIR CACHE) 27 | endif() 28 | 29 | if(WIN32) 30 | if(CMAKE_SIZEOF_VOID_P EQUAL 4) 31 | find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS "$ENV{OPEN_NI_INSTALL_PATH}Include" DOC "OpenNI2 c++ interface header") 32 | find_library(OPENNI2_LIBRARY "OpenNI2" PATHS $ENV{OPENNI2_LIB} DOC "OpenNI2 library") 33 | else() 34 | find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS $ENV{OPENNI2_INCLUDE64} "$ENV{OPEN_NI_INSTALL_PATH64}Include" DOC "OpenNI2 c++ interface header") 35 | find_library(OPENNI2_LIBRARY "OpenNI2" PATHS $ENV{OPENNI2_LIB64} DOC "OpenNI2 library") 36 | endif() 37 | set(LIBRARY_TYPE STATIC) 38 | elseif(UNIX OR APPLE) 39 | find_file(OPENNI2_INCLUDES "OpenNI.h" PATHS "/usr/include/ni2" "/usr/include/openni2" $ENV{OPENNI2_INCLUDE} DOC "OpenNI2 c++ interface header") 40 | find_library(OPENNI2_LIBRARY "OpenNI2" PATHS "/usr/lib" $ENV{OPENNI2_REDIST} DOC "OpenNI2 library") 41 | set(LIBRARY_TYPE SHARED) 42 | endif() 43 | 44 | if(OPENNI2_LIBRARY AND OPENNI2_INCLUDES) 45 | set(HAVE_OPENNI2 TRUE) 46 | endif() 47 | 48 | get_filename_component(OPENNI2_LIB_DIR "${OPENNI2_LIBRARY}" PATH) 49 | get_filename_component(OPENNI2_INCLUDE_DIR ${OPENNI2_INCLUDES} PATH) 50 | 51 | if(HAVE_OPENNI2) 52 | set(OPENNI2_LIB_DIR "${OPENNI2_LIB_DIR}" CACHE PATH "Path to OpenNI2 libraries" FORCE) 53 | set(OPENNI2_INCLUDE_DIR "${OPENNI2_INCLUDE_DIR}" CACHE PATH "Path to OpenNI2 headers" FORCE) 54 | endif() 55 | 56 | if(OPENNI2_LIBRARY) 57 | set(OPENNI2_LIB_DIR_INTERNAL "${OPENNI2_LIB_DIR}" CACHE INTERNAL "This is the value of the last time OPENNI_LIB_DIR was set successfully." FORCE) 58 | else() 59 | message(WARNING, "OpenNI2 library directory (set by OPENNI2_LIB_DIR variable) is not found or does not have OpenNI2 libraries.") 60 | endif() 61 | 62 | if(OPENNI2_INCLUDES) 63 | set(OPENNI2_INCLUDE_DIR_INTERNAL "${OPENNI2_INCLUDE_DIR}" CACHE INTERNAL "This is the value of the last time OPENNI2_INCLUDE_DIR was set successfully." FORCE) 64 | else() 65 | message(WARNING, "OpenNI2 include directory (set by OPENNI2_INCLUDE_DIR variable) is not found or does not have OpenNI2 include files.") 66 | endif() 67 | 68 | mark_as_advanced(FORCE OPENNI2_LIBRARY) 69 | mark_as_advanced(FORCE OPENNI2_INCLUDES) 70 | 71 | if(HAVE_OPENNI2) 72 | 73 | ## Imported target 74 | add_library(openni2 ${LIBRARY_TYPE} IMPORTED) 75 | set_property(TARGET openni2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${OPENNI2_INCLUDE_DIR}) 76 | set_property(TARGET openni2 PROPERTY IMPORTED_LOCATION ${OPENNI2_LIBRARY}) 77 | 78 | ## Version 79 | parse_header("${OPENNI2_INCLUDE_DIR}/OniVersion.h" ONI_VERSION_LINE ONI_VERSION_MAJOR ONI_VERSION_MINOR ONI_VERSION_MAINTENANCE ONI_VERSION_BUILD) 80 | if(ONI_VERSION_MAJOR) 81 | set(OPENNI2_VERSION ${ONI_VERSION_MAJOR}.${ONI_VERSION_MINOR}.${ONI_VERSION_MAINTENANCE} CACHE INTERNAL "OpenNI2 version") 82 | set(OPENNI2_VERSION_BUILD ${ONI_VERSION_BUILD} CACHE INTERNAL "OpenNI2 build version") 83 | endif() 84 | 85 | endif() 86 | 87 | include(FindPackageHandleStandardArgs) 88 | find_package_handle_standard_args(OpenNI2 89 | FOUND_VAR OPENNI2_FOUND 90 | REQUIRED_VARS OPENNI2_LIBRARY OPENNI2_INCLUDE_DIR 91 | VERSION_VAR OPENNI2_VERSION 92 | ) 93 | 94 | if(OPENNI2_FOUND AND NOT OpenNI2_FIND_QUIETLY) 95 | message(STATUS "Found OpenNI2 (version: ${OPENNI2_VERSION}, location: ${OPENNI2_LIBRARY})") 96 | endif() 97 | -------------------------------------------------------------------------------- /include/radical/exceptions.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | 29 | namespace radical { 30 | 31 | class Exception : public std::runtime_error { 32 | public: 33 | explicit Exception(const std::string& message) 34 | : std::runtime_error(message) {} 35 | }; 36 | 37 | class SerializationException : public Exception { 38 | public: 39 | explicit SerializationException(const std::string& message) 40 | : Exception(message) {} 41 | 42 | explicit SerializationException(const std::string& message, const std::string& filename) 43 | : Exception(message) 44 | , filename(filename) {} 45 | 46 | const std::string filename; 47 | }; 48 | 49 | class MatException : public Exception { 50 | public: 51 | explicit MatException(const std::string& message) 52 | : Exception(message) {} 53 | }; 54 | 55 | class MatChannelsException : public MatException { 56 | public: 57 | explicit MatChannelsException(const std::string& mat_name, int expected_channels, int actual_channels) 58 | : MatException(mat_name + " does not have expected number of channels") 59 | , expected_channels(expected_channels) 60 | , actual_channels(actual_channels) {} 61 | 62 | const int expected_channels; 63 | const int actual_channels; 64 | }; 65 | 66 | class MatDepthException : public MatException { 67 | public: 68 | explicit MatDepthException(const std::string& mat_name, int expected_depth, int actual_depth) 69 | : MatException(mat_name + " does not have expected depth") 70 | , expected_depth(expected_depth) 71 | , actual_depth(actual_depth) {} 72 | 73 | const int expected_depth; 74 | const int actual_depth; 75 | }; 76 | 77 | class MatMaxDimensionsException : public MatException { 78 | public: 79 | explicit MatMaxDimensionsException(const std::string& mat_name, int expected_max_dims, int actual_dims) 80 | : MatException(mat_name + " has more than expected dimensions") 81 | , expected_max_dims(expected_max_dims) 82 | , actual_dims(actual_dims) {} 83 | 84 | const int expected_max_dims; 85 | const int actual_dims; 86 | }; 87 | 88 | class MatSizeException : public MatException { 89 | public: 90 | explicit MatSizeException(const std::string& mat_name, const cv::Size& expected_size, const cv::Size& actual_size) 91 | : MatException(mat_name + " does not have expected size") 92 | , expected_size(expected_size) 93 | , actual_size(actual_size) {} 94 | 95 | const cv::Size expected_size; 96 | const cv::Size actual_size; 97 | }; 98 | 99 | class MatTypeException : public MatException { 100 | public: 101 | explicit MatTypeException(const std::string& mat_name, int expected_type, int actual_type) 102 | : MatException(mat_name + " does not have expected type") 103 | , expected_type(expected_type) 104 | , actual_type(actual_type) {} 105 | 106 | const int expected_type; 107 | const int actual_type; 108 | }; 109 | 110 | } // namespace radical 111 | -------------------------------------------------------------------------------- /tests/test_polynomial_vignetting_model.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "test.h" 24 | 25 | #include 26 | #include 27 | 28 | using namespace radical; 29 | 30 | BOOST_AUTO_TEST_CASE(MatConstructor) { 31 | // Invalid initialization, should throw 32 | cv::Mat m; 33 | cv::Size s(100, 100); 34 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(m, s), MatException); 35 | m.create(10, 10, CV_64FC3); 36 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(m, s), MatSizeException); 37 | m.create(1, 4, CV_64FC3); 38 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(m, s), MatSizeException); 39 | m.create(1, 5, CV_32FC1); 40 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(m, s), MatTypeException); 41 | // Valid initialization 42 | m.create(1, 5, CV_64FC3); 43 | m.setTo(1.0f); 44 | BOOST_CHECK_NO_THROW(PolynomialVignettingModel<3> vm(m, s)); 45 | m.create(5, 1, CV_64FC3); 46 | m.setTo(1.0f); 47 | BOOST_CHECK_NO_THROW(PolynomialVignettingModel<3> vm(m, s)); 48 | } 49 | 50 | BOOST_AUTO_TEST_CASE(LoadConstructor) { 51 | // Invalid initialization, should throw 52 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(getTestFilename("file_that_does_not_exist.vgn")), 53 | SerializationException); 54 | BOOST_CHECK_THROW(PolynomialVignettingModel<3> vm(getTestFilename("vignetting_model_empty.vgn")), 55 | SerializationException); 56 | // Valid initialization 57 | BOOST_CHECK_NO_THROW(PolynomialVignettingModel<3> vm(getTestFilename("polynomial_vignetting_model_identity.vgn"))); 58 | } 59 | 60 | BOOST_AUTO_TEST_CASE(GetName) { 61 | PolynomialVignettingModel<3> vm(getTestFilename("polynomial_vignetting_model_identity.vgn")); 62 | BOOST_CHECK_EQUAL(vm.getName(), "polynomial 3"); 63 | } 64 | 65 | BOOST_AUTO_TEST_CASE(GetImageSize) { 66 | PolynomialVignettingModel<3> vm(cv::Mat(1, 5, CV_64FC3), cv::Size(10, 10)); 67 | BOOST_CHECK_EQUAL(vm.getImageSize().width, 10); 68 | BOOST_CHECK_EQUAL(vm.getImageSize().height, 10); 69 | } 70 | 71 | BOOST_AUTO_TEST_CASE(GetModelCoefficients) { 72 | cv::Mat m(5, 1, CV_64FC3); 73 | m.setTo(1.0f); 74 | PolynomialVignettingModel<3> vm(m, cv::Size(10, 10)); 75 | BOOST_CHECK_EQUAL_MAT(vm.getModelCoefficients(), m, cv::Vec3d); 76 | } 77 | 78 | BOOST_AUTO_TEST_CASE(ModelEvaluation) { 79 | // Identity loaded from file 80 | { 81 | PolynomialVignettingModel<3> vm(getTestFilename("polynomial_vignetting_model_identity.vgn")); 82 | for (int row = 0; row < vm.getImageSize().height; ++row) 83 | for (int col = 0; col < vm.getImageSize().width; ++col) { 84 | BOOST_CHECK_EQUAL(vm(cv::Vec2f(row, col)), cv::Vec3f(1.0, 1.0, 1.0)); 85 | BOOST_CHECK_EQUAL(vm(row, col), cv::Vec3f(1.0, 1.0, 1.0)); 86 | } 87 | } 88 | } 89 | 90 | BOOST_AUTO_TEST_CASE(SaveLoad) { 91 | cv::Mat m(1, 5, CV_64FC3); 92 | m.setTo(1.0f); 93 | auto f = getTemporaryFilename(); 94 | PolynomialVignettingModel<3>(m, cv::Size(640, 480)).save(f); 95 | PolynomialVignettingModel<3> vm(f); 96 | BOOST_CHECK_EQUAL_MAT(vm.getModelCoefficients(), m, cv::Vec3d); 97 | BOOST_CHECK_EQUAL(vm.getImageSize(), cv::Size(640, 480)); 98 | } 99 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/dataset_collection.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include "dataset_collection.h" 30 | 31 | DatasetCollection::DatasetCollection(grabbers::Grabber::Ptr grabber, const Parameters& params) 32 | : grabber_(grabber) 33 | , params_(params) 34 | , dataset_(new Dataset) 35 | , mean_(false, params.num_average_frames) 36 | , mean_mask_(false, params.num_average_frames) 37 | , morph_(cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(params_.bloom_radius * 2, params_.bloom_radius * 2))) { 38 | BOOST_ASSERT(params_.exposure_min <= params_.exposure_max); 39 | BOOST_ASSERT(params_.exposure_factor > 1.0); 40 | exposure_ = params_.exposure_min; 41 | skip_frames_ = params_.exposure_control_lag; 42 | images_to_accumulate_ = params_.num_images; 43 | std::cout << "Starting data collection" << std::endl; 44 | std::cout << "Exposure range: " << params_.exposure_min << " → " << params_.exposure_max << " with factor " 45 | << params_.exposure_factor << std::endl; 46 | std::cout << "Exposure: " << exposure_ << std::flush; 47 | } 48 | 49 | bool DatasetCollection::addFrame(const cv::Mat& frame) { 50 | if (skip_frames_-- > 0) 51 | return false; 52 | 53 | auto averaging_done = mean_.add(frame); 54 | mean_mask_.add(computeSaturationMask(frame)); 55 | if (!averaging_done) 56 | return false; 57 | 58 | auto mask = mean_mask_.getMean(); // everything below 255 was saturated in at least one frame 59 | cv::threshold(mask, mask, 254, 255, cv::THRESH_BINARY_INV); 60 | auto mean = mean_.getMean().clone(); 61 | mean.reshape(1, 1).setTo(0, mask.reshape(1, 1)); // reshape to single-channel, otherwise masking will not work 62 | dataset_->insert(exposure_, mean); 63 | 64 | if (--images_to_accumulate_ > 0) 65 | return false; 66 | 67 | exposure_ += static_cast(std::ceil(exposure_ * (params_.exposure_factor - 1.0))); 68 | skip_frames_ = params_.exposure_control_lag; 69 | images_to_accumulate_ = params_.num_images; 70 | grabber_->setExposure(exposure_); 71 | std::cout << " " << exposure_ << std::flush; 72 | 73 | if (exposure_ > params_.exposure_max) { 74 | std::cout << std::endl; 75 | return true; 76 | } 77 | return false; 78 | } 79 | 80 | Dataset::Ptr DatasetCollection::getDataset() const { 81 | return dataset_; 82 | } 83 | 84 | cv::Mat DatasetCollection::computeSaturationMask(const cv::Mat& image) { 85 | static std::vector mask_channels; 86 | cv::Mat mask; 87 | // Set overexposed (per channel) pixels to 0, everything else to 255 88 | cv::threshold(image, mask, params_.valid_intensity_max, 255, cv::THRESH_BINARY_INV); 89 | cv::split(mask, mask_channels); 90 | // Pixels that are overexposed in all channels are set to 0 91 | cv::Mat bloom_mask = mask_channels[0]; 92 | for (size_t i = 1; i < mask_channels.size(); ++i) 93 | bloom_mask |= mask_channels[i]; 94 | cv::erode(bloom_mask, bloom_mask, morph_); 95 | for (auto& mask_channel : mask_channels) 96 | mask_channel &= bloom_mask; 97 | cv::merge(mask_channels, mask); 98 | return mask; 99 | } 100 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/calibration.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include "calibration.h" 28 | 29 | using boost::format; 30 | using boost::str; 31 | 32 | static const char* NAMES[] = {"Blue", "Green", "Red"}; 33 | 34 | cv::Mat Calibration::calibrate(const Dataset& data) { 35 | if (verbosity_) 36 | std::cout << "Starting " << getMethodName() << " calibration procedure" << std::endl; 37 | 38 | auto datasets = data.splitChannels(); 39 | 40 | std::vector response_channels; 41 | for (channel_ = 0; channel_ < static_cast(datasets.size()); ++channel_) 42 | response_channels.push_back(calibrateChannel(datasets[channel_])); 43 | 44 | // Post-process the response: 45 | // * rescale such that maximum value is 1 46 | // * map intensity below min_valid to 0 47 | // * map intensity above max_valid to 1 48 | // * sort to ensure invertability 49 | for (auto& response_channel : response_channels) { 50 | double min, max; 51 | cv::minMaxIdx(response_channel, &min, &max); 52 | cv::divide(response_channel, max, response_channel); 53 | for (int j = 0; j < min_valid_; ++j) 54 | response_channel.at(j) = 0; 55 | for (int j = max_valid_ + 1; j < 256; ++j) 56 | response_channel.at(j) = 1; 57 | cv::sort(response_channel, response_channel, cv::SORT_EVERY_ROW | cv::SORT_ASCENDING); 58 | } 59 | 60 | cv::Mat response; 61 | cv::merge(response_channels, response); 62 | return response; 63 | } 64 | 65 | void Calibration::setMaxNumIterations(unsigned int max_num_iterations) { 66 | max_num_iterations_ = max_num_iterations; 67 | } 68 | 69 | void Calibration::setVerbosity(unsigned int level) { 70 | verbosity_ = level; 71 | } 72 | 73 | void Calibration::setValidPixelRange(unsigned char min_valid, unsigned char max_valid) { 74 | min_valid_ = min_valid; 75 | max_valid_ = max_valid; 76 | } 77 | 78 | void Calibration::setVisualizeProgress(const std::function& imshow) { 79 | imshow_ = imshow; 80 | } 81 | 82 | bool Calibration::isPixelValid(unsigned char pixel) const { 83 | return pixel >= min_valid_ && pixel <= max_valid_; 84 | } 85 | 86 | bool Calibration::isPixelValid(const cv::Vec3b& pixel) const { 87 | return isPixelValid(pixel[0]) && isPixelValid(pixel[1]) && isPixelValid(pixel[2]); 88 | } 89 | 90 | void Calibration::printHeader() const { 91 | if (verbosity_) 92 | std::cout << str(format("| %=7s | %=5s | %=14s | %=14s |\n") % "Channel" % "Iter" % "Residual" % "Delta"); 93 | } 94 | 95 | void Calibration::printFooter() const { 96 | if (verbosity_) 97 | std::cout << boost::format("%53T-") << std::endl; 98 | } 99 | 100 | void Calibration::printIteration(unsigned int iteration, double residual, double delta, const char extra) { 101 | if (verbosity_) { 102 | if (iteration == 1) 103 | std::cout << str(format("| %=7s | %=4d%c | %14.6f | %=14s |\n") % NAMES[channel_] % iteration % extra % residual % 104 | ""); 105 | else 106 | std::cout << str(format("| %=7s | %=4d%c | %14.6f | %14.6f |\n") % "" % iteration % extra % residual % delta); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/apps/calibrate_vignetting_response/model_fitting.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #ifdef HAVE_CERES 24 | 25 | #include 26 | 27 | #include "model_fitting.h" 28 | 29 | using ceres::AutoDiffCostFunction; 30 | using ceres::CauchyLoss; 31 | using ceres::Problem; 32 | using ceres::Solve; 33 | using ceres::Solver; 34 | 35 | struct Residual { 36 | double x_; 37 | double y_; 38 | double i_; 39 | 40 | Residual(int x, int y, float i) 41 | : x_(x) 42 | , y_(y) 43 | , i_(i) {} 44 | 45 | template 46 | bool operator()(const T* const c, const T* const b, T* residual) const { 47 | auto dx = c[0] - T(x_); 48 | auto dy = c[1] - T(y_); 49 | auto r_2 = dx * dx + dy * dy; 50 | auto r_4 = r_2 * r_2; 51 | auto r_6 = r_4 * r_2; 52 | auto v = T(1.0 + b[0] * r_2 + b[1] * r_4 + b[2] * r_6); 53 | residual[0] = T(i_) - v; 54 | return true; 55 | } 56 | }; 57 | 58 | radical::PolynomialVignettingModel<3>::Ptr fitPolynomialModel(cv::InputArray _data, bool fixed_center, 59 | unsigned int max_num_iterations, bool verbose) { 60 | cv::Mat_ data = _data.getMat(); 61 | cv::Mat coeff(1, 5, CV_64FC3); 62 | for (size_t i = 0; i < 3; ++i) { 63 | double c[2]; 64 | double b[3]; 65 | 66 | c[0] = data.cols / 2; 67 | c[1] = data.rows / 2; 68 | int W = 640 / data.cols; 69 | b[0] = -7.5e-06 * std::pow(W, 2); 70 | b[1] = 5e-11 * std::pow(W, 4); 71 | b[2] = -2e-16 * std::pow(W, 6); 72 | 73 | Problem problem; 74 | auto loss = new CauchyLoss(0.5); 75 | 76 | problem.AddParameterBlock(c, 2); 77 | problem.AddParameterBlock(b, 3); 78 | 79 | for (int y = 0; y < data.rows; ++y) 80 | for (int x = 0; x < data.cols; ++x) 81 | problem.AddResidualBlock(new AutoDiffCostFunction(new Residual{x, y, data(y, x)[i]}), loss, 82 | c, b); 83 | 84 | problem.SetParameterLowerBound(c, 0, c[0] * 0.9); 85 | problem.SetParameterUpperBound(c, 0, c[0] * 1.1); 86 | problem.SetParameterLowerBound(c, 1, c[1] * 0.9); 87 | problem.SetParameterUpperBound(c, 1, c[1] * 1.1); 88 | 89 | if (fixed_center) 90 | problem.SetParameterBlockConstant(c); 91 | 92 | Solver::Options options; 93 | options.max_num_iterations = max_num_iterations; 94 | options.linear_solver_type = ceres::DENSE_QR; 95 | options.minimizer_progress_to_stdout = false; 96 | 97 | Solver::Summary summary; 98 | Solve(options, &problem, &summary); 99 | if (verbose) 100 | std::cout << summary.FullReport() << std::endl; 101 | else 102 | std::cout << summary.BriefReport() << std::endl; 103 | 104 | std::cout << "Final cx: " << c[0] << " cy: " << c[1] << " b1: " << b[0] << " b2: " << b[1] << " b3: " << b[2] 105 | << "\n"; 106 | coeff.at(0, 0)[i] = c[0]; 107 | coeff.at(0, 1)[i] = c[1]; 108 | coeff.at(0, 2)[i] = b[0]; 109 | coeff.at(0, 3)[i] = b[1]; 110 | coeff.at(0, 4)[i] = b[2]; 111 | } 112 | 113 | return std::make_shared>(coeff, data.size()); 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /tests/test_nonparametric_vignetting_model.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "test.h" 24 | 25 | #include 26 | #include 27 | 28 | using namespace radical; 29 | 30 | BOOST_AUTO_TEST_CASE(MatConstructor) { 31 | // Invalid initialization, should throw 32 | cv::Mat m; 33 | BOOST_CHECK_THROW(NonparametricVignettingModel vm(m), MatException); 34 | m.create(10, 10, CV_8UC3); 35 | BOOST_CHECK_THROW(NonparametricVignettingModel vm(m), MatTypeException); 36 | m.create(10, 10, CV_32FC1); 37 | BOOST_CHECK_THROW(NonparametricVignettingModel vm(m), MatTypeException); 38 | // Valid initialization 39 | m.create(10, 10, CV_32FC3); 40 | m.setTo(1.0f); 41 | BOOST_CHECK_NO_THROW(NonparametricVignettingModel vm(m)); 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE(LoadConstructor) { 45 | // Invalid initialization, should throw 46 | BOOST_CHECK_THROW(NonparametricVignettingModel vm(getTestFilename("file_that_does_not_exist.vgn")), 47 | SerializationException); 48 | BOOST_CHECK_THROW(NonparametricVignettingModel vm(getTestFilename("vignetting_model_empty.vgn")), 49 | SerializationException); 50 | // Valid initialization 51 | BOOST_CHECK_NO_THROW(NonparametricVignettingModel vm(getTestFilename("nonparametric_vignetting_model_identity.vgn"))); 52 | } 53 | 54 | BOOST_AUTO_TEST_CASE(GetName) { 55 | NonparametricVignettingModel vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 56 | BOOST_CHECK_EQUAL(vm.getName(), "nonparametric"); 57 | } 58 | 59 | BOOST_AUTO_TEST_CASE(GetImageSize) { 60 | NonparametricVignettingModel vm(cv::Mat(10, 10, CV_32FC3)); 61 | BOOST_CHECK_EQUAL(vm.getImageSize().width, 10); 62 | BOOST_CHECK_EQUAL(vm.getImageSize().height, 10); 63 | } 64 | 65 | BOOST_AUTO_TEST_CASE(GetModelCoefficients) { 66 | cv::Mat m(10, 10, CV_32FC3); 67 | m.setTo(1.0f); 68 | NonparametricVignettingModel vm(m); 69 | BOOST_CHECK_EQUAL_MAT(vm.getModelCoefficients(), m, cv::Vec3f); 70 | } 71 | 72 | BOOST_AUTO_TEST_CASE(ModelEvaluation) { 73 | // Model with random numbers 74 | { 75 | cv::Mat m(10, 10, CV_32FC3); 76 | cv::randu(m, cv::Scalar(0, 0, 0), cv::Scalar(1, 1, 1)); 77 | NonparametricVignettingModel vm(m); 78 | for (int row = 0; row < m.rows; ++row) 79 | for (int col = 0; col < m.cols; ++col) { 80 | BOOST_CHECK_EQUAL(vm(cv::Vec2f(row, col)), m.at(col, row)); 81 | BOOST_CHECK_EQUAL(vm(cv::Vec2f(row + 0.1, col + 0.8)), m.at(col, row)); 82 | BOOST_CHECK_EQUAL(vm(row, col), m.at(col, row)); 83 | } 84 | } 85 | // Identity loaded from file 86 | { 87 | NonparametricVignettingModel vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 88 | for (int row = 0; row < vm.getImageSize().height; ++row) 89 | for (int col = 0; col < vm.getImageSize().width; ++col) { 90 | BOOST_CHECK_EQUAL(vm(cv::Vec2f(row, col)), cv::Vec3f(1.0, 1.0, 1.0)); 91 | BOOST_CHECK_EQUAL(vm(row, col), cv::Vec3f(1.0, 1.0, 1.0)); 92 | } 93 | } 94 | } 95 | 96 | BOOST_AUTO_TEST_CASE(SaveLoad) { 97 | cv::Mat m(10, 10, CV_32FC3); 98 | m.setTo(1.0f); 99 | auto f = getTemporaryFilename(); 100 | NonparametricVignettingModel(m).save(f); 101 | NonparametricVignettingModel vm(f); 102 | BOOST_CHECK_EQUAL_MAT(vm.getModelCoefficients(), m, cv::Vec3f); 103 | } 104 | -------------------------------------------------------------------------------- /src/radical/polynomial_vignetting_model.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | namespace radical { 33 | 34 | template 35 | PolynomialVignettingModel::PolynomialVignettingModel(cv::InputArray _coefficients, cv::Size image_size) { 36 | Check("Polynomial vignetting model", _coefficients).notEmpty().hasSize(Degree + 2).hasType(CV_64FC3); 37 | coefficients_ = _coefficients.getMat(); 38 | image_size_ = image_size; 39 | } 40 | 41 | template 42 | PolynomialVignettingModel::PolynomialVignettingModel(const std::string& filename) { 43 | std::ifstream file(filename, std::ios::in | std::ios::binary); 44 | if (file.is_open()) { 45 | std::string line, name; 46 | unsigned int degree, width, height; 47 | { 48 | std::getline(file, line); 49 | std::stringstream(line) >> name >> degree >> width >> height; 50 | if (name != "PolynomialVignettingModel" || degree != Degree) { 51 | std::stringstream msg; 52 | msg << "Vignetting model stored in the file is not polynomial of degree " << Degree; 53 | throw SerializationException(msg.str(), filename); 54 | } 55 | } 56 | image_size_ = cv::Size(width, height); 57 | coefficients_ = readMat(file); 58 | // TODO: Check read coefficients 59 | file.close(); 60 | } else { 61 | throw SerializationException("Unable to open vignetting model file", filename); 62 | } 63 | } 64 | 65 | template 66 | std::string PolynomialVignettingModel::getName() const { 67 | std::stringstream name; 68 | name << "polynomial " << Degree; 69 | return name.str(); 70 | } 71 | 72 | template 73 | void PolynomialVignettingModel::save(const std::string& filename) const { 74 | std::ofstream file(filename, std::ios::out | std::ios::binary); 75 | if (file.is_open()) { 76 | std::stringstream header; 77 | header << "PolynomialVignettingModel " << Degree << " " << image_size_.width << " " << image_size_.height << "\n"; 78 | auto str = header.str(); 79 | file.write(str.c_str(), str.size()); 80 | writeMat(file, coefficients_); 81 | file.close(); 82 | } else { 83 | throw SerializationException("Unable to open file to save vignetting model", filename); 84 | } 85 | } 86 | 87 | template 88 | cv::Vec3f PolynomialVignettingModel::operator()(const cv::Vec2f& p) const { 89 | cv::Vec3f result = {1.0, 1.0, 1.0}; 90 | for (int i = 0; i < 3; ++i) { 91 | auto coeff = coefficients_.ptr(); 92 | auto dx = coeff[0][i] - p[0]; 93 | auto dy = coeff[1][i] - p[1]; 94 | double radius_sqr = dx * dx + dy * dy; 95 | double r = radius_sqr; 96 | for (unsigned int j = 2; j < Degree + 2; ++j) { 97 | result[i] += r * coeff[j][i]; 98 | r *= radius_sqr; 99 | } 100 | } 101 | return result; 102 | } 103 | 104 | template 105 | cv::Size PolynomialVignettingModel::getImageSize() const { 106 | return image_size_; 107 | } 108 | 109 | template 110 | cv::Mat PolynomialVignettingModel::getModelCoefficients() const { 111 | return coefficients_; 112 | } 113 | 114 | template class PolynomialVignettingModel<3>; 115 | 116 | } // namespace radical 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Linux build Status](https://travis-ci.org/taketwo/radical.svg?branch=master)](https://travis-ci.org/taketwo/radical) 2 | [![Windows build status](https://ci.appveyor.com/api/projects/status/3d7eoit2j90wi3ep?svg=true)](https://ci.appveyor.com/project/taketwo/radical) 3 | [![Gitter](https://badges.gitter.im/taketwo/radical.svg)](https://gitter.im/taketwo/radical?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 4 | [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/taketwo/radical/blob/master/LICENSE.md) 5 | 6 | Consumer-grade color cameras suffer from significant optical nonlinearities, 7 | often referred to as vignetting effects. For example, in Asus Xtion Pro Live 8 | cameras the pixels in the corners are two times darker than those in the center 9 | of the image. The vignetting effects in Intel RealSense cameras are less severe, 10 | but are still noticeable, as can be seen below: 11 | 12 | ![Vignetting responses](doc/vignetting-responses.png "Vignetting responses") 13 | 14 | Luckily, it is possible to calibrate the camera and remove the effects from the 15 | images: 16 | 17 |

18 | 19 | This repository contains a collection of apps to calibrate radiometric and 20 | vignetting responses of a camera and a runtime library that you can link into 21 | your project to load calibration files and radiometrically rectify the images 22 | delivered by the camera. 23 | 24 | Table of contents 25 | ----------------- 26 | 27 | * [Requirements](#requirements) 28 | * [Installation](#installation) 29 | * [Calibration](#calibration) 30 | * [Radiometric response](doc/calibrate-radiometric-response.md) 31 | * [Vignetting response](doc/calibrate-vignetting-response.md) 32 | * [Library usage](#library-usage) 33 | * [Citing](#citing) 34 | * [License](#license) 35 | 36 | Requirements 37 | ------------ 38 | 39 | You need a compiler that supports C++11. 40 | 41 | ### Runtime library: 42 | 43 | * OpenCV (2 or 3) 44 | 45 | ### Calibration apps: 46 | 47 | * OpenCV (2 or 3) 48 | * Boost 49 | * OpenNI2 (optional, only if you want to calibrate an Asus Xtion camera) 50 | * librealsense (optional, only if you want to calibrate a RealSense camera) 51 | * Pylon SDK (optional, only if you want to calibrate a Pylon camera) 52 | * Ceres (optional, needed for radiometric calibration with Debevec method and 53 | for vignetting calibration with polynomial vignetting model) 54 | 55 | Installation 56 | ------------ 57 | 58 | 1. Clone this repository. 59 | 60 | 2. Configure the project. By default, both runtime library and calibration apps 61 | will be built. If only the runtime library is needed, then configure as 62 | follows: 63 | 64 | ```bash 65 | cmake .. -DBUILD_APPS=OFF 66 | ``` 67 | 68 | Note that if you want to enable support for Debevec camera response function 69 | calibration and fitting of polynomial vignetting model in the calibration 70 | app, you need to tell CMake where Ceres is installed: 71 | 72 | ```bash 73 | cmake .. -DCeres_DIR=/share/Ceres 74 | ``` 75 | 76 | 3. Install the project with `make install`. 77 | 78 | Calibration 79 | ----------- 80 | 81 | In order to radiometrically rectify images (e.g. invert the non-linear camera 82 | response function and correct the vignetting effects), one needs to calibrate 83 | the camera. Refer to the instructions below: 84 | 85 | * [Calibrate radiometric response](doc/calibrate-radiometric-response.md) 86 | * [Calibrate vignetting response](doc/calibrate-vignetting-response.md) 87 | 88 | Library usage 89 | ------------- 90 | 91 | In your *CMakeLists.txt* find the library as follows: 92 | 93 | ```cmake 94 | find_package(radical CONFIG REQUIRED) 95 | ``` 96 | 97 | And link it to your target: 98 | 99 | ```cmake 100 | target_link_libraries(your_target radical) 101 | ``` 102 | 103 | When configuring your project provide the path where *radical* was installed: 104 | 105 | ```bash 106 | cmake .. -Dradical_DIR=/lib/cmake/radical 107 | ``` 108 | 109 | In you app include the headers: 110 | 111 | ```cpp 112 | #include 113 | #include 114 | ``` 115 | 116 | Load calibration files: 117 | 118 | ```cpp 119 | radical::RadiometricResponse rr("calibration-file-path.crf"); 120 | radical::VignettingResponse vr("calibration-file-path.vgn"); 121 | ``` 122 | 123 | Undo vignetting effects in frames coming from the camera: 124 | 125 | ```cpp 126 | cv::Mat frame; // color image from the camera 127 | cv::Mat irradiance, radiance; // temporary storage 128 | cv::Mat frame_corrected; // output image with vignette removed 129 | rr.inverseMap(frame, irradiance); 130 | vr.remove(irradiance, radiance); 131 | rr.directMap(radiance, frame_corrected); 132 | ``` 133 | 134 | Citing 135 | ------ 136 | 137 | If you use this in the academic context, please cite the following paper: 138 | 139 | > **Calibration and Correction of Vignetting Effects with an Application to 3D 140 | Mapping**, *S. V. Alexandrov, J. Prankl, M. Zillich, M. Vincze*, IROS'16 141 | 142 | Licence 143 | ------- 144 | 145 | MIT 146 | -------------------------------------------------------------------------------- /src/radical/radiometric_response.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | /** Helper function for inverse look-up in a table (cv::Vec3f → Vec3b). */ 32 | inline cv::Vec3b inverseLUT(const std::vector& lut, const cv::Vec3f& in) { 33 | cv::Vec3b out; 34 | for (int c = 0; c < 3; ++c) { 35 | auto begin = &lut[c].at(0); 36 | auto p = std::lower_bound(begin, &lut[c].at(255), in[c]); 37 | out[c] = std::distance(begin, p); 38 | } 39 | return out; 40 | } 41 | 42 | namespace radical { 43 | 44 | RadiometricResponse::RadiometricResponse(cv::InputArray _response) { 45 | Check("Radiometric response", _response).hasSize(256).hasType(CV_32FC3); 46 | response_ = _response.getMat(); 47 | cv::split(response_, response_channels_); 48 | cv::log(response_, log_response_); 49 | // Logarithm is only defined for positive numbers, everything else should map to -Inf 50 | const auto Inf = std::numeric_limits::infinity(); 51 | cv::Mat_ positive = (response_.reshape(1) > 0.0f) & (response_.reshape(1) != Inf); 52 | // We used the following code to assign all masked values to -Inf: 53 | // 54 | // log_response_.reshape(1).setTo(-Inf, ~positive); 55 | // 56 | // However, it turned out that with certain compilers and OpenCV versions this assigns very large values, which are 57 | // not exactly Inf and thus don't pass std::isinf() test. The code below is more verbose, but works consistently. 58 | cv::Mat_ log_response_flat = log_response_.reshape(1); 59 | for (size_t i = 0; i < log_response_flat.total(); ++i) 60 | if (!positive(i)) 61 | log_response_flat(i) = -Inf; 62 | } 63 | 64 | RadiometricResponse::RadiometricResponse(const std::string& filename) 65 | : RadiometricResponse(readMat(filename)) {} 66 | 67 | RadiometricResponse::~RadiometricResponse() = default; 68 | 69 | cv::Mat RadiometricResponse::getInverseResponse() const { 70 | return response_; 71 | } 72 | 73 | void RadiometricResponse::save(const std::string& filename) const { 74 | writeMat(filename, response_); 75 | } 76 | 77 | cv::Vec3b RadiometricResponse::directMap(const cv::Vec3f& E) const { 78 | return inverseLUT(response_channels_, E); 79 | } 80 | 81 | void RadiometricResponse::directMap(cv::InputArray _E, cv::OutputArray _I) const { 82 | if (_E.empty()) { 83 | _I.clear(); 84 | return; 85 | } 86 | Check("Irradiance image", _E).hasType(CV_32FC3); 87 | auto E = _E.getMat(); 88 | _I.create(_E.size(), CV_8UC3); 89 | auto I = _I.getMat(); 90 | #if CV_MAJOR_VERSION > 2 91 | E.forEach( 92 | [&I, this](cv::Vec3f& v, const int* p) { I.at(p[0], p[1]) = inverseLUT(response_channels_, v); }); 93 | #else 94 | for (int i = 0; i < E.rows; i++) 95 | for (int j = 0; j < E.cols; j++) 96 | I.at(i, j) = inverseLUT(response_channels_, E.at(i, j)); 97 | #endif 98 | } 99 | 100 | cv::Vec3f RadiometricResponse::inverseMap(const cv::Vec3b& _I) const { 101 | cv::Mat I(1, 1, CV_8UC3); 102 | I.at(0, 0) = _I; 103 | cv::Mat E; 104 | cv::LUT(I, response_, E); 105 | return E.at(0, 0); 106 | } 107 | 108 | void RadiometricResponse::inverseMap(cv::InputArray _I, cv::OutputArray _E) const { 109 | if (_I.empty()) { 110 | _E.clear(); 111 | return; 112 | } 113 | Check("Brightness image", _I).hasType(CV_8UC3); 114 | cv::LUT(_I, response_, _E); 115 | } 116 | 117 | cv::Vec3f RadiometricResponse::inverseLogMap(const cv::Vec3b& _I) const { 118 | cv::Mat I(1, 1, CV_8UC3); 119 | I.at(0, 0) = _I; 120 | cv::Mat E; 121 | cv::LUT(I, log_response_, E); 122 | return E.at(0, 0); 123 | } 124 | 125 | void RadiometricResponse::inverseLogMap(cv::InputArray _I, cv::OutputArray _E) const { 126 | if (_I.empty()) { 127 | _E.clear(); 128 | return; 129 | } 130 | Check("Brightness image", _I).hasType(CV_8UC3); 131 | cv::LUT(_I, log_response_, _E); 132 | } 133 | 134 | } // namespace radical 135 | -------------------------------------------------------------------------------- /src/apps/calibrate_radiometric_response/engel_calibration.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "engel_calibration.h" 24 | 25 | #include "utils/colors.h" 26 | #include "utils/plot_radiometric_response.h" 27 | 28 | cv::Mat EngelCalibration::calibrateChannel(const Dataset& dataset) { 29 | dataset_ = &dataset; 30 | converged_ = false; 31 | energy_ = 0; 32 | delta_ = 0; 33 | scale_ = 1.0; 34 | 35 | U_.create(1, 256, CV_64FC1); 36 | for (int i = 0; i < 256; ++i) 37 | U_.at(i) = (1.0 / 255.0) * i; 38 | 39 | B_.create(dataset_->getImageSize(), CV_64FC1); 40 | 41 | printHeader(); 42 | 43 | unsigned int iteration = 0; 44 | while (iteration < max_num_iterations_) { 45 | optimizeIrradiance(); 46 | auto e = computeEnergy(); 47 | if (iteration > 0) { 48 | delta_ = energy_ - e; 49 | if (delta_ < convergence_threshold_) 50 | converged_ = true; 51 | } 52 | energy_ = e; 53 | printIteration(++iteration, energy_, delta_, 'B'); 54 | 55 | optimizeInverseResponse(); 56 | e = computeEnergy(); 57 | delta_ = energy_ - e; 58 | if (energy_ > 0 && delta_ < convergence_threshold_) 59 | converged_ = true; 60 | energy_ = e; 61 | printIteration(++iteration, energy_, delta_, 'U'); 62 | 63 | rescale(); 64 | visualizeProgress(); 65 | 66 | if (converged_) 67 | break; 68 | } 69 | 70 | printFooter(); 71 | visualizeProgress(); 72 | 73 | cv::Mat response; 74 | U_.convertTo(response, CV_32F); 75 | return response; 76 | } 77 | 78 | void EngelCalibration::setConvergenceThreshold(double threshold) { 79 | convergence_threshold_ = threshold; 80 | } 81 | 82 | void EngelCalibration::optimizeInverseResponse() { 83 | // Eqn. 7 84 | sum_omega_k_.fill(0); 85 | size_omega_k_.fill(0); 86 | 87 | for (const auto& t : dataset_->getExposureTimes()) 88 | for (const auto& image : dataset_->getImages(t)) 89 | for (int i = 0; i < image.rows; ++i) 90 | for (int j = 0; j < image.cols; ++j) { 91 | const auto& p = image.at(i, j); 92 | sum_omega_k_[p] += t * B_.at(i, j); 93 | size_omega_k_[p] += 1; 94 | } 95 | 96 | U_.setTo(0); 97 | for (int k = min_valid_; k <= max_valid_; ++k) 98 | U_.at(k) = sum_omega_k_[k] /= size_omega_k_[k]; 99 | 100 | double max = 2 * U_.at(max_valid_) - U_.at(max_valid_ - 1); 101 | for (int k = max_valid_ + 1; k < 256; ++k) 102 | U_.at(k) = max; 103 | } 104 | 105 | void EngelCalibration::optimizeIrradiance() { 106 | // Eqn. 8 107 | sum_t2_i_.create(dataset_->getImageSize()); 108 | sum_t2_i_.setTo(0); 109 | B_.setTo(0); 110 | 111 | for (const auto& t : dataset_->getExposureTimes()) 112 | for (const auto& image : dataset_->getImages(t)) 113 | for (int i = 0; i < image.rows; ++i) 114 | for (int j = 0; j < image.cols; ++j) { 115 | const auto& p = image.at(i, j); 116 | if (!isPixelValid(p)) 117 | continue; 118 | B_.at(i, j) += U_.at(p) * t; 119 | sum_t2_i_(i, j) += t * t; 120 | } 121 | 122 | cv::divide(B_, sum_t2_i_, B_); 123 | } 124 | 125 | double EngelCalibration::computeEnergy() { 126 | long double energy = 0; 127 | long unsigned int num = 0; 128 | for (const auto& t : dataset_->getExposureTimes()) 129 | for (const auto& image : dataset_->getImages(t)) 130 | for (int i = 0; i < image.rows; ++i) 131 | for (int j = 0; j < image.cols; ++j) 132 | if (isPixelValid(image.at(i, j))) { 133 | long double r = U_.at(image.at(i, j)) - t * B_.at(i, j); 134 | energy += r * r; 135 | num += 1; 136 | } 137 | // Scale the energy to account for all the rescalings that happened along the way 138 | return static_cast(std::sqrt(energy / num) / scale_); 139 | } 140 | 141 | void EngelCalibration::rescale() { 142 | auto scale = 1.0 / U_.at(128); 143 | cv::multiply(U_, scale, U_); 144 | cv::multiply(B_, scale, B_); 145 | // Remember the total scale relative to the initial energy 146 | scale_ *= scale; 147 | } 148 | 149 | void EngelCalibration::visualizeProgress() { 150 | if (imshow_) { 151 | cv::Mat response; 152 | U_.convertTo(response, CV_32F); 153 | imshow_(utils::plotRadiometricResponse(response, cv::Size(500, 500), utils::colors::BGR[channel_])); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/radical/vignetting_response.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | // Custom hasher to allow unordered map with cv::Size keys. 33 | namespace std { 34 | template <> 35 | struct hash { 36 | using argument_type = cv::Size; 37 | using result_type = std::size_t; 38 | result_type operator()(argument_type const& s) const { 39 | result_type const h1(std::hash()(s.width)); 40 | result_type const h2(std::hash()(s.height)); 41 | return h1 ^ (h2 << 1); 42 | } 43 | }; 44 | } // namespace std 45 | 46 | namespace radical { 47 | 48 | struct VignettingResponse::ResponseCache { 49 | std::unordered_map precomputed_responses_; 50 | std::unordered_map precomputed_log_responses_; 51 | const VignettingModel& model_; 52 | 53 | ResponseCache(const VignettingModel& model) 54 | : model_(model) {} 55 | 56 | cv::Mat get(const cv::Size& image_size) { 57 | if (precomputed_responses_.count(image_size) == 0) { 58 | auto x_scale = static_cast(model_.getImageSize().width) / image_size.width; 59 | auto y_scale = static_cast(model_.getImageSize().height) / image_size.height; 60 | if (std::abs(x_scale - y_scale) > std::numeric_limits::epsilon()) 61 | throw Exception("Unable to compute vignetting response on the given image size (different aspect ratio)"); 62 | cv::Mat response(image_size, CV_32FC3); 63 | #if CV_MAJOR_VERSION > 2 64 | response.forEach( 65 | [x_scale, y_scale, this](cv::Vec3f& v, const int* p) { v = model_(x_scale * p[1], y_scale * p[0]); }); 66 | #else 67 | for (int i = 0; i < response.rows; i++) 68 | for (int j = 0; j < response.cols; j++) 69 | response.at(i, j) = model_(x_scale * j, y_scale * i); 70 | #endif 71 | precomputed_responses_.insert({image_size, response}); 72 | } 73 | return precomputed_responses_[image_size]; 74 | } 75 | 76 | cv::Mat getLog(const cv::Size& image_size) { 77 | if (precomputed_log_responses_.count(image_size) == 0) { 78 | cv::Mat log; 79 | cv::log(get(image_size), log); 80 | precomputed_log_responses_.insert({image_size, log}); 81 | } 82 | return precomputed_log_responses_[image_size]; 83 | } 84 | }; 85 | 86 | VignettingResponse::VignettingResponse(const std::string& filename) { 87 | model_ = VignettingModel::load(filename); 88 | if (!model_) 89 | throw SerializationException("File does not contain any valid vignetting model", filename); 90 | 91 | response_cache_.reset(new ResponseCache(*model_)); 92 | } 93 | 94 | VignettingResponse::~VignettingResponse() = default; 95 | 96 | VignettingModel::ConstPtr VignettingResponse::getModel() const { 97 | return model_; 98 | } 99 | 100 | cv::Mat VignettingResponse::getResponse() const { 101 | return response_cache_->get(model_->getImageSize()); 102 | } 103 | 104 | cv::Mat VignettingResponse::getResponse(cv::Size image_size) const { 105 | return response_cache_->get(image_size); 106 | } 107 | 108 | cv::Mat VignettingResponse::getLogResponse() const { 109 | return response_cache_->getLog(model_->getImageSize()); 110 | } 111 | 112 | cv::Mat VignettingResponse::getLogResponse(cv::Size image_size) const { 113 | return response_cache_->getLog(image_size); 114 | } 115 | 116 | void VignettingResponse::remove(cv::InputArray _E, cv::OutputArray _L) const { 117 | if (_E.empty()) { 118 | _L.clear(); 119 | return; 120 | } 121 | Check("Irradiance image", _E).hasType(CV_32FC3); 122 | cv::divide(_E, getResponse(_E.size()), _L); 123 | } 124 | 125 | void VignettingResponse::removeLog(cv::InputArray _E, cv::OutputArray _L) const { 126 | if (_E.empty()) { 127 | _L.clear(); 128 | return; 129 | } 130 | Check("Irradiance image", _E).hasType(CV_32FC3); 131 | cv::subtract(_E, getLogResponse(_E.size()), _L); 132 | } 133 | 134 | void VignettingResponse::add(cv::InputArray _L, cv::OutputArray _E) const { 135 | if (_L.empty()) { 136 | _E.clear(); 137 | return; 138 | } 139 | Check("Radiance image", _L).hasType(CV_32FC3); 140 | cv::multiply(_L, getResponse(_L.size()), _E); 141 | } 142 | 143 | void VignettingResponse::addLog(cv::InputArray _L, cv::OutputArray _E) const { 144 | if (_L.empty()) { 145 | _E.clear(); 146 | return; 147 | } 148 | Check("Radiance image", _L).hasType(CV_32FC3); 149 | cv::add(_L, getLogResponse(_L.size()), _E); 150 | } 151 | 152 | } // namespace radical 153 | -------------------------------------------------------------------------------- /include/utils/mean_image.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | namespace utils { 28 | 29 | /** Compute (weighted) running average and (optionally) variance for a stream of images. */ 30 | class MeanImage { 31 | public: 32 | /** Constructor. 33 | * 34 | * Until the first call to add() the object is in uninitialized state and all getter functions return empty matrices. 35 | * The accumulation is reset when the desired number of samples has been reached for every pixel. 36 | * 37 | * \param[in] compute_variance Enable/disable variance computation. 38 | * \param[in] num_samples required number of samples, 0 means no limits. */ 39 | MeanImage(bool compute_variance, unsigned int num_samples); 40 | 41 | ~MeanImage(); 42 | 43 | /** Accumulate one more image. 44 | * 45 | * Optional parameter \arg mask has the following meaning depending on its content: 46 | * 47 | * - Empty : Ignored. 48 | * All pixels will be updated assuming unity weights. 49 | * - CV_8UC1 : Per-pixel mask. 50 | * Positive values indicate pixels that should be accumulated; zero values indicate pixels that should 51 | * be skipped. 52 | * 53 | * Unless empty, \arg mask should have the same dimensions as images and have a single channel. 54 | * 55 | * \return flag indicating whether the reuired number of samples has been collected for every pixel. If unlimited 56 | * accumulation was selected at construction time, then this will always return false. */ 57 | bool add(cv::InputArray image, cv::InputArray mask = cv::noArray()); 58 | 59 | /** Accumulate one more image (with weights). 60 | * 61 | * Supports either single weight for the whole image (if \arg weights is a scalar), or per-pixel weights. In the 62 | * latter case \arg weights should be of type CV_64FC1 and have the same dimensions as image. If a weight is zero or 63 | * negative, then the corresponding pixel is not updated. 64 | * 65 | * \return flag indicating whether the reuired number of samples has been collected for every pixel. If unlimited 66 | * accumulation was selected at construction time, then this will always return false. */ 67 | bool addWeighted(cv::InputArray image, cv::InputArray weights, cv::InputArray mask = cv::noArray()); 68 | 69 | /** Get the current mean image. 70 | * 71 | * Accumulation happens in double-precision floating point numbers. The user has a choice either to get these numbers 72 | * as is, or to convert to the original type of the images. 73 | * 74 | * Note: returned matrix is only valid until the next add() or addWeighted() call, afterwards the memory it points to 75 | * will be reused. */ 76 | cv::Mat getMean(bool as_original_type = true); 77 | 78 | /** Get the current variance of the mean image. 79 | * 80 | * Will be filled with zeros if variance computation is not enabled. 81 | * 82 | * Note: returned matrix is only valid until the next add() or addWeighted() call, afterwards the memory it points to 83 | * will be reused. */ 84 | cv::Mat getVariance(); 85 | 86 | /** Get the inverse of the current variance of the mean image. 87 | * 88 | * Will be filled with zeros if variance computation is not enabled. 89 | * 90 | * Note: returned matrix is only valid until the next add() or addWeighted() call, afterwards the memory it points to 91 | * will be reused. */ 92 | cv::Mat getVarianceInverse(); 93 | 94 | /** Get the number of accumulated samples per pixel. 95 | * 96 | * Note: returned matrix is only valid until the next add() or addWeighted() call, afterwards the memory it points to 97 | * will be reused. 98 | * 99 | * \param[in] normalize if set, the number of collected samples will be divided by the target number of samples, 100 | * producing a number between 0 and 1. Has no effect if unlitimed accumulation was selected at construction time. */ 101 | cv::Mat getNumSamples(bool normalize = false); 102 | 103 | private: 104 | void reset(const cv::Size& size, int type); 105 | 106 | const bool compute_variance_; 107 | const unsigned int num_samples_; 108 | bool done_; 109 | int type_; 110 | cv::Size size_; 111 | cv::Mat M_; 112 | cv::Mat W_; 113 | cv::Mat S_; 114 | cv::Mat counter_; 115 | 116 | // Storage to avoid reallocations 117 | cv::Mat image_minus_M_; 118 | cv::Mat image_minus_M_new_; 119 | cv::Mat weights_32f_; 120 | cv::Mat mask_; 121 | cv::Mat get_mean_output_; 122 | cv::Mat get_num_samples_output_; 123 | cv::Mat get_variance_output_; 124 | cv::Mat get_inverse_variance_output_; 125 | cv::Mat zeros_mask_; 126 | cv::Mat nonzeros_mask_; 127 | }; 128 | } // namespace utils 129 | -------------------------------------------------------------------------------- /tests/test_vignetting_response.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016-2017 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include "test.h" 24 | 25 | #include 26 | #include 27 | 28 | using namespace radical; 29 | 30 | BOOST_AUTO_TEST_CASE(Constructor) { 31 | // Invalid initialization, should throw 32 | BOOST_CHECK_THROW(VignettingResponse vv(getTestFilename("vignetting_model_empty.vgn")), SerializationException); 33 | // Valid initialization 34 | BOOST_CHECK_NO_THROW(VignettingResponse vv(getTestFilename("nonparametric_vignetting_model_identity.vgn"))); 35 | } 36 | 37 | BOOST_AUTO_TEST_CASE(GetModel) { 38 | VignettingResponse vv(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 39 | BOOST_CHECK(vv.getModel() != nullptr); 40 | } 41 | 42 | BOOST_AUTO_TEST_CASE(GetResponse) { 43 | VignettingResponse vv(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 44 | { 45 | cv::Mat response_expected(10, 10, CV_32FC3); 46 | // Normal 47 | response_expected.setTo(1.0); 48 | BOOST_CHECK_EQUAL_MAT(vv.getResponse(), response_expected, cv::Vec3f); 49 | BOOST_CHECK_EQUAL_MAT(vv.getResponse({10, 10}), response_expected, cv::Vec3f); 50 | // Logarithm 51 | response_expected.setTo(0.0); 52 | BOOST_CHECK_EQUAL_MAT(vv.getLogResponse(), response_expected, cv::Vec3f); 53 | BOOST_CHECK_EQUAL_MAT(vv.getLogResponse({10, 10}), response_expected, cv::Vec3f); 54 | } 55 | { 56 | cv::Mat response_expected(4, 4, CV_32FC3); 57 | // Normal 58 | response_expected.setTo(1.0); 59 | BOOST_CHECK_EQUAL_MAT(vv.getResponse({4, 4}), response_expected, cv::Vec3f); 60 | // Logarithm 61 | response_expected.setTo(0.0); 62 | BOOST_CHECK_EQUAL_MAT(vv.getLogResponse({4, 4}), response_expected, cv::Vec3f); 63 | } 64 | { 65 | cv::Mat response_expected(20, 20, CV_32FC3); 66 | // Normal 67 | response_expected.setTo(1.0); 68 | BOOST_CHECK_EQUAL_MAT(vv.getResponse({20, 20}), response_expected, cv::Vec3f); 69 | // Logarithm 70 | response_expected.setTo(0.0); 71 | BOOST_CHECK_EQUAL_MAT(vv.getLogResponse({20, 20}), response_expected, cv::Vec3f); 72 | } 73 | { 74 | cv::Mat response_expected(53, 53, CV_32FC3); 75 | // Normal 76 | response_expected.setTo(1.0); 77 | BOOST_CHECK_EQUAL_MAT(vv.getResponse({53, 53}), response_expected, cv::Vec3f); 78 | // Logarithm 79 | response_expected.setTo(0.0); 80 | BOOST_CHECK_EQUAL_MAT(vv.getLogResponse({53, 53}), response_expected, cv::Vec3f); 81 | } 82 | } 83 | 84 | BOOST_AUTO_TEST_CASE(GetResponseInvalidScale) { 85 | VignettingResponse vv(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 86 | BOOST_CHECK_THROW(vv.getResponse({30, 10}), Exception); 87 | BOOST_CHECK_THROW(vv.getLogResponse({30, 10}), Exception); 88 | } 89 | 90 | BOOST_AUTO_TEST_CASE(RemoveInvalid) { 91 | VignettingResponse vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 92 | cv::Mat E, L; 93 | vm.remove(E, L); 94 | BOOST_CHECK(L.empty()); 95 | E.create(10, 10, CV_32FC2); 96 | BOOST_CHECK_THROW(vm.remove(E, L), MatTypeException); 97 | BOOST_CHECK_THROW(vm.removeLog(E, L), MatTypeException); 98 | E.create(20, 10, CV_32FC3); 99 | BOOST_CHECK_THROW(vm.remove(E, L), Exception); // invalid aspect ratio 100 | BOOST_CHECK_THROW(vm.removeLog(E, L), Exception); // invalid aspect ratio 101 | } 102 | 103 | BOOST_AUTO_TEST_CASE(RemoveIdentity) { 104 | cv::Mat L; 105 | cv::Mat E(10, 10, CV_32FC3); 106 | cv::randu(E, cv::Scalar(0, 0, 0), cv::Scalar(1, 1, 1)); 107 | VignettingResponse vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 108 | // Normal 109 | vm.remove(E, L); 110 | BOOST_CHECK_EQUAL_MAT(L, E, cv::Vec3f); 111 | // Logarithm 112 | vm.removeLog(E, L); 113 | BOOST_CHECK_EQUAL_MAT(L, E, cv::Vec3f); 114 | } 115 | 116 | BOOST_AUTO_TEST_CASE(AddInvalid) { 117 | VignettingResponse vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 118 | cv::Mat L, E; 119 | vm.add(L, E); 120 | BOOST_CHECK(E.empty()); 121 | L.create(10, 10, CV_32FC2); 122 | BOOST_CHECK_THROW(vm.add(L, E), MatTypeException); 123 | BOOST_CHECK_THROW(vm.addLog(L, E), MatTypeException); 124 | L.create(10, 20, CV_32FC2); 125 | BOOST_CHECK_THROW(vm.add(L, E), Exception); // invalid aspect ratio 126 | BOOST_CHECK_THROW(vm.addLog(L, E), Exception); // invalid aspect ratio 127 | } 128 | 129 | BOOST_AUTO_TEST_CASE(AddIdentity) { 130 | cv::Mat E; 131 | cv::Mat L(10, 10, CV_32FC3); 132 | cv::randu(L, cv::Scalar(0, 0, 0), cv::Scalar(1, 1, 1)); 133 | VignettingResponse vm(getTestFilename("nonparametric_vignetting_model_identity.vgn")); 134 | // Normal 135 | vm.add(L, E); 136 | BOOST_CHECK_EQUAL_MAT(E, L, cv::Vec3f); 137 | // Logarithm 138 | vm.addLog(L, E); 139 | BOOST_CHECK_EQUAL_MAT(E, L, cv::Vec3f); 140 | } 141 | -------------------------------------------------------------------------------- /src/grabbers/realsense_grabber.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2016 Sergey Alexandrov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | ******************************************************************************/ 22 | 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | 32 | namespace grabbers { 33 | 34 | struct RealSenseGrabber::Impl { 35 | rs::context ctx; 36 | rs::device* device; 37 | 38 | cv::Size color_image_resolution = {0, 0}; 39 | int next_frame_index = 0; 40 | 41 | uint64_t timestamp_zero_offset = 0; 42 | 43 | Impl() { 44 | if (ctx.get_device_count() == 0) 45 | BOOST_THROW_EXCEPTION(GrabberException("No RealSense devices connected")); 46 | 47 | device = ctx.get_device(0); 48 | // device->enable_stream(rs::stream::color, rs::preset::best_quality); 49 | device->enable_stream(rs::stream::color, 640, 480, rs::format::bgr8, 15); 50 | 51 | if (!device->is_stream_enabled(rs::stream::color) || 52 | device->get_stream_format(rs::stream::color) != rs::format::bgr8) 53 | BOOST_THROW_EXCEPTION(GrabberException("Failed to create color stream with requested format")); 54 | 55 | color_image_resolution.width = device->get_stream_width(rs::stream::color); 56 | color_image_resolution.height = device->get_stream_height(rs::stream::color); 57 | 58 | device->start(); 59 | } 60 | 61 | ~Impl() { 62 | if (device->is_streaming()) 63 | device->stop(); 64 | device->disable_stream(rs::stream::color); 65 | } 66 | 67 | double grabFrame(cv::Mat& color) { 68 | device->wait_for_frames(); 69 | 70 | auto color_data = device->get_frame_data(rs::stream::color); 71 | memcpy(color.data, color_data, color.total() * color.elemSize()); 72 | 73 | ++next_frame_index; 74 | return computeTimestamp(device->get_frame_timestamp(rs::stream::color)); 75 | } 76 | 77 | double computeTimestamp(int device_timestamp) { 78 | if (timestamp_zero_offset == 0) { 79 | auto now = std::chrono::high_resolution_clock::now(); 80 | auto ms = std::chrono::duration_cast(now.time_since_epoch()).count(); 81 | timestamp_zero_offset = ms - device_timestamp; 82 | } 83 | return (timestamp_zero_offset + device_timestamp) * 0.001; 84 | } 85 | }; 86 | 87 | RealSenseGrabber::RealSenseGrabber(const std::string& /*device_uri*/) 88 | : p(new Impl) {} 89 | 90 | RealSenseGrabber::~RealSenseGrabber() = default; 91 | 92 | inline bool RealSenseGrabber::hasMoreFrames() const { 93 | return true; 94 | } 95 | 96 | bool RealSenseGrabber::grabFrame(cv::OutputArray _color) { 97 | if (_color.kind() != cv::_InputArray::MAT) 98 | BOOST_THROW_EXCEPTION(GrabberException("Grabbing only into cv::Mat")); 99 | 100 | _color.create(p->color_image_resolution.height, p->color_image_resolution.width, CV_8UC3); 101 | cv::Mat color = _color.getMat(); 102 | 103 | return p->grabFrame(color); 104 | } 105 | 106 | void RealSenseGrabber::setAutoWhiteBalanceEnabled(bool state) { 107 | p->device->set_option(rs::option::color_enable_auto_white_balance, state ? 1.0 : 0.0); 108 | } 109 | 110 | void RealSenseGrabber::setAutoExposureEnabled(bool state) { 111 | p->device->set_option(rs::option::color_enable_auto_exposure, state ? 1.0 : 0.0); 112 | } 113 | 114 | void RealSenseGrabber::setExposure(int exposure) { 115 | p->device->set_option(rs::option::color_exposure, exposure); 116 | } 117 | 118 | int RealSenseGrabber::getExposure() const { 119 | return p->device->get_option(rs::option::color_exposure); 120 | } 121 | 122 | std::pair RealSenseGrabber::getExposureRange() const { 123 | double min, max, step; 124 | p->device->get_option_range(rs::option::color_exposure, min, max, step); 125 | return {min, max}; 126 | } 127 | 128 | void RealSenseGrabber::setGain(int gain) { 129 | p->device->set_option(rs::option::color_gain, gain); 130 | } 131 | 132 | int RealSenseGrabber::getGain() const { 133 | return p->device->get_option(rs::option::color_gain); 134 | } 135 | 136 | std::pair RealSenseGrabber::getGainRange() const { 137 | double min, max, step; 138 | p->device->get_option_range(rs::option::color_gain, min, max, step); 139 | return {min, max}; 140 | } 141 | 142 | std::string RealSenseGrabber::getCameraModelName() const { 143 | std::string expected_prefix = "Intel RealSense "; 144 | std::string name = p->device->get_name(); 145 | auto pos = name.find(expected_prefix); 146 | if (pos == std::string::npos) 147 | BOOST_THROW_EXCEPTION(GrabberException("Unable to extract camera model name from: " + name)); 148 | name = name.substr(pos + expected_prefix.size()); 149 | boost::algorithm::to_lower(name); 150 | return name; 151 | } 152 | 153 | std::string RealSenseGrabber::getCameraSerialNumber() const { 154 | return std::string(p->device->get_serial()); 155 | } 156 | 157 | } // namespace grabbers 158 | --------------------------------------------------------------------------------