├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── example_data ├── e1.png ├── e2.png ├── e2_result.png ├── e3.png ├── e4.png ├── e5.png ├── e6.png ├── e6_result.png ├── e7.png └── e7_result.png ├── include └── libcbdetect │ ├── board_energy.h │ ├── boards_from_corners.h │ ├── config.h │ ├── create_correlation_patch.h │ ├── filter_board.h │ ├── filter_corners.h │ ├── find_corners.h │ ├── find_modes_meanshift.h │ ├── get_image_patch.h │ ├── get_init_location.h │ ├── grow_board.h │ ├── image_normalization_and_gradients.h │ ├── init_board.h │ ├── non_maximum_suppression.h │ ├── plot_boards.h │ ├── plot_corners.h │ ├── polynomial_fit.h │ ├── refine_corners.h │ ├── score_corners.h │ └── weight_mask.h └── src ├── CMakeLists.txt ├── example.cc └── libcbdetect ├── CMakeLists.txt ├── board_energy.cc ├── boards_from_corners.cc ├── create_correlation_patch.cc ├── filter_board.cc ├── filter_corners.cc ├── find_corners.cc ├── find_modes_meanshift.cc ├── get_image_patch.cc ├── get_init_location.cc ├── grow_board.cc ├── image_normalization_and_gradients.cc ├── init_board.cc ├── non_maximum_suppression.cc ├── plot_boards.cc ├── plot_corners.cc ├── polynomial_fit.cc ├── refine_corners.cc ├── score_corners.cc └── weight_mask.cc /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### CMake template 3 | CMakeCache.txt 4 | CMakeFiles 5 | CMakeScripts 6 | Testing 7 | Makefile 8 | cmake_install.cmake 9 | install_manifest.txt 10 | compile_commands.json 11 | CTestTestfile.cmake 12 | ### JetBrains template 13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 14 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 15 | 16 | # User-specific stuff 17 | .idea/**/workspace.xml 18 | .idea/**/tasks.xml 19 | .idea/**/usage.statistics.xml 20 | .idea/**/dictionaries 21 | .idea/**/shelf 22 | 23 | # Sensitive or high-churn files 24 | .idea/**/dataSources/ 25 | .idea/**/dataSources.ids 26 | .idea/**/dataSources.local.xml 27 | .idea/**/sqlDataSources.xml 28 | .idea/**/dynamic.xml 29 | .idea/**/uiDesigner.xml 30 | .idea/**/dbnavigator.xml 31 | 32 | # Gradle 33 | .idea/**/gradle.xml 34 | .idea/**/libraries 35 | 36 | # Gradle and Maven with auto-import 37 | # When using Gradle or Maven with auto-import, you should exclude module files, 38 | # since they will be recreated, and may cause churn. Uncomment if using 39 | # auto-import. 40 | # .idea/modules.xml 41 | # .idea/*.iml 42 | # .idea/modules 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | fabric.properties 70 | 71 | # Editor-based Rest Client 72 | .idea/httpRequests 73 | ### C++ template 74 | # Prerequisites 75 | *.d 76 | 77 | # Compiled Object files 78 | *.slo 79 | *.lo 80 | *.o 81 | *.obj 82 | 83 | # Precompiled Headers 84 | *.gch 85 | *.pch 86 | 87 | # Compiled Dynamic libraries 88 | *.so 89 | *.dylib 90 | *.dll 91 | 92 | # Fortran module files 93 | *.mod 94 | *.smod 95 | 96 | # Compiled Static libraries 97 | *.lai 98 | *.la 99 | *.a 100 | *.lib 101 | 102 | # Executables 103 | *.exe 104 | *.out 105 | *.app 106 | 107 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | 3 | matrix: 4 | include: 5 | - name: (Xenial) gcc-5 compilation 6 | dist: xenial 7 | before_install: 8 | - sudo apt-get update 9 | - sudo apt-get install -y libopencv-core-dev libopencv-highgui-dev libopencv-imgcodecs-dev libopencv-imgproc-dev 10 | script: 11 | - mkdir build 12 | - cd build 13 | - cmake -DCMAKE_C_COMPILER="gcc" -DCMAKE_CXX_COMPILER="g++" .. 14 | - make 15 | - make clean 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(Libcbdetect) 3 | 4 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin/${CMAKE_BUILD_TYPE}) 5 | set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib/${CMAKE_BUILD_TYPE}) 6 | set(CMAKE_LIBRARY_PATH ${PROJECT_SOURCE_DIR}/lib/${CMAKE_BUILD_TYPE}) 7 | 8 | if (UNIX) 9 | set(CMAKE_CXX_COMPILER g++) 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fopenmp") 11 | add_definitions(-DUNIX) 12 | elseif (WIN32) 13 | add_definitions(-DWINDOWS) 14 | if (MSVC) 15 | set(CMAKE_CXX_STANDARD 14) 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -openmp") 17 | add_definitions("/EHsc") 18 | endif () 19 | endif () 20 | 21 | option(SHARED_LIB "Request build of shared libraries." ON) 22 | if (SHARED_LIB) 23 | if (WIN32) 24 | add_definitions("-DIS_A_DLL") 25 | endif () 26 | set(LIB_TYPE SHARED) 27 | else () 28 | set(LIB_TYPE STATIC) 29 | endif () 30 | 31 | find_package(OpenCV REQUIRED) 32 | 33 | set(THIRD_PARTY_INCLUDE_DIRS ${OpenCV_INCLUDE_DIRS}) 34 | set(THIRD_PARTY_LIBS ${OpenCV_LIBS}) 35 | include_directories(${THIRD_PARTY_INCLUDE_DIRS} 36 | ${PROJECT_SOURCE_DIR}/include) 37 | 38 | add_subdirectory(${PROJECT_SOURCE_DIR}/src) 39 | add_subdirectory(${PROJECT_SOURCE_DIR}/src/libcbdetect) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Libcbdetect 2 | --- 3 | - Unofficial implemention of [libcbdetect](http://www.cvlibs.net/software/libcbdetect/) in C++. 4 | - Deltille detector. 5 | 6 | Libdetect is a fully automatic sub-pixel checkerboard / chessboard / deltille pattern detection. The algorithm autmatically extracts corners to sub-pixel accuracy and combines them to (rectangular) checkerboards / chessboard-like / deltille patterns. 7 | 8 | My calibration tool: [Libcalib](https://github.com/ftdlyc/libcalib) 9 | 10 | #### Require 11 | - C++ 14 12 | - Opencv >= 3.0 13 | 14 | #### Example 15 | > using namespace cbdetect; 16 | > cv::Mat img = cv::imread("image.bmp", cv::IMREAD_COLOR); 17 | > Params params; 18 | > find_corners(img, corners, params); 19 | > plot_corners(img, corners); 20 | > boards_from_corners(corners, boards); 21 | > plot_boards(img, corners, boards); 22 | 23 | ![image](https://github.com/ftdlyc/libcbdetect/blob/master/example_data/e2_result.png) 24 | ![image](https://github.com/ftdlyc/libcbdetect/blob/master/example_data/e6_result.png) 25 | ![image](https://github.com/ftdlyc/libcbdetect/blob/master/example_data/e7_result.png) 26 | 27 | #### Reference Papers 28 | [1] Geiger, A., Moosmann, F., Car, Ö., & Schuster, B. (2012, May). Automatic camera and range sensor calibration using a single shot. In Robotics and Automation (ICRA), 2012 IEEE International Conference on (pp. 3936-3943). IEEE. 29 | [2] Schönbein, M., Strauß, T., & Geiger, A. (2014, May). Calibrating and centering quasi-central catadioptric cameras. In Robotics and Automation (ICRA), 2014 IEEE International Conference on (pp. 4443-4450). IEEE. 30 | [3] Placht, S., Fürsattel, P., Mengue, E. A., Hofmann, H., Schaller, C., Balda, M., & Angelopoulou, E. (2014, September). Rochade: Robust checkerboard advanced detection for camera calibration. In European Conference on Computer Vision (pp. 766-779). Springer, Cham. 31 | [4] Ha, H., Perdoch, M., Alismail, H., Kweon, I. S., & Sheikh, Y. (2017, October). Deltille Grids for Geometric Camera Calibration. In 2017 IEEE International Conference on Computer Vision (ICCV) (pp. 5354-5362). IEEE. 32 | [5] Duda, A., & Frese, U. (2018, September). Accurate Detection and Localization of Checkerboard Corners for Calibration. In British Machine Vision Conference (BMCV), 2018. 33 | -------------------------------------------------------------------------------- /example_data/e1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e1.png -------------------------------------------------------------------------------- /example_data/e2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e2.png -------------------------------------------------------------------------------- /example_data/e2_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e2_result.png -------------------------------------------------------------------------------- /example_data/e3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e3.png -------------------------------------------------------------------------------- /example_data/e4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e4.png -------------------------------------------------------------------------------- /example_data/e5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e5.png -------------------------------------------------------------------------------- /example_data/e6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e6.png -------------------------------------------------------------------------------- /example_data/e6_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e6_result.png -------------------------------------------------------------------------------- /example_data/e7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e7.png -------------------------------------------------------------------------------- /example_data/e7_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ftdlyc/libcbdetect/ba290625f8c7828b74ce6fdcd6642fdc94c25e92/example_data/e7_result.png -------------------------------------------------------------------------------- /include/libcbdetect/board_energy.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_BOARD_ENERGY_H 39 | #define LIBCBDETECT_BOARD_ENERGY_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | 45 | namespace cbdetect { 46 | 47 | LIBCBDETECT_DLL_DECL cv::Point3i board_energy(const Corner& corners, Board& board, const Params& params); 48 | 49 | } 50 | 51 | #endif //LIBCBDETECT_BOARD_ENERGY_H 52 | -------------------------------------------------------------------------------- /include/libcbdetect/boards_from_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_BOARD_FROM_CORNRES_H 39 | #define LIBCBDETECT_BOARD_FROM_CORNRES_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | 47 | namespace cbdetect { 48 | 49 | LIBCBDETECT_DLL_DECL void boards_from_corners(const cv::Mat& img, const Corner& corners, 50 | std::vector& boards, const Params& params); 51 | 52 | } 53 | 54 | #endif //LIBCBDETECT_BOARD_FROM_CORNRES_H 55 | -------------------------------------------------------------------------------- /include/libcbdetect/config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_CONFIG_H 39 | #define LIBCBDETECT_CONFIG_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #if CV_VERSION_MAJOR == 3 && CV_VERSION_MINOR <= 2 46 | #include 47 | namespace cv { 48 | class ParallelLoopBodyLambdaWrapper : public ParallelLoopBody { 49 | private: 50 | std::function m_functor; 51 | 52 | public: 53 | ParallelLoopBodyLambdaWrapper(std::function functor) 54 | : m_functor(functor) {} 55 | 56 | virtual void operator()(const cv::Range& range) const { 57 | m_functor(range); 58 | } 59 | }; 60 | 61 | inline void parallel_for_(const Range& range, std::function functor, double nstripes = -1.) { 62 | parallel_for_(range, ParallelLoopBodyLambdaWrapper(functor), nstripes); 63 | } 64 | } // namespace cv 65 | #endif 66 | 67 | #ifdef _MSC_VER 68 | #define M_PI 3.14159265358979323846 /* pi */ 69 | #define M_PI_2 1.57079632679489661923 /* pi/2 */ 70 | #define M_PI_4 0.78539816339744830962 /* pi/4 */ 71 | #define M_1_PI 0.31830988618379067154 /* 1/pi */ 72 | #define M_2_PI 0.63661977236758134308 /* 2/pi */ 73 | #endif 74 | 75 | #ifndef LIBCBDETECT_DLL_DECL 76 | #if IS_A_DLL && defined(_MSC_VER) 77 | #define LIBCBDETECT_DLL_DECL __declspec(dllexport) 78 | #else 79 | #define LIBCBDETECT_DLL_DECL 80 | #endif 81 | #endif 82 | 83 | namespace cbdetect { 84 | 85 | enum DetectMethod { 86 | // origin method fast mode 87 | TemplateMatchFast = 0, 88 | // origin method slow mode 89 | TemplateMatchSlow, 90 | 91 | // compute hessian of image, detect by a threshold 92 | // form https://github.com/facebookincubator/deltille 93 | HessianResponse, 94 | 95 | // paper: Accurate Detection and Localization of Checkerboard Corners for Calibration 96 | LocalizedRadonTransform 97 | }; 98 | 99 | enum CornerType { 100 | SaddlePoint = 0, 101 | MonkeySaddlePoint 102 | }; 103 | 104 | typedef struct Params { 105 | bool show_processing; 106 | bool show_debug_image; 107 | bool show_grow_processing; 108 | bool norm; 109 | bool polynomial_fit; 110 | int norm_half_kernel_size; 111 | int polynomial_fit_half_kernel_size; 112 | double init_loc_thr; 113 | double score_thr; 114 | bool strict_grow; 115 | bool overlay; 116 | bool occlusion; 117 | DetectMethod detect_method; 118 | CornerType corner_type; 119 | std::vector radius; 120 | 121 | Params() 122 | : show_processing(true) 123 | , show_debug_image(false) 124 | , show_grow_processing(false) 125 | , norm(false) 126 | , polynomial_fit(true) 127 | , norm_half_kernel_size(31) 128 | , polynomial_fit_half_kernel_size(4) 129 | , init_loc_thr(0.01) 130 | , score_thr(0.01) 131 | , strict_grow(true) 132 | , overlay(false) 133 | , occlusion(true) 134 | , detect_method(HessianResponse) 135 | , corner_type(SaddlePoint) 136 | , radius({5, 7}) {} 137 | } Params; 138 | 139 | typedef struct Corner { 140 | std::vector p; 141 | std::vector r; 142 | std::vector v1; 143 | std::vector v2; 144 | std::vector v3; 145 | std::vector score; 146 | } Corner; 147 | 148 | typedef struct Board { 149 | std::vector> idx; 150 | std::vector>> energy; 151 | int num; 152 | 153 | Board() 154 | : num(0) {} 155 | } Board; 156 | 157 | } // namespace cbdetect 158 | 159 | #endif //LIBCBDETECT_CONFIG_H 160 | -------------------------------------------------------------------------------- /include/libcbdetect/create_correlation_patch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_CREATE_CORRELATION_PATCH_H 39 | #define LIBCBDETECT_CREATE_CORRELATION_PATCH_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | 47 | namespace cbdetect { 48 | 49 | LIBCBDETECT_DLL_DECL void create_correlation_patch(std::vector& template_kernel, 50 | double angle_1, double angle_2, int radius); 51 | 52 | LIBCBDETECT_DLL_DECL void create_correlation_patch(std::vector& template_kernel, 53 | double angle_1, double angle_2, double angle_3, int radius); 54 | 55 | } // namespace cbdetect 56 | 57 | #endif //LIBCBDETECT_CREATE_CORRELATION_PATCH_H 58 | -------------------------------------------------------------------------------- /include/libcbdetect/filter_board.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #pragma once 19 | #ifndef LIBCBDETECT_FILTER_BOARD_H 20 | #define LIBCBDETECT_FILTER_BOARD_H 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "libcbdetect/config.h" 27 | 28 | namespace cbdetect { 29 | 30 | LIBCBDETECT_DLL_DECL void filter_board(const Corner& corners, std::vector& used, Board& board, 31 | std::vector& proposal, double& energy, const Params& params); 32 | 33 | } 34 | 35 | #endif //LIBCBDETECT_FILTER_BOARD_H 36 | -------------------------------------------------------------------------------- /include/libcbdetect/filter_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_FILTER_CORNERS_H 39 | #define LIBCBDETECT_FILTER_CORNERS_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | #include "libcbdetect/find_corners.h" 47 | 48 | namespace cbdetect { 49 | 50 | LIBCBDETECT_DLL_DECL void filter_corners(const cv::Mat& img, const cv::Mat& img_angle, const cv::Mat& img_weight, 51 | Corner& corner, const Params& params); 52 | 53 | } 54 | 55 | #endif //LIBCBDETECT_FILTER_CORNERS_H 56 | -------------------------------------------------------------------------------- /include/libcbdetect/find_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_FIND_CORNERS_H 39 | #define LIBCBDETECT_FIND_CORNERS_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | 47 | namespace cbdetect { 48 | 49 | LIBCBDETECT_DLL_DECL void find_corners(const cv::Mat& img, Corner& corners, 50 | const Params& params = Params()); 51 | 52 | } 53 | 54 | #endif //CALIBRATION_FIND_CORNERS_H 55 | -------------------------------------------------------------------------------- /include/libcbdetect/find_modes_meanshift.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_FIND_MODES_MEANSHIFT_H 39 | #define LIBCBDETECT_FIND_MODES_MEANSHIFT_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | 45 | namespace cbdetect { 46 | 47 | LIBCBDETECT_DLL_DECL std::vector> find_modes_meanshift(const std::vector& hist, 48 | double sigma); 49 | 50 | } 51 | 52 | #endif //LIBCBDETECT_FIND_MODES_MEANSHIFT_H 53 | -------------------------------------------------------------------------------- /include/libcbdetect/get_image_patch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #pragma once 19 | #ifndef LIBCBDETECT_GET_IMAGE_PATCH_H 20 | #define LIBCBDETECT_GET_IMAGE_PATCH_H 21 | 22 | #include 23 | 24 | namespace cbdetect { 25 | 26 | void get_image_patch(const cv::Mat& img, double u, double v, int r, cv::Mat& img_sub); 27 | 28 | void get_image_patch_with_mask(const cv::Mat& img, const cv::Mat& mask, double u, double v, int r, cv::Mat& img_sub); 29 | 30 | } // namespace cbdetect 31 | 32 | #endif //LIBCBDETECT_GET_IMAGE_PATCH_H 33 | -------------------------------------------------------------------------------- /include/libcbdetect/get_init_location.h: -------------------------------------------------------------------------------- 1 | // c++ version by ftdlyc 2 | 3 | /** 4 | * Copyright 2018, ftdlyc 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, see . 18 | */ 19 | 20 | /* 21 | % Copyright 2012. All rights reserved. 22 | % Author: Andreas Geiger 23 | % Institute of Measurement and Control Systems (MRT) 24 | % Karlsruhe Institute of Technology (KIT), Germany 25 | 26 | % This is free software; you can redistribute it and/or modify it under the 27 | % terms of the GNU General Public License as published by the Free Software 28 | % Foundation; either version 3 of the License, or any later version. 29 | 30 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 31 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 32 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 33 | 34 | % You should have received a copy of the GNU General Public License along with 35 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 36 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 37 | */ 38 | 39 | #pragma once 40 | #ifndef LIBCBDETECT_GET_INIT_LOCATION_H 41 | #define LIBCBDETECT_GET_INIT_LOCATION_H 42 | 43 | #include 44 | 45 | #include 46 | 47 | #include "libcbdetect/config.h" 48 | 49 | namespace cbdetect { 50 | 51 | void get_init_location(const cv::Mat& img, const cv::Mat& img_du, const cv::Mat& img_dv, 52 | Corner& corners, const Params& parmas); 53 | 54 | } 55 | 56 | #endif //LIBCBDETECT_GET_INIT_LOCATION_H 57 | -------------------------------------------------------------------------------- /include/libcbdetect/grow_board.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_GROW_BOARD_H 39 | #define LIBCBDETECT_GROW_BOARD_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | 45 | namespace cbdetect { 46 | 47 | enum GrowType { 48 | GrowType_Failure = 0, 49 | GrowType_Inside, 50 | GrowType_Boundary, 51 | }; 52 | 53 | LIBCBDETECT_DLL_DECL GrowType grow_board(const Corner& corners, std::vector& used, Board& board, 54 | std::vector& proposal, int direction, const Params& params); 55 | 56 | } // namespace cbdetect 57 | 58 | #endif //LIBCBDETECT_GROW_BOARD_H 59 | -------------------------------------------------------------------------------- /include/libcbdetect/image_normalization_and_gradients.h: -------------------------------------------------------------------------------- 1 | // c++ version by ftdlyc 2 | 3 | /* 4 | % Copyright 2012. All rights reserved. 5 | % Author: Andreas Geiger 6 | % Institute of Measurement and Control Systems (MRT) 7 | % Karlsruhe Institute of Technology (KIT), Germany 8 | 9 | % This is free software; you can redistribute it and/or modify it under the 10 | % terms of the GNU General Public License as published by the Free Software 11 | % Foundation; either version 3 of the License, or any later version. 12 | 13 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 14 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | 17 | % You should have received a copy of the GNU General Public License along with 18 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 19 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 20 | */ 21 | 22 | #pragma once 23 | #ifndef LIBCBDETECT_BOX_FILTER_H 24 | #define LIBCBDETECT_BOX_FILTER_H 25 | 26 | #include 27 | 28 | #include "libcbdetect/config.h" 29 | 30 | namespace cbdetect { 31 | 32 | LIBCBDETECT_DLL_DECL void box_filter(const cv::Mat& img, cv::Mat& blur_img, int kernel_size_x, int kernel_size_y = -1); 33 | 34 | LIBCBDETECT_DLL_DECL void image_normalization_and_gradients(cv::Mat& img, cv::Mat& img_du, cv::Mat& img_dv, 35 | cv::Mat& img_angle, cv::Mat& img_weight, 36 | const Params& params); 37 | 38 | } // namespace cbdetect 39 | 40 | #endif //LIBCBDETECT_BOX_FILTER_H 41 | -------------------------------------------------------------------------------- /include/libcbdetect/init_board.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_INIT_BOARD_H 39 | #define LIBCBDETECT_INIT_BOARD_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | 45 | namespace cbdetect { 46 | 47 | LIBCBDETECT_DLL_DECL bool init_board(const Corner& corners, std::vector& used, Board& board, int idx); 48 | 49 | } 50 | 51 | #endif //LIBCBDETECT_INIT_BOARD_H 52 | -------------------------------------------------------------------------------- /include/libcbdetect/non_maximum_suppression.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_NON_MAXIMUM_SUPPRESSION_H 39 | #define LIBCBDETECT_NON_MAXIMUM_SUPPRESSION_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | #include "libcbdetect/find_corners.h" 45 | 46 | namespace cbdetect { 47 | 48 | LIBCBDETECT_DLL_DECL void non_maximum_suppression(const cv::Mat& img, int n, double tau, int margin, Corner& corners); 49 | 50 | LIBCBDETECT_DLL_DECL void non_maximum_suppression_sparse(Corner& corners, int n, cv::Size img_size, 51 | const Params& params); 52 | 53 | } // namespace cbdetect 54 | 55 | #endif //LIBCBDETECT_NON_MAXIMUM_SUPPRESSION_H 56 | -------------------------------------------------------------------------------- /include/libcbdetect/plot_boards.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #pragma once 19 | #ifndef LIBCBDETECT_PLOT_DELTILLES_H 20 | #define LIBCBDETECT_PLOT_DELTILLES_H 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "libcbdetect/config.h" 27 | namespace cbdetect { 28 | 29 | LIBCBDETECT_DLL_DECL void plot_boards(const cv::Mat& img, const Corner& corners, 30 | const std::vector& boards, const Params& params); 31 | 32 | } 33 | 34 | #endif //LIBCBDETECT_PLOT_DELTILLES_H 35 | -------------------------------------------------------------------------------- /include/libcbdetect/plot_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_PLOT_CORNERS_H 39 | #define LIBCBDETECT_PLOT_CORNERS_H 40 | 41 | #include 42 | 43 | #include "libcbdetect/config.h" 44 | 45 | namespace cbdetect { 46 | 47 | LIBCBDETECT_DLL_DECL void plot_corners(const cv::Mat& img, const std::vector& corners, const char* str); 48 | 49 | LIBCBDETECT_DLL_DECL void plot_corners(const cv::Mat& img, const Corner& corners); 50 | 51 | } // namespace cbdetect 52 | 53 | #endif //LIBCBDETECT_PLOT_CORNERS_H 54 | -------------------------------------------------------------------------------- /include/libcbdetect/polynomial_fit.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #pragma once 19 | #ifndef LIBCBDETECT_POLYNOMIAL_FIT_H 20 | #define LIBCBDETECT_POLYNOMIAL_FIT_H 21 | 22 | #include 23 | 24 | #include "libcbdetect/config.h" 25 | 26 | namespace cbdetect { 27 | 28 | void polynomial_fit(const cv::Mat& img, Corner& corners, const Params& params); 29 | 30 | } 31 | 32 | #endif //LIBCBDETECT_POLYNOMIAL_FIT_H 33 | -------------------------------------------------------------------------------- /include/libcbdetect/refine_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_REFINE_CORNERS_H 39 | #define LIBCBDETECT_REFINE_CORNERS_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | 47 | namespace cbdetect { 48 | 49 | LIBCBDETECT_DLL_DECL void refine_corners(const cv::Mat& img_du, const cv::Mat& img_dv, const cv::Mat& img_angle, 50 | const cv::Mat& img_weight, Corner& corners, const Params& params); 51 | 52 | } 53 | 54 | #endif //LIBCBDETECT_REFINE_CORNERS_H 55 | -------------------------------------------------------------------------------- /include/libcbdetect/score_corners.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_SCORE_CORNERS_H 39 | #define LIBCBDETECT_SCORE_CORNERS_H 40 | 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/config.h" 46 | #include "libcbdetect/find_corners.h" 47 | 48 | namespace cbdetect { 49 | 50 | LIBCBDETECT_DLL_DECL void sorce_corners(const cv::Mat& img, const cv::Mat& img_weight, 51 | Corner& corners, const Params& params); 52 | 53 | LIBCBDETECT_DLL_DECL void remove_low_scoring_corners(double tau, Corner& corners, const Params& params); 54 | 55 | } // namespace cbdetect 56 | 57 | #endif //LIBCBDETECT_SCORE_CORNERS_H 58 | -------------------------------------------------------------------------------- /include/libcbdetect/weight_mask.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #pragma once 38 | #ifndef LIBCBDETECT_WEIGHT_MASK 39 | #define LIBCBDETECT_WEIGHT_MASK 40 | 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | #include "libcbdetect/config.h" 47 | 48 | namespace cbdetect { 49 | 50 | LIBCBDETECT_DLL_DECL std::unordered_map weight_mask(const std::vector& radius); 51 | 52 | } 53 | 54 | #endif //LIBCBDETECT_WEIGHT_MASK 55 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/include) 2 | add_executable(example example.cc) 3 | target_link_libraries(example cbdetect ${OpenCV_LIBS}) 4 | -------------------------------------------------------------------------------- /src/example.cc: -------------------------------------------------------------------------------- 1 | #include "libcbdetect/boards_from_corners.h" 2 | #include "libcbdetect/config.h" 3 | #include "libcbdetect/find_corners.h" 4 | #include "libcbdetect/plot_boards.h" 5 | #include "libcbdetect/plot_corners.h" 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std::chrono; 11 | 12 | void detect(const char* str, cbdetect::CornerType corner_type) { 13 | cbdetect::Corner corners; 14 | std::vector boards; 15 | cbdetect::Params params; 16 | params.corner_type = corner_type; 17 | 18 | cv::Mat img = cv::imread(str, cv::IMREAD_COLOR); 19 | 20 | auto t1 = high_resolution_clock::now(); 21 | cbdetect::find_corners(img, corners, params); 22 | auto t2 = high_resolution_clock::now(); 23 | cbdetect::plot_corners(img, corners); 24 | auto t3 = high_resolution_clock::now(); 25 | cbdetect::boards_from_corners(img, corners, boards, params); 26 | auto t4 = high_resolution_clock::now(); 27 | printf("Find corners took: %.3f ms\n", duration_cast(t2 - t1).count() / 1000.0); 28 | printf("Find boards took: %.3f ms\n", duration_cast(t4 - t3).count() / 1000.0); 29 | printf("Total took: %.3f ms\n", duration_cast(t2 - t1).count() / 1000.0 + duration_cast(t4 - t3).count() / 1000.0); 30 | cbdetect::plot_boards(img, corners, boards, params); 31 | } 32 | 33 | int main(int argc, char* argv[]) { 34 | printf("chessboards..."); 35 | detect("../../example_data/e2.png", cbdetect::SaddlePoint); 36 | printf("deltilles..."); 37 | detect("../../example_data/e6.png", cbdetect::MonkeySaddlePoint); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /src/libcbdetect/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/include/libcbdetect) 2 | aux_source_directory(. SOURCE_LIST) 3 | add_library(cbdetect ${LIB_TYPE} ${SOURCE_LIST}) 4 | target_link_libraries(cbdetect ${OpenCV_LIBS}) 5 | -------------------------------------------------------------------------------- /src/libcbdetect/board_energy.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | 41 | #include "libcbdetect/board_energy.h" 42 | 43 | namespace cbdetect { 44 | 45 | cv::Point3i board_energy(const Corner& corners, Board& board, const Params& params) { 46 | // energy: number of corners 47 | double E_corners = -1.0 * board.num; 48 | 49 | // energy: structure 50 | double max_E_structure = std::numeric_limits::min(); 51 | int res_x = 0, res_y = 0, res_z = 0; 52 | 53 | // walk through v1 54 | for(int i = 0; i < board.idx.size(); ++i) { 55 | for(int j = 0; j < board.idx[i].size() - 2; ++j) { 56 | int idx1 = board.idx[i][j]; 57 | int idx2 = board.idx[i][j + 1]; 58 | int idx3 = board.idx[i][j + 2]; 59 | if(idx1 >= 0 && idx2 >= 0 && idx3 >= 0) { 60 | const cv::Point2d& x1 = corners.p[idx1]; 61 | const cv::Point2d& x2 = corners.p[idx2]; 62 | const cv::Point2d& x3 = corners.p[idx3]; 63 | double E_structure = cv::norm(x1 + x3 - 2 * x2) / cv::norm(x1 - x3); 64 | board.energy[i][j][0] = E_corners * (1 - E_structure); 65 | if(E_structure > max_E_structure) { 66 | max_E_structure = E_structure; 67 | res_x = j; 68 | res_y = i; 69 | res_z = 0; 70 | } 71 | } 72 | } 73 | } 74 | 75 | if(params.corner_type == MonkeySaddlePoint) { 76 | // walk through v2 77 | for(int i = 0; i < board.idx.size() - 2; ++i) { 78 | for(int j = 0; j < board.idx[i].size() - 2; ++j) { 79 | int idx1 = board.idx[i][j]; 80 | int idx2 = board.idx[i + 1][j + 1]; 81 | int idx3 = board.idx[i + 2][j + 2]; 82 | if(idx1 >= 0 && idx2 >= 0 && idx3 >= 0) { 83 | const cv::Point2d& x1 = corners.p[idx1]; 84 | const cv::Point2d& x2 = corners.p[idx2]; 85 | const cv::Point2d& x3 = corners.p[idx3]; 86 | double E_structure = cv::norm(x1 + x3 - 2 * x2) / cv::norm(x1 - x3); 87 | board.energy[i][j][1] = E_corners * (1 - E_structure); 88 | if(E_structure > max_E_structure) { 89 | max_E_structure = E_structure; 90 | res_x = j; 91 | res_y = i; 92 | res_z = 1; 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | // walk through v3 100 | for(int i = 0; i < board.idx.size() - 2; ++i) { 101 | for(int j = 0; j < board.idx[i].size(); ++j) { 102 | int idx1 = board.idx[i][j]; 103 | int idx2 = board.idx[i + 1][j]; 104 | int idx3 = board.idx[i + 2][j]; 105 | if(idx1 >= 0 && idx2 >= 0 && idx3 >= 0) { 106 | const cv::Point2d& x1 = corners.p[idx1]; 107 | const cv::Point2d& x2 = corners.p[idx2]; 108 | const cv::Point2d& x3 = corners.p[idx3]; 109 | double E_structure = cv::norm(x1 + x3 - 2 * x2) / cv::norm(x1 - x3); 110 | board.energy[i][j][2] = E_corners * (1 - E_structure); 111 | if(E_structure > max_E_structure) { 112 | max_E_structure = E_structure; 113 | res_x = j; 114 | res_y = i; 115 | res_z = 2; 116 | } 117 | } 118 | } 119 | } 120 | 121 | // final energy 122 | return {res_x, res_y, res_z}; 123 | } 124 | 125 | } // namespace cbdetect 126 | -------------------------------------------------------------------------------- /src/libcbdetect/boards_from_corners.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | 45 | #include "libcbdetect/board_energy.h" 46 | #include "libcbdetect/boards_from_corners.h" 47 | #include "libcbdetect/config.h" 48 | #include "libcbdetect/filter_board.h" 49 | #include "libcbdetect/grow_board.h" 50 | #include "libcbdetect/init_board.h" 51 | 52 | namespace cbdetect { 53 | 54 | void debug_grow_process(const cv::Mat& img, const Corner& corners, const Board& board, 55 | const std::vector& proposal, int direction, bool type) { 56 | cv::Mat img_show; 57 | if(img.channels() != 3) { 58 | #if CV_VERSION_MAJOR >= 4 59 | cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR); 60 | #else 61 | cv::cvtColor(img, img_show, CV_GRAY2BGR); 62 | #endif 63 | } else { 64 | img_show = img.clone(); 65 | } 66 | 67 | cv::Point2d mean(0.0, 0.0); 68 | for(int i = 0; i < board.idx.size(); ++i) { 69 | for(int j = 0; j < board.idx[i].size(); ++j) { 70 | if(board.idx[i][j] < 0) { 71 | continue; 72 | } 73 | cv::circle(img_show, corners.p[board.idx[i][j]], 4, cv::Scalar(255, 0, 0), -1); 74 | cv::putText(img_show, std::to_string(board.idx[i][j]), 75 | cv::Point2i(corners.p[board.idx[i][j]].x - 12, corners.p[board.idx[i][j]].y - 6), 76 | cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1); 77 | mean += corners.p[board.idx[i][j]]; 78 | } 79 | } 80 | mean /= (double)(board.num); 81 | mean.x -= 10; 82 | mean.y += 10; 83 | cv::putText(img_show, std::to_string(direction), mean, 84 | cv::FONT_HERSHEY_SIMPLEX, 1.3, cv::Scalar(196, 196, 0), 2); 85 | 86 | for(const auto& i : proposal) { 87 | if(board.idx[i.y][i.x] < 0) { 88 | continue; 89 | } 90 | if(type) { 91 | cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 255, 0), -1); 92 | } else { 93 | cv::circle(img_show, corners.p[board.idx[i.y][i.x]], 4, cv::Scalar(0, 0, 255), -1); 94 | } 95 | } 96 | 97 | cv::imshow("grow_process", img_show); 98 | cv::waitKey(); 99 | } 100 | 101 | void boards_from_corners(const cv::Mat& img, const Corner& corners, std::vector& boards, const Params& params) { 102 | // intialize boards 103 | boards.clear(); 104 | Board board; 105 | std::vector used(corners.p.size(), 0); 106 | 107 | int start = 0; 108 | if(!params.overlay) { 109 | // start from random index 110 | std::default_random_engine e; 111 | auto time = std::chrono::system_clock::now().time_since_epoch(); 112 | e.seed(static_cast(time.count())); 113 | start = e() % corners.p.size(); 114 | } 115 | 116 | // for all seed corners do 117 | int n = 0; 118 | while(n++ < corners.p.size()) { 119 | // init 3x3 board from seed i 120 | int i = (n + start) % corners.p.size(); 121 | if(used[i] == 1 || !init_board(corners, used, board, i)) { 122 | continue; 123 | } 124 | 125 | // check if this is a useful initial guess 126 | cv::Point3i maxE_pos = board_energy(corners, board, params); 127 | double energy = board.energy[maxE_pos.y][maxE_pos.x][maxE_pos.z]; 128 | if(energy > -6.0) { 129 | for(int jj = 0; jj < 3; ++jj) { 130 | for(int ii = 0; ii < 3; ++ii) { 131 | used[board.idx[jj][ii]] = 0; 132 | } 133 | } 134 | continue; 135 | } 136 | 137 | // grow boards 138 | while(1) { 139 | int num_corners = board.num; 140 | 141 | for(int j = 0; j < (params.corner_type == MonkeySaddlePoint ? 6 : 4); ++j) { 142 | std::vector proposal; 143 | GrowType grow_type = grow_board(corners, used, board, proposal, j, params); 144 | if(grow_type == GrowType_Failure) { 145 | continue; 146 | } 147 | 148 | if(params.show_grow_processing) { 149 | for(int ii = 0; ii < board.idx.size(); ++ii) { 150 | for(int jj = 0; jj < board.idx[ii].size(); ++jj) { 151 | std::cout << board.idx[ii][jj] << " "; 152 | } 153 | std::cout << "\n"; 154 | } 155 | std::cout << "\n"; 156 | debug_grow_process(img, corners, board, proposal, j, false); 157 | } 158 | 159 | filter_board(corners, used, board, proposal, energy, params); 160 | 161 | if(params.show_grow_processing) { 162 | for(int ii = 0; ii < board.idx.size(); ++ii) { 163 | for(int jj = 0; jj < board.idx[ii].size(); ++jj) { 164 | std::cout << board.idx[ii][jj] << " "; 165 | } 166 | std::cout << "\n"; 167 | } 168 | std::cout << "\n"; 169 | debug_grow_process(img, corners, board, proposal, j, true); 170 | } 171 | 172 | if(grow_type == GrowType_Inside) { 173 | --j; 174 | } 175 | } 176 | 177 | // exit loop 178 | if(board.num == num_corners) { 179 | break; 180 | } 181 | } 182 | 183 | if(!params.overlay) { 184 | boards.emplace_back(board); 185 | continue; 186 | } 187 | 188 | std::vector> overlap; 189 | for(int j = 0; j < boards.size(); ++j) { 190 | // check if new chessboard proposal overlaps with existing chessboards 191 | for(int k1 = 0; k1 < board.idx.size(); ++k1) { 192 | for(int k2 = 0; k2 < board.idx[0].size(); ++k2) { 193 | for(int l1 = 0; l1 < boards[j].idx.size(); ++l1) { 194 | for(int l2 = 0; l2 < boards[j].idx[0].size(); ++l2) { 195 | if(board.idx[k1][k2] != -1 && board.idx[k1][k2] != -2 && board.idx[k1][k2] == boards[j].idx[l1][l2]) { 196 | cv::Point3i maxE_pos_tmp = board_energy(corners, boards[j], params); 197 | overlap.emplace_back(std::make_pair(j, boards[j].energy[maxE_pos_tmp.y][maxE_pos_tmp.x][maxE_pos_tmp.z])); 198 | goto GOTO_BREAK; 199 | } 200 | } 201 | } 202 | } 203 | } 204 | } 205 | GOTO_BREAK:; 206 | 207 | if(overlap.empty()) { 208 | boards.emplace_back(board); 209 | } else { 210 | bool is_better = true; 211 | for(int j = 0; j < overlap.size(); ++j) { 212 | if(overlap[j].second <= energy) { 213 | is_better = false; 214 | break; 215 | } 216 | } 217 | if(is_better) { 218 | std::vector tmp; 219 | for(int j = 0, k = 0; j < boards.size(); ++j) { 220 | if(overlap[k].first == j) { 221 | continue; 222 | ++k; 223 | } 224 | tmp.emplace_back(boards[j]); 225 | } 226 | std::swap(tmp, boards); 227 | boards.emplace_back(board); 228 | } 229 | } 230 | std::fill(used.begin(), used.end(), 0); 231 | n += 2; 232 | } 233 | } 234 | 235 | } // namespace cbdetect 236 | -------------------------------------------------------------------------------- /src/libcbdetect/create_correlation_patch.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include "libcbdetect/create_correlation_patch.h" 40 | #include "libcbdetect/config.h" 41 | 42 | namespace cbdetect { 43 | 44 | void create_correlation_patch(std::vector& template_kernel, double angle_1, double angle_2, int radius) { 45 | // width and height 46 | int width = radius * 2 + 1; 47 | int height = radius * 2 + 1; 48 | 49 | // initialize template 50 | template_kernel[0] = cv::Mat::zeros(height, width, CV_64F); 51 | template_kernel[1] = cv::Mat::zeros(height, width, CV_64F); 52 | template_kernel[2] = cv::Mat::zeros(height, width, CV_64F); 53 | template_kernel[3] = cv::Mat::zeros(height, width, CV_64F); 54 | 55 | // midpoint 56 | int mu = radius + 1; 57 | int mv = radius + 1; 58 | 59 | // compute normals from angles 60 | double n1[2]{-std::sin(angle_1), std::cos(angle_1)}; 61 | double n2[2]{-std::sin(angle_2), std::cos(angle_2)}; 62 | 63 | // for all points in template do 64 | for(int u = 0; u < width; ++u) { 65 | for(int v = 0; v < height; ++v) { 66 | // vector 67 | int vec[2]{u + 1 - mu, v + 1 - mv}; 68 | double dist = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1]); 69 | 70 | // check on which side of the normals we are 71 | double s1 = vec[0] * n1[0] + vec[1] * n1[1]; 72 | double s2 = vec[0] * n2[0] + vec[1] * n2[1]; 73 | 74 | if(dist <= radius) { 75 | if(s1 <= -0.1 && s2 <= -0.1) { 76 | template_kernel[0].at(v, u) = 1; 77 | } else if(s1 >= 0.1 && s2 >= 0.1) { 78 | template_kernel[1].at(v, u) = 1; 79 | } else if(s1 <= -0.1 && s2 >= 0.1) { 80 | template_kernel[2].at(v, u) = 1; 81 | } else if(s1 >= 0.1 && s2 <= -0.1) { 82 | template_kernel[3].at(v, u) = 1; 83 | } 84 | } 85 | } 86 | } 87 | 88 | // normalize 89 | double sum = cv::sum(template_kernel[0])[0]; 90 | if(sum > 1e-5) { 91 | template_kernel[0] /= sum; 92 | } 93 | sum = cv::sum(template_kernel[1])[0]; 94 | if(sum > 1e-5) { 95 | template_kernel[1] /= sum; 96 | } 97 | sum = cv::sum(template_kernel[2])[0]; 98 | if(sum > 1e-5) { 99 | template_kernel[2] /= sum; 100 | } 101 | sum = cv::sum(template_kernel[3])[0]; 102 | if(sum > 1e-5) { 103 | template_kernel[3] /= sum; 104 | } 105 | } 106 | 107 | void create_correlation_patch(std::vector& template_kernel, 108 | double angle_1, double angle_2, double angle_3, int radius) { 109 | // width and height 110 | int width = radius * 2 + 1; 111 | int height = radius * 2 + 1; 112 | 113 | // initialize template 114 | template_kernel[0] = cv::Mat::zeros(height, width, CV_64F); 115 | template_kernel[1] = cv::Mat::zeros(height, width, CV_64F); 116 | template_kernel[2] = cv::Mat::zeros(height, width, CV_64F); 117 | template_kernel[3] = cv::Mat::zeros(height, width, CV_64F); 118 | template_kernel[4] = cv::Mat::zeros(height, width, CV_64F); 119 | template_kernel[5] = cv::Mat::zeros(height, width, CV_64F); 120 | 121 | // midpoint 122 | int mu = radius + 1; 123 | int mv = radius + 1; 124 | 125 | // compute normals from angles 126 | double n1[2]{-std::sin(angle_1), std::cos(angle_1)}; 127 | double n2[2]{-std::sin(angle_2), std::cos(angle_2)}; 128 | double n3[3]{-std::sin(angle_3), std::cos(angle_3)}; 129 | 130 | // for all points in template do 131 | for(int u = 0; u < width; ++u) { 132 | for(int v = 0; v < height; ++v) { 133 | // vector 134 | int vec[2]{u + 1 - mu, v + 1 - mv}; 135 | double dist = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1]); 136 | 137 | // check on which side of the normals we are 138 | double s1 = vec[0] * n1[0] + vec[1] * n1[1]; 139 | double s2 = vec[0] * n2[0] + vec[1] * n2[1]; 140 | double s3 = vec[0] * n3[0] + vec[1] * n3[1]; 141 | 142 | if(dist <= radius) { 143 | if(s1 >= -0.1 && s2 <= -0.1) { 144 | template_kernel[0].at(v, u) = 1; 145 | } else if(s1 >= 0.1 && s3 >= 0.1) { 146 | template_kernel[1].at(v, u) = 1; 147 | } else if(s2 <= -0.1 && s3 >= 0.1) { 148 | template_kernel[2].at(v, u) = 1; 149 | } else if(s1 <= 0.1 && s2 >= -0.1) { 150 | template_kernel[3].at(v, u) = 1; 151 | } else if(s1 <= 0.1 && s3 <= -0.1) { 152 | template_kernel[4].at(v, u) = 1; 153 | } else if(s2 >= 0.1 && s3 <= -0.1) { 154 | template_kernel[5].at(v, u) = 1; 155 | } 156 | } 157 | } 158 | } 159 | 160 | // normalize 161 | double sum = cv::sum(template_kernel[0])[0]; 162 | if(sum > 1e-5) { 163 | template_kernel[0] /= sum; 164 | } 165 | sum = cv::sum(template_kernel[1])[0]; 166 | if(sum > 1e-5) { 167 | template_kernel[1] /= sum; 168 | } 169 | sum = cv::sum(template_kernel[2])[0]; 170 | if(sum > 1e-5) { 171 | template_kernel[2] /= sum; 172 | } 173 | sum = cv::sum(template_kernel[3])[0]; 174 | if(sum > 1e-5) { 175 | template_kernel[3] /= sum; 176 | } 177 | sum = cv::sum(template_kernel[4])[0]; 178 | if(sum > 1e-5) { 179 | template_kernel[4] /= sum; 180 | } 181 | sum = cv::sum(template_kernel[5])[0]; 182 | if(sum > 1e-5) { 183 | template_kernel[5] /= sum; 184 | } 185 | } 186 | 187 | } // namespace cbdetect 188 | -------------------------------------------------------------------------------- /src/libcbdetect/filter_board.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "libcbdetect/board_energy.h" 23 | #include "libcbdetect/config.h" 24 | #include "libcbdetect/filter_board.h" 25 | 26 | namespace cbdetect { 27 | 28 | double find_minE(const Board& board, const cv::Point2i& p) { 29 | double minE = std::min(std::min(board.energy[p.y][p.x][0], board.energy[p.y][p.x][1]), 30 | board.energy[p.y][p.x][2]); 31 | if(p.x - 1 >= 0) { 32 | minE = std::min(minE, board.energy[p.y][p.x - 1][0]); 33 | } 34 | if(p.x - 1 >= 0 && p.y - 1 >= 0) { 35 | minE = std::min(minE, board.energy[p.y - 1][p.x - 1][1]); 36 | } 37 | if(p.y - 1 >= 0) { 38 | minE = std::min(minE, board.energy[p.y - 1][p.x][2]); 39 | } 40 | if(p.x - 2 >= 0) { 41 | minE = std::min(minE, board.energy[p.y][p.x - 2][0]); 42 | } 43 | if(p.x - 2 >= 0 && p.y - 2 >= 0) { 44 | minE = std::min(minE, board.energy[p.y - 2][p.x - 2][1]); 45 | } 46 | if(p.y - 2 >= 0) { 47 | minE = std::min(minE, board.energy[p.y - 2][p.x][2]); 48 | } 49 | return minE; 50 | } 51 | 52 | void filter_board(const Corner& corners, std::vector& used, Board& board, 53 | std::vector& proposal, double& energy, const Params& params) { 54 | // erase wrong corners 55 | while(!proposal.empty()) { 56 | cv::Point3i maxE_pos = board_energy(corners, board, params); 57 | double p_energy = board.energy[maxE_pos.y][maxE_pos.x][maxE_pos.z]; 58 | if(p_energy <= energy) { 59 | energy = p_energy; 60 | break; 61 | } 62 | if(params.corner_type == SaddlePoint && !params.occlusion) { 63 | for(const auto& p : proposal) { 64 | used[board.idx[p.y][p.x]] = 0; 65 | board.idx[p.y][p.x] = -2; 66 | --board.num; 67 | } 68 | return; 69 | } 70 | 71 | // find the wrongest corner 72 | cv::Point2i p[3]; 73 | p[0] = {maxE_pos.x, maxE_pos.y}; 74 | switch(maxE_pos.z) { 75 | case 0: { 76 | p[1] = {maxE_pos.x + 1, maxE_pos.y}; 77 | p[2] = {maxE_pos.x + 2, maxE_pos.y}; 78 | break; 79 | } 80 | case 1: { 81 | p[1] = {maxE_pos.x + 1, maxE_pos.y + 1}; 82 | p[2] = {maxE_pos.x + 2, maxE_pos.y + 2}; 83 | break; 84 | } 85 | case 2: { 86 | p[1] = {maxE_pos.x, maxE_pos.y + 1}; 87 | p[2] = {maxE_pos.x, maxE_pos.y + 2}; 88 | break; 89 | } 90 | default: 91 | break; 92 | } 93 | double minE_wrong[3]; 94 | minE_wrong[0] = find_minE(board, p[0]); 95 | minE_wrong[1] = find_minE(board, p[1]); 96 | minE_wrong[2] = find_minE(board, p[2]); 97 | 98 | double minE = -DBL_MAX; 99 | int iter = 0; 100 | for(auto it = proposal.begin(); it < proposal.end(); ++it) { 101 | if(it->x == p[0].x && it->y == p[0].y && minE_wrong[0] > minE) { 102 | minE = minE_wrong[0]; 103 | maxE_pos.x = it->x; 104 | maxE_pos.y = it->y; 105 | iter = it - proposal.begin(); 106 | } 107 | if(it->x == p[1].x && it->y == p[1].y && minE_wrong[1] > minE) { 108 | minE = minE_wrong[1]; 109 | maxE_pos.x = it->x; 110 | maxE_pos.y = it->y; 111 | iter = it - proposal.begin(); 112 | } 113 | if(it->x == p[2].x && it->y == p[2].y && minE_wrong[2] > minE) { 114 | minE = minE_wrong[2]; 115 | maxE_pos.x = it->x; 116 | maxE_pos.y = it->y; 117 | iter = it - proposal.begin(); 118 | } 119 | } 120 | 121 | proposal.erase(proposal.begin() + iter); 122 | used[board.idx[maxE_pos.y][maxE_pos.x]] = 0; 123 | board.idx[maxE_pos.y][maxE_pos.x] = -2; 124 | --board.num; 125 | } 126 | } 127 | 128 | } // namespace cbdetect 129 | -------------------------------------------------------------------------------- /src/libcbdetect/filter_corners.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | #include "libcbdetect/config.h" 47 | #include "libcbdetect/filter_corners.h" 48 | #include "libcbdetect/find_corners.h" 49 | #include "libcbdetect/find_modes_meanshift.h" 50 | #include "libcbdetect/weight_mask.h" 51 | 52 | namespace cbdetect { 53 | 54 | void filter_corners(const cv::Mat& img, const cv::Mat& img_angle, const cv::Mat& img_weight, 55 | Corner& corners, const Params& params) { 56 | int n_cicle, n_bin, crossing_thr, need_crossing, need_mode; 57 | if(params.corner_type == SaddlePoint) { 58 | n_cicle = n_bin = 32; 59 | crossing_thr = 3; 60 | need_crossing = 4; 61 | need_mode = 2; 62 | } else if(params.corner_type == MonkeySaddlePoint) { 63 | n_cicle = 48; 64 | n_bin = 32; 65 | crossing_thr = 3; 66 | need_crossing = 6; 67 | need_mode = 3; 68 | } 69 | int width = img.cols, height = img.rows; 70 | std::vector corners_out_p; 71 | std::vector corners_out_r; 72 | std::vector choose(corners.p.size(), 0); 73 | std::vector cos_v(n_cicle), sin_v(n_cicle); 74 | for(int i = 0; i < n_cicle; ++i) { 75 | cos_v[i] = std::cos(i * 2.0 * M_PI / (n_cicle - 1)); 76 | sin_v[i] = std::sin(i * 2.0 * M_PI / (n_cicle - 1)); 77 | } 78 | auto mask = weight_mask(params.radius); 79 | 80 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 81 | for(int i = range.start; i < range.end; ++i) { 82 | int num_crossings = 0, num_modes = 0; 83 | int center_u = std::round(corners.p[i].x); 84 | int center_v = std::round(corners.p[i].y); 85 | int r = corners.r[i]; 86 | if(center_u - r < 0 || center_u + r >= width - 1 || center_v - r < 0 || center_v + r >= height - 1) { 87 | continue; 88 | } 89 | 90 | // extract circle locations and its value 91 | std::vector c(n_cicle); 92 | for(int j = 0; j < n_cicle; ++j) { 93 | int circle_u = static_cast(std::round(center_u + 0.75 * r * cos_v[j])); 94 | int circle_v = static_cast(std::round(center_v + 0.75 * r * sin_v[j])); 95 | circle_u = std::min(std::max(circle_u, 0), width - 1); 96 | circle_v = std::min(std::max(circle_v, 0), height - 1); 97 | c[j] = img.at(circle_v, circle_u); 98 | } 99 | auto minmax = std::minmax_element(c.begin(), c.end()); 100 | double min_c = *minmax.first, max_c = *minmax.second; 101 | for(int j = 0; j < n_cicle; ++j) { 102 | c[j] = c[j] - min_c - (max_c - min_c) / 2; 103 | } 104 | 105 | // count number of zero-crossings 106 | int fisrt_cross_index = 0; 107 | for(int j = 0; j < n_cicle; ++j) { 108 | if((c[j] > 0) ^ (c[(j + 1) % n_cicle] > 0)) { 109 | fisrt_cross_index = (j + 1) % n_cicle; 110 | break; 111 | } 112 | } 113 | for(int j = fisrt_cross_index, count = 1; j < n_cicle + fisrt_cross_index; ++j, ++count) { 114 | if((c[j % n_cicle] > 0) ^ (c[(j + 1) % n_cicle] > 0)) { 115 | if(count >= crossing_thr) { 116 | ++num_crossings; 117 | } 118 | count = 1; 119 | } 120 | } 121 | 122 | int top_left_u = std::max(center_u - r, 0); 123 | int top_left_v = std::max(center_v - r, 0); 124 | int bottom_right_u = std::min(center_u + r, width - 1); 125 | int bottom_right_v = std::min(center_v + r, height - 1); 126 | cv::Mat img_weight_sub = cv::Mat::zeros(2 * r + 1, 2 * r + 1, CV_64F); 127 | img_weight.rowRange(top_left_v, bottom_right_v + 1).colRange(top_left_u, bottom_right_u + 1).copyTo(img_weight_sub(cv::Range(top_left_v - center_v + r, bottom_right_v - center_v + r + 1), cv::Range(top_left_u - center_u + r, bottom_right_u - center_u + r + 1))); 128 | img_weight_sub = img_weight_sub.mul(mask[r]); 129 | double tmp_maxval = 0; 130 | cv::minMaxLoc(img_weight_sub, NULL, &tmp_maxval); 131 | img_weight_sub.forEach([&tmp_maxval](double& val, const int* pos) -> void { 132 | val = val < 0.5 * tmp_maxval ? 0 : val; 133 | }); 134 | 135 | // create histogram 136 | std::vector angle_hist(n_bin, 0); 137 | for(int j2 = top_left_v; j2 <= bottom_right_v; ++j2) { 138 | for(int i2 = top_left_u; i2 <= bottom_right_u; ++i2) { 139 | int bin = static_cast(std::floor(img_angle.at(j2, i2) / (M_PI / n_bin))) % n_bin; 140 | angle_hist[bin] += img_weight_sub.at(j2 - center_v + r, i2 - center_u + r); 141 | } 142 | } 143 | 144 | auto modes = find_modes_meanshift(angle_hist, 1.5); 145 | for(const auto& j : modes) { 146 | if(2 * j.second > modes[0].second) { 147 | ++num_modes; 148 | } 149 | } 150 | 151 | if(num_crossings == need_crossing && num_modes == need_mode) { 152 | choose[i] = 1; 153 | } 154 | } 155 | }); 156 | 157 | for(int i = 0; i < corners.p.size(); ++i) { 158 | if(choose[i] == 1) { 159 | corners_out_p.emplace_back(cv::Point2d(corners.p[i].x, corners.p[i].y)); 160 | corners_out_r.emplace_back(corners.r[i]); 161 | } 162 | } 163 | corners.p = std::move(corners_out_p); 164 | corners.r = std::move(corners_out_r); 165 | } 166 | 167 | } // namespace cbdetect 168 | -------------------------------------------------------------------------------- /src/libcbdetect/find_corners.cc: -------------------------------------------------------------------------------- 1 | // c++ version by ftdlyc 2 | 3 | /* 4 | % Copyright 2012. All rights reserved. 5 | % Author: Andreas Geiger 6 | % Institute of Measurement and Control Systems (MRT) 7 | % Karlsruhe Institute of Technology (KIT), Germany 8 | 9 | % This is free software; you can redistribute it and/or modify it under the 10 | % terms of the GNU General Public License as published by the Free Software 11 | % Foundation; either version 3 of the License, or any later version. 12 | 13 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 14 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | 17 | % You should have received a copy of the GNU General Public License along with 18 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 19 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include "libcbdetect/config.h" 30 | #include "libcbdetect/filter_corners.h" 31 | #include "libcbdetect/find_corners.h" 32 | #include "libcbdetect/get_init_location.h" 33 | #include "libcbdetect/image_normalization_and_gradients.h" 34 | #include "libcbdetect/non_maximum_suppression.h" 35 | #include "libcbdetect/plot_corners.h" 36 | #include "libcbdetect/polynomial_fit.h" 37 | #include "libcbdetect/refine_corners.h" 38 | #include "libcbdetect/score_corners.h" 39 | 40 | namespace cbdetect { 41 | 42 | void find_corners_reiszed(const cv::Mat& img, Corner& corners, const Params& params) { 43 | cv::Mat img_resized, img_norm; 44 | Corner corners_resized; 45 | 46 | // resize image 47 | double scale = 0; 48 | if(img.rows < 640 || img.cols < 480) { 49 | scale = 2.0; 50 | } else if(img.rows >= 640 || img.cols >= 480) { 51 | scale = 0.5; 52 | } else { 53 | return; 54 | } 55 | cv::resize(img, img_resized, cv::Size(img.cols * scale, img.rows * scale), 0, 0, cv::INTER_LINEAR); 56 | 57 | if(img_resized.channels() == 3) { 58 | #if CV_VERSION_MAJOR >= 4 59 | cv::cvtColor(img_resized, img_norm, cv::COLOR_BGR2GRAY); 60 | #else 61 | cv::cvtColor(img_resized, img_norm, CV_BGR2GRAY); 62 | #endif 63 | img_norm.convertTo(img_norm, CV_64F, 1 / 255.0, 0); 64 | } else { 65 | img_resized.convertTo(img_norm, CV_64F, 1 / 255.0, 0); 66 | } 67 | 68 | // normalize image and calculate gradients 69 | cv::Mat img_du, img_dv, img_angle, img_weight; 70 | image_normalization_and_gradients(img_norm, img_du, img_dv, img_angle, img_weight, params); 71 | if(params.show_debug_image && params.norm) { 72 | cv::Mat img_show; 73 | img_norm.convertTo(img_show, CV_8U, 255, 0); 74 | cv::imshow("norm image resized", img_show); 75 | cv::waitKey(); 76 | } 77 | 78 | // get corner's initial locaiton 79 | get_init_location(img_norm, img_du, img_dv, corners_resized, params); 80 | if(corners_resized.p.empty()) { 81 | return; 82 | } 83 | if(params.show_processing) { 84 | printf("Initializing conres (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners_resized.p.size()); 85 | } 86 | if(params.show_debug_image) { 87 | plot_corners(img_resized, corners_resized.p, "init location resized"); 88 | } 89 | 90 | // pre-filter corners according to zero crossings 91 | filter_corners(img_norm, img_angle, img_weight, corners_resized, params); 92 | if(params.show_processing) { 93 | printf("Filtering corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners_resized.p.size()); 94 | } 95 | if(params.show_debug_image) { 96 | plot_corners(img_resized, corners_resized.p, "filter corners resized"); 97 | } 98 | 99 | // refinement 100 | refine_corners(img_du, img_dv, img_angle, img_weight, corners_resized, params); 101 | if(params.show_processing) { 102 | printf("Refining corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners_resized.p.size()); 103 | } 104 | if(params.show_debug_image) { 105 | plot_corners(img_resized, corners_resized.p, "refine corners resized"); 106 | } 107 | 108 | // merge corners 109 | std::for_each(corners_resized.p.begin(), corners_resized.p.end(), [&scale](auto& p) { p /= scale; }); 110 | // std::for_each(corners_resized.r.begin(), corners_resized.r.end(), [&scale](auto &r) { r = (double) r / scale; }); 111 | double min_dist_thr = scale > 1 ? 3 : 5; 112 | for(int i = 0; i < corners_resized.p.size(); ++i) { 113 | double min_dist = DBL_MAX; 114 | cv::Point2d& p2 = corners_resized.p[i]; 115 | for(int j = 0; j < corners.p.size(); ++j) { 116 | cv::Point2d& p1 = corners.p[j]; 117 | double dist = cv::norm(p2 - p1); 118 | min_dist = dist < min_dist ? dist : min_dist; 119 | } 120 | if(min_dist > min_dist_thr) { 121 | corners.p.emplace_back(corners_resized.p[i]); 122 | corners.r.emplace_back(corners_resized.r[i]); 123 | corners.v1.emplace_back(corners_resized.v1[i]); 124 | corners.v2.emplace_back(corners_resized.v2[i]); 125 | if(params.corner_type == MonkeySaddlePoint) { 126 | corners.v3.emplace_back(corners_resized.v3[i]); 127 | } 128 | } 129 | } 130 | } 131 | 132 | void find_corners(const cv::Mat& img, Corner& corners, const Params& params) { 133 | // clear old data 134 | corners.p.clear(); 135 | corners.r.clear(); 136 | corners.v1.clear(); 137 | corners.v2.clear(); 138 | corners.v3.clear(); 139 | corners.score.clear(); 140 | 141 | // convert to double grayscale image 142 | cv::Mat img_norm; 143 | if(img.channels() == 3) { 144 | #if CV_VERSION_MAJOR >= 4 145 | cv::cvtColor(img, img_norm, cv::COLOR_BGR2GRAY); 146 | #else 147 | cv::cvtColor(img, img_norm, CV_BGR2GRAY); 148 | #endif 149 | img_norm.convertTo(img_norm, CV_64F, 1. / 255., 0); 150 | } else { 151 | img.convertTo(img_norm, CV_64F, 1. / 255., 0); 152 | } 153 | 154 | // normalize image and calculate gradients 155 | cv::Mat img_du, img_dv, img_angle, img_weight; 156 | image_normalization_and_gradients(img_norm, img_du, img_dv, img_angle, img_weight, params); 157 | if(params.show_debug_image && params.norm) { 158 | cv::Mat img_show; 159 | img_norm.convertTo(img_show, CV_8U, 255., 0); 160 | cv::imshow("norm image", img_show); 161 | cv::waitKey(); 162 | } 163 | 164 | // get corner's initial locaiton 165 | get_init_location(img_norm, img_du, img_dv, corners, params); 166 | if(corners.p.empty()) { 167 | return; 168 | } 169 | if(params.show_processing) { 170 | printf("Initializing conres (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners.p.size()); 171 | } 172 | if(params.show_debug_image) { 173 | plot_corners(img, corners.p, "init location"); 174 | } 175 | 176 | // pre-filter corners according to zero crossings 177 | filter_corners(img_norm, img_angle, img_weight, corners, params); 178 | if(params.show_processing) { 179 | printf("Filtering corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners.p.size()); 180 | } 181 | if(params.show_debug_image) { 182 | plot_corners(img, corners.p, "filter corners"); 183 | } 184 | 185 | // refinement 186 | refine_corners(img_du, img_dv, img_angle, img_weight, corners, params); 187 | if(params.show_processing) { 188 | printf("Refining corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners.p.size()); 189 | } 190 | if(params.show_debug_image) { 191 | plot_corners(img, corners.p, "refine corners"); 192 | } 193 | 194 | // resize image to detect more corners 195 | find_corners_reiszed(img, corners, params); 196 | if(params.show_processing) { 197 | printf("Merging corners (%d x %d) ... %lu\n", img.cols, img.rows, corners.p.size()); 198 | } 199 | if(params.show_debug_image) { 200 | plot_corners(img, corners.p, "merge corners"); 201 | } 202 | 203 | // polynomial fit 204 | if(params.polynomial_fit) { 205 | polynomial_fit(img_norm, corners, params); 206 | if(params.show_processing) { 207 | printf("Polyfitting corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners.p.size()); 208 | } 209 | if(params.show_debug_image) { 210 | plot_corners(img, corners.p, "polynomial fit corners"); 211 | } 212 | } 213 | 214 | // score corners 215 | sorce_corners(img_norm, img_weight, corners, params); 216 | 217 | // remove low scoring corners 218 | remove_low_scoring_corners(params.score_thr, corners, params); 219 | 220 | // non maximum suppression 221 | non_maximum_suppression_sparse(corners, 3, img.size(), params); 222 | if(params.show_processing) { 223 | printf("Scoring corners (%d x %d) ... %lu\n", img_norm.cols, img_norm.rows, corners.p.size()); 224 | } 225 | if(params.show_debug_image) { 226 | plot_corners(img, corners.p, "scoring corners"); 227 | } 228 | } 229 | 230 | } // namespace cbdetect 231 | -------------------------------------------------------------------------------- /src/libcbdetect/find_modes_meanshift.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | #include 41 | #include 42 | 43 | #include "libcbdetect/find_modes_meanshift.h" 44 | #include "libcbdetect/config.h" 45 | 46 | namespace cbdetect { 47 | 48 | // efficient mean-shift approximation by histogram smoothing 49 | std::vector> find_modes_meanshift(const std::vector& hist, double sigma) { 50 | std::unordered_map hash_table; 51 | std::vector> modes; 52 | 53 | int r = static_cast(std::round(2 * sigma)); 54 | std::vector weight(2 * r + 1, 0); 55 | for(int i = 0; i < 2 * r + 1; ++i) { 56 | weight[i] = std::exp(-0.5 * (i - r) * (i - r) / sigma / sigma) / std::sqrt(2 * M_PI) / sigma; 57 | } 58 | 59 | // compute smoothed histogram 60 | int n = hist.size(); 61 | std::vector hist_smoothed(n, 0); 62 | for(int i = 0; i < n; ++i) { 63 | for(int j = 0; j < 2 * r + 1; ++j) { 64 | hist_smoothed[(i + r) % n] += hist[(i + j) % n] * weight[j]; 65 | } 66 | } 67 | 68 | // check if at least one entry is non-zero 69 | // (otherwise mode finding may run infinitly) 70 | auto max_hist_val = std::max_element(hist_smoothed.begin(), hist_smoothed.end()); 71 | if(*max_hist_val < 1e-6) { 72 | return modes; 73 | } 74 | 75 | // mode finding 76 | std::vector visited(n, 0); 77 | for(int i = 0; i < n; ++i) { 78 | int j = i; 79 | if(!visited[j]) { 80 | while(1) { 81 | visited[j] = 1; 82 | int j1 = (j + 1) % n, j2 = (j + n - 1) % n; 83 | double h0 = hist_smoothed[j]; 84 | double h1 = hist_smoothed[j1]; 85 | double h2 = hist_smoothed[j2]; 86 | if(h1 >= h0 && h1 >= h2) { 87 | j = j1; 88 | } else if(h2 > h0 && h2 > h1) { 89 | j = j2; 90 | } else { 91 | break; 92 | } 93 | } 94 | hash_table[j] = hist_smoothed[j]; 95 | } 96 | } 97 | 98 | for(const auto& i : hash_table) { 99 | modes.emplace_back(i); 100 | } 101 | std::sort(modes.begin(), modes.end(), [](const auto& i1, const auto& i2) -> bool { 102 | return i1.second > i2.second; 103 | }); 104 | 105 | return modes; 106 | }; 107 | 108 | } // namespace cbdetect -------------------------------------------------------------------------------- /src/libcbdetect/get_image_patch.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include "libcbdetect/get_image_patch.h" 21 | 22 | namespace cbdetect { 23 | 24 | void get_image_patch(const cv::Mat& img, double u, double v, int r, cv::Mat& img_sub) { 25 | int iu = u; 26 | int iv = v; 27 | double du = u - iu; 28 | double dv = v - iv; 29 | double a00 = 1 - du - dv + du * dv; 30 | double a01 = du - du * dv; 31 | double a10 = dv - du * dv; 32 | double a11 = du * dv; 33 | 34 | img_sub.create(2 * r + 1, 2 * r + 1, CV_64F); 35 | for(int j = -r; j <= r; ++j) { 36 | for(int i = -r; i <= r; ++i) { 37 | img_sub.at(j + r, i + r) = 38 | a00 * img.at(iv + j, iu + i) + a01 * img.at(iv + j, iu + i + 1) + 39 | a10 * img.at(iv + j + 1, iu + i) + a11 * img.at(iv + j + 1, iu + i + 1); 40 | } 41 | } 42 | } 43 | 44 | void get_image_patch_with_mask(const cv::Mat& img, const cv::Mat& mask, double u, double v, int r, cv::Mat& img_sub) { 45 | int iu = u; 46 | int iv = v; 47 | double du = u - iu; 48 | double dv = v - iv; 49 | double a00 = 1 - du - dv + du * dv; 50 | double a01 = du - du * dv; 51 | double a10 = dv - du * dv; 52 | double a11 = du * dv; 53 | 54 | img_sub.create((2 * r + 1) * (2 * r + 1), 1, CV_64F); 55 | int num = 0; 56 | for(int j = -r; j <= r; ++j) { 57 | for(int i = -r; i <= r; ++i) { 58 | if(mask.at(j + r, i + r) >= 1e-6) { 59 | img_sub.at(num, 0) = 60 | a00 * img.at(iv + j, iu + i) + a01 * img.at(iv + j, iu + i + 1) + 61 | a10 * img.at(iv + j + 1, iu + i) + a11 * img.at(iv + j + 1, iu + i + 1); 62 | ++num; 63 | } 64 | } 65 | } 66 | img_sub.resize(num); 67 | } 68 | 69 | } // namespace cbdetect 70 | -------------------------------------------------------------------------------- /src/libcbdetect/get_init_location.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | /** 38 | * Copyright (C) 2017-present, Facebook, Inc. 39 | * 40 | * This library is free software; you can redistribute it and/or 41 | * modify it under the terms of the GNU Lesser General Public 42 | * License as published by the Free Software Foundation; either 43 | * version 2.1 of the License, or (at your option) any later version. 44 | * 45 | * This library is distributed in the hope that it will be useful, 46 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 47 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 48 | * Lesser General Public License for more details. 49 | * 50 | * You should have received a copy of the GNU Lesser General Public 51 | * License along with this library; if not, write to the Free Software 52 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 53 | */ 54 | 55 | #include 56 | 57 | #include 58 | 59 | #include "libcbdetect/config.h" 60 | #include "libcbdetect/create_correlation_patch.h" 61 | #include "libcbdetect/get_image_patch.h" 62 | #include "libcbdetect/get_init_location.h" 63 | #include "libcbdetect/image_normalization_and_gradients.h" 64 | #include "libcbdetect/non_maximum_suppression.h" 65 | 66 | namespace cbdetect { 67 | 68 | // form https://github.com/facebookincubator/deltille 69 | void hessian_response(const cv::Mat& img_in, cv::Mat& img_out) { 70 | const int rows = img_in.rows; 71 | const int cols = img_in.cols; 72 | const int stride = cols; 73 | 74 | // allocate output 75 | img_out = cv::Mat::zeros(rows, cols, CV_64F); 76 | 77 | cv::parallel_for_(cv::Range(1, rows - 1), [&img_in, &img_out, &stride, &cols](const cv::Range& range) -> void { 78 | // setup input and output pointer to be centered at 1,0 and 1,1 resp. 79 | auto* in = img_in.ptr(range.start); 80 | auto* out = img_out.ptr(range.start) + 1; 81 | 82 | for(int i = range.start; i < range.end; ++i) { 83 | double v11, v12, v21, v22, v31, v32; 84 | /* fill in shift registers at the beginning of the row */ 85 | v11 = in[-stride]; 86 | v12 = in[1 - stride]; 87 | v21 = in[0]; 88 | v22 = in[1]; 89 | v31 = in[+stride]; 90 | v32 = in[1 + stride]; 91 | /* move input pointer to (1,2) of the 3x3 square */ 92 | in += 2; 93 | for(int c = 1; c < cols - 1; ++c) { 94 | /* fetch remaining values (last column) */ 95 | const double v13 = in[-stride]; 96 | const double v23 = *in; 97 | const double v33 = in[+stride]; 98 | 99 | // compute 3x3 Hessian values from symmetric differences. 100 | double Lxx = (v21 - 2 * v22 + v23); 101 | double Lyy = (v12 - 2 * v22 + v32); 102 | double Lxy = (v13 - v11 + v31 - v33) / 4.; 103 | 104 | /* normalize and write out */ 105 | *out = Lxx * Lyy - Lxy * Lxy; 106 | 107 | /* move window */ 108 | v11 = v12; 109 | v12 = v13; 110 | v21 = v22; 111 | v22 = v23; 112 | v31 = v32; 113 | v32 = v33; 114 | 115 | /* move input/output pointers */ 116 | in++; 117 | out++; 118 | } 119 | out += 2; 120 | } 121 | }); 122 | } 123 | 124 | void rotate_image(const cv::Mat& img_in, double angle, cv::Mat& img_out, cv::Size out_size = cv::Size()) { 125 | if(std::abs(angle) < 1e-3) { 126 | img_out = img_in.clone(); 127 | return; 128 | } 129 | 130 | // cal new width and height 131 | double in_center_u = (img_in.cols - 1) / 2.; 132 | double in_center_v = (img_in.rows - 1) / 2.; 133 | if(out_size.width <= 0 || out_size.height <= 0) { 134 | cv::Point2i tl(std::round(-in_center_u * std::cos(angle) - in_center_v * std::sin(angle)), 135 | std::round(in_center_u * std::sin(angle) - in_center_v * std::cos(angle))); 136 | cv::Point2i tr(std::round(in_center_u * std::cos(angle) - in_center_v * std::sin(angle)), 137 | std::round(-in_center_u * std::sin(angle) - in_center_v * std::cos(angle))); 138 | cv::Point2i bl(std::round(-in_center_u * std::cos(angle) + in_center_v * std::sin(angle)), 139 | std::round(in_center_u * std::sin(angle) + in_center_v * std::cos(angle))); 140 | cv::Point2i br(std::round(in_center_u * std::cos(angle) + in_center_v * std::sin(angle)), 141 | std::round(-in_center_u * std::sin(angle) + in_center_v * std::cos(angle))); 142 | if(std::min(tl.x, br.x) > std::min(tr.x, bl.x)) { 143 | out_size = cv::Size(std::abs(tr.x - bl.x) + 1, std::abs(tl.y - br.y) + 1); 144 | } else { 145 | out_size = cv::Size(std::abs(tl.x - br.x) + 1, std::abs(tr.y - bl.y) + 1); 146 | } 147 | } 148 | double out_center_u = (out_size.width - 1) / 2.; 149 | double out_center_v = (out_size.height - 1) / 2.; 150 | 151 | // // rotate image 152 | // img_out.create(height, width, CV_64F); 153 | // double new_center_u = (width - 1) / 2.0; 154 | // double new_center_v = (height - 1) / 2.0; 155 | // for (int j = 0; j < img_out.rows; ++j) { 156 | // for (int i = 0; i < img_out.cols; ++i) { 157 | // double u = (i - new_center_u) * std::cos(angle) - (j - new_center_v) * sin(angle) + center_u; 158 | // double v = (i - new_center_u) * std::sin(angle) + (j - new_center_v) * cos(angle) + center_v; 159 | // if (u < 0 || u >= img_in.cols - 1 || v < 0 || v >= img_in.rows - 1) { 160 | // img_out.at(j, i) = 0; 161 | // continue; 162 | // } 163 | // 164 | // int iu = u; 165 | // int iv = v; 166 | // double du = u - iu; 167 | // double dv = v - iv; 168 | // double a00 = 1 - du - dv + du * dv; 169 | // double a01 = du - du * dv; 170 | // double a10 = dv - du * dv; 171 | // double a11 = du * dv; 172 | // img_out.at(j, i) = img_in.at(v, u) * a00 + img_in.at(v, u + 1) * a01 + 173 | // img_in.at(v + 1, u) * a10 + img_in.at(v + 1, u + 1) * a11; 174 | // } 175 | // } 176 | 177 | // rotate image 178 | double shift_u = out_center_u - in_center_u * std::cos(angle) - in_center_v * std::sin(angle); 179 | double shift_v = out_center_v + in_center_u * std::sin(angle) - in_center_v * std::cos(angle); 180 | cv::Mat rot = (cv::Mat_(2, 3) << std::cos(angle), std::sin(angle), shift_u, 181 | -std::sin(angle), std::cos(angle), shift_v); 182 | cv::warpAffine(img_in, img_out, rot, out_size); 183 | } 184 | 185 | // paper: Accurate Detection and Localization of Checkerboard Corners for Calibration 186 | void localized_radon_transform(const cv::Mat& img_in, cv::Mat& img_out) { 187 | std::vector angles = {0, M_PI / 4}; 188 | std::vector rb_imgs(4); 189 | for(int i = 0; i < 2; ++i) { 190 | cv::Mat r_img, u_img, v_img; 191 | rotate_image(img_in, -angles[i], r_img); 192 | cv::blur(r_img, u_img, cv::Size(11, 3)); 193 | cv::blur(r_img, v_img, cv::Size(3, 11)); 194 | rotate_image(u_img, angles[i], rb_imgs[2 * i], img_in.size()); 195 | rotate_image(v_img, angles[i], rb_imgs[2 * i + 1], img_in.size()); 196 | } 197 | cv::Mat max_img_1 = cv::max(rb_imgs[0], rb_imgs[1]); 198 | cv::Mat max_img_2 = cv::max(rb_imgs[2], rb_imgs[3]); 199 | cv::Mat min_img_1 = cv::min(rb_imgs[0], rb_imgs[1]); 200 | cv::Mat min_img_2 = cv::min(rb_imgs[2], rb_imgs[3]); 201 | img_out = cv::max(max_img_1, max_img_2) - cv::min(min_img_1, min_img_2); 202 | img_out.forEach([](double& pixel, const int* position) -> void { 203 | pixel = pixel * pixel; 204 | }); 205 | } 206 | 207 | void get_init_location(const cv::Mat& img, const cv::Mat& img_du, const cv::Mat& img_dv, 208 | Corner& corners, const Params& params) { 209 | DetectMethod detect_method = params.corner_type == MonkeySaddlePoint ? HessianResponse : params.detect_method; 210 | switch(detect_method) { 211 | case TemplateMatchFast: 212 | case TemplateMatchSlow: { 213 | // templates and scales 214 | std::vector tprops; 215 | if(detect_method == TemplateMatchFast) { 216 | tprops = {0, M_PI_2, 217 | M_PI_4, -M_PI_4}; 218 | } else { 219 | tprops = {0, M_PI_2, 220 | M_PI_4, -M_PI_4, 221 | 0, M_PI_4, 222 | 0, -M_PI_4, 223 | M_PI_4, M_PI_2, 224 | -M_PI_4, M_PI_2, 225 | -3 * M_PI / 8, 3 * M_PI / 8, 226 | -M_PI / 8, M_PI / 8, 227 | -M_PI / 8, -3 * M_PI / 8, 228 | M_PI / 8, 3 * M_PI / 8}; 229 | } 230 | 231 | // for all scales do 232 | for(const auto& r : params.radius) { 233 | // filter image 234 | cv::Mat img_corners = cv::Mat::zeros(img.size(), CV_64F); 235 | cv::Mat img_corners_a1, img_corners_a2, img_corners_b1, img_corners_b2, img_corners_mu, 236 | img_corners_a, img_corners_b, img_corners_s1, img_corners_s2; 237 | 238 | for(int i = 0; i < tprops.size(); i += 2) { 239 | std::vector template_kernel(4); // a1, a2, b1, b2 240 | create_correlation_patch(template_kernel, tprops[i], tprops[i + 1], r); 241 | 242 | // filter image with current template 243 | cv::filter2D(img, img_corners_a1, -1, template_kernel[0], cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 244 | cv::filter2D(img, img_corners_a2, -1, template_kernel[1], cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 245 | cv::filter2D(img, img_corners_b1, -1, template_kernel[2], cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 246 | cv::filter2D(img, img_corners_b2, -1, template_kernel[3], cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 247 | 248 | // compute mean 249 | img_corners_mu = (img_corners_a1 + img_corners_a2 + img_corners_b1 + img_corners_b2) / 4; 250 | 251 | // case 1: a=white, b=black 252 | img_corners_a = cv::min(img_corners_a1, img_corners_a2) - img_corners_mu; 253 | img_corners_b = img_corners_mu - cv::max(img_corners_b1, img_corners_b2); 254 | img_corners_s1 = cv::min(img_corners_a, img_corners_b); 255 | // case 2: b=white, a=black 256 | img_corners_a = img_corners_mu - cv::max(img_corners_a1, img_corners_a2); 257 | img_corners_b = cv::min(img_corners_b1, img_corners_b2) - img_corners_mu; 258 | img_corners_s2 = cv::min(img_corners_a, img_corners_b); 259 | 260 | // combine both 261 | img_corners = cv::max(img_corners, cv::max(img_corners_s1, img_corners_s2)); 262 | } 263 | non_maximum_suppression(img_corners, 1, params.init_loc_thr, r, corners); 264 | } 265 | break; 266 | } 267 | case HessianResponse: { 268 | cv::Mat gauss_img; 269 | cv::GaussianBlur(img, gauss_img, cv::Size(7, 7), 1.5, 1.5); 270 | cv::Mat hessian_img; 271 | hessian_response(gauss_img, hessian_img); 272 | double mn = 0, mx = 0; 273 | cv::minMaxIdx(hessian_img, &mn, &mx, NULL, NULL); 274 | hessian_img = cv::abs(hessian_img); 275 | double thr = std::abs(mn * params.init_loc_thr); 276 | for(const auto& r : params.radius) { 277 | non_maximum_suppression(hessian_img, r, thr, r, corners); 278 | } 279 | break; 280 | } 281 | case LocalizedRadonTransform: { 282 | cv::Mat response_img; 283 | localized_radon_transform(img, response_img); 284 | for(const auto& r : params.radius) { 285 | non_maximum_suppression(response_img, r, params.init_loc_thr / 10.0, r, corners); 286 | } 287 | break; 288 | } 289 | default: 290 | break; 291 | } 292 | 293 | // location refinement 294 | int width = img.cols, height = img.rows; 295 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 296 | for(int i = range.start; i < range.end; ++i) { 297 | double u = corners.p[i].x; 298 | double v = corners.p[i].y; 299 | int r = corners.r[i]; 300 | 301 | cv::Mat G = cv::Mat::zeros(2, 2, CV_64F); 302 | cv::Mat b = cv::Mat::zeros(2, 1, CV_64F); 303 | 304 | // get subpixel gradiant 305 | cv::Mat img_du_sub, img_dv_sub; 306 | if(u - r < 0 || u + r >= width - 1 || v - r < 0 || v + r >= height - 1) { 307 | break; 308 | } 309 | get_image_patch(img_du, u, v, r, img_du_sub); 310 | get_image_patch(img_dv, u, v, r, img_dv_sub); 311 | 312 | for(int j2 = 0; j2 < 2 * r + 1; ++j2) { 313 | for(int i2 = 0; i2 < 2 * r + 1; ++i2) { 314 | // pixel orientation vector 315 | double o_du = img_du_sub.at(j2, i2); 316 | double o_dv = img_dv_sub.at(j2, i2); 317 | double o_norm = std::sqrt(o_du * o_du + o_dv * o_dv); 318 | if(o_norm < 0.1) { 319 | continue; 320 | } 321 | 322 | // do not consider center pixel 323 | if(i2 == r && j2 == r) { 324 | continue; 325 | } 326 | G.at(0, 0) += o_du * o_du; 327 | G.at(0, 1) += o_du * o_dv; 328 | G.at(1, 0) += o_du * o_dv; 329 | G.at(1, 1) += o_dv * o_dv; 330 | b.at(0, 0) += o_du * o_du * (i2 - r + u) + o_du * o_dv * (j2 - r + v); 331 | b.at(1, 0) += o_du * o_dv * (i2 - r + u) + o_dv * o_dv * (j2 - r + v); 332 | } 333 | } 334 | 335 | cv::Mat new_pos = G.inv() * b; 336 | if(std::abs(new_pos.at(0, 0) - corners.p[i].x) + 337 | std::abs(new_pos.at(1, 0) - corners.p[i].y) < 338 | corners.r[i] * 2) { 339 | corners.p[i].x = new_pos.at(0, 0); 340 | corners.p[i].y = new_pos.at(1, 0); 341 | } 342 | } 343 | }); 344 | } 345 | 346 | } // namespace cbdetect 347 | -------------------------------------------------------------------------------- /src/libcbdetect/grow_board.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | 41 | #include "libcbdetect/config.h" 42 | #include "libcbdetect/grow_board.h" 43 | 44 | namespace cbdetect { 45 | 46 | // linear prediction (old) 47 | // function pred = predict_corners(p1,p2,p3) 48 | // pred = 2*p3-p2; 49 | // 50 | // replica prediction (new) 51 | std::vector predict_corners(const Corner& corners, 52 | const std::vector& p1, 53 | const std::vector& p2, 54 | const std::vector& p3) { 55 | std::vector pred(p3.size()); 56 | if(p1.empty()) { 57 | for(int i = 0; i < pred.size(); ++i) { 58 | pred[i] = 2 * corners.p[p3[i]] - corners.p[p2[i]]; 59 | } 60 | } else { 61 | for(int i = 0; i < pred.size(); ++i) { 62 | // compute vectors 63 | cv::Point2d v1 = corners.p[p2[i]] - corners.p[p1[i]]; 64 | cv::Point2d v2 = corners.p[p3[i]] - corners.p[p2[i]]; 65 | 66 | // predict angles 67 | double a1 = std::atan2(v1.y, v1.x); 68 | double a2 = std::atan2(v2.y, v2.x); 69 | double a3 = 2 * a2 - a1; 70 | 71 | // predict scales 72 | double s1 = cv::norm(v1); 73 | double s2 = cv::norm(v2); 74 | double s3 = 2 * s2 - s1; 75 | 76 | // predict p4 (the factor 0.75 ensures that under extreme 77 | // distortions (omnicam) the closer prediction is selected) 78 | pred[i].x = corners.p[p3[i]].x + 0.75 * s3 * std::cos(a3); 79 | pred[i].y = corners.p[p3[i]].y + 0.75 * s3 * std::sin(a3); 80 | } 81 | } 82 | return pred; 83 | } 84 | 85 | std::vector predict_board_corners(const Corner& corners, 86 | std::vector& used, 87 | std::vector& p1, 88 | std::vector& p2, 89 | std::vector& p3) { 90 | std::vector pred = predict_corners(corners, p1, p2, p3); 91 | std::vector pred_idx(pred.size(), -2); 92 | 93 | // build distance matrix 94 | std::vector> D(pred.size(), std::vector(corners.p.size(), DBL_MAX)); 95 | for(int i = 0; i < pred.size(); ++i) { 96 | cv::Point2d w = pred[i] - corners.p[p3[i]]; 97 | // double angle_w = std::atan2(w.y, w.x); 98 | for(int j = 0; j < corners.p.size(); ++j) { 99 | if(used[j] == 1) { 100 | continue; 101 | } 102 | cv::Point2d v_tmp = corners.p[j] - corners.p[p3[i]]; 103 | cv::Point2d v(v_tmp.dot(w), v_tmp.dot(cv::Point2d(w.y, -w.x))); 104 | v = v / (cv::norm(w) * cv::norm(w)); 105 | double d1 = std::atan2(v.y, v.x); 106 | double d2 = (1 - cv::norm(v)); 107 | D[i][j] = std::sqrt(std::abs(d1) + d2 * d2 * d2 * d2); 108 | // cv::Point2d v = corners.p[j] - corners.p[p3[i]]; 109 | // double d_angle = angle_w - std::atan2(v.y, v.x); 110 | // double d1 = 1 - cv::norm(v) / cv::norm(w); 111 | // double d2 = std::abs(1 - 1 / (std::cos(d_angle) + 0.001)); 112 | // D[i][j] = d1 * d1 + 10 * d2; 113 | } 114 | } 115 | 116 | // search for closest corners 117 | for(int i = 0; i < pred.size(); ++i) { 118 | double min_D = DBL_MAX; 119 | int min_row = 0; 120 | int min_col = 0; 121 | for(int j = 0; j < pred.size(); ++j) { 122 | int min_row_2 = std::min_element(D[j].begin(), D[j].end()) - D[j].begin(); 123 | if(D[j][min_row_2] < min_D) { 124 | min_D = D[j][min_row_2]; 125 | min_row = min_row_2; 126 | min_col = j; 127 | } 128 | } 129 | 130 | // all detect corners have been used 131 | if(DBL_MAX - min_D < 1e-6) { 132 | break; 133 | } 134 | 135 | for(auto& j : D[min_col]) { 136 | j = DBL_MAX; 137 | } 138 | for(int j = 0; j < pred.size(); ++j) { 139 | D[j][min_row] = DBL_MAX; 140 | } 141 | pred_idx[min_col] = min_row; 142 | used[min_row] = 1; 143 | } 144 | 145 | return pred_idx; 146 | } 147 | 148 | bool add_board_boundary(Board& board, int direction) { 149 | int rows = board.idx.size(), cols = board.idx[0].size(); 150 | bool add_board = false; 151 | 152 | // top/left/bottom//right 153 | switch(direction) { 154 | case 0: { 155 | for(int i = 0; i < cols; ++i) { 156 | if(board.idx[0][i] != -2 && board.idx[0][i] != -1) { 157 | add_board = true; 158 | break; 159 | } 160 | } 161 | if(add_board) { 162 | std::vector idx(cols, -1); 163 | std::vector> energy(cols, std::vector(3, DBL_MAX)); 164 | board.idx.insert(board.idx.begin(), idx); 165 | board.energy.insert(board.energy.begin(), energy); 166 | } 167 | break; 168 | } 169 | case 1: { 170 | for(int i = 0; i < rows; ++i) { 171 | if(board.idx[i][0] != -2 && board.idx[i][0] != -1) { 172 | add_board = true; 173 | break; 174 | } 175 | } 176 | if(add_board) { 177 | for(int i = 0; i < rows; ++i) { 178 | board.idx[i].insert(board.idx[i].begin(), -1); 179 | board.energy[i].insert(board.energy[i].begin(), std::vector(3, DBL_MAX)); 180 | } 181 | } 182 | break; 183 | } 184 | case 2: { 185 | for(int i = 0; i < cols; ++i) { 186 | if(board.idx[rows - 1][i] != -2 && board.idx[rows - 1][i] != -1) { 187 | add_board = true; 188 | break; 189 | } 190 | } 191 | if(add_board) { 192 | std::vector idx(cols, -1); 193 | std::vector> energy(cols, std::vector(3, DBL_MAX)); 194 | board.idx.emplace_back(idx); 195 | board.energy.emplace_back(energy); 196 | } 197 | break; 198 | } 199 | case 3: { 200 | for(int i = 0; i < rows; ++i) { 201 | if(board.idx[i][cols - 1] != -2 && board.idx[i][cols - 1] != -1) { 202 | add_board = true; 203 | break; 204 | } 205 | } 206 | if(add_board) { 207 | for(int i = 0; i < rows; ++i) { 208 | board.idx[i].emplace_back(-1); 209 | board.energy[i].emplace_back(std::vector(3, DBL_MAX)); 210 | } 211 | } 212 | break; 213 | } 214 | default: 215 | break; 216 | } 217 | 218 | return add_board; 219 | } 220 | 221 | GrowType grow_board(const Corner& corners, std::vector& used, Board& board, 222 | std::vector& proposal, int direction, const Params& params) { 223 | // return immediately, if there do not exist any chessboards 224 | if(board.idx.empty()) { 225 | return GrowType_Failure; 226 | } 227 | int cols = board.idx[0].size(); 228 | int rows = board.idx.size(); 229 | std::vector idx, p1, p2, p3, pred; 230 | 231 | // fill inside corners top/left/bottom/right/top-left/bottom-right 232 | if(params.corner_type == MonkeySaddlePoint || params.occlusion) { 233 | switch(direction) { 234 | case 0: { 235 | for(int i = rows - 4; i >= 0; --i) { 236 | for(int j = 0; j < cols; ++j) { 237 | if(board.idx[i][j] != -1) { 238 | continue; 239 | } 240 | int idx1 = board.idx[i + 3][j]; 241 | int idx2 = board.idx[i + 2][j]; 242 | int idx3 = board.idx[i + 1][j]; 243 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 244 | continue; 245 | } 246 | p1.emplace_back(idx1); 247 | p2.emplace_back(idx2); 248 | p3.emplace_back(idx3); 249 | proposal.emplace_back(cv::Point2i(j, i)); 250 | } 251 | } 252 | if(!proposal.empty() || params.strict_grow) { 253 | break; 254 | } 255 | for(int i = rows - 3; i >= 0; --i) { 256 | for(int j = 0; j < cols; ++j) { 257 | if(board.idx[i][j] != -1) { 258 | continue; 259 | } 260 | int idx2 = board.idx[i + 2][j]; 261 | int idx3 = board.idx[i + 1][j]; 262 | if(idx2 < 0 || idx3 < 0) { 263 | continue; 264 | } 265 | p2.emplace_back(idx2); 266 | p3.emplace_back(idx3); 267 | proposal.emplace_back(cv::Point2i(j, i)); 268 | } 269 | } 270 | break; 271 | } 272 | case 1: { 273 | for(int i = 0; i < rows; ++i) { 274 | for(int j = cols - 4; j >= 0; --j) { 275 | if(board.idx[i][j] != -1) { 276 | continue; 277 | } 278 | int idx1 = board.idx[i][j + 3]; 279 | int idx2 = board.idx[i][j + 2]; 280 | int idx3 = board.idx[i][j + 1]; 281 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 282 | continue; 283 | } 284 | p1.emplace_back(idx1); 285 | p2.emplace_back(idx2); 286 | p3.emplace_back(idx3); 287 | proposal.emplace_back(cv::Point2i(j, i)); 288 | } 289 | } 290 | if(!proposal.empty() || params.strict_grow) { 291 | break; 292 | } 293 | for(int i = 0; i < rows; ++i) { 294 | for(int j = cols - 3; j >= 0; --j) { 295 | if(board.idx[i][j] != -1) { 296 | continue; 297 | } 298 | int idx2 = board.idx[i][j + 2]; 299 | int idx3 = board.idx[i][j + 1]; 300 | if(idx2 < 0 || idx3 < 0) { 301 | continue; 302 | } 303 | p2.emplace_back(idx2); 304 | p3.emplace_back(idx3); 305 | proposal.emplace_back(cv::Point2i(j, i)); 306 | } 307 | } 308 | break; 309 | } 310 | case 2: { 311 | for(int i = 3; i < rows; ++i) { 312 | for(int j = 0; j < cols; ++j) { 313 | if(board.idx[i][j] != -1) { 314 | continue; 315 | } 316 | int idx1 = board.idx[i - 3][j]; 317 | int idx2 = board.idx[i - 2][j]; 318 | int idx3 = board.idx[i - 1][j]; 319 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 320 | continue; 321 | } 322 | p1.emplace_back(idx1); 323 | p2.emplace_back(idx2); 324 | p3.emplace_back(idx3); 325 | proposal.emplace_back(cv::Point2i(j, i)); 326 | } 327 | } 328 | if(!proposal.empty() || params.strict_grow) { 329 | break; 330 | } 331 | for(int i = 2; i < rows; ++i) { 332 | for(int j = 0; j < cols; ++j) { 333 | if(board.idx[i][j] != -1) { 334 | continue; 335 | } 336 | int idx2 = board.idx[i - 2][j]; 337 | int idx3 = board.idx[i - 1][j]; 338 | if(idx2 < 0 || idx3 < 0) { 339 | continue; 340 | } 341 | p2.emplace_back(idx2); 342 | p3.emplace_back(idx3); 343 | proposal.emplace_back(cv::Point2i(j, i)); 344 | } 345 | } 346 | break; 347 | } 348 | case 3: { 349 | for(int i = 0; i < rows; ++i) { 350 | for(int j = 3; j < cols; ++j) { 351 | if(board.idx[i][j] != -1) { 352 | continue; 353 | } 354 | int idx1 = board.idx[i][j - 3]; 355 | int idx2 = board.idx[i][j - 2]; 356 | int idx3 = board.idx[i][j - 1]; 357 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 358 | continue; 359 | } 360 | p1.emplace_back(idx1); 361 | p2.emplace_back(idx2); 362 | p3.emplace_back(idx3); 363 | proposal.emplace_back(cv::Point2i(j, i)); 364 | } 365 | } 366 | if(!proposal.empty() || params.strict_grow) { 367 | break; 368 | } 369 | for(int i = 0; i < rows; ++i) { 370 | for(int j = 2; j < cols; ++j) { 371 | if(board.idx[i][j] != -1) { 372 | continue; 373 | } 374 | int idx2 = board.idx[i][j - 2]; 375 | int idx3 = board.idx[i][j - 1]; 376 | if(idx2 < 0 || idx3 < 0) { 377 | continue; 378 | } 379 | p2.emplace_back(idx2); 380 | p3.emplace_back(idx3); 381 | proposal.emplace_back(cv::Point2i(j, i)); 382 | } 383 | } 384 | break; 385 | } 386 | case 4: { 387 | for(int i = 3; i < rows; ++i) { 388 | for(int j = 3; j < cols; ++j) { 389 | if(board.idx[i][j] != -1) { 390 | continue; 391 | } 392 | int idx1 = board.idx[i - 3][j - 3]; 393 | int idx2 = board.idx[i - 2][j - 2]; 394 | int idx3 = board.idx[i - 1][j - 1]; 395 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 396 | continue; 397 | } 398 | p1.emplace_back(idx1); 399 | p2.emplace_back(idx2); 400 | p3.emplace_back(idx3); 401 | proposal.emplace_back(cv::Point2i(j, i)); 402 | } 403 | } 404 | if(!proposal.empty() || params.strict_grow) { 405 | break; 406 | } 407 | for(int i = 2; i < rows; ++i) { 408 | for(int j = 2; j < cols; ++j) { 409 | if(board.idx[i][j] != -1) { 410 | continue; 411 | } 412 | int idx2 = board.idx[i - 2][j - 2]; 413 | int idx3 = board.idx[i - 1][j - 1]; 414 | if(idx2 < 0 || idx3 < 0) { 415 | continue; 416 | } 417 | p2.emplace_back(idx2); 418 | p3.emplace_back(idx3); 419 | proposal.emplace_back(cv::Point2i(j, i)); 420 | } 421 | } 422 | break; 423 | } 424 | case 5: { 425 | for(int i = 0; i < rows - 3; ++i) { 426 | for(int j = 0; j < cols - 3; ++j) { 427 | if(board.idx[i][j] != -1) { 428 | continue; 429 | } 430 | int idx1 = board.idx[i + 3][j + 3]; 431 | int idx2 = board.idx[i + 2][j + 2]; 432 | int idx3 = board.idx[i + 1][j + 1]; 433 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 434 | continue; 435 | } 436 | p1.emplace_back(idx1); 437 | p2.emplace_back(idx2); 438 | p3.emplace_back(idx3); 439 | proposal.emplace_back(cv::Point2i(j, i)); 440 | } 441 | } 442 | if(!proposal.empty() || params.strict_grow) { 443 | break; 444 | } 445 | for(int i = 0; i < rows - 2; ++i) { 446 | for(int j = 0; j < cols - 2; ++j) { 447 | if(board.idx[i][j] != -1) { 448 | continue; 449 | } 450 | int idx2 = board.idx[i + 2][j + 2]; 451 | int idx3 = board.idx[i + 1][j + 1]; 452 | if(idx2 < 0 || idx3 < 0) { 453 | continue; 454 | } 455 | p2.emplace_back(idx2); 456 | p3.emplace_back(idx3); 457 | proposal.emplace_back(cv::Point2i(j, i)); 458 | } 459 | } 460 | break; 461 | } 462 | default: 463 | break; 464 | } 465 | 466 | // predict inside corners 467 | pred = predict_board_corners(corners, used, p1, p2, p3); 468 | board.num += proposal.size(); 469 | for(int i = 0; i < proposal.size(); ++i) { 470 | if(pred[i] == -2) { 471 | --board.num; 472 | } 473 | board.idx[proposal[i].y][proposal[i].x] = pred[i]; 474 | } 475 | } 476 | 477 | // grow inside corners 478 | if(!proposal.empty()) { 479 | return GrowType_Inside; 480 | } 481 | 482 | // add proposal top/left/bottom/right 483 | if(!add_board_boundary(board, direction)) { 484 | return GrowType_Failure; 485 | } 486 | p1.clear(); 487 | p2.clear(); 488 | p3.clear(); 489 | cols = board.idx[0].size(); 490 | rows = board.idx.size(); 491 | 492 | // grow board corners top/left/bottom/right 493 | switch(direction) { 494 | case 0: { 495 | for(int i = 0; i < cols; ++i) { 496 | int idx1 = board.idx[3][i]; 497 | int idx2 = board.idx[2][i]; 498 | int idx3 = board.idx[1][i]; 499 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 500 | continue; 501 | } 502 | p1.emplace_back(idx1); 503 | p2.emplace_back(idx2); 504 | p3.emplace_back(idx3); 505 | proposal.emplace_back(cv::Point2i(i, 0)); 506 | } 507 | break; 508 | } 509 | case 1: { 510 | for(int i = 0; i < rows; ++i) { 511 | int idx1 = board.idx[i][3]; 512 | int idx2 = board.idx[i][2]; 513 | int idx3 = board.idx[i][1]; 514 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 515 | continue; 516 | } 517 | p1.emplace_back(idx1); 518 | p2.emplace_back(idx2); 519 | p3.emplace_back(idx3); 520 | proposal.emplace_back(cv::Point2i(0, i)); 521 | } 522 | break; 523 | } 524 | case 2: { 525 | for(int i = 0; i < cols; ++i) { 526 | int idx1 = board.idx[rows - 4][i]; 527 | int idx2 = board.idx[rows - 3][i]; 528 | int idx3 = board.idx[rows - 2][i]; 529 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 530 | continue; 531 | } 532 | p1.emplace_back(idx1); 533 | p2.emplace_back(idx2); 534 | p3.emplace_back(idx3); 535 | proposal.emplace_back(cv::Point2i(i, rows - 1)); 536 | } 537 | break; 538 | } 539 | case 3: { 540 | for(int i = 0; i < rows; ++i) { 541 | int idx1 = board.idx[i][cols - 4]; 542 | int idx2 = board.idx[i][cols - 3]; 543 | int idx3 = board.idx[i][cols - 2]; 544 | if(idx1 < 0 || idx2 < 0 || idx3 < 0) { 545 | continue; 546 | } 547 | p1.emplace_back(idx1); 548 | p2.emplace_back(idx2); 549 | p3.emplace_back(idx3); 550 | proposal.emplace_back(cv::Point2i(cols - 1, i)); 551 | } 552 | break; 553 | } 554 | default: 555 | break; 556 | } 557 | 558 | // predict board corners 559 | pred = predict_board_corners(corners, used, p1, p2, p3); 560 | if(params.corner_type == SaddlePoint && !params.occlusion) { 561 | for(int i = 0; i < proposal.size(); ++i) { 562 | if(pred[i] == -2) { 563 | board.num -= proposal.size(); 564 | proposal.clear(); 565 | return GrowType_Failure; 566 | } 567 | } 568 | } 569 | board.num += proposal.size(); 570 | for(int i = 0; i < proposal.size(); ++i) { 571 | if(pred[i] == -2) { 572 | --board.num; 573 | } 574 | board.idx[proposal[i].y][proposal[i].x] = pred[i]; 575 | } 576 | 577 | if(proposal.empty()) { 578 | return GrowType_Failure; 579 | } 580 | return GrowType_Boundary; 581 | } 582 | 583 | } // namespace cbdetect 584 | -------------------------------------------------------------------------------- /src/libcbdetect/image_normalization_and_gradients.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | #include "libcbdetect/config.h" 43 | #include "libcbdetect/image_normalization_and_gradients.h" 44 | 45 | namespace cbdetect { 46 | 47 | void box_filter(const cv::Mat& img, cv::Mat& blur_img, int kernel_size_x, int kernel_size_y) { 48 | if(kernel_size_y < 0) { 49 | kernel_size_y = kernel_size_x; 50 | } 51 | blur_img.create(img.size(), CV_64F); 52 | std::vector buf(img.cols, 0); 53 | std::vector count_buf(img.cols, 0); 54 | int count = 0; 55 | for(int j = 0; j < std::min(kernel_size_y, img.rows - 1); ++j) { 56 | for(int i = 0; i < img.cols; ++i) { 57 | buf[i] += img.at(j, i); 58 | ++count_buf[i]; 59 | } 60 | } 61 | for(int j = 0; j < img.rows; ++j) { 62 | if(j > kernel_size_y) { 63 | for(int i = 0; i < img.cols; ++i) { 64 | buf[i] -= img.at(j - kernel_size_y - 1, i); 65 | --count_buf[i]; 66 | } 67 | } 68 | if(j + kernel_size_y < img.rows) { 69 | for(int i = 0; i < img.cols; ++i) { 70 | buf[i] += img.at(j + kernel_size_y, i); 71 | ++count_buf[i]; 72 | } 73 | } 74 | blur_img.at(j, 0) = 0; 75 | count = 0; 76 | for(int i = 0; i <= std::min(kernel_size_x, img.cols - 1); ++i) { 77 | blur_img.at(j, 0) += buf[i]; 78 | count += count_buf[i]; 79 | } 80 | for(int i = 1; i < img.cols; ++i) { 81 | blur_img.at(j, i) = blur_img.at(j, i - 1); 82 | blur_img.at(j, i - 1) /= count; 83 | if(i > kernel_size_x) { 84 | blur_img.at(j, i) -= buf[i - kernel_size_x - 1]; 85 | count -= count_buf[i - kernel_size_x - 1]; 86 | } 87 | if(i + kernel_size_x < img.cols) { 88 | blur_img.at(j, i) += buf[i + kernel_size_x]; 89 | count += count_buf[i + kernel_size_x]; 90 | } 91 | } 92 | blur_img.at(j, img.cols - 1) /= count; 93 | } 94 | } 95 | 96 | void image_normalization_and_gradients(cv::Mat& img, cv::Mat& img_du, cv::Mat& img_dv, 97 | cv::Mat& img_angle, cv::Mat& img_weight, const Params& params) { 98 | // normalize image 99 | if(params.norm) { 100 | cv::Mat blur_img; 101 | box_filter(img, blur_img, params.norm_half_kernel_size); 102 | img = img - blur_img; 103 | img = 2.5 * (cv::max(cv::min(img + 0.2, 0.4), 0)); 104 | } 105 | 106 | // sobel masks 107 | #if CV_VERSION_MAJOR == 4 108 | cv::Mat_ du({3, 3}, {1, 0, -1, 2, 0, -2, 1, 0, -1}); 109 | cv::Mat_ dv({3, 3}, {1, 2, 1, 0, 0, 0, -1, -2, -1}); 110 | #else 111 | double du_array[9] = {1, 0, -1, 2, 0, -2, 1, 0, -1}; 112 | double dv_array[9] = {1, 2, 1, 0, 0, 0, -1, -2, -1}; 113 | cv::Mat du(3, 3, CV_64F, du_array); 114 | cv::Mat dv(3, 3, CV_64F, dv_array); 115 | #endif 116 | 117 | // compute image derivatives (for principal axes estimation) 118 | cv::filter2D(img, img_du, -1, du, cv::Point(-1, -1), 0, cv::BORDER_REFLECT); 119 | cv::filter2D(img, img_dv, -1, dv, cv::Point(-1, -1), 0, cv::BORDER_REFLECT); 120 | img_angle.create(img.size(), img.type()); 121 | img_weight.create(img.size(), img.type()); 122 | if(!img_du.isContinuous()) { 123 | cv::Mat tmp = img_du.clone(); 124 | std::swap(tmp, img_du); 125 | } 126 | if(!img_dv.isContinuous()) { 127 | cv::Mat tmp = img_dv.clone(); 128 | std::swap(tmp, img_dv); 129 | } 130 | if(!img_angle.isContinuous()) { 131 | cv::Mat tmp = img_angle.clone(); 132 | std::swap(tmp, img_angle); 133 | } 134 | if(!img_weight.isContinuous()) { 135 | cv::Mat tmp = img_weight.clone(); 136 | std::swap(tmp, img_weight); 137 | } 138 | cv::hal::fastAtan64f((const double*)img_dv.data, (const double*)img_du.data, 139 | (double*)img_angle.data, img.rows * img.cols, false); 140 | img_angle.forEach([](double& pixel, const int* pos) -> void { 141 | pixel = pixel >= M_PI ? pixel - M_PI : pixel; 142 | }); 143 | img_weight.forEach([&img_du, &img_dv](double& pixel, const int* pos) -> void { 144 | int u = pos[1]; 145 | int v = pos[0]; 146 | pixel = std::sqrt( 147 | img_du.at(v, u) * img_du.at(v, u) + img_dv.at(v, u) * img_dv.at(v, u)); 148 | }); 149 | 150 | // scale input image 151 | double img_min = 0, img_max = 1; 152 | cv::minMaxLoc(img, &img_min, &img_max); 153 | img = (img - img_min) / (img_max - img_min); 154 | } 155 | 156 | } // namespace cbdetect 157 | -------------------------------------------------------------------------------- /src/libcbdetect/init_board.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include "libcbdetect/config.h" 40 | 41 | namespace cbdetect { 42 | 43 | int directional_neighbor(const Corner& corners, const std::vector& used, 44 | int idx, const cv::Point2d& v, double& min_dist) { 45 | std::vector dists(corners.p.size(), 1e10); 46 | 47 | // distances 48 | for(int i = 0; i < corners.p.size(); ++i) { 49 | if(used[i]) { 50 | continue; 51 | } 52 | cv::Point2d dir = corners.p[i] - corners.p[idx]; 53 | double dist_point = dir.x * v.x + dir.y * v.y; 54 | dir = dir - dist_point * v; 55 | double dist_edge = cv::norm(dir); 56 | double dist = dist_point + 5 * dist_edge; 57 | if(dist_point >= 0) { 58 | dists[i] = dist; 59 | } 60 | } 61 | 62 | // find best neighbor 63 | int neighbor_idx = std::min_element(dists.begin(), dists.end()) - dists.begin(); 64 | min_dist = dists[neighbor_idx]; 65 | return neighbor_idx; 66 | } 67 | 68 | bool init_board(const Corner& corners, std::vector& used, Board& board, int idx) { 69 | board.idx.clear(); 70 | // return if not enough corners 71 | if(corners.p.size() < 9) { 72 | return false; 73 | } 74 | 75 | // init chessboard hypothesis 76 | board.idx = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; 77 | 78 | // extract feature index and orientation (central element) 79 | const cv::Point2d& v1 = corners.v1[idx]; 80 | const cv::Point2d& v2 = corners.v3.empty() ? corners.v2[idx] : corners.v3[idx]; 81 | board.idx[1][1] = idx; 82 | used[idx] = 1; 83 | double min_dist[8]; 84 | 85 | // find left/right/top/bottom neighbors 86 | board.idx[1][0] = directional_neighbor(corners, used, idx, -v1, min_dist[0]); 87 | used[board.idx[1][0]] = 1; 88 | board.idx[1][2] = directional_neighbor(corners, used, idx, v1, min_dist[1]); 89 | used[board.idx[1][2]] = 1; 90 | board.idx[0][1] = directional_neighbor(corners, used, idx, -v2, min_dist[2]); 91 | used[board.idx[0][1]] = 1; 92 | board.idx[2][1] = directional_neighbor(corners, used, idx, v2, min_dist[3]); 93 | used[board.idx[2][1]] = 1; 94 | 95 | // find top-left/top-right/bottom-left/bottom-right neighbors 96 | int tmp1, tmp2; 97 | double d1, d2, min_dist_tmp1, min_dist_tmp2; 98 | tmp1 = directional_neighbor(corners, used, board.idx[1][0], -v2, min_dist_tmp1); 99 | tmp2 = directional_neighbor(corners, used, board.idx[0][1], -v1, min_dist_tmp2); 100 | if(tmp1 != tmp2) { 101 | d1 = std::abs(cv::norm(corners.p[tmp1] - corners.p[board.idx[1][0]]) - 102 | cv::norm(corners.p[tmp1] - corners.p[board.idx[0][1]])); 103 | d2 = std::abs(cv::norm(corners.p[tmp2] - corners.p[board.idx[1][0]]) - 104 | cv::norm(corners.p[tmp2] - corners.p[board.idx[0][1]])); 105 | if(d1 > d2) { 106 | std::swap(tmp1, tmp2); 107 | std::swap(min_dist_tmp1, min_dist_tmp2); 108 | } 109 | } 110 | board.idx[0][0] = tmp1; 111 | min_dist[4] = min_dist_tmp1; 112 | used[tmp1] = 1; 113 | 114 | tmp1 = directional_neighbor(corners, used, board.idx[1][2], -v2, min_dist_tmp1); 115 | tmp2 = directional_neighbor(corners, used, board.idx[0][1], v1, min_dist_tmp2); 116 | if(tmp1 != tmp2) { 117 | d1 = std::abs(cv::norm(corners.p[tmp1] - corners.p[board.idx[1][2]]) - 118 | cv::norm(corners.p[tmp1] - corners.p[board.idx[0][1]])); 119 | d2 = std::abs(cv::norm(corners.p[tmp2] - corners.p[board.idx[1][2]]) - 120 | cv::norm(corners.p[tmp2] - corners.p[board.idx[0][1]])); 121 | if(d1 > d2) { 122 | std::swap(tmp1, tmp2); 123 | std::swap(min_dist_tmp1, min_dist_tmp2); 124 | } 125 | } 126 | board.idx[0][2] = tmp1; 127 | min_dist[5] = min_dist_tmp1; 128 | used[tmp1] = 1; 129 | 130 | tmp1 = directional_neighbor(corners, used, board.idx[1][0], v2, min_dist_tmp1); 131 | tmp2 = directional_neighbor(corners, used, board.idx[2][1], -v1, min_dist_tmp2); 132 | if(tmp1 != tmp2) { 133 | d1 = std::abs(cv::norm(corners.p[tmp1] - corners.p[board.idx[1][0]]) - 134 | cv::norm(corners.p[tmp1] - corners.p[board.idx[2][1]])); 135 | d2 = std::abs(cv::norm(corners.p[tmp2] - corners.p[board.idx[1][0]]) - 136 | cv::norm(corners.p[tmp2] - corners.p[board.idx[2][1]])); 137 | if(d1 > d2) { 138 | std::swap(tmp1, tmp2); 139 | std::swap(min_dist_tmp1, min_dist_tmp2); 140 | } 141 | } 142 | board.idx[2][0] = tmp1; 143 | min_dist[6] = min_dist_tmp1; 144 | used[tmp1] = 1; 145 | 146 | tmp1 = directional_neighbor(corners, used, board.idx[1][2], v2, min_dist_tmp1); 147 | tmp2 = directional_neighbor(corners, used, board.idx[2][1], v1, min_dist_tmp2); 148 | if(tmp1 != tmp2) { 149 | d1 = std::abs(cv::norm(corners.p[tmp1] - corners.p[board.idx[1][2]]) - 150 | cv::norm(corners.p[tmp1] - corners.p[board.idx[2][1]])); 151 | d2 = std::abs(cv::norm(corners.p[tmp2] - corners.p[board.idx[1][2]]) - 152 | cv::norm(corners.p[tmp2] - corners.p[board.idx[2][1]])); 153 | if(d1 > d2) { 154 | std::swap(tmp1, tmp2); 155 | std::swap(min_dist_tmp1, min_dist_tmp2); 156 | } 157 | } 158 | board.idx[2][2] = tmp1; 159 | min_dist[7] = min_dist_tmp1; 160 | used[tmp1] = 1; 161 | 162 | // initialization must be homogenously distributed 163 | for(int i = 0; i < 8; ++i) { 164 | if(std::abs(min_dist[i] - 1e10) < 1) { 165 | for(int jj = 0; jj < 3; ++jj) { 166 | for(int ii = 0; ii < 3; ++ii) { 167 | used[board.idx[jj][ii]] = 0; 168 | } 169 | } 170 | board.idx.clear(); 171 | return false; 172 | } 173 | } 174 | 175 | board.num = 9; 176 | board.energy = std::move( 177 | std::vector>>(3, 178 | std::vector>(3, 179 | std::vector(3, DBL_MAX)))); 180 | return true; 181 | } 182 | 183 | } // namespace cbdetect -------------------------------------------------------------------------------- /src/libcbdetect/non_maximum_suppression.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | 41 | #include "libcbdetect/config.h" 42 | #include "libcbdetect/non_maximum_suppression.h" 43 | 44 | namespace cbdetect { 45 | 46 | void non_maximum_suppression(const cv::Mat& img, int n, double tau, int margin, Corner& corners) { 47 | cv::Mat choose_img = cv::Mat::zeros(img.size(), CV_8U); 48 | cv::parallel_for_(cv::Range(1, std::floor((img.rows - 2 * margin) / (n + 1)) + 1), [&](const cv::Range& range) -> void { 49 | for(int j = range.start * (n + 1) + margin - 1; j < range.end * (n + 1) + margin - 1; j += n + 1) { 50 | for(int i = n + margin; i < img.cols - n - margin; i += n + 1) { 51 | int maxi = i, maxj = j; 52 | double maxval = img.at(j, i); 53 | 54 | for(int j2 = j; j2 <= j + n; ++j2) { 55 | for(int i2 = i; i2 <= i + n; ++i2) { 56 | if(img.at(j2, i2) > maxval) { 57 | maxi = i2; 58 | maxj = j2; 59 | maxval = img.at(j2, i2); 60 | } 61 | } 62 | } 63 | 64 | // maximum 65 | for(int j2 = maxj - n; j2 <= std::min(maxj + n, img.rows - 1 - margin); ++j2) { 66 | for(int i2 = maxi - n; i2 <= std::min(maxi + n, img.cols - 1 - margin); ++i2) { 67 | if(img.at(j2, i2) > maxval) { 68 | goto GOTO_FAILED; 69 | } 70 | } 71 | } 72 | 73 | if(maxval > tau) { 74 | choose_img.at(maxj, maxi) = 1; 75 | } 76 | GOTO_FAILED:; 77 | } 78 | } 79 | }); 80 | 81 | for(int j = margin; j < img.rows - margin; ++j) { 82 | for(int i = margin; i < img.cols - margin; ++i) { 83 | if(choose_img.at(j, i) == 1) { 84 | corners.p.emplace_back(cv::Point2d(i, j)); 85 | corners.r.emplace_back(margin); 86 | } 87 | } 88 | } 89 | } 90 | 91 | void non_maximum_suppression_sparse(Corner& corners, int n, cv::Size img_size, const Params& params) { 92 | cv::Mat img_score = cv::Mat::zeros(img_size, CV_64F); 93 | cv::Mat used = cv::Mat::ones(img_size, CV_32S) * -1; 94 | for(int i = 0; i < corners.p.size(); ++i) { 95 | int u = std::round(corners.p[i].x); 96 | int v = std::round(corners.p[i].y); 97 | if(img_score.at(v, u) < corners.score[i]) { 98 | img_score.at(v, u) = corners.score[i]; 99 | used.at(v, u) = i; 100 | } 101 | } 102 | std::vector corners_out_p, corners_out_v1, corners_out_v2, corners_out_v3; 103 | std::vector corners_out_score; 104 | std::vector corners_out_r; 105 | bool is_monkey_saddle = params.corner_type == MonkeySaddlePoint; 106 | for(int i = 0; i < corners.p.size(); ++i) { 107 | int u = std::round(corners.p[i].x); 108 | int v = std::round(corners.p[i].y); 109 | double score = corners.score[i]; 110 | if(used.at(v, u) != i) { 111 | continue; 112 | } 113 | for(int j2 = v - n; j2 <= v + n; ++j2) { 114 | for(int i2 = u - n; i2 <= u + n; ++i2) { 115 | if(j2 < 0 || j2 >= img_size.height || i2 < 0 || i2 >= img_size.width) { 116 | continue; 117 | } 118 | if(img_score.at(j2, i2) > score && (i2 != u || j2 != v)) { 119 | goto GOTO_FAILED; 120 | } 121 | } 122 | } 123 | corners_out_p.emplace_back(corners.p[i]); 124 | corners_out_r.emplace_back(corners.r[i]); 125 | corners_out_v1.emplace_back(corners.v1[i]); 126 | corners_out_v2.emplace_back(corners.v2[i]); 127 | if(is_monkey_saddle) { 128 | corners_out_v3.emplace_back(corners.v3[i]); 129 | } 130 | corners_out_score.emplace_back(corners.score[i]); 131 | GOTO_FAILED:; 132 | } 133 | corners.p = std::move(corners_out_p); 134 | corners.r = std::move(corners_out_r); 135 | corners.v1 = std::move(corners_out_v1); 136 | corners.v2 = std::move(corners_out_v2); 137 | if(is_monkey_saddle) { 138 | corners.v3 = std::move(corners_out_v3); 139 | } 140 | corners.score = std::move(corners_out_score); 141 | } 142 | 143 | } // namespace cbdetect 144 | -------------------------------------------------------------------------------- /src/libcbdetect/plot_boards.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include 21 | 22 | #include "libcbdetect/plot_boards.h" 23 | #include "libcbdetect/config.h" 24 | 25 | namespace cbdetect { 26 | 27 | void plot_boards(const cv::Mat& img, const Corner& corners, 28 | const std::vector& boards, const Params& params) { 29 | cv::Mat img_show; 30 | if(img.channels() != 3) { 31 | #if CV_VERSION_MAJOR >= 4 32 | cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR); 33 | #else 34 | cv::cvtColor(img, img_show, CV_GRAY2BGR); 35 | #endif 36 | } else { 37 | img_show = img.clone(); 38 | } 39 | 40 | for(int n = 0; n < boards.size(); ++n) { 41 | const auto& board = boards[n]; 42 | 43 | for(int i = 1; i < board.idx.size() - 1; ++i) { 44 | for(int j = 1; j < board.idx[i].size() - 1; ++j) { 45 | if(board.idx[i][j] < 0) { 46 | continue; 47 | } 48 | // plot lines in color 49 | if(board.idx[i][j + 1] >= 0) { 50 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i][j + 1]], 51 | cv::Scalar(0, 0, 255), 3, cv::LINE_AA); 52 | } 53 | if(params.corner_type == MonkeySaddlePoint && board.idx[i + 1][j + 1] >= 0) { 54 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i + 1][j + 1]], 55 | cv::Scalar(0, 0, 255), 3, cv::LINE_AA); 56 | } 57 | if(board.idx[i + 1][j] >= 0) { 58 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i + 1][j]], 59 | cv::Scalar(0, 0, 255), 3, cv::LINE_AA); 60 | } 61 | 62 | // plot lines in white 63 | if(board.idx[i][j + 1] >= 0) { 64 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i][j + 1]], 65 | cv::Scalar(255, 255, 255), 1, cv::LINE_AA); 66 | } 67 | if(params.corner_type == MonkeySaddlePoint && board.idx[i + 1][j + 1] >= 0) { 68 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i + 1][j + 1]], 69 | cv::Scalar(255, 255, 255), 1, cv::LINE_AA); 70 | } 71 | if(board.idx[i + 1][j] >= 0) { 72 | cv::line(img_show, corners.p[board.idx[i][j]], corners.p[board.idx[i + 1][j]], 73 | cv::Scalar(255, 255, 255), 1, cv::LINE_AA); 74 | } 75 | } 76 | } 77 | 78 | // plot coordinate system 79 | for(int i = 1; i < board.idx.size() * board.idx[0].size(); ++i) { 80 | int row = i / board.idx[0].size(); 81 | int col = i % board.idx[0].size(); 82 | if(board.idx[row][col] < 0 || col == board.idx[0].size() - 1 || 83 | board.idx[row][col + 1] < 0 || board.idx[row + 1][col] < 0) { 84 | continue; 85 | } 86 | cv::line(img_show, corners.p[board.idx[row][col]], corners.p[board.idx[row][col + 1]], 87 | cv::Scalar(255, 0, 0), 3, cv::LINE_AA); 88 | cv::line(img_show, corners.p[board.idx[row][col]], corners.p[board.idx[row + 1][col]], 89 | cv::Scalar(0, 255, 0), 3, cv::LINE_AA); 90 | break; 91 | } 92 | 93 | // plot numbers 94 | cv::Point2d mean(0.0, 0.0); 95 | for(int i = 1; i < board.idx.size() - 1; ++i) { 96 | for(int j = 1; j < board.idx[i].size() - 1; ++j) { 97 | if(board.idx[i][j] < 0) { 98 | continue; 99 | } 100 | mean += corners.p[board.idx[i][j]]; 101 | } 102 | } 103 | mean /= (double)(board.num); 104 | mean.x -= 10; 105 | mean.y += 10; 106 | cv::putText(img_show, std::to_string(n), mean, 107 | cv::FONT_HERSHEY_SIMPLEX, 1.3, cv::Scalar(196, 196, 0), 2); 108 | } 109 | 110 | cv::imshow("boards_img", img_show); 111 | cv::waitKey(); 112 | } 113 | 114 | } // namespace cbdetect 115 | -------------------------------------------------------------------------------- /src/libcbdetect/plot_corners.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include "libcbdetect/config.h" 40 | #include "libcbdetect/plot_corners.h" 41 | 42 | namespace cbdetect { 43 | 44 | void plot_corners(const cv::Mat& img, const std::vector& corners, const char* str) { 45 | cv::Mat img_show; 46 | if(img.channels() != 3) { 47 | #if CV_VERSION_MAJOR >= 4 48 | cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR); 49 | #else 50 | cv::cvtColor(img, img_show, CV_GRAY2BGR); 51 | #endif 52 | } else { 53 | img_show = img.clone(); 54 | } 55 | for(int i = 0; i < corners.size(); ++i) { 56 | cv::circle(img_show, corners[i], 2, cv::Scalar(0, 0, 255), -1); 57 | } 58 | cv::imshow(str, img_show); 59 | cv::waitKey(); 60 | } 61 | 62 | void plot_corners(const cv::Mat& img, const Corner& corners) { 63 | cv::Mat img_show; 64 | if(img.channels() != 3) { 65 | #if CV_VERSION_MAJOR >= 4 66 | cv::cvtColor(img, img_show, cv::COLOR_GRAY2BGR); 67 | #else 68 | cv::cvtColor(img, img_show, CV_GRAY2BGR); 69 | #endif 70 | } else { 71 | img_show = img.clone(); 72 | } 73 | for(int i = 0; i < corners.p.size(); ++i) { 74 | cv::line(img_show, corners.p[i], corners.p[i] + 20 * corners.v1[i], cv::Scalar(255, 0, 0), 2); 75 | cv::line(img_show, corners.p[i], corners.p[i] + 20 * corners.v2[i], cv::Scalar(0, 255, 0), 2); 76 | if(!corners.v3.empty()) { 77 | cv::line(img_show, corners.p[i], corners.p[i] + 20 * corners.v3[i], cv::Scalar(0, 0, 255), 2); 78 | } 79 | cv::circle(img_show, corners.p[i], 3, cv::Scalar(0, 0, 255), -1); 80 | cv::putText(img_show, std::to_string(i), cv::Point2i(corners.p[i].x - 12, corners.p[i].y - 6), 81 | cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1); 82 | } 83 | cv::imshow("corners_img", img_show); 84 | // cv::imwrite("corners_img.png", img_show); 85 | } 86 | 87 | } // namespace cbdetect 88 | -------------------------------------------------------------------------------- /src/libcbdetect/polynomial_fit.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include "libcbdetect/config.h" 21 | #include "libcbdetect/get_image_patch.h" 22 | #include "libcbdetect/polynomial_fit.h" 23 | 24 | namespace cbdetect { 25 | 26 | void create_gaussin_filter_kernel(cv::Mat& kernel, double sigma) { 27 | int r = std::round(3 * sigma); 28 | kernel.create(2 * r + 1, 2 * r + 1, CV_64F); 29 | double sum = 0.0; 30 | for(int i = -r; i <= r; ++i) { 31 | for(int j = -r; j <= r; ++j) { 32 | kernel.at(i + r, j + r) = std::exp(-(i * i + j * j) / 2 / sigma / sigma); 33 | sum += kernel.at(i + r, j + r); 34 | } 35 | } 36 | kernel /= sum; 37 | } 38 | 39 | // paper: ROCHADE Robust Checkerboard Advanced Detection for Camera Calibration, ECCV 2014 40 | int create_cone_filter_kernel(cv::Mat& kernel, int r) { 41 | kernel.create(2 * r + 1, 2 * r + 1, CV_64F); 42 | double sum = 0.0; 43 | int nzs = 0; 44 | for(int i = -r; i <= r; ++i) { 45 | for(int j = -r; j <= r; ++j) { 46 | kernel.at(i + r, j + r) = std::max(0.0, r + 1 - std::sqrt(i * i + j * j)); 47 | sum += kernel.at(i + r, j + r); 48 | if(kernel.at(i + r, j + r) < 1e-6) { 49 | ++nzs; 50 | } 51 | } 52 | } 53 | kernel /= sum; 54 | return nzs; 55 | } 56 | 57 | void polynomial_fit_saddle(const cv::Mat& img, int r, Corner& corners) { 58 | // maximum iterations and precision 59 | int max_iteration = 5; 60 | double eps = 0.01; 61 | int width = img.cols; 62 | int height = img.rows; 63 | 64 | std::vector corners_out_p, corners_out_v1, corners_out_v2; 65 | std::vector corners_out_r; 66 | std::vector choose(corners.p.size(), 0); 67 | 68 | // cone filter 69 | cv::Mat blur_kernel, blur_img, mask; 70 | create_cone_filter_kernel(blur_kernel, r); 71 | int nzs = create_cone_filter_kernel(mask, r); 72 | cv::filter2D(img, blur_img, -1, blur_kernel, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 73 | 74 | cv::Mat A((2 * r + 1) * (2 * r + 1) - nzs, 6, CV_64F); 75 | int A_row = 0; 76 | for(int j = -r; j <= r; ++j) { 77 | for(int i = -r; i <= r; ++i) { 78 | if(mask.at(j + r, i + r) >= 1e-6) { 79 | A.at(A_row, 0) = i * i; 80 | A.at(A_row, 1) = j * j; 81 | A.at(A_row, 2) = i * j; 82 | A.at(A_row, 3) = i; 83 | A.at(A_row, 4) = j; 84 | A.at(A_row, 5) = 1; 85 | ++A_row; 86 | } 87 | } 88 | } 89 | cv::Mat invAtAAt = (A.t() * A).inv(cv::DECOMP_SVD) * A.t(); 90 | 91 | // for all corners do 92 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 93 | for(int i = range.start; i < range.end; ++i) { 94 | double u_init = corners.p[i].x; 95 | double v_init = corners.p[i].y; 96 | double u_cur = u_init, v_cur = v_init; 97 | bool is_saddle_point = true; 98 | 99 | // fit f(x, y) = k0 * x^2 + k1 * y^2 + k2 * x * y + k3 * x + k4 * y + k5 100 | // coef: [k0; k1; k2; k3; k4; k5] 101 | for(int num_it = 0; num_it < max_iteration; ++num_it) { 102 | cv::Mat k, b; 103 | if(u_cur - r < 0 || u_cur + r >= width - 1 || v_cur - r < 0 || v_cur + r >= height - 1) { 104 | is_saddle_point = false; 105 | break; 106 | } 107 | get_image_patch_with_mask(blur_img, mask, u_cur, v_cur, r, b); 108 | k = invAtAAt * b; 109 | 110 | // check if it is still a saddle point 111 | double det = 4 * k.at(0, 0) * k.at(1, 0) - k.at(2, 0) * k.at(2, 0); 112 | if(det > 0) { 113 | is_saddle_point = false; 114 | break; 115 | } 116 | 117 | // saddle point is the corner 118 | double dx = (k.at(2, 0) * k.at(4, 0) - 2 * k.at(1, 0) * k.at(3, 0)) / det; 119 | double dy = (k.at(2, 0) * k.at(3, 0) - 2 * k.at(0, 0) * k.at(4, 0)) / det; 120 | 121 | u_cur += dx; 122 | v_cur += dy; 123 | 124 | double dist = std::sqrt((u_cur - u_init) * (u_cur - u_init) + (v_cur - v_init) * (v_cur - v_init)); 125 | if(dist > r) { 126 | is_saddle_point = false; 127 | break; 128 | } 129 | if(std::sqrt(dx * dx + dy * dy) <= eps) { 130 | break; 131 | } 132 | } 133 | 134 | // add to corners 135 | if(is_saddle_point) { 136 | choose[i] = 1; 137 | corners.p[i] = cv::Point2d(u_cur, v_cur); 138 | } 139 | } 140 | }); 141 | 142 | for(int i = 0; i < corners.p.size(); ++i) { 143 | if(choose[i] == 1) { 144 | corners_out_p.emplace_back(corners.p[i]); 145 | corners_out_r.emplace_back(corners.r[i]); 146 | corners_out_v1.emplace_back(corners.v1[i]); 147 | corners_out_v2.emplace_back(corners.v2[i]); 148 | } 149 | } 150 | corners.p = std::move(corners_out_p); 151 | corners.r = std::move(corners_out_r); 152 | corners.v1 = std::move(corners_out_v1); 153 | corners.v2 = std::move(corners_out_v2); 154 | } 155 | 156 | // paper: Deltille Grids for Geometric Camera Calibration, ICCV 2017 157 | void polynomial_fit_monkey_saddle(const cv::Mat& img, int r, Corner& corners) { 158 | // maximum iterations and precision 159 | int max_iteration = 5; 160 | double eps = 0.001; 161 | int width = img.cols; 162 | int height = img.rows; 163 | 164 | std::vector corners_out_p, corners_out_v1, corners_out_v2, corners_out_v3; 165 | std::vector corners_out_r; 166 | std::vector choose(corners.p.size(), 0); 167 | 168 | // cone filter 169 | cv::Mat blur_kernel, blur_img, mask; 170 | create_cone_filter_kernel(blur_kernel, r); 171 | int nzs = create_cone_filter_kernel(mask, r); 172 | cv::filter2D(img, blur_img, -1, blur_kernel, cv::Point(-1, -1), 0, cv::BORDER_REPLICATE); 173 | 174 | cv::Mat A((2 * r + 1) * (2 * r + 1) - nzs, 10, CV_64F); 175 | int A_row = 0; 176 | for(int j = -r; j <= r; ++j) { 177 | for(int i = -r; i <= r; ++i) { 178 | if(mask.at(j + r, i + r) >= 1e-6) { 179 | A.at(A_row, 0) = i * i * i; 180 | A.at(A_row, 1) = i * i * j; 181 | A.at(A_row, 2) = i * j * j; 182 | A.at(A_row, 3) = j * j * j; 183 | A.at(A_row, 4) = i * i; 184 | A.at(A_row, 5) = i * j; 185 | A.at(A_row, 6) = j * j; 186 | A.at(A_row, 7) = i; 187 | A.at(A_row, 8) = j; 188 | A.at(A_row, 9) = 1; 189 | ++A_row; 190 | } 191 | } 192 | } 193 | cv::Mat invAtAAt = (A.t() * A).inv(cv::DECOMP_SVD) * A.t(); 194 | 195 | // for all corners do 196 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 197 | for(int i = range.start; i < range.end; ++i) { 198 | double u_init = corners.p[i].x; 199 | double v_init = corners.p[i].y; 200 | double u_cur = u_init, v_cur = v_init; 201 | bool is_monkey_saddle = true; 202 | 203 | // fit f(x, y) = k0 * x^3 + k1 * x^2*y + k2* x*y^2 + k3 * y^3 + k4 * x^2 + k5 * x*y + k6 * y^2 + k7 * x + k8 * y + k9 204 | // coef: [k0; k1; k2; k3; k4; k5; k6; k7; k8; k9] 205 | for(int num_it = 0; num_it < max_iteration; ++num_it) { 206 | cv::Mat k, b; 207 | if(u_cur - r < 0 || u_cur + r >= width - 1 || v_cur - r < 0 || v_cur + r >= height - 1) { 208 | is_monkey_saddle = false; 209 | break; 210 | } 211 | get_image_patch_with_mask(blur_img, mask, u_cur, v_cur, r, b); 212 | k = invAtAAt * b; 213 | 214 | // check if it is still a monkey saddle point 215 | double det = 3 * (k.at(0, 0) * k.at(2, 0) + k.at(1, 0) * k.at(3, 0)) - 216 | (k.at(1, 0) * k.at(1, 0) + k.at(2, 0) * k.at(2, 0)); 217 | if(det > 0) { 218 | is_monkey_saddle = false; 219 | break; 220 | } 221 | 222 | // the monkey saddle point is a degenerate critical point where all of its second derivatives are zero 223 | cv::Mat tmp_a = (cv::Mat_(3, 2) << 3.0 * k.at(0, 0), k.at(1, 0), 224 | 2.0 * k.at(1, 0), 2.0 * k.at(2, 0), 225 | k.at(2, 0), 3.0 * k.at(3, 0)); 226 | cv::Mat tmp_b = (cv::Mat_(3, 1) << -k.at(4, 0), -k.at(5, 0), -k.at(6, 0)); 227 | cv::Mat tmp_x = (tmp_a.t() * tmp_a).inv() * tmp_a.t() * tmp_b; 228 | double dx = tmp_x.at(0, 0); 229 | double dy = tmp_x.at(1, 0); 230 | 231 | u_cur += dx; 232 | v_cur += dy; 233 | 234 | double dist = std::sqrt((u_cur - u_init) * (u_cur - u_init) + (v_cur - v_init) * (v_cur - v_init)); 235 | if(dist > r) { 236 | is_monkey_saddle = false; 237 | break; 238 | } 239 | if(std::sqrt(dx * dx + dy * dy) <= eps) { 240 | break; 241 | } 242 | } 243 | 244 | // add to corners 245 | if(is_monkey_saddle) { 246 | choose[i] = 1; 247 | corners.p[i] = cv::Point2d(u_cur, v_cur); 248 | } 249 | } 250 | }); 251 | 252 | for(int i = 0; i < corners.p.size(); ++i) { 253 | if(choose[i] == 1) { 254 | corners_out_p.emplace_back(corners.p[i]); 255 | corners_out_r.emplace_back(corners.r[i]); 256 | corners_out_v1.emplace_back(corners.v1[i]); 257 | corners_out_v2.emplace_back(corners.v2[i]); 258 | corners_out_v3.emplace_back(corners.v3[i]); 259 | } 260 | } 261 | corners.p = std::move(corners_out_p); 262 | corners.r = std::move(corners_out_r); 263 | corners.v1 = std::move(corners_out_v1); 264 | corners.v2 = std::move(corners_out_v2); 265 | corners.v3 = std::move(corners_out_v3); 266 | } 267 | 268 | void polynomial_fit(const cv::Mat& img, Corner& corners, const Params& params) { 269 | if(params.corner_type == SaddlePoint) { 270 | polynomial_fit_saddle(img, params.polynomial_fit_half_kernel_size, corners); 271 | } else if(params.corner_type == MonkeySaddlePoint) { 272 | polynomial_fit_monkey_saddle(img, params.polynomial_fit_half_kernel_size, corners); 273 | } 274 | } 275 | 276 | } // namespace cbdetect 277 | -------------------------------------------------------------------------------- /src/libcbdetect/refine_corners.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | 41 | #include "libcbdetect/refine_corners.h" 42 | #include "libcbdetect/config.h" 43 | #include "libcbdetect/find_corners.h" 44 | #include "libcbdetect/find_modes_meanshift.h" 45 | #include "libcbdetect/get_image_patch.h" 46 | #include "libcbdetect/weight_mask.h" 47 | 48 | namespace cbdetect { 49 | 50 | std::vector> edge_orientations(cv::Mat& img_angle, cv::Mat& img_weight) { 51 | // number of bins (histogram parameter) 52 | int n = 32; 53 | 54 | // convert angles from normals to directions 55 | img_angle.forEach([](double& val, const int* pos) -> void { 56 | val += M_PI / 2; 57 | val = val >= M_PI ? val - M_PI : val; 58 | }); 59 | 60 | // create histogram 61 | std::vector angle_hist(n, 0); 62 | for(int i = 0; i < img_angle.cols; ++i) { 63 | for(int j = 0; j < img_angle.rows; ++j) { 64 | int bin = static_cast(std::floor(img_angle.at(j, i) / (M_PI / n))); 65 | angle_hist[bin] += img_weight.at(j, i); 66 | } 67 | } 68 | 69 | // find modes of smoothed histogram 70 | auto modes = find_modes_meanshift(angle_hist, 1.5); 71 | 72 | // if only one or no mode => return invalid corner 73 | if(modes.size() <= 1) { 74 | return std::vector>(); 75 | } 76 | 77 | // compute orientation at modes 78 | // extract 2 strongest modes and sort by angle 79 | double angle_1 = modes[0].first * M_PI / n + M_PI / n / 2; 80 | double angle_2 = modes[1].first * M_PI / n + M_PI / n / 2; 81 | if(angle_1 > angle_2) { 82 | std::swap(angle_1, angle_2); 83 | } 84 | 85 | // compute angle between modes 86 | double delta_angle = std::min(angle_2 - angle_1, angle_1 + M_PI - angle_2); 87 | 88 | // if angle too small => return invalid corner 89 | if(delta_angle <= 0.3) { 90 | return std::vector>(); 91 | } 92 | 93 | // set statistics: orientations 94 | std::vector> v(2, std::vector(2)); 95 | v[0][0] = std::cos(angle_1); 96 | v[0][1] = std::sin(angle_1); 97 | v[1][0] = std::cos(angle_2); 98 | v[1][1] = std::sin(angle_2); 99 | return v; 100 | } 101 | 102 | std::vector> edge_3_orientations(cv::Mat& img_angle, cv::Mat& img_weight) { 103 | // number of bins (histogram parameter) 104 | int n = 32; 105 | 106 | // convert angles from normals to directions 107 | img_angle.forEach([](double& val, const int* pos) -> void { 108 | val += M_PI / 2; 109 | val = val >= M_PI ? val - M_PI : val; 110 | }); 111 | 112 | // create histogram 113 | std::vector angle_hist(n, 0); 114 | for(int i = 0; i < img_angle.cols; ++i) { 115 | for(int j = 0; j < img_angle.rows; ++j) { 116 | int bin = static_cast(std::floor(img_angle.at(j, i) / (M_PI / n))); 117 | angle_hist[bin] += img_weight.at(j, i); 118 | } 119 | } 120 | 121 | // find modes of smoothed histogram 122 | auto modes = find_modes_meanshift(angle_hist, 1.5); 123 | 124 | // if only one or no mode => return invalid corner 125 | if(modes.size() <= 2) { 126 | return std::vector>(); 127 | } 128 | 129 | // compute orientation at modes 130 | // extract 2 strongest modes and sort by angle 131 | double angle_1 = modes[0].first * M_PI / n + M_PI / n / 2; 132 | double angle_2 = modes[1].first * M_PI / n + M_PI / n / 2; 133 | double angle_3 = modes[2].first * M_PI / n + M_PI / n / 2; 134 | if(angle_1 > angle_2) { 135 | std::swap(angle_1, angle_2); 136 | } 137 | if(angle_1 > angle_3) { 138 | std::swap(angle_1, angle_3); 139 | } 140 | if(angle_2 > angle_3) { 141 | std::swap(angle_2, angle_3); 142 | } 143 | 144 | // compute angle between modes 145 | double delta_angle_1 = std::min(angle_2 - angle_1, angle_1 + M_PI - angle_2); 146 | double delta_angle_2 = std::min(angle_3 - angle_2, angle_2 + M_PI - angle_3); 147 | double delta_angle_3 = std::min(angle_3 - angle_1, angle_1 + M_PI - angle_3); 148 | 149 | // if angle too small => return invalid corner 150 | if(delta_angle_1 <= 0.2 || delta_angle_2 <= 0.2 || delta_angle_3 <= 0.2) { 151 | return std::vector>(); 152 | } 153 | 154 | // set statistics: orientations 155 | std::vector> v(3, std::vector(2)); 156 | v[0][0] = std::cos(angle_1); 157 | v[0][1] = std::sin(angle_1); 158 | v[1][0] = std::cos(angle_2); 159 | v[1][1] = std::sin(angle_2); 160 | v[2][0] = std::cos(angle_3); 161 | v[2][1] = std::sin(angle_3); 162 | return v; 163 | } 164 | 165 | void refine_corners(const cv::Mat& img_du, const cv::Mat& img_dv, const cv::Mat& img_angle, const cv::Mat& img_weight, 166 | Corner& corners, const Params& params) { 167 | // maximum iterations and precision 168 | int max_iteration = 5; 169 | double eps = 0.01; 170 | bool is_monkey_saddle = params.corner_type == MonkeySaddlePoint; 171 | 172 | int width = img_du.cols, height = img_du.rows; 173 | std::vector corners_out_p, corners_out_v1, corners_out_v2, corners_out_v3; 174 | std::vector corners_out_r; 175 | std::vector choose(corners.p.size(), 0); 176 | corners.v1.resize(corners.p.size()); 177 | corners.v2.resize(corners.p.size()); 178 | if(is_monkey_saddle) { 179 | corners.v3.resize(corners.p.size()); 180 | } 181 | auto mask = weight_mask(params.radius); 182 | 183 | // for all corners do 184 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 185 | for(int i = range.start; i < range.end; ++i) { 186 | // extract current corner location 187 | int ui = std::round(corners.p[i].x); 188 | int vi = std::round(corners.p[i].y); 189 | double u_init = corners.p[i].x; 190 | double v_init = corners.p[i].y; 191 | int r = corners.r[i]; 192 | 193 | // estimate edge orientations (continue, if too close to border) 194 | if(ui - r < 0 || ui + r >= width - 1 || vi - r < 0 || vi + r >= height - 1) { 195 | continue; 196 | } 197 | cv::Mat img_angle_sub, img_weight_sub; 198 | get_image_patch(img_angle, ui, vi, r, img_angle_sub); 199 | get_image_patch(img_weight, ui, vi, r, img_weight_sub); 200 | img_weight_sub = img_weight_sub.mul(mask[r]); 201 | auto v = is_monkey_saddle ? edge_3_orientations(img_angle_sub, img_weight_sub) : edge_orientations(img_angle_sub, img_weight_sub); 202 | 203 | // continue, if invalid edge orientations 204 | if(v.empty()) { 205 | continue; 206 | } 207 | 208 | //corner orientation refinement 209 | cv::Mat A1 = cv::Mat::zeros(2, 2, CV_64F); 210 | cv::Mat A2 = cv::Mat::zeros(2, 2, CV_64F); 211 | cv::Mat A3 = cv::Mat::zeros(2, 2, CV_64F); 212 | for(int j2 = vi - r; j2 <= vi + r; ++j2) { 213 | for(int i2 = ui - r; i2 <= ui + r; ++i2) { 214 | //pixel orientation vector 215 | double o_du = img_du.at(j2, i2); 216 | double o_dv = img_dv.at(j2, i2); 217 | double o_norm = std::sqrt(o_du * o_du + o_dv * o_dv); 218 | if(o_norm < 0.1) { 219 | continue; 220 | } 221 | double o_du_norm = o_du / o_norm; 222 | double o_dv_norm = o_dv / o_norm; 223 | 224 | // robust refinement of orientation 1 225 | if(std::abs(o_du_norm * v[0][0] + o_dv_norm * v[0][1]) < 0.25) { 226 | A1.at(0, 0) += o_du * o_du; 227 | A1.at(0, 1) += o_du * o_dv; 228 | A1.at(1, 0) += o_du * o_dv; 229 | A1.at(1, 1) += o_dv * o_dv; 230 | } 231 | 232 | // robust refinement of orientation 2 233 | if(std::abs(o_du_norm * v[1][0] + o_dv_norm * v[1][1]) < 0.25) { 234 | A2.at(0, 0) += o_du * o_du; 235 | A2.at(0, 1) += o_du * o_dv; 236 | A2.at(1, 0) += o_du * o_dv; 237 | A2.at(1, 1) += o_dv * o_dv; 238 | } 239 | 240 | // robust refinement of orientation 3 241 | if(is_monkey_saddle && std::abs(o_du_norm * v[2][0] + o_dv_norm * v[2][1]) < 0.25) { 242 | A3.at(0, 0) += o_du * o_du; 243 | A3.at(0, 1) += o_du * o_dv; 244 | A3.at(1, 0) += o_du * o_dv; 245 | A3.at(1, 1) += o_dv * o_dv; 246 | } 247 | } 248 | } 249 | 250 | // set new corner orientation 251 | cv::Mat eig_tmp1, eig_tmp2; 252 | cv::eigen(A1, eig_tmp1, eig_tmp2); 253 | v[0][0] = eig_tmp2.at(1, 0); 254 | v[0][1] = eig_tmp2.at(1, 1); 255 | cv::eigen(A2, eig_tmp1, eig_tmp2); 256 | v[1][0] = eig_tmp2.at(1, 0); 257 | v[1][1] = eig_tmp2.at(1, 1); 258 | if(is_monkey_saddle) { 259 | cv::eigen(A3, eig_tmp1, eig_tmp2); 260 | v[2][0] = eig_tmp2.at(1, 0); 261 | v[2][1] = eig_tmp2.at(1, 1); 262 | } 263 | 264 | std::sort(v.begin(), v.end(), [](const auto& a1, const auto& a2) { 265 | return a1[0] * a2[1] - a1[1] * a2[0] > 0; 266 | }); 267 | v[v.size() - 1][0] = -v[v.size() - 1][0]; 268 | v[v.size() - 1][1] = -v[v.size() - 1][1]; 269 | std::sort(v.begin(), v.end(), [](const auto& a1, const auto& a2) { 270 | return a1[0] * a2[1] - a1[1] * a2[0] > 0; 271 | }); 272 | 273 | if(params.polynomial_fit) { 274 | choose[i] = 1; 275 | corners.v1[i] = cv::Point2d(v[0][0], v[0][1]); 276 | corners.v2[i] = cv::Point2d(v[1][0], v[1][1]); 277 | if(is_monkey_saddle) { 278 | corners.v3[i] = cv::Point2d(v[2][0], v[2][1]); 279 | } 280 | continue; 281 | } 282 | 283 | // corner location refinement 284 | double u_cur = u_init, v_cur = v_init, u_last = u_cur, v_last = v_cur; 285 | for(int num_it = 0; num_it < max_iteration; ++num_it) { 286 | cv::Mat G = cv::Mat::zeros(2, 2, CV_64F); 287 | cv::Mat b = cv::Mat::zeros(2, 1, CV_64F); 288 | 289 | // get subpixel gradiant 290 | cv::Mat img_du_sub, img_dv_sub; 291 | if(u_cur - r < 0 || u_cur + r >= width || v_cur - r < 0 || v_cur + r >= height) { 292 | break; 293 | } 294 | get_image_patch(img_du, u_cur, v_cur, r, img_du_sub); 295 | get_image_patch(img_dv, u_cur, v_cur, r, img_dv_sub); 296 | 297 | for(int j2 = 0; j2 < 2 * r + 1; ++j2) { 298 | for(int i2 = 0; i2 < 2 * r + 1; ++i2) { 299 | // pixel orientation vector 300 | double o_du = img_du_sub.at(j2, i2); 301 | double o_dv = img_dv_sub.at(j2, i2); 302 | double o_norm = std::sqrt(o_du * o_du + o_dv * o_dv); 303 | if(o_norm < 0.1) { 304 | continue; 305 | } 306 | double o_du_norm = o_du / o_norm; 307 | double o_dv_norm = o_dv / o_norm; 308 | 309 | // do not consider center pixel 310 | if(i2 == r && j2 == r) { 311 | continue; 312 | } 313 | 314 | // robust subpixel corner estimation 315 | // compute rel. position of pixel and distance to vectors 316 | double w_u = i2 - r - ((i2 - r) * v[0][0] + (j2 - r) * v[0][1]) * v[0][0]; 317 | double v_u = j2 - r - ((i2 - r) * v[0][0] + (j2 - r) * v[0][1]) * v[0][1]; 318 | double d1 = std::sqrt(w_u * w_u + v_u * v_u); 319 | w_u = i2 - r - ((i2 - r) * v[1][0] + (j2 - r) * v[1][1]) * v[1][0]; 320 | v_u = j2 - r - ((i2 - r) * v[1][0] + (j2 - r) * v[1][1]) * v[1][1]; 321 | double d2 = std::sqrt(w_u * w_u + v_u * v_u); 322 | 323 | // if pixel corresponds with either of the vectors / directions 324 | if((d1 < 3 && std::abs(o_du_norm * v[0][0] + o_dv_norm * v[0][1]) < 0.25) || 325 | (d2 < 3 && std::abs(o_du_norm * v[1][0] + o_dv_norm * v[1][1]) < 0.25)) { 326 | G.at(0, 0) += o_du * o_du; 327 | G.at(0, 1) += o_du * o_dv; 328 | G.at(1, 0) += o_du * o_dv; 329 | G.at(1, 1) += o_dv * o_dv; 330 | b.at(0, 0) += o_du * o_du * (i2 - r + u_cur) + o_du * o_dv * (j2 - r + v_cur); 331 | b.at(1, 0) += o_du * o_dv * (i2 - r + u_cur) + o_dv * o_dv * (j2 - r + v_cur); 332 | } 333 | 334 | if(is_monkey_saddle) { 335 | w_u = i2 - r - ((i2 - r) * v[2][0] + (j2 - r) * v[2][1]) * v[2][0]; 336 | v_u = j2 - r - ((i2 - r) * v[2][0] + (j2 - r) * v[2][1]) * v[2][1]; 337 | double d3 = std::sqrt(w_u * w_u + v_u * v_u); 338 | if(d3 < 3 && std::abs(o_du_norm * v[2][0] + o_dv_norm * v[2][1]) < 0.25) { 339 | G.at(0, 0) += o_du * o_du; 340 | G.at(0, 1) += o_du * o_dv; 341 | G.at(1, 0) += o_du * o_dv; 342 | G.at(1, 1) += o_dv * o_dv; 343 | b.at(0, 0) += o_du * o_du * (i2 - r + u_cur) + o_du * o_dv * (j2 - r + v_cur); 344 | b.at(1, 0) += o_du * o_dv * (i2 - r + u_cur) + o_dv * o_dv * (j2 - r + v_cur); 345 | } 346 | } 347 | } 348 | } 349 | 350 | // set new corner location if G has full rank 351 | cv::Mat new_pos = G.inv() * b; 352 | u_last = u_cur; 353 | v_last = v_cur; 354 | u_cur = new_pos.at(0, 0); 355 | v_cur = new_pos.at(1, 0); 356 | double dist = std::sqrt((u_cur - u_last) * (u_cur - u_last) + (v_cur - v_last) * (v_cur - v_last)); 357 | if(dist >= 3) { 358 | u_cur = u_last; 359 | v_cur = v_last; 360 | break; 361 | } 362 | if(dist <= eps) { 363 | break; 364 | } 365 | } 366 | 367 | // add to corners 368 | if(std::sqrt((u_cur - u_init) * (u_cur - u_init) + (v_cur - v_init) * (v_cur - v_init)) < std::max(r / 2, 3)) { 369 | choose[i] = 1; 370 | corners.p[i] = cv::Point2d(u_cur, v_cur); 371 | corners.v1[i] = cv::Point2d(v[0][0], v[0][1]); 372 | corners.v2[i] = cv::Point2d(v[1][0], v[1][1]); 373 | if(is_monkey_saddle) { 374 | corners.v3[i] = cv::Point2d(v[2][0], v[2][1]); 375 | } 376 | } 377 | } 378 | }); 379 | 380 | for(int i = 0; i < corners.p.size(); ++i) { 381 | if(choose[i] == 1) { 382 | corners_out_p.emplace_back(corners.p[i]); 383 | corners_out_r.emplace_back(corners.r[i]); 384 | corners_out_v1.emplace_back(corners.v1[i]); 385 | corners_out_v2.emplace_back(corners.v2[i]); 386 | if(is_monkey_saddle) { 387 | corners_out_v3.emplace_back(corners.v3[i]); 388 | } 389 | } 390 | } 391 | corners.p = std::move(corners_out_p); 392 | corners.r = std::move(corners_out_r); 393 | corners.v1 = std::move(corners_out_v1); 394 | corners.v2 = std::move(corners_out_v2); 395 | if(is_monkey_saddle) { 396 | corners.v3 = std::move(corners_out_v3); 397 | } 398 | } 399 | 400 | } // namespace cbdetect 401 | -------------------------------------------------------------------------------- /src/libcbdetect/score_corners.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | 39 | #include 40 | 41 | #include "libcbdetect/score_corners.h" 42 | #include "libcbdetect/config.h" 43 | #include "libcbdetect/create_correlation_patch.h" 44 | #include "libcbdetect/find_corners.h" 45 | #include "libcbdetect/get_image_patch.h" 46 | #include "libcbdetect/weight_mask.h" 47 | 48 | namespace cbdetect { 49 | 50 | double corner_correlation_score(const cv::Mat& img, const cv::Mat& img_weight, 51 | const cv::Point2d& v1, const cv::Point2d& v2) { 52 | // compute gradient filter kernel (bandwith = 3 px) 53 | double center = (img.cols - 1) / 2; 54 | cv::Mat img_filter = cv::Mat::ones(img.size(), CV_64F) * -1; 55 | for(int u = 0; u < img.cols; ++u) { 56 | for(int v = 0; v < img.rows; ++v) { 57 | cv::Point2d p1{u - center, v - center}; 58 | cv::Point2d p2{(p1.x * v1.x + p1.y * v1.y) * v1.x, (p1.x * v1.x + p1.y * v1.y) * v1.y}; 59 | cv::Point2d p3{(p1.x * v2.x + p1.y * v2.y) * v2.x, (p1.x * v2.x + p1.y * v2.y) * v2.y}; 60 | if(cv::norm(p1 - p2) <= 1.5 || cv::norm(p1 - p3) <= 1.5) { 61 | img_filter.at(v, u) = 1; 62 | } 63 | } 64 | } 65 | 66 | // normalize 67 | cv::Scalar mean, std; 68 | cv::meanStdDev(img_filter, mean, std); 69 | img_filter = (img_filter - mean[0]) / std[0]; 70 | cv::meanStdDev(img_weight, mean, std); 71 | cv::Mat img_weight_norm = (img_weight - mean[0]) / std[0]; 72 | 73 | // compute gradient score 74 | double score_gradient = cv::sum(img_weight_norm.mul(img_filter))[0]; 75 | score_gradient = std::max(score_gradient / (img.cols * img.rows - 1), 0.); 76 | 77 | // create intensity filter kernel 78 | std::vector template_kernel(4); // a1, a2, b1, b2 79 | create_correlation_patch(template_kernel, std::atan2(v1.y, v1.x), std::atan2(v2.y, v2.x), (img.cols - 1) / 2); 80 | 81 | // checkerboard responses 82 | double a1 = cv::sum(img.mul(template_kernel[0]))[0]; 83 | double a2 = cv::sum(img.mul(template_kernel[1]))[0]; 84 | double b1 = cv::sum(img.mul(template_kernel[2]))[0]; 85 | double b2 = cv::sum(img.mul(template_kernel[3]))[0]; 86 | 87 | // mean 88 | double mu = (a1 + a2 + b1 + b2) / 4; 89 | 90 | // case 1: a=white, b=black 91 | double s1 = std::min(std::min(a1, a2) - mu, mu - std::min(b1, b2)); 92 | 93 | // case 2: b=white, a=black 94 | double s2 = std::min(mu - std::min(a1, a2), std::min(b1, b2) - mu); 95 | 96 | // intensity score: max. of the 2 cases 97 | double score_intensity = std::max(std::max(s1, s2), 0.); 98 | 99 | // final score: product of gradient and intensity score 100 | return score_gradient * score_intensity; 101 | } 102 | 103 | double corner_correlation_score(const cv::Mat& img, const cv::Mat& img_weight, 104 | const cv::Point2d& v1, const cv::Point2d& v2, const cv::Point2d& v3) { 105 | // compute gradient filter kernel (bandwith = 3 px) 106 | double center = (img.cols - 1) / 2; 107 | cv::Mat img_filter = cv::Mat::ones(img.size(), CV_64F) * -1; 108 | for(int u = 0; u < img.cols; ++u) { 109 | for(int v = 0; v < img.rows; ++v) { 110 | cv::Point2d p1{u - center, v - center}; 111 | cv::Point2d p2{(p1.x * v1.x + p1.y * v1.y) * v1.x, (p1.x * v1.x + p1.y * v1.y) * v1.y}; 112 | cv::Point2d p3{(p1.x * v2.x + p1.y * v2.y) * v2.x, (p1.x * v2.x + p1.y * v2.y) * v2.y}; 113 | cv::Point2d p4{(p1.x * v3.x + p1.y * v3.y) * v3.x, (p1.x * v3.x + p1.y * v3.y) * v3.y}; 114 | if(cv::norm(p1 - p2) <= 1.5 || cv::norm(p1 - p3) <= 1.5 || cv::norm(p1 - p4) <= 1.5) { 115 | img_filter.at(v, u) = 1; 116 | } 117 | } 118 | } 119 | 120 | // normalize 121 | cv::Scalar mean, std; 122 | cv::meanStdDev(img_filter, mean, std); 123 | img_filter = (img_filter - mean[0]) / std[0]; 124 | cv::meanStdDev(img_weight, mean, std); 125 | cv::Mat img_weight_norm = (img_weight - mean[0]) / std[0]; 126 | 127 | // compute gradient score 128 | double score_gradient = cv::sum(img_weight_norm.mul(img_filter))[0]; 129 | score_gradient = std::max(score_gradient / (img.cols * img.rows - 1), 0.); 130 | 131 | // create intensity filter kernel 132 | std::vector template_kernel(6); // a1, a2, a3, b1, b2, b3 133 | create_correlation_patch(template_kernel, 134 | std::atan2(v1.y, v1.x), std::atan2(v2.y, v2.x), std::atan2(v3.y, v3.x), (img.cols - 1) / 2); 135 | 136 | // checkerboard responses 137 | double a1 = cv::sum(img.mul(template_kernel[0]))[0]; 138 | double a2 = cv::sum(img.mul(template_kernel[1]))[0]; 139 | double a3 = cv::sum(img.mul(template_kernel[2]))[0]; 140 | double b1 = cv::sum(img.mul(template_kernel[3]))[0]; 141 | double b2 = cv::sum(img.mul(template_kernel[4]))[0]; 142 | double b3 = cv::sum(img.mul(template_kernel[5]))[0]; 143 | 144 | // mean 145 | double mu = (a1 + a2 + a3 + b1 + b2 + b3) / 6; 146 | double min_a = std::min(std::min(a1, a2), a3); 147 | double min_b = std::min(std::min(b1, b2), b3); 148 | 149 | // case 1: a=white, b=black 150 | double s1 = std::min(min_a - mu, mu - min_b); 151 | 152 | // case 2: b=white, a=black 153 | double s2 = std::min(mu - min_a, min_b - mu); 154 | 155 | // intensity score: max. of the 2 cases 156 | double score_intensity = std::max(std::max(s1, s2), 0.); 157 | 158 | // final score: product of gradient and intensity score 159 | return score_gradient * score_intensity; 160 | } 161 | 162 | void sorce_corners(const cv::Mat& img, const cv::Mat& img_weight, Corner& corners, const Params& params) { 163 | corners.score.resize(corners.p.size()); 164 | int width = img.cols, height = img.rows; 165 | auto mask = weight_mask(params.radius); 166 | 167 | // for all corners do 168 | cv::parallel_for_(cv::Range(0, corners.p.size()), [&](const cv::Range& range) -> void { 169 | for(int i = range.start; i < range.end; ++i) { 170 | // corner location 171 | double u = corners.p[i].x; 172 | double v = corners.p[i].y; 173 | int r = corners.r[i]; 174 | 175 | if(u - r < 0 || u + r >= width - 1 || v - r < 0 || v + r >= height - 1) { 176 | corners.score[i] = 0.; 177 | continue; 178 | } 179 | cv::Mat img_sub, img_weight_sub; 180 | get_image_patch(img, u, v, r, img_sub); 181 | get_image_patch(img_weight, u, v, r, img_weight_sub); 182 | img_weight_sub = img_weight_sub.mul(mask[r]); 183 | if(params.corner_type == SaddlePoint) { 184 | corners.score[i] = corner_correlation_score(img_sub, img_weight_sub, corners.v1[i], corners.v2[i]); 185 | } else if(params.corner_type == MonkeySaddlePoint) { 186 | corners.score[i] = 187 | corner_correlation_score(img_sub, img_weight_sub, corners.v1[i], corners.v2[i], corners.v3[i]); 188 | } 189 | } 190 | }); 191 | } 192 | 193 | void remove_low_scoring_corners(double tau, Corner& corners, const Params& params) { 194 | std::vector corners_out_p, corners_out_v1, corners_out_v2, corners_out_v3; 195 | std::vector corners_out_score; 196 | std::vector corners_out_r; 197 | bool is_monkey_saddle = params.corner_type == MonkeySaddlePoint; 198 | for(int i = 0; i < corners.p.size(); ++i) { 199 | if(corners.score[i] > tau) { 200 | corners_out_p.emplace_back(corners.p[i]); 201 | corners_out_r.emplace_back(corners.r[i]); 202 | corners_out_v1.emplace_back(corners.v1[i]); 203 | corners_out_v2.emplace_back(corners.v2[i]); 204 | if(is_monkey_saddle) { 205 | corners_out_v3.emplace_back(corners.v3[i]); 206 | } 207 | corners_out_score.emplace_back(corners.score[i]); 208 | } 209 | } 210 | corners.p = std::move(corners_out_p); 211 | corners.r = std::move(corners_out_r); 212 | corners.v1 = std::move(corners_out_v1); 213 | corners.v2 = std::move(corners_out_v2); 214 | if(is_monkey_saddle) { 215 | corners.v3 = std::move(corners_out_v3); 216 | } 217 | corners.score = std::move(corners_out_score); 218 | } 219 | 220 | } // namespace cbdetect 221 | -------------------------------------------------------------------------------- /src/libcbdetect/weight_mask.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018, ftdlyc 3 | * 4 | * This library is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU Lesser General Public 6 | * License as published by the Free Software Foundation; either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | * Lesser General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU Lesser General Public 15 | * License along with this library; if not, see . 16 | */ 17 | 18 | /* 19 | % Copyright 2012. All rights reserved. 20 | % Author: Andreas Geiger 21 | % Institute of Measurement and Control Systems (MRT) 22 | % Karlsruhe Institute of Technology (KIT), Germany 23 | 24 | % This is free software; you can redistribute it and/or modify it under the 25 | % terms of the GNU General Public License as published by the Free Software 26 | % Foundation; either version 3 of the License, or any later version. 27 | 28 | % This software is distributed in the hope that it will be useful, but WITHOUT ANY 29 | % WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 30 | % PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | % You should have received a copy of the GNU General Public License along with 33 | % this software; if not, write to the Free Software Foundation, Inc., 51 Franklin 34 | % Street, Fifth Floor, Boston, MA 02110-1301, USA 35 | */ 36 | 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | #include "libcbdetect/config.h" 43 | #include "libcbdetect/weight_mask.h" 44 | 45 | namespace cbdetect { 46 | 47 | std::unordered_map weight_mask(const std::vector& radius) { 48 | std::unordered_map mask; 49 | for(const auto& r : radius) { 50 | mask[r] = cv::Mat::zeros(r * 2 + 1, r * 2 + 1, CV_64F); 51 | cv::Mat& mat = mask[r]; 52 | for(int v = 0; v < r * 2 + 1; ++v) { 53 | for(int u = 0; u < r * 2 + 1; ++u) { 54 | double dist = std::sqrt((u - r) * (u - r) + (v - r) * (v - r)) / r; 55 | dist = std::min(std::max(dist, 0.7), 1.3); 56 | mat.at(v, u) = (1.3 - dist) / 0.6; 57 | } 58 | } 59 | } 60 | return mask; 61 | }; 62 | 63 | } // namespace cbdetect --------------------------------------------------------------------------------