├── .gitignore ├── LICENSE ├── README.txt ├── data ├── 000001.png ├── 000011.png ├── 000046.png ├── 000056.png ├── climb1.png ├── climb2.png ├── dino1.jpg ├── dino2.jpg ├── liberty1.png └── liberty2.png ├── deepMatching.pro ├── deepMatching.pro.user ├── deepmatching-static └── src ├── COPYING ├── array_types.h ├── conv.cpp ├── conv.h ├── deep_matching.cpp ├── deep_matching.h ├── deepmatching.i ├── deepmatching.m ├── deepmatching.py ├── deepmatching_matlab.cpp ├── deepmatching_wrap.c ├── deepmtchinfo.cpp ├── deepmtchinfo.h ├── hog.cpp ├── hog.h ├── image.cpp ├── image.h ├── io.cpp ├── io.h ├── main.cpp ├── main.h ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── maxfilter.cpp ├── maxfilter.h ├── pixel_desc.cpp ├── pixel_desc.h ├── rescore.py ├── runDeepMatching.m ├── std.cpp ├── std.h └── viz.py /.gitignore: -------------------------------------------------------------------------------- 1 | ### Globally Useful gitignores 2 | # 3 | #This directory contains globally useful gitignores, 4 | #e.g. OS-specific and editor specific. 5 | # 6 | #For more on global gitignores: 7 | # 8 | # 9 | #And a good blog post about 'em: 10 | # 11 | #.redcar 12 | # It's better to unpack these files and commit the raw source because 13 | 14 | # git has its own built in compression methods. 15 | *.7z 16 | *.jar 17 | *.rar 18 | *.zip 19 | *.gz 20 | *.bzip 21 | *.bz2 22 | *.xz 23 | *.lzma 24 | *.cab 25 | 26 | #package management formats 27 | *.dmg 28 | *.xpi 29 | *.gem 30 | *.egg 31 | *.deb 32 | *.rpm 33 | *.msi 34 | *.msm 35 | *.msp 36 | 37 | # -*- mode: gitignore; -*- 38 | *~ 39 | \#*\# 40 | /.emacs.desktop 41 | /.emacs.desktop.lock 42 | *.elc 43 | auto-save-list 44 | tramp 45 | .\#* 46 | 47 | ## Delete the backups, we have GIT for that !! 48 | # Windows default autosave extension 49 | *.asv 50 | 51 | # Notepad++ backups # 52 | *.bak 53 | .DS_Store 54 | .AppleDouble 55 | .LSOverride 56 | # C++ objects and libs 57 | 58 | *.slo 59 | *.lo 60 | *.o 61 | *.a 62 | *.la 63 | *.lai 64 | *.so 65 | *.dll 66 | *.dylib 67 | # Qt-es 68 | 69 | /.qmake.cache 70 | /.qmake.stash 71 | *.pro.user 72 | *.pro.user.* 73 | *.qbs.user 74 | *.qbs.user.* 75 | *.moc 76 | moc_*.cpp 77 | qrc_*.cpp 78 | ui_*.h 79 | Makefile* 80 | *-build-* 81 | # Thumbnails 82 | ._* 83 | # Ignore tags created by etags, ctags, gtags (GNU global) and cscope 84 | TAGS 85 | !TAGS/ 86 | tags 87 | !tags/ 88 | gtags.files 89 | GTAGS 90 | GRTAGS 91 | GPATH 92 | cscope.files 93 | cscope.out 94 | cscope.in.out 95 | cscope.po.out 96 | 97 | *.tmproj 98 | *.tmproject 99 | tmtags 100 | .vagrant/ 101 | [._]*.s[a-w][a-z] 102 | [._]s[a-w][a-z] 103 | *.un~ 104 | Session.vim 105 | .netrwhist 106 | *~ 107 | 108 | # QtCreator 109 | 110 | *.autosave 111 | 112 | #QtCtreator Qml 113 | *.qmlproject.user 114 | *.qmlproject.user.* 115 | 116 | # Compiled Object files 117 | *.slo 118 | *.lo 119 | *.o 120 | *.obj 121 | 122 | # Precompiled Headers 123 | *.gch 124 | *.pch 125 | 126 | # Compiled Dynamic libraries 127 | *.so 128 | *.dylib 129 | *.dll 130 | 131 | # Fortran module files 132 | *.mod 133 | 134 | # Compiled Static libraries 135 | *.lai 136 | *.la 137 | *.a 138 | *.lib 139 | 140 | # Executables 141 | *.exe 142 | *.out 143 | *.app 144 | 145 | #Backup file 146 | *~ 147 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CANSEN JIANG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Implementation of the Deep Matching algorithm, published at ICCV 2013 in 2 | "DeepFlow: Large displacement optical flow with deep matching" by Philippe 3 | Weinzaepfel, Jerome Revaud, Zaid Harchaoui and Cordelia Schmid. 4 | Code and idea by Jerome Revaud, INRIA. The code is only for scientific 5 | or personnal use. Please contact me/INRIA for commercial use. 6 | Email: jerome.revaud@inria.fr 7 | 8 | Copyright (C) 2015 Jerome Revaud 9 | 10 | Version 1.2.2 11 | 12 | License: 13 | 14 | This program is free software: you can redistribute it and/or modify 15 | it under the terms of the GNU General Public License as published by 16 | the Free Software Foundation, either version 3 of the License, or 17 | (at your option) any later version. 18 | 19 | This program is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | GNU General Public License for more details. 23 | 24 | You should have received a copy of the GNU General Public License 25 | along with this program. If not, see 26 | 27 | 28 | Installation: 29 | 30 | make clean all 31 | 32 | This program has been built on a fedora18 x64 machine and tested on Mac OS X. 33 | *No assistance* will be given to compile the code on other OS. However, if 34 | you are able to sucessfully adapt the code for other platforms (Windows), 35 | please notify me so that I can release these versions on the webpage: 36 | 37 | http://lear.inrialpes.fr/src/deepmatching/ 38 | 39 | 40 | Matlab wrapper: 41 | [Prerequisite: to have compiled the executable, see above.] 42 | 43 | 1) Launch matlab by preloading the same 'libatlas' than the one used to compile ./deepmatching: 44 | LD_PRELOAD=/usr/lib64/atlas/libtatlas.so.3 matlab 45 | 46 | 2) Compile the MEX file: 47 | mex deepmatching_matlab.cpp deep_matching.o conv.o hog.o image.o io.o main.o maxfilter.o pixel_desc.o -output deepmatching '-DUSEOMP' CFLAGS="-fPIC -Wall -g -std=c++11 -O3 -fopenmp" LDFLAGS="-fopenmp" -lpng -ljpeg -lm /usr/lib64/atlas/libsatlas.so 48 | 49 | 3) Try executing the code: 50 | >> help deepmatching 51 | >> deepmatching() % show some help about options 52 | >> img1 = single(imread('liberty1.png')); 53 | >> img2 = single(imread('liberty2.png')); 54 | >> matches = deepmatching( img1, img2, '-downscale 2 -v' ); 55 | >> matches % print matches, should be as the listing shown below 56 | 57 | Python wrapper: 58 | 1) Compile the python module: 59 | make python 60 | 61 | 2) Try executing the code: 62 | >> import deepmatching as dm 63 | >> help(dm.deepmatching) 64 | >> dm.deepmatching() # show some help about options 65 | >> from PIL import Image 66 | >> import numpy as np 67 | >> img1 = np.array(Image.open('liberty1.png')) 68 | >> img2 = np.array(Image.open('liberty2.png')) 69 | >> matches = dm.deepmatching( img1, img2, '-downscale 2 -v' ) 70 | >> matches % print matches, should be as the listing shown below 71 | 72 | 73 | 74 | 75 | 76 | 77 | Example usages and explanations: 78 | 79 | To get detailed information on parameters: 80 | ./deepmatching -h 81 | ./deepmatching --help 82 | 83 | 84 | * Build verification: 85 | ./deepmatching liberty1.png liberty2.png -downscale 2 -v 86 | 87 | should produce the following output: 88 | layer 0, patch_size = 16x16 89 | remaining 16 big cells (actually, 16 are unique) 90 | layer 1, patch_size = 32x32 91 | remaining 25 big cells (actually, 25 are unique) 92 | layer 2, patch_size = 64x64 93 | remaining 25 big cells (actually, 25 are unique) 94 | found 625 local matches 95 | gathering correspondences 96%... 96 | 8 8 0 12 2.6554 10 97 | 8 40 4 48 2.65679 11 98 | 8 24 8 32 2.5486 11 99 | 40 40 40 32 2.64178 0 100 | 40 56 44 52 2.58631 0 101 | 40 24 40 12 2.65065 0 102 | 56 40 56 28 2.64225 0 103 | 56 24 56 12 2.68497 0 104 | 24 40 24 32 2.62045 3 105 | 24 56 28 60 2.5849 12 106 | 107 | * To visualize the output correspondences: 108 | Use the "viz.py" python script provided. 109 | ./deepmatching climb1.png climb2.png -nt 0 | python viz.py climb1.png climb2.png 110 | 111 | * To restrict matching to local neighborhood: 112 | The "-ngh_rad " option restricts the matching to a radius of pixels. 113 | It uses less memory and is faster. For instance, This should produce about 114 | the same output as before but consumes 2 times less memory and cpu: 115 | 116 | ./deepmatching climb1.png climb2.png -nt 0 -ngh_rad 192 | python viz.py climb1.png climb2.png 117 | 118 | * To rescore matches prior to calling deepflow / epicflow: 119 | simply pipe the output correspondences in 'rescore.py' 120 | ./deepmatching img1 img2 [args] | python rescore.py img1 img2 121 | 122 | 123 | * Scale and invariant version: (see the --help) 124 | ./deepmatching dino1.jpg dino2.jpg -nt 0 -downscale 1 -max_scale 2 -rot_range -45 +45 -v | python viz.py dino1.jpg dino2.jpg 125 | 126 | param -max_scale: maximum scale factor (here x2, default = x5) 127 | param -rot_range: rotation range in degrees (default = from 0 to 360) 128 | 129 | 130 | For details about the options, please refer to the help, the papers or the code. 131 | 132 | 133 | Important tip: 134 | If the program stops with "segmentation fault", then it means that your machine 135 | does not have enough memory. In this case, you should consider increasing the 136 | "-downscale" parameter. 137 | 138 | 139 | Version history: 140 | 141 | version 1.0.2: 142 | Many thanks to Bowen Zhang from Tongji University for reporting an issue with the makefile 143 | 144 | version 1.1: 145 | - New mode added for "fully scale & rotation invariant DeepMatching". 146 | - Improved visualisation (viz.py) 147 | - Removed useless/suboptimal options (-iccv_settings) 148 | - Fixed a bug related to memory allocation for large images 149 | 150 | version 1.2: 151 | - Added a new option "-ngh_rad" to restrict the matching to a local neighborhood, which allows 152 | much reduced memory usage and computations. 153 | - static-compiled version is now fully multhi-threaded with BLAS 154 | - few minor bugfix, code cleaning and updates. 155 | 156 | version 1.2.1: 157 | - Now performing the maxpooling and subsampling steps jointly, 158 | which results in 2/3 of memory usage compared to before. Also, it is now a bit faster. 159 | - Removed some useless/confusing options in the executable. 160 | 161 | version 1.2.2: 162 | - Now include a Matlab and a Python wrapper! 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /data/000001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/000001.png -------------------------------------------------------------------------------- /data/000011.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/000011.png -------------------------------------------------------------------------------- /data/000046.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/000046.png -------------------------------------------------------------------------------- /data/000056.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/000056.png -------------------------------------------------------------------------------- /data/climb1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/climb1.png -------------------------------------------------------------------------------- /data/climb2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/climb2.png -------------------------------------------------------------------------------- /data/dino1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/dino1.jpg -------------------------------------------------------------------------------- /data/dino2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/dino2.jpg -------------------------------------------------------------------------------- /data/liberty1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/liberty1.png -------------------------------------------------------------------------------- /data/liberty2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/data/liberty2.png -------------------------------------------------------------------------------- /deepMatching.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2015-02-02T15:14:11 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | QT += gui 9 | 10 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 11 | QT += widgets 12 | TEMPLATE = app 13 | 14 | TARGET = softwareName 15 | 16 | CONFIG += console 17 | CONFIG -= app_bundle 18 | CONFIG += debug 19 | 20 | PKGCONFIG += opencv-2.4.10 21 | PKGCONFIG += libfreenect 22 | PKGCONFIG += libjpeg 23 | PKGCONFIG += libpng 24 | 25 | 26 | # Enable c++ 11 standard Qt5 27 | #CONFIG += c++11 28 | #CONFIG += gnu++11 29 | # Enable c++ 11 standard Qt 4.8 30 | QMAKE_CXXFLAGS += -std=c++11 31 | #QMAKE_CXXFLAGS += -gnu++11 32 | 33 | TEMPLATE = app 34 | 35 | # add the source files 36 | SOURCES += \ 37 | src/main.cpp \ 38 | src/deep_matching.cpp \ 39 | src/std.cpp \ 40 | src/io.cpp \ 41 | src/pixel_desc.cpp \ 42 | src/maxfilter.cpp \ 43 | src/hog.cpp \ 44 | src/image.cpp \ 45 | src/conv.cpp \ 46 | src/mainwindow.cpp \ 47 | src/deepmtchinfo.cpp 48 | 49 | # add the headers 50 | HEADERS += \ 51 | src/std.h \ 52 | src/array_types.h \ 53 | src/conv.h \ 54 | src/maxfilter.h \ 55 | src/image.h \ 56 | src/pixel_desc.h \ 57 | src/deep_matching.h \ 58 | src/io.h \ 59 | src/hog.h \ 60 | src/mainwindow.h \ 61 | src/deepmtchinfo.h 62 | 63 | FORMS += \ 64 | src/mainwindow.ui 65 | 66 | # add GUI 67 | # FORMS += src/PclViewer.ui 68 | 69 | ## add ROS opencv libraries 70 | #LIBS += -L/opt/ros/groovy/lib \ 71 | # -lopencv_calib3d \ 72 | # -lopencv_contrib \ 73 | # -lopencv_core \ 74 | # -lopencv_features2d \ 75 | # -lopencv_flann \ 76 | # -lopencv_gpu \ 77 | # -lopencv_highgui \ 78 | # -lopencv_imgproc \ 79 | # -lopencv_legacy \ 80 | # -lopencv_ml \ 81 | # -lopencv_nonfree \ 82 | # -lopencv_objdetect \ 83 | # -lopencv_photo \ 84 | # -lopencv_stitching \ 85 | # -lopencv_ts \ 86 | # -lopencv_video \ 87 | # -lopencv_videostab 88 | 89 | ## add 2.4.10 opencv libs 90 | #LIBS += -L/usr/local/lib \ 91 | # -lopencv_video \ 92 | # -lopencv_core \ 93 | # -lopencv_highgui \ 94 | # -lopencv_imgproc 95 | 96 | ## include opencv path 97 | #INCLUDEPATH += /usr/local/include/opencv-2.4.10/opencv \ 98 | # /usr/local/include/opencv-2.4.10/opencv2 \ 99 | # /usr/local/include/opencv-2.4.10 100 | 101 | ## include VTK Pcl Eigen Boost Ni path 102 | #INCLUDEPATH += /usr/include/vtk-5.8 \ 103 | # /usr/local/include/pcl-1.8 \ 104 | # /home/jiang/CvLibs/eigen3.2.3 \ 105 | # /usr/include/boost \ 106 | # /usr/include/ni 107 | 108 | ## add Pcl libs 109 | #LIBS += -L/usr/local/lib \ 110 | # -lpcl_io \ 111 | # -lpcl_visualization \ 112 | # -lpcl_common \ 113 | # -lpcl_filters \ 114 | # -lpcl_octree \ 115 | # -lpcl_kdtree \ 116 | # -lpcl_registration \ 117 | # -lpcl_features \ 118 | # -lpcl_keypoints \ 119 | # -lpcl_recognition \ 120 | # -lpcl_sample_consensus \ 121 | # -lpcl_search \ 122 | # -lpcl_surface \ 123 | # -lpcl_ml \ 124 | # -lpcl_outofcore \ 125 | # -lpcl_people \ 126 | # -lpcl_segmentation \ 127 | # -lpcl_tracking \ 128 | # -lpcl_stereo 129 | 130 | ## add Boost libs 131 | #LIBS += -L/usr/lib \ 132 | # -lboost_filesystem \ 133 | # -lboost_thread \ 134 | # -lboost_system 135 | 136 | ## add VTK libs 137 | #LIBS += -L/usr/lib \ 138 | # -lQVTK \ 139 | # -lvtkRendering \ 140 | # -lvtkIO \ 141 | # -lvtkCommon \ 142 | # -lvtkWidgets \ 143 | # -lvtkFiltering \ 144 | # -lvtkGraphics 145 | 146 | # add jpeg library 147 | INCLUDEPATH += /usr/include \ 148 | /usr/lib/openmpi/include/openmpi 149 | 150 | LIBS += -L/usr/lib/x86_64-linux-gnu \ 151 | -ljpeg \ 152 | -lpng \ 153 | -lblas 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /deepMatching.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ProjectExplorer.Project.ActiveTarget 7 | 0 8 | 9 | 10 | ProjectExplorer.Project.EditorSettings 11 | 12 | true 13 | false 14 | true 15 | 16 | Cpp 17 | 18 | CppGlobal 19 | 20 | 21 | 22 | QmlJS 23 | 24 | QmlJSGlobal 25 | 26 | 27 | 2 28 | UTF-8 29 | false 30 | 4 31 | false 32 | true 33 | 1 34 | true 35 | 0 36 | true 37 | 0 38 | 8 39 | true 40 | 1 41 | true 42 | true 43 | true 44 | false 45 | 46 | 47 | 48 | ProjectExplorer.Project.PluginSettings 49 | 50 | 51 | 52 | ProjectExplorer.Project.Target.0 53 | 54 | Qt4 55 | Qt4 56 | {2f4d092c-fac6-4812-9df4-d5704272a46f} 57 | 1 58 | 0 59 | 0 60 | 61 | /home/jiang/CvTools/build-deepMatching-Qt4-Debug 62 | 63 | 64 | true 65 | qmake 66 | 67 | QtProjectManager.QMakeBuildStep 68 | false 69 | true 70 | 71 | false 72 | 73 | 74 | true 75 | Make 76 | 77 | Qt4ProjectManager.MakeStep 78 | 79 | -w 80 | -r 81 | 82 | false 83 | 84 | 85 | 86 | 2 87 | Build 88 | 89 | ProjectExplorer.BuildSteps.Build 90 | 91 | 92 | 93 | true 94 | Make 95 | 96 | Qt4ProjectManager.MakeStep 97 | 98 | -w 99 | -r 100 | 101 | true 102 | clean 103 | 104 | 105 | 1 106 | Clean 107 | 108 | ProjectExplorer.BuildSteps.Clean 109 | 110 | 2 111 | false 112 | 113 | Debug 114 | 115 | Qt4ProjectManager.Qt4BuildConfiguration 116 | 2 117 | true 118 | 119 | 120 | /home/jiang/CvTools/build-deepMatching-Qt4-Release 121 | 122 | 123 | true 124 | qmake 125 | 126 | QtProjectManager.QMakeBuildStep 127 | false 128 | true 129 | 130 | false 131 | 132 | 133 | true 134 | Make 135 | 136 | Qt4ProjectManager.MakeStep 137 | 138 | -w 139 | -r 140 | 141 | false 142 | 143 | 144 | 145 | 2 146 | Build 147 | 148 | ProjectExplorer.BuildSteps.Build 149 | 150 | 151 | 152 | true 153 | Make 154 | 155 | Qt4ProjectManager.MakeStep 156 | 157 | -w 158 | -r 159 | 160 | true 161 | clean 162 | 163 | 164 | 1 165 | Clean 166 | 167 | ProjectExplorer.BuildSteps.Clean 168 | 169 | 2 170 | false 171 | 172 | Release 173 | 174 | Qt4ProjectManager.Qt4BuildConfiguration 175 | 0 176 | true 177 | 178 | 2 179 | 180 | 181 | 0 182 | Deploy 183 | 184 | ProjectExplorer.BuildSteps.Deploy 185 | 186 | 1 187 | Deploy locally 188 | 189 | ProjectExplorer.DefaultDeployConfiguration 190 | 191 | 1 192 | 193 | 194 | 195 | false 196 | false 197 | false 198 | false 199 | true 200 | 0.01 201 | 10 202 | true 203 | 1 204 | 25 205 | 206 | 1 207 | true 208 | false 209 | true 210 | valgrind 211 | 212 | 0 213 | 1 214 | 2 215 | 3 216 | 4 217 | 5 218 | 6 219 | 7 220 | 8 221 | 9 222 | 10 223 | 11 224 | 12 225 | 13 226 | 14 227 | 228 | 2 229 | 230 | deepMatching 231 | 232 | Qt4ProjectManager.Qt4RunConfiguration:/home/jiang/CvTools/deep_Matching_GUI/deepMatching.pro 233 | 234 | deepMatching.pro 235 | false 236 | true 237 | 238 | 3768 239 | true 240 | false 241 | false 242 | false 243 | true 244 | 245 | 1 246 | 247 | 248 | 249 | ProjectExplorer.Project.TargetCount 250 | 1 251 | 252 | 253 | ProjectExplorer.Project.Updater.EnvironmentId 254 | {76727a67-93b2-4850-9de4-dca017a0ba6a} 255 | 256 | 257 | ProjectExplorer.Project.Updater.FileVersion 258 | 15 259 | 260 | 261 | -------------------------------------------------------------------------------- /deepmatching-static: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CansenJIANG/deepMatchingGUI/1d833f2cc4eee965a93ff65529fe0693cd39c807/deepmatching-static -------------------------------------------------------------------------------- /src/array_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___ARRAY_TYPES_H___ 18 | #define ___ARRAY_TYPES_H___ 19 | 20 | typedef unsigned char UBYTE; 21 | typedef unsigned int UINT; 22 | 23 | 24 | /************************ 25 | * 1D Array 26 | 27 | Equivalences: 28 | 29 | C/Python/numpy: array.shape = (tx,) 30 | array[x] := array->pixels[x] 31 | 32 | Matlab/Fortran: [1, tx] = size(array) 33 | array(x, 1) := array->pixels[x-1] 34 | */ 35 | 36 | #define DEFINE_ARRAY(type) \ 37 | typedef struct { \ 38 | type* pixels; \ 39 | int tx; \ 40 | } type##_array; 41 | 42 | DEFINE_ARRAY(UBYTE) 43 | DEFINE_ARRAY(int) 44 | DEFINE_ARRAY(UINT) 45 | DEFINE_ARRAY(float) 46 | 47 | #define ASSERT_ARRAY_ZEROS(arr) {int size=arr->tx; assert((arr->pixels[0]==0 && arr->pixels[size/2]==0 && arr->pixels[size-1]==0) || !"error: matrix " #arr "is supposed to be zeros");} 48 | 49 | 50 | /************************ 51 | * 2D Image 52 | 53 | Equivalences: 54 | 55 | C/Python/numpy: array.shape = (ty, tx) 56 | array[y, x] := array->pixels[x + y*tx] 57 | 58 | Matlab/Fortran: [tx, ty] = size(array) 59 | array(x, y) := array->pixels[(x-1) + (y-1)*tx] 60 | */ 61 | 62 | #define DEFINE_IMG(type) \ 63 | typedef struct { \ 64 | type* pixels;\ 65 | int tx,ty;\ 66 | } type##_image; 67 | 68 | DEFINE_IMG(UBYTE) 69 | DEFINE_IMG(int) 70 | DEFINE_IMG(UINT) 71 | DEFINE_IMG(float) 72 | 73 | #define ASSERT_SAME_SIZE ASSERT_SAME_IMG_SIZE 74 | #define ASSERT_IMG_SIZE ASSERT_SAME_IMG_SIZE 75 | #define ASSERT_SAME_IMG_SIZE(im1,im2) if(im1 && im2) assert(im1->tx==im2->tx && im1->ty==im2->ty); 76 | 77 | #define ASSERT_IMAGE_ZEROS 78 | #define ASSERT_IMG_ZEROS(img) {int size=img->tx*img->ty; assert((img->pixels[0]==0 && img->pixels[size/2]==0 && img->pixels[size-1]==0) || !"error: matrix " #img "is supposed to be zeros");} 79 | #define IMG_SIZE(img) (long((img)->tx)*(img)->ty) 80 | 81 | 82 | 83 | /************************ 84 | * 3D Image = Cube (Z coordinates are contiguous) 85 | 86 | Equivalences: 87 | 88 | C/Python/numpy: array.shape = (ty, tx, tz) 89 | array[y, x, z] := array->pixels[z + x*tz + y*tx*tz] 90 | 91 | Matlab/Fortran: [tz, tx, ty] = size(array) 92 | array(z, x, y) := array->pixels[(z-1) + (x-1)*tz + (y-1)*tx*tz] 93 | */ 94 | 95 | #define DEFINE_CUBE(type) \ 96 | typedef struct { \ 97 | type* pixels; \ 98 | int tx,ty,tz; \ 99 | } type##_cube; 100 | 101 | DEFINE_CUBE(UBYTE) 102 | DEFINE_CUBE(short) 103 | DEFINE_CUBE(int) 104 | DEFINE_CUBE(UINT) 105 | DEFINE_CUBE(float) 106 | 107 | #define ASSERT_SAME_CUBE_SIZE(im1, im2) \ 108 | if((im1) && (im2)) assert((im1)->tx==(im2)->tx && (im1)->ty==(im2)->ty && (im1)->tz==(im2)->tz); 109 | 110 | #define ASSERT_CUBE_ZEROS(img) {int size=img->tx*img->ty*img->tz; assert((img->pixels[0]==0 && img->pixels[size/2]==0 && img->pixels[size-1]==0) || !"error: matrix " #img "is supposed to be zeros");} 111 | #define CUBE_SIZE(cube) (long((cube)->tx)*(cube)->ty*(cube)->tz) 112 | 113 | 114 | 115 | /************************ 116 | * 3D Image = concatenation of XY layers 117 | 118 | Equivalences: 119 | 120 | C/Python/numpy: array.shape = (tz, ty, tx) 121 | array[z, y, x] := array->pixels[x + y*tx + z*tx*ty] 122 | 123 | Matlab/Fortran: [tx, ty, tz] = size(array) 124 | array(x, y, z) := array->pixels[(x-1) + (y-1)*tx + (z-1)*tx*ty] 125 | */ 126 | 127 | #define DEFINE_LAYERS(type) \ 128 | typedef struct { \ 129 | type* pixels; \ 130 | int tx,ty,tz; \ 131 | } type##_layers; \ 132 | 133 | DEFINE_LAYERS(UBYTE) 134 | DEFINE_LAYERS(int) 135 | DEFINE_LAYERS(UINT) 136 | DEFINE_LAYERS(float) 137 | 138 | 139 | #define ASSERT_SAME_LAYERS_SIZE(im1,im2) ASSERT_SAME_CUBE_SIZE(im1,im2) 140 | #define ASSERT_LAYERS_ZEROS ASSERT_CUBE_ZEROS 141 | #define LAYERS_SIZE(layers) CUBE_SIZE(layers) 142 | 143 | 144 | 145 | /***************** 146 | creation, reshaping macros 147 | */ 148 | 149 | #define empty_array(type,tx) ((type##_array){NEWA(type,long(tx)),tx}) 150 | #define empty_image(type,tx,ty) ((type##_image){NEWA(type,long(tx)*(ty)),tx,ty}) 151 | #define empty_cube(type,tx,ty,tz) ((type##_cube ){NEWA(type,long(tx)*(ty)*long(tz)),tx,ty,tz}) 152 | #define empty_layers(type,tx,ty,tz) ((type##_layers){NEWA(type,long(tx)*(ty)*(tz)),tx,ty,tz}) 153 | 154 | #define zeros_array(type,tx) ((type##_array){NEWAC(type,long(tx)),tx}) 155 | #define zeros_image(type,tx,ty) ((type##_image){NEWAC(type,long(tx)*(ty)),tx,ty}) 156 | #define zeros_cube(type,tx,ty,tz) ((type##_cube ){NEWAC(type,long(tx)*(ty)*(tz)),tx,ty,tz}) 157 | #define zeros_layers(type,tx,ty,tz) ((type##_layers){NEWAC(type,long(tx)*(ty)*(tz)),tx,ty,tz}) 158 | 159 | #define array_like(type,l) ((type##_array){NEWA(type,long((l)->tx)),(l)->tx}) 160 | #define image_like(type,l) ((type##_image){NEWA(type,long((l)->tx)*(l)->ty),(l)->tx,(l)->ty}) 161 | #define cube_like(type,l) ((type##_cube ){NEWA(type,long((l)->tx)*(l)->ty*(l)->tz),(l)->tx,(l)->ty,(l)->tz}) 162 | #define layers_like(type,l) ((type##_layers){NEWA(type,long((l)->tx)*(l)->ty*(l)->tz),(l)->tx,(l)->ty,(l)->tz}) 163 | 164 | 165 | #define reshape_xy(type, arr) ((type##_array){(arr)->pixels, (arr)->tx*(arr)->ty}) 166 | #define reshape_xyz(type, arr) ((type##_array){(arr)->pixels, (arr)->tx*(arr)->ty*(arr)->tz}) 167 | #define reshape_xy_z(type, arr) ((type##_image){(arr)->pixels, (arr)->tx*(arr)->ty, (arr)->tz}) 168 | #define reshape_z_xy(type, arr) ((type##_image){(arr)->pixels, (arr)->tz, (arr)->tx*(arr)->ty}) 169 | #define reshape_x_yz(type, arr) ((type##_image){(arr)->pixels, (arr)->tx, (arr)->ty*(arr)->tz}) 170 | 171 | 172 | #define free_image(img) if(img){free(img->pixels); free(img); img=NULL;} 173 | #define free_cube(cube) free_image(cube) 174 | #define free_layers(cube) free_cube(cube) 175 | 176 | 177 | // debugging only 178 | //#include 179 | //inline long hash_arr(char* ptr, int nb, bool show) { 180 | // long res = 0; 181 | // if(show) printf("hashing ["); 182 | // for(int i=0; i>17) | (res<<47); 186 | // } 187 | // if(show) printf("]\n"); 188 | // return res; 189 | //} 190 | //#define H(arr,val) printf("hash(" #arr ") = %ld\n",val); 191 | //#define hash_array(arr) H(arr,hash_arr((char*)(arr)->pixels,(arr)->tx*sizeof(*(arr)->pixels),0)) 192 | //#define hash_image(arr) H(arr,hash_arr((char*)(arr)->pixels,(arr)->tx*(arr)->ty*sizeof(*(arr)->pixels),0)) 193 | //#define hash_cube(arr) H(arr,hash_arr((char*)(arr)->pixels,(arr)->tx*(arr)->ty*(arr)->tz*sizeof(*(arr)->pixels),0)) 194 | //#define hash_layers(arr) hash_cube(arr) 195 | 196 | //inline void save_raw(const char* fname, int* shape, int ndim, char* ptr, int size) { 197 | // FILE* f = fopen(fname, "w"); 198 | // fwrite( &ndim, sizeof(int), 1, f); 199 | // fwrite( shape, sizeof(int), ndim, f); 200 | // fwrite( ptr, sizeof(*ptr), size, f); 201 | // fclose(f); 202 | //} 203 | //#define save_cube(fname,cube) {int sh[3] = {(cube)->ty, (cube)->tx, (cube)->tz}; save_raw(fname, sh, 3, (char*)(cube)->pixels, sizeof(*(cube)->pixels)*CUBE_SIZE(cube));} 204 | //#define save_layers(fname,layers) {int sh[3] = {(layers)->tz, (layers)->ty, (layers)->tx}; save_raw(fname, sh, 3, (char*)(layers)->pixels, sizeof(*(layers)->pixels)*LAYERS_SIZE(layers));} 205 | 206 | #endif 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/conv.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___CONV_H___ 18 | #define ___CONV_H___ 19 | #include "array_types.h" 20 | #include "deep_matching.h" 21 | 22 | 23 | /* Return the vectorized dimension of a HOG patch 24 | */ 25 | int get_patch_desc_dim( float_layers* hog, int patch_size ); 26 | 27 | 28 | /* Sample a set of patches from a HOG image. 29 | pos : array of (x,y) position of the patches 30 | size: size of the patches, ie. [x,x+size[ x [y,y+size[ 31 | res: result array, n_patches x desc_dim 32 | desc_dim = n_layers * size**2 33 | norms: result, n_patches x 1, norm of each patch 34 | */ 35 | void _sample_patches( float_layers* hog, float_layers* color, int_image* pos, int size, float norm, 36 | float_image* res, float_array* norms, int n_thread ); 37 | 38 | 39 | /* normalize each pixel of a multi-layers image 40 | norm = {0:nothing, 1:L2-normalization, 0-1: normalization by (L2-norm)** } 41 | */ 42 | void norm_layers( float_layers* res, float norm, int n_thread ); 43 | 44 | 45 | /* Prepare a grid of cell positions in the first image for a given scale. Big cells inherit the cell at the previous scale. 46 | size = size of cells at current scale 47 | offset, step = grid generator: (offset + i*step, offset + j*step) 48 | child_grid = grid of the previous layer (or None if first layer) 49 | child_norms = image containing the norms of the patch at the previous level 50 | grid = result center positions of cells in current scale 51 | children = index of cells in previous scale used to construct big cells 52 | norms = norms of the cells of this level 53 | */ 54 | void _prepare_big_cells( int size, int offset, int step, 55 | int_cube* child_grid, float_image* child_norms, 56 | int_cube* grid, int_cube* children, float_image* norms ); 57 | 58 | 59 | 60 | /* Compute the correlation of all patches with the second image (hog). 61 | In case of ngh_rad>0, the correlation is only computed in a small local neighborhood 62 | (whose size is parameterized by ngh_rad). 63 | if extend: width and height of output maps are extended 64 | if norm: correlation are normalized afterwards. 65 | */ 66 | void fastconv( float_image* patches, float_layers* hog, int patch_size, int ngh_rad, 67 | int extend, float norm, int nt, res_scale* res ); 68 | 69 | 70 | 71 | /* Compute the (sparse) convolutions specified by on and put the result in . 72 | A standard order is assumed on the children: 73 | a response map #p is built from the children[p] at positions 74 | [(gap*dx,gap*dy) for dy in dys for dx in dxs] 75 | where dxs = [-1,1] or [-1,0,1] 76 | dys = [-1,1] or [-1,0,1] 77 | child_assign denote assignement of the children level, while assign is for the next level 78 | child_norms contain the norms of small patches and norms for big new cells 79 | */ 80 | int _sparse_conv( int_image* children, int_array* child_assign, int gap, float trans_inv, 81 | float_layers* child_map, int_image* offsets, float_array* child_norms, float_array* norms, 82 | int_array* assign, float_layers* res, int_image* res_offsets, int n_thread ); 83 | 84 | 85 | 86 | /* Compute: arr **= p 87 | */ 88 | void fastipow( float_layers* arr, const float p, int n_thread ); 89 | 90 | /* Compute: arr = max(0,(arr-p)/(1-p)) 91 | */ 92 | void fasthinge( float_layers* arr, const float p, int n_thread ); 93 | 94 | /* Compute: arr = exp(-arr) 95 | */ 96 | void fastnegexp( float_image* arr ); 97 | 98 | 99 | 100 | /* incorporate the color difference between patches into existing patch similarity 101 | 102 | formula: new_response = ( color_sim*addw + old_response*(1-addw) ) * ( mulw*color_sim + 1-mulw ) 103 | 104 | if mulw=1, adddw=0, then: new_response = old_response * color_sim 105 | if mulw=0, adddw=0.5,then: new_response = (old_response + color_sim )/2 106 | */ 107 | void incorporate_color( int_cube* grid, int_array* assign, float_layers* lab0, float_layers* var0, 108 | float_layers* lab1, float_layers* var1, 109 | float_layers* res_maps, float L_std, float ab_std, int sym_dist, int n_opening, 110 | const float addw, const float mulw, int n_thread ); 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /src/deep_matching.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___DEEP_MATCHING_H___ 18 | #define ___DEEP_MATCHING_H___ 19 | #include "array_types.h" 20 | #include "pixel_desc.h" 21 | #include "image.h" 22 | 23 | #include 24 | using namespace std; 25 | 26 | 27 | // deep matching parameters 28 | typedef struct { 29 | desc_params_t desc_params; 30 | 31 | int prior_img_downscale;// downscale the image by 2^(this) prior to matching 32 | int rot45; // rotate second img by (45*rot45) prior to matching 33 | int overlap; // pyramid level at which patches starts to overlap (999 => no overlap at all) 34 | bool subsample_ref; // true if larger patches higher in the pyramid are not densely sampled 35 | float nlpow; // non-linear power rectification 36 | int ngh_rad; // neighborhood size in pixels => crop res_map (0 == infinite) 37 | int maxima_mode; // 1: standard / 0: from all top-level patches 38 | int min_level; // minimum pyramid level to retrieve maxima 39 | int max_psize; // maximum patch size 40 | int low_mem; // use less memory to retrieve the maxima (but approximate result) 41 | int scoring_mode; // 0: like ICCV paper / 1: improved scoring mode 42 | int verbose; // verbosity 43 | int n_thread; // parallelization on several cores, when possible 44 | 45 | } dm_params_t; 46 | 47 | // set default parameters 48 | void set_default_dm_params( dm_params_t* params ); 49 | 50 | // scale & rotation invariant version 51 | typedef struct { 52 | bool fast; // avoid comparing small scaled versions of both images 53 | int min_sc0, max_sc0; // scale range of image0 (expressed as scale=2^(-n/2)) 54 | int min_sc1, max_sc1; // scale range of image1 (expressed as scale=2^(-n/2)) 55 | int min_rot, max_rot; // rotation range (expressed as multiples of 45 degrees) 56 | 57 | } scalerot_params_t; 58 | 59 | // set default parameters 60 | void set_default_scalerot_params( scalerot_params_t* params ); 61 | 62 | 63 | // response maps at a given scale 64 | typedef struct { 65 | int f; // subsampling factor with respect to original image size 66 | int patch_size; // patch size in original image coordinates in first image 67 | int_cube grid; // position (center) of each patch in first image 68 | float_image norms; // norm of each patch in first image 69 | int_array assign; // mapping between patches and their response maps 70 | float_layers res_map; // response map of the patches on the second image 71 | float_layers max_map; // max-filtered response map 72 | int true_shape[2]; // true res_map shape (width, height) in case of crop (if ngh_rad>0) 73 | int_image offsets; // res_map offsets in case of crop (if ngh_rad>0) 74 | int_cube children; // index of children patches in the previous level 75 | float_array passed; // remember the best score so far at each response when doing argmax 76 | 77 | } res_scale; 78 | 79 | typedef vector matching_pyramid_t; 80 | 81 | 82 | // output correspondences 83 | typedef struct { 84 | float x0, y0; // position in first image (reference image) 85 | float x1, y1; // position in second image (target image) 86 | float maxima; // from which maxima it was generated (index) 87 | float score; // matching score 88 | } corres_t; 89 | 90 | // for scale rot invariant matching 91 | typedef struct { 92 | float rot[6]; 93 | float_cube corres0; 94 | float_cube corres1; 95 | } full_corres_t; 96 | 97 | 98 | // main function. Returns a float_image where each row is 99 | float_image* deep_matching( image_t* img0, image_t* img1, const dm_params_t* params, 100 | full_corres_t* corres_out ); // NULL if you don't use it 101 | 102 | // main function for scale & invariant matching. output is same as above. 103 | float_image* deep_matching_scale_rot( image_t* img0, image_t* img1, dm_params_t* params, 104 | const scalerot_params_t* sr_params ); 105 | 106 | 107 | #endif 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/deepmatching.i: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | %module(docstring="Module to compute DeepMatching") deepmatching 18 | 19 | %{ 20 | #define SWIG_FILE_WITH_INIT 21 | 22 | #include 23 | 24 | 25 | #define CHECK_NUMPY_ARRAY(a, expected_npy) \ 26 | if(!a) { \ 27 | fprintf(stderr,"error in %s(): NULL input\n",__PRETTY_FUNCTION__); \ 28 | return NULL; \ 29 | } \ 30 | if(!PyArray_Check(a)) { \ 31 | fprintf(stderr,"error in %s(): input not numpy array\n",__PRETTY_FUNCTION__); \ 32 | return NULL; \ 33 | } \ 34 | if(!PyArray_ISCONTIGUOUS(a)) { \ 35 | fprintf(stderr,"error in %s(): array is not C-contiguous\n",__PRETTY_FUNCTION__); \ 36 | return NULL; \ 37 | } \ 38 | if(PyArray_TYPE(a)!=expected_npy) { \ 39 | fprintf(stderr,"error in %s(): input has bad type (type id %d != " #expected_npy " %d)\n",__PRETTY_FUNCTION__, \ 40 | PyArray_TYPE(a),expected_npy); \ 41 | return NULL; \ 42 | } 43 | 44 | %} 45 | 46 | %init %{ 47 | import_array(); 48 | %} 49 | 50 | 51 | %{ 52 | #include "image.h" 53 | #include "array_types.h" 54 | %} 55 | 56 | %typemap(in) 57 | (color_image_t* cimg) 58 | (color_image_t cimage) { 59 | 60 | PyObject* a = $input; 61 | if(a==Py_None) { 62 | $1 = NULL; 63 | } else { 64 | CHECK_NUMPY_ARRAY(a, NPY_FLOAT) 65 | cimage.c1 = (float*) PyArray_DATA(a); 66 | a = PyObject_GetAttrString($input,"shape"); 67 | assert(PyTuple_Size(a)==3); 68 | assert( PyInt_AsLong(PyTuple_GetItem(a,0)) == 3); 69 | cimage.height = PyInt_AsLong(PyTuple_GetItem(a,1)); 70 | cimage.width = PyInt_AsLong(PyTuple_GetItem(a,2)); 71 | cimage.c2 = cimage.c1 + cimage.width*cimage.height; 72 | cimage.c3 = cimage.c2 + cimage.width*cimage.height; 73 | $1=&cimage; 74 | } 75 | } 76 | %apply (color_image_t* cimg) {(color_image_t* )}; 77 | 78 | %typemap(out) float_image* corres { 79 | PyObject *o; 80 | npy_intp n_elem[2] = {$1->ty, $1->tx}; 81 | o = PyArray_SimpleNewFromData(2,n_elem,NPY_FLOAT,$1->pixels); 82 | PyArray_FLAGS(o) |= NPY_OWNDATA; 83 | 84 | // append to current function result as a tuple 85 | $result = o; 86 | 87 | } 88 | %apply (float_image* corres) {(float_image* )}; 89 | 90 | float_image* deepmatching_numpy( color_image_t* cim1, color_image_t* cim2, char *options); 91 | 92 | void usage_python(); 93 | 94 | %{ 95 | #include "deep_matching.h" 96 | #include "io.h" 97 | #include "main.h" 98 | #include 99 | 100 | static inline bool ispowerof2( long n ) { 101 | return (n & (n-1))==0; 102 | } 103 | 104 | float_image* deepmatching_numpy( color_image_t* cim1, color_image_t* cim2, char *options){ 105 | // convert images to gray 106 | image_t *im1=image_gray_from_color(cim1), *im2=image_gray_from_color(cim2); 107 | 108 | // set params to default 109 | dm_params_t params; 110 | set_default_dm_params(¶ms); 111 | scalerot_params_t sr_params; 112 | set_default_scalerot_params(&sr_params); 113 | bool use_scalerot = false; 114 | float fx=1, fy=1; 115 | 116 | // read options 117 | if( options!=NULL ){ 118 | int argc=0; 119 | const char* argv[256]; 120 | argv[argc] = strtok(options," "); 121 | while(argv[argc]!=NULL) 122 | argv[++argc] = strtok(NULL," "); 123 | 124 | parse_options(¶ms, &sr_params, &use_scalerot, &fx, &fy, argc, argv, PYTHON_OPTIONS, &im1, &im2); 125 | } 126 | 127 | 128 | if( use_scalerot ) 129 | assert( params.ngh_rad == 0 || !"max trans cannot be used in full scale and rotation mode"); 130 | else 131 | if( params.subsample_ref && (!ispowerof2(im1->width) || !ispowerof2(im1->height)) ) { 132 | fprintf(stderr, "WARNING: first image has dimension which are not power-of-2\n"); 133 | fprintf(stderr, "For improved results, you should consider resizing the images with '-resize '\n"); 134 | } 135 | 136 | // compute deep matching 137 | float_image* corres = use_scalerot ? 138 | deep_matching_scale_rot( im1, im2, ¶ms, &sr_params ) : 139 | deep_matching ( im1, im2, ¶ms, NULL ); // standard call 140 | 141 | image_delete(im1); image_delete(im2); 142 | return corres; 143 | } 144 | 145 | void usage_python() { 146 | usage(PYTHON_OPTIONS); 147 | } 148 | 149 | %} 150 | 151 | 152 | %pythoncode %{ 153 | from numpy import float32, rollaxis, ascontiguousarray 154 | def deepmatching( im1=None, im2=None, options=""): 155 | """ 156 | matches = deepmatching.deepmatching(image1, image2, options='') 157 | Compute the 'DeepMatching' between two images. 158 | Images must be HxWx3 numpy arrays (converted to float32). 159 | Options is an optional string argument ('' by default), to set the options. 160 | The function returns a numpy array with 6 columns, each row being x1 y1 x2 y2 score index. 161 | (index refers to the local maximum from which the match was retrieved) 162 | Version 1.2""" 163 | if None in (im1,im2): 164 | usage_python() 165 | return 166 | 167 | # convert images 168 | if im1.dtype != float32: 169 | im1 = im1.astype(float32) 170 | if im2.dtype != float32: 171 | im2 = im2.astype(float32) 172 | assert len(im1.shape)==3 and len(im2.shape)==3, "images must have 3 dimensions" 173 | h, w, nchannels = im1.shape 174 | assert nchannels==3, "images must have 3 channels" 175 | im1 = ascontiguousarray(rollaxis(im1,2)) 176 | im2 = ascontiguousarray(rollaxis(im2,2)) 177 | corres = deepmatching_numpy( im1, im2, options) 178 | return corres 179 | %} 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/deepmatching.m: -------------------------------------------------------------------------------- 1 | % mex wrapper to compute the 'DeepMatching' between two images. 2 | % 3 | % matches = deepmatching(image1, image2, options) 4 | % 5 | % Images must be HxWx3 single matrices. 6 | % Options is an optional string argument ('' by default). 7 | % Availalble options are listed when calling deepmatching() without args. 8 | % 9 | % The function returns a matrix with 6 columns, each row being x1 y1 x2 y2 score index. 10 | % (index refers to the local maximum from which the match was retrieved) 11 | % 12 | % Version 1.2.2 13 | % 14 | % Copyright (C) 2014 Jerome Revaud 15 | % 16 | % This program is free software: you can redistribute it and/or modify 17 | % it under the terms of the GNU General Public License as published by 18 | % the Free Software Foundation, either version 3 of the License, or 19 | % (at your option) any later version. 20 | % 21 | % This program is distributed in the hope that it will be useful, 22 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 23 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 | % GNU General Public License for more details. 25 | % 26 | % You should have received a copy of the GNU General Public License 27 | % along with this program. If not, see 28 | % 29 | -------------------------------------------------------------------------------- /src/deepmatching.py: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by SWIG (http://www.swig.org). 2 | # Version 3.0.7 3 | # 4 | # Do not make changes to this file unless you know what you are doing--modify 5 | # the SWIG interface file instead. 6 | 7 | 8 | 9 | 10 | """ 11 | Module to compute DeepMatching 12 | """ 13 | 14 | 15 | from sys import version_info 16 | if version_info >= (2, 6, 0): 17 | def swig_import_helper(): 18 | from os.path import dirname 19 | import imp 20 | fp = None 21 | try: 22 | fp, pathname, description = imp.find_module('_deepmatching', [dirname(__file__)]) 23 | except ImportError: 24 | import _deepmatching 25 | return _deepmatching 26 | if fp is not None: 27 | try: 28 | _mod = imp.load_module('_deepmatching', fp, pathname, description) 29 | finally: 30 | fp.close() 31 | return _mod 32 | _deepmatching = swig_import_helper() 33 | del swig_import_helper 34 | else: 35 | import _deepmatching 36 | del version_info 37 | try: 38 | _swig_property = property 39 | except NameError: 40 | pass # Python < 2.2 doesn't have 'property'. 41 | 42 | 43 | def _swig_setattr_nondynamic(self, class_type, name, value, static=1): 44 | if (name == "thisown"): 45 | return self.this.own(value) 46 | if (name == "this"): 47 | if type(value).__name__ == 'SwigPyObject': 48 | self.__dict__[name] = value 49 | return 50 | method = class_type.__swig_setmethods__.get(name, None) 51 | if method: 52 | return method(self, value) 53 | if (not static): 54 | if _newclass: 55 | object.__setattr__(self, name, value) 56 | else: 57 | self.__dict__[name] = value 58 | else: 59 | raise AttributeError("You cannot add attributes to %s" % self) 60 | 61 | 62 | def _swig_setattr(self, class_type, name, value): 63 | return _swig_setattr_nondynamic(self, class_type, name, value, 0) 64 | 65 | 66 | def _swig_getattr_nondynamic(self, class_type, name, static=1): 67 | if (name == "thisown"): 68 | return self.this.own() 69 | method = class_type.__swig_getmethods__.get(name, None) 70 | if method: 71 | return method(self) 72 | if (not static): 73 | return object.__getattr__(self, name) 74 | else: 75 | raise AttributeError(name) 76 | 77 | def _swig_getattr(self, class_type, name): 78 | return _swig_getattr_nondynamic(self, class_type, name, 0) 79 | 80 | 81 | def _swig_repr(self): 82 | try: 83 | strthis = "proxy of " + self.this.__repr__() 84 | except: 85 | strthis = "" 86 | return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) 87 | 88 | try: 89 | _object = object 90 | _newclass = 1 91 | except AttributeError: 92 | class _object: 93 | pass 94 | _newclass = 0 95 | 96 | 97 | 98 | def deepmatching_numpy(cim1, cim2, options): 99 | return _deepmatching.deepmatching_numpy(cim1, cim2, options) 100 | deepmatching_numpy = _deepmatching.deepmatching_numpy 101 | 102 | def usage_python(): 103 | return _deepmatching.usage_python() 104 | usage_python = _deepmatching.usage_python 105 | 106 | from numpy import float32, rollaxis, ascontiguousarray 107 | def deepmatching( im1=None, im2=None, options=""): 108 | """ 109 | matches = deepmatching.deepmatching(image1, image2, options='') 110 | Compute the 'DeepMatching' between two images. 111 | Images must be HxWx3 numpy arrays (converted to float32). 112 | Options is an optional string argument ('' by default), to set the options. 113 | The function returns a numpy array with 6 columns, each row being x1 y1 x2 y2 score index. 114 | (index refers to the local maximum from which the match was retrieved) 115 | Version 1.2""" 116 | if None in (im1,im2): 117 | usage_python() 118 | return 119 | 120 | # convert images 121 | if im1.dtype != float32: 122 | im1 = im1.astype(float32) 123 | if im2.dtype != float32: 124 | im2 = im2.astype(float32) 125 | assert len(im1.shape)==3 and len(im2.shape)==3, "images must have 3 dimensions" 126 | h, w, nchannels = im1.shape 127 | assert nchannels==3, "images must have 3 channels" 128 | im1 = ascontiguousarray(rollaxis(im1,2)) 129 | im2 = ascontiguousarray(rollaxis(im2,2)) 130 | corres = deepmatching_numpy( im1, im2, options) 131 | return corres 132 | 133 | # This file is compatible with both classic and new-style classes. 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/deepmatching_matlab.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | void std_printf(const char* format, ... ) { 27 | va_list arglist; 28 | va_start( arglist, format ); 29 | char buffer[1024]; 30 | vsprintf( buffer, format, arglist ); 31 | va_end(arglist); 32 | 33 | mexPrintf(buffer); 34 | } 35 | 36 | void err_printf(const char* format, ... ) { 37 | va_list arglist; 38 | va_start( arglist, format ); 39 | char buffer[1024]; 40 | vsprintf( buffer, format, arglist ); 41 | va_end(arglist); 42 | 43 | mexErrMsgTxt(buffer); 44 | } 45 | 46 | 47 | #include "image.h" 48 | #include "deep_matching.h" 49 | #include "io.h" 50 | #include "main.h" 51 | 52 | 53 | static inline bool ispowerof2( long n ) { 54 | return (n & (n-1))==0; 55 | } 56 | 57 | color_image_t *input3darray_to_color_image(const mxArray *p){ 58 | const int *dims = mxGetDimensions(p); 59 | const int h = dims[0], w = dims[1]; 60 | assert( dims[2]==3 ); 61 | float *in = (float*) mxGetData(p); 62 | color_image_t *out = color_image_new(w, h); 63 | for(int c=0 ; c<3 ; c++){ 64 | float *inptr = in + c*w*h; 65 | float *outptr = out->c1 + c*w*h; 66 | for( int j=0 ; jty, w = corres->tx; 77 | float *data = (float*) mxGetData(p); 78 | for( int j=0 ; jpixels[j*w+i]; 81 | } 82 | } 83 | } 84 | 85 | void mexFunction( int nl, mxArray *pl[], int nr, const mxArray *pr[] ) { 86 | 87 | if( nr==0 ) { 88 | usage(MATLAB_OPTIONS); 89 | return; 90 | } 91 | 92 | if ( nl != 1){ 93 | usage(MATLAB_OPTIONS); 94 | mexErrMsgTxt("error: returns one output"); 95 | } 96 | if( nr < 2 || nr > 3){ 97 | usage(MATLAB_OPTIONS); 98 | mexErrMsgTxt("error: takes two to four inputs"); 99 | } 100 | 101 | // The code is originally written for C-order arrays. 102 | // We thus transpose all arrays in this mex-function which is not efficient... 103 | 104 | const int *pDims; 105 | if(mxGetNumberOfDimensions(pr[0]) != 3) mexErrMsgTxt("input images must have 3 dimensions"); 106 | if(!mxIsClass(pr[0], "single")) mexErrMsgTxt("input images must be single"); 107 | pDims = mxGetDimensions(pr[0]); 108 | if( pDims[2]!=3 ) mexErrMsgTxt("input images must have 3 channels"); 109 | const int h = pDims[0], w = pDims[1]; 110 | color_image_t *cim1 = input3darray_to_color_image( pr[0] ); 111 | 112 | if(mxGetNumberOfDimensions(pr[1]) != 3) mexErrMsgTxt("input images must have 3 dimensions"); 113 | if(!mxIsClass(pr[1], "single")) mexErrMsgTxt("input images must be single"); 114 | pDims = mxGetDimensions(pr[1]); 115 | if( pDims[2]!=3) mexErrMsgTxt("input images must have 3 channels"); 116 | color_image_t *cim2 = input3darray_to_color_image( pr[1] ); 117 | 118 | // convert images to gray 119 | image_t *im1=image_gray_from_color(cim1), *im2=image_gray_from_color(cim2);; 120 | color_image_delete(cim1); 121 | color_image_delete(cim2); 122 | 123 | // set params to default 124 | dm_params_t params; 125 | set_default_dm_params(¶ms); 126 | scalerot_params_t sr_params; 127 | set_default_scalerot_params(&sr_params); 128 | bool use_scalerot = false; 129 | float fx=1, fy=1; 130 | 131 | // read options 132 | if( nr == 3 ){ 133 | char *options = mxArrayToString(pr[2]); 134 | if( !options ) mexErrMsgTxt("Third parameter must be a string"); 135 | int argc=0; 136 | const char* argv[256]; 137 | argv[argc] = strtok(options," "); 138 | while(argv[argc]!=NULL) 139 | argv[++argc] = strtok(NULL," "); 140 | 141 | parse_options(¶ms, &sr_params, &use_scalerot, &fx, &fy, argc, argv, MATLAB_OPTIONS, &im1, &im2); 142 | } 143 | 144 | if( use_scalerot ) 145 | assert( params.ngh_rad == 0 || !"max trans cannot be used in full scale and rotation mode"); 146 | else 147 | if( params.subsample_ref && (!ispowerof2(im1->width) || !ispowerof2(im1->height)) ) { 148 | std_printf("WARNING: first image has dimension which are not power-of-2\n"); 149 | std_printf("For improved results, you should consider resizing the images with '-resize '\n"); 150 | } 151 | 152 | // compute deep matching 153 | float_image* corres = use_scalerot ? 154 | deep_matching_scale_rot( im1, im2, ¶ms, &sr_params ) : 155 | deep_matching ( im1, im2, ¶ms, NULL ); // standard call 156 | 157 | // // output 158 | // pl[0] = mxCreateNumericMatrix(corres->ty, corres->tx, mxSINGLE_CLASS, mxREAL); 159 | // corres_to_output(corres, pl[0]); 160 | // 161 | // image_delete(im1); 162 | // image_delete(im2); 163 | // free_image(corres); 164 | pl[0] = mxCreateNumericMatrix(100,100, mxSINGLE_CLASS, mxREAL); 165 | return; 166 | } 167 | -------------------------------------------------------------------------------- /src/deepmtchinfo.cpp: -------------------------------------------------------------------------------- 1 | #include "deepmtchinfo.h" 2 | #include 3 | deepMtchInfo::deepMtchInfo() 4 | { 5 | } 6 | 7 | void deepMtchInfo::usage(const int language) 8 | { 9 | #define p(msg) std_printf(msg "\n"); 10 | p("usage:"); 11 | switch(language){ 12 | case EXE_OPTIONS: 13 | p("./deepmatching image1 image2 [options]"); 14 | p("Compute the 'DeepMatching' between two images and print a list of") 15 | p("pair-wise point correspondences:") 16 | p(" x1 y1 x2 y2 score index ...") 17 | p("(index refers to the local maximum from which the match was retrieved)") 18 | p("Images must be in PPM, PNG or JPG format. Version 1.2.2") 19 | break; 20 | case MATLAB_OPTIONS: 21 | p("matches = deepmatching(image1, image2 [, options])") 22 | p("Compute the 'DeepMatching' between two images.") 23 | p("Images must be HxWx3 single matrices.") 24 | p("Options is an optional string argument ('' by default).") 25 | p("The function returns a matrix with 6 columns, each row being x1 y1 x2 y2 score index.") 26 | p("(index refers to the local maximum from which the match was retrieved)") 27 | p("Version 1.2.2") 28 | break; 29 | case PYTHON_OPTIONS: 30 | p("matches = deepmatching.deepmatching(image1, image2, options='')") 31 | p("Compute the 'DeepMatching' between two images.") 32 | p("Images must be HxWx3 numpy arrays (converted to float32).") 33 | p("Options is an optional string argument ('' by default).") 34 | p("The function returns a numpy array with 6 columns, each row being x1 y1 x2 y2 score index.") 35 | p("(index refers to the local maximum from which the match was retrieved)") 36 | p("Version 1.2.2") 37 | break; 38 | } 39 | p("") 40 | p("Options:") 41 | p(" -h, --help print this message") 42 | //p(" HOG parameters (low-level pixel descriptor):") 43 | //p(" -png_settings (auto) recommended for uncompressed images") 44 | //p(" -jpg_settings (auto) recommended for compressed images") 45 | //p(" in more details: (for fine-tuning)") 46 | //p(" -hog.presm prior image smoothing") 47 | //p(" -hog.midsm intermediate HOG smoothing") 48 | //p(" -hog.sig sigmoid strength") 49 | //p(" -hog.postsm final HOG-smoothing") 50 | //p(" -hog.ninth robustness to pixel noise (eg. JPEG artifacts)") 51 | p("") 52 | p(" Matching parameters:") 53 | //p(" -iccv_settings settings used for the ICCV paper") 54 | //p(" -improved_settings (default) supposedly improved settings") 55 | //p(" in more details: (for fine-tuning)") 56 | p(" -downscale/-R downsize the input images by a factor 2^n") 57 | //p(" -overlap use overlapping patches in image1 from level n") 58 | //p(" -subref 0: denser sampling or 1: not of image1 patches") 59 | p(" -ngh_rad if n>0: restrict matching to n pxl neighborhood") 60 | p(" -nlpow non-linear rectification x := x^f") 61 | //p(" -maxima_mode 0: from all top cells / 1: from local maxima") 62 | //p(" -min_level skip maxima in levels [0, 1, ..., n-1]") 63 | p(" -mem if n>0: optimize memory footprint (bit unstable)") 64 | //p(" -scoring_mode type of correspondence scoring mode (0/1)") 65 | p("") 66 | p(" Fully scale & rotation invariant DeepMatching:") 67 | p(" if either one of these options is used, then this mode is activated:") 68 | p(" -max_scale max scaling factor") 69 | p(" -rot_range rotation range") 70 | p("") 71 | p(" Other parameters:") 72 | p(" -resize to resize input images beforehand") 73 | p(" -v increase verbosity") 74 | p(" -nt multi-threading with threads") 75 | if(language==EXE_OPTIONS) { 76 | p(" -out output correspondences in a file") 77 | return;} 78 | } 79 | 80 | bool deepMtchInfo::endswith(const char *str, const char *suffix) 81 | { 82 | if(!str || !suffix) return false; 83 | size_t lenstr = strlen(str); 84 | size_t lensuffix = strlen(suffix); 85 | if(lensuffix > lenstr) return false; 86 | return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 87 | } 88 | 89 | image_t* deepMtchInfo::rescale_image( image_t* im, int width, int height ) 90 | { 91 | image_t* res = image_new(width,height); 92 | image_resize_bilinear_newsize(res, im, width, height); 93 | image_delete(im); 94 | return res; 95 | } 96 | 97 | 98 | void deepMtchInfo::initialization(dm_params_t *params, scalerot_params_t *sr_params,\ 99 | bool *use_scalerot, float *fx, float *fy, \ 100 | int argc, char **argv, const int language,\ 101 | image_t **im1, image_t **im2, const char* out_filename) { 102 | int current_arg = 0; 103 | // parse options 104 | while(current_arg < argc) 105 | { 106 | const char* a = argv[current_arg]; 107 | #define isarg(key) !strcmp(a,key) 108 | 109 | if(isarg("-h") || isarg("--help") ) usage(language); 110 | // HOG and patch parameters 111 | //else if(isarg("-hog.presm")) 112 | // params->desc_params.presmooth_sigma = atof(argv[current_arg++]); 113 | //else if(isarg("-hog.sig")) 114 | // params->desc_params.hog_sigmoid = atof(argv[current_arg++]); 115 | //else if(isarg("-hog.midsm")) 116 | // params->desc_params.mid_smoothing = atof(argv[current_arg++]); 117 | //else if(isarg("-hog.postsm")) 118 | // params->desc_params.post_smoothing = atof(argv[current_arg++]); 119 | //else if(isarg("-hog.ninth")) 120 | // params->desc_params.ninth_dim = atof(argv[current_arg++]); 121 | //else if(isarg("-hog.nrmpix")) 122 | // params->desc_params.norm_pixels = atof(argv[current_arg++]); 123 | else if(isarg("-png_settings")) { 124 | params->desc_params.presmooth_sigma = 0; // no image smoothing since the image is uncompressed 125 | params->desc_params.hog_sigmoid = 0.2; 126 | params->desc_params.mid_smoothing = 1.5; 127 | params->desc_params.post_smoothing = 1; 128 | params->desc_params.ninth_dim = 0.1; } // low ninth_dim since image PSNR is high 129 | else if(isarg("-jpg_settings")) { 130 | params->desc_params.presmooth_sigma = 1; // smooth the image to remove jpg artifacts 131 | params->desc_params.hog_sigmoid = 0.2; 132 | params->desc_params.mid_smoothing = 1.5; 133 | params->desc_params.post_smoothing = 1; 134 | params->desc_params.ninth_dim = 0.3; } // higher ninth_dim because of pixel noise 135 | // matching parameters 136 | else if(isarg("-R") || isarg("-downscale")) 137 | params->prior_img_downscale = atoi(argv[current_arg++]); 138 | //else if(isarg("-overlap")) 139 | // params->overlap = atoi(argv[current_arg++]); 140 | //else if(isarg("-subref")) 141 | // params->subsample_ref = atoi(argv[current_arg++]); 142 | else if(isarg("-nlpow")) 143 | params->nlpow = atof(argv[current_arg++]); 144 | else if(isarg("-ngh_rad")) 145 | params->ngh_rad = atoi(argv[current_arg++]); 146 | // maxima parameters 147 | //else if(isarg("-maxima_mode")) 148 | // params->maxima_mode = atoi(argv[current_arg++]); 149 | else if(isarg("-mem")) { 150 | params->low_mem = atoi(argv[current_arg++]); } 151 | //else if(isarg("-min_level")) 152 | // params->min_level = atoi(argv[current_arg++]); 153 | //else if(isarg("-scoring_mode")) 154 | // params->scoring_mode = atoi(argv[current_arg++]); 155 | //else if(isarg("-iccv_settings")) { 156 | // params->prior_img_downscale = 2; 157 | // params->overlap = 0; // overlap from level 0 158 | // params->subsample_ref = 1; 159 | // params->nlpow = 1.6; 160 | // params->maxima_mode = 1; 161 | // params->low_mem = 0; 162 | // params->min_level = 2; 163 | // params->scoring_mode = 0; } 164 | //else if(isarg("-improved_settings")) { 165 | // params->prior_img_downscale = 1; // less down-scale 166 | // params->overlap = 999; // no overlap 167 | // params->subsample_ref = 0; // dense patch sampling at every level in first image 168 | // params->nlpow = 1.4; 169 | // params->maxima_mode = 0; 170 | // params->low_mem = 1; 171 | // params->min_level = 2; 172 | // params->scoring_mode = 1; } // improved scoring 173 | //else if(isarg("-max_psize")) { 174 | // params->max_psize = atoi(argv[current_arg++]); } 175 | // scale & rot invariant version 176 | else if(isarg("-scale") || isarg("-max_scale")) { 177 | *use_scalerot = true; 178 | float scale = atof(argv[current_arg++]); 179 | sr_params->max_sc0 = sr_params->max_sc1 = int(1 + 2*log2(scale)); } 180 | else if(isarg("-rot") || isarg("-rot_range")) { 181 | *use_scalerot = true; 182 | int min_rot = atoi(argv[current_arg++]); 183 | int max_rot = atoi(argv[current_arg++]); 184 | while( min_rot < 0 ) { 185 | min_rot += 360; 186 | max_rot += 360; 187 | } 188 | sr_params->min_rot = int(floor(0.5 + min_rot/45.)); 189 | sr_params->max_rot = int(floor(1.5 + max_rot/45.)); 190 | while( sr_params->max_rot - sr_params->min_rot > 8 ) 191 | sr_params->max_rot--; 192 | assert( sr_params->min_rot < sr_params->max_rot ); } 193 | // other parameters 194 | else if(isarg("-resize")) { 195 | assert((*im1)->width==(*im2)->width && (*im1)->height==(*im2)->height); 196 | int width = atoi(argv[current_arg++]); 197 | int height = atoi(argv[current_arg++]); 198 | *fx *= (*im1)->width / float(width); 199 | *fy *= (*im1)->height / float(height); 200 | *im1 = rescale_image(*im1, width, height); 201 | *im2 = rescale_image(*im2, width, height); } 202 | else if(isarg("-v")) 203 | params->verbose++; 204 | else if(isarg("-nt")) { 205 | params->n_thread = atoi(argv[current_arg++]); 206 | if (params->n_thread==0) 207 | params->n_thread = std::thread::hardware_concurrency(); } 208 | else if(language == EXE_OPTIONS && isarg("-out")) 209 | out_filename = argv[current_arg++]; 210 | else { 211 | err_printf("error: unexpected parameter '%s'", a); 212 | return; 213 | } 214 | current_arg++; 215 | } 216 | 217 | if( *use_scalerot ) 218 | assert( params->ngh_rad == 0 || !"max trans cannot be used in full scale and rotation mode"); 219 | else 220 | if( params->subsample_ref && (!ispowerof2((*im1)->width) || !ispowerof2((*im1)->height)) ) { 221 | err_printf("WARNING: first image has dimension which are not power-of-2\n"); 222 | err_printf("For improved results, you should consider resizing the images with '-resize '\n"); 223 | } 224 | 225 | return/*out_filename*/; 226 | } 227 | -------------------------------------------------------------------------------- /src/deepmtchinfo.h: -------------------------------------------------------------------------------- 1 | #ifndef DEEPMTCHINFO_H 2 | #define DEEPMTCHINFO_H 3 | #include "std.h" 4 | #include "image.h" 5 | #include "io.h" 6 | #include "deep_matching.h" 7 | 8 | #define EXE_OPTIONS 0 9 | #define MATLAB_OPTIONS 1 10 | #define PYTHON_OPTIONS 2 11 | 12 | class deepMtchInfo 13 | { 14 | public: 15 | deepMtchInfo(); 16 | void usage(const int language); 17 | bool endswith(const char *str, const char *suffix); 18 | image_t* rescale_image( image_t* im, int width, int height); 19 | void initialization(dm_params_t *params, \ 20 | scalerot_params_t *sr_params, bool *use_scalerot, \ 21 | float *fx, float *fy, int argc, char **argv, \ 22 | const int language, image_t **im1, image_t **im2, 23 | const char* out_filename); 24 | }; 25 | 26 | #endif // DEEPMTCHINFO_H 27 | -------------------------------------------------------------------------------- /src/hog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include "hog.h" 18 | #include "std.h" 19 | 20 | 21 | /* compute horizontal gradient centered with [-1,0,1] mask 22 | */ 23 | void _diff_horiz(int tx, int ty, UBYTE* pixels, float* res) { 24 | int x,y,pos=0; 25 | float* r=res; 26 | for(y=0; ytz==2); 54 | int tx = img->tx; 55 | int ty = img->ty; 56 | 57 | // compute horizontal gradient 58 | _diff_vert(tx,ty,img->pixels,grad->pixels); 59 | 60 | // compute vertical gradient 61 | _diff_horiz(tx,ty,img->pixels,grad->pixels+tx*ty); 62 | } 63 | 64 | /* compute horizontal smoothing with 3-sized mask 65 | */ 66 | template 67 | void _smooth_3_horiz(int tx, int ty, const int w_center, const int w_side, TData* pixels, TData* _res, int n_thread) { 68 | int y; 69 | const int sum_w = 2*w_side + w_center; 70 | #if defined(USE_OPENMP) 71 | #pragma omp parallel for num_threads(n_thread) 72 | #endif 73 | for(y=0; y 86 | void _smooth_5_horiz( int tx, int ty, const int w_center, const int w_side1, const int w_side2, 87 | TData* pixels, TData* _res, int n_thread) { 88 | int y; 89 | const int sum_w = 2*(w_side1 + w_side2) + w_center; 90 | #if defined(USE_OPENMP) 91 | #pragma omp parallel for num_threads(n_thread) 92 | #endif 93 | for(y=0; y 135 | void _smooth_7_horiz(int tx, int ty, const int w_center, const int w_side1, const int w_side2, const int w_side3, 136 | TData* pixels, TData* _res, int n_thread) { 137 | int y; 138 | const int sum_w = 2*(w_side1 + w_side2 + w_side3) + w_center; 139 | #if defined(USE_OPENMP) 140 | #pragma omp parallel for num_threads(n_thread) 141 | #endif 142 | for(y=0; y 216 | void _smooth_3_vert(int tx, int ty, const int w_center, const int w_side, TData* pixels, TData* res, int n_thread) { 217 | int x,y,pos=0; 218 | const int sum_w = 2*w_side + w_center; 219 | for(x=0; x 237 | void _smooth_5_vert(int tx, int ty, const int w_center, const int w_side1, const int w_side2, 238 | TData* pixels, TData* res, int n_thread) { 239 | int x,y,pos=0; 240 | const int sum_w = 2*(w_side1 + w_side2) + w_center; 241 | const int tx1=tx,tx2=2*tx; 242 | for(x=0; x 292 | void _smooth_7_vert(int tx, int ty, const int w_center, const int w_side1, const int w_side2, const int w_side3, 293 | TData* pixels, TData* res, int n_thread) { 294 | int x,y,pos=0; 295 | const int sum_w = 2*(w_side1 + w_side2 + w_side3) + w_center; 296 | const int tx1=tx,tx2=2*tx,tx3=3*tx; 297 | for(x=0; x 380 | void _smooth_gaussian_alltype( const int tx, const int ty, TData* img, float _sigma, TData* res, int n_thread ) { 381 | const float MAX_SIGMA = 1.86f; 382 | 383 | TData* img2 = img; 384 | if(_sigma>MAX_SIGMA) { // reallocate if more than one smoothing pass is required 385 | img2 = NEWA(TData,tx*ty); 386 | memcpy(img2,img,tx*ty*sizeof(TData)); 387 | } 388 | TData* tmp = NEWA(TData,tx*ty); 389 | TData* old_res = res; 390 | 391 | float remaining = _sigma*_sigma; 392 | while( 1 ) { 393 | float sigma = MIN(MAX_SIGMA,sqrt(remaining)); 394 | remaining -= sigma*sigma; 395 | 396 | // compute gaussian filter coefficients 397 | const int wcenter = 1000; 398 | const int wside1 = int(0.5 + wcenter*exp( -pow2(1./sigma)/2 )); 399 | const int wside2 = int(0.5 + wcenter*exp( -pow2(2./sigma)/2 )); 400 | const int wside3 = int(0.5 + wcenter*exp( -pow2(3./sigma)/2 )); 401 | const int wside4 = int(0.5 + wcenter*exp( -pow2(4./sigma)/2 )); 402 | assert( wside4 < wcenter/10 || !"error: smoothing is too large" ); 403 | 404 | if ( wside2 < wcenter/10 ) { 405 | _smooth_3_horiz( tx, ty, wcenter, wside1, img2, tmp, n_thread ); 406 | _smooth_3_vert( tx, ty, wcenter, wside1, tmp, res, n_thread ); 407 | } else if( wside3 < wcenter/10 ) { 408 | _smooth_5_horiz( tx, ty, wcenter, wside1, wside2, img2, tmp, n_thread ); 409 | _smooth_5_vert( tx, ty, wcenter, wside1, wside2, tmp, res, n_thread ); 410 | } else { 411 | _smooth_7_horiz( tx, ty, wcenter, wside1, wside2, wside3, img2, tmp, n_thread ); 412 | _smooth_7_vert( tx, ty, wcenter, wside1, wside2, wside3, tmp, res, n_thread ); 413 | } 414 | 415 | if(remaining < 0.001) 416 | break; 417 | else { 418 | TData* tmp3; 419 | tmp3 = img2; 420 | img2 = res; 421 | res = tmp3; 422 | } 423 | } 424 | 425 | if(res!=old_res) { // copy to true res 426 | memcpy(old_res,res,tx*ty*sizeof(TData)); 427 | img2 = res; 428 | } 429 | if(_sigma>MAX_SIGMA) 430 | free(img2); 431 | free(tmp); 432 | } 433 | 434 | void _smooth_gaussian( UBYTE_image* img, float _sigma, UBYTE_image* res, int n_thread ) { 435 | ASSERT_SAME_SIZE(img,res); 436 | _smooth_gaussian_alltype(img->tx,img->ty,img->pixels,_sigma,res->pixels,n_thread); 437 | } 438 | 439 | 440 | /* compute gradient smoothed with Sobel mask 441 | */ 442 | void _compute_sobel_gradient( UBYTE_image* img, float_layers* grad, int n_thread ) { 443 | ASSERT_SAME_SIZE(img,grad); 444 | assert(grad->tz==2); 445 | int tx = img->tx; 446 | int ty = img->ty; 447 | UBYTE* tmp = NEWA(UBYTE,tx*ty); 448 | 449 | // compute horizontal gradient 450 | _smooth_121_horiz(tx,ty,img->pixels,tmp, n_thread); 451 | _diff_vert(tx,ty,tmp,grad->pixels); 452 | 453 | // compute vertical gradient 454 | _smooth_121_vert(tx,ty,img->pixels,tmp, n_thread); 455 | _diff_horiz(tx,ty,tmp,grad->pixels+tx*ty); 456 | 457 | // free everything 458 | free(tmp); 459 | } 460 | 461 | /* Compute the dx,dy gradient on the image based on a [-1,0,1] mask. 462 | =0 : no prior smoothing 463 | =1 : sobel smoothing 464 | */ 465 | void _compute_grad_101( UBYTE_image* img, int method, float_layers* grad, int n_thread ) { 466 | ASSERT_SAME_SIZE(img,grad); 467 | assert(grad->tz==2); 468 | 469 | // compute gradient 470 | if( method == 0 ) 471 | _compute_pure_gradient(img, grad); 472 | else if( method == 1 ) 473 | _compute_sobel_gradient(img, grad, n_thread); 474 | else 475 | assert(!"error: unknown method for compute_grad_101"); 476 | } 477 | 478 | 479 | /* Compute the Histogram of oriented gradient for each pixel. 480 | Number of orientations is determined by hog->tz; 481 | method determines orientation bining: 482 | =0 : atan + linear interpolation 483 | =1 : fast cos projection 484 | */ 485 | void _compute_hog( float_layers* grad, int method, float_layers* hog, int n_thread ) { 486 | ASSERT_SAME_SIZE(grad,hog); 487 | const int n_ori = hog->tz; 488 | const int npix = hog->tx*hog->ty; 489 | 490 | const float* dx = grad->pixels; 491 | const float* dy = grad->pixels + npix; 492 | 493 | if( method == 0 ) { 494 | // use atan 495 | memset(hog->pixels,0,n_ori*npix*sizeof(float)); 496 | int i; 497 | for(i=0; ipixels[ ((q_angle ) )*npix + i ] += (1-coef)*norm; 507 | hog->pixels[ ((q_angle+1)%n_ori)*npix + i ] += ( coef)*norm; 508 | } 509 | } else if (method == 1 ) { 510 | int l; 511 | #if defined(USE_OPENMP) 512 | #pragma omp parallel for num_threads(n_thread) 513 | #endif 514 | for(l=0; lpixels + l*npix; 519 | int i; 520 | for(i=0; i 0 ) ? value : 0; 523 | } 524 | } 525 | } else 526 | assert(!"error: unknown method for compute_hog"); 527 | } 528 | 529 | 530 | /* compute 8 directions of gradient per pixels 531 | using 4 oriented filters extremely simple like [-1,1] 532 | */ 533 | void _compute_hog_8_direct( UBYTE_image* image, float_layers* hog_out, int n_thread ) { 534 | ASSERT_SAME_SIZE(image,hog_out); 535 | assert(hog_out->tz==8); 536 | int j,tx=image->tx, ty=image->ty; 537 | int npix=tx*image->ty; 538 | 539 | // init output 540 | memset(hog_out->pixels,0,8*npix*sizeof(float)); 541 | 542 | // compute horizontal filter 543 | #if defined(USE_OPENMP) 544 | #pragma omp parallel for num_threads(n_thread) 545 | #endif 546 | for(j=0; jpixels + j*tx; 548 | UBYTE* lastimg = img + tx-1; 549 | float* hog0f = hog_out->pixels + 0*npix + j*tx; // first 550 | float* hog0l = hog0f+1; // last 551 | float* hog1f = hog_out->pixels + 4*npix + j*tx; // first 552 | float* hog1l = hog1f+1; // last 553 | 554 | for(; imgpixels + j*tx; 577 | UBYTE* lastimg = img + tx; 578 | const int offset = tx; 579 | UBYTE* img2 = img + offset; 580 | float* hog0f = hog_out->pixels + 2*npix + j*tx; // first 581 | float* hog0l = hog0f + offset; // last 582 | float* hog1f = hog_out->pixels + 6*npix + j*tx; // first 583 | float* hog1l = hog1f + offset; // last 584 | 585 | while(imgpixels + j*tx; 610 | UBYTE* lastimg = img + tx-1; 611 | const int offset = 1+tx; 612 | UBYTE* img2 = img + offset; 613 | float* hog0f = hog_out->pixels + 1*npix + j*tx; // first 614 | float* hog0l = hog0f + offset; // last 615 | float* hog1f = hog_out->pixels + 5*npix + j*tx; // first 616 | float* hog1l = hog1f + offset; // last 617 | 618 | while(imgpixels + j*tx; 641 | UBYTE* lastimg = img + tx-1; 642 | const int offset = 1-tx; 643 | UBYTE* img2 = img + offset; 644 | float* hog0f = hog_out->pixels + 7*npix + j*tx; // first 645 | float* hog0l = hog0f + offset; // last 646 | float* hog1f = hog_out->pixels + 3*npix + j*tx; // first 647 | float* hog1l = hog1f + offset; // last 648 | 649 | while(imgtx*hog->ty; 673 | int l; 674 | float* sum = NEWAC(float, npix); 675 | float* max = NEWAC(float, npix); 676 | 677 | // compute mean per pixel 678 | #if defined(USE_OPENMP) 679 | #pragma omp parallel for num_threads(n_thread) 680 | #endif 681 | for(l=0; ltz; l++) { 682 | float* p = sum; 683 | float* m = max; 684 | float* hog_pix = hog->pixels + l*npix; 685 | int i; 686 | for(i=0; imax) *m=v; 691 | } 692 | } 693 | 694 | // subtract coef*mean 695 | coef /= hog->tz; 696 | #if defined(USE_OPENMP) 697 | #pragma omp parallel for num_threads(n_thread) 698 | #endif 699 | for(l=0; ltz; l++) { 700 | float* p = sum; 701 | float* m = max; 702 | float* hog_pix = hog->pixels + l*npix; 703 | int i; 704 | for(i=0; i= Max ) 708 | *hog_pix = 0; 709 | else { 710 | *hog_pix = Max*(1 - (Max - (*hog_pix))/(Max - mean + 1e-8f)); 711 | if(*hog_pix<0) *hog_pix = 0; 712 | } 713 | hog_pix++; 714 | } 715 | } 716 | 717 | free(sum); 718 | free(max); 719 | } 720 | 721 | 722 | /* Pass the gradient image through a sigmoid 723 | */ 724 | void sigmoid_array( float_array* img, float coef, float offset, int n_thread ) { 725 | assert(coef>0); 726 | const int npix=img->tx; 727 | // float* p = img->pixels; 728 | // for(i=0; ipixels + start; 752 | int i; 753 | for(i=0; imaxindex) v=maxindex; 756 | int n = int(v); 757 | float w = v-n; 758 | *p++ = (1-w)*precom[n] + w*precom[n+1]; 759 | } 760 | } 761 | } 762 | 763 | 764 | /* Compute a spatially smoothed version of the HOG. 765 | */ 766 | void smooth_hog_gaussian( float_layers* hog, float smoothing, int n_thread ) { 767 | int l; 768 | const int npix = hog->tx*hog->ty; 769 | for(l=0; ltz; l++) 770 | _smooth_gaussian_alltype(hog->tx,hog->ty,hog->pixels+l*npix,smoothing,hog->pixels+l*npix, n_thread); 771 | } 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | -------------------------------------------------------------------------------- /src/hog.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___HOG_H___ 18 | #define ___HOG_H___ 19 | #include "array_types.h" 20 | 21 | 22 | /* * * * * * IMAGE SMOOTHING * * * * * * */ 23 | 24 | /* Smooth an image using a Gaussian filter. 25 | */ 26 | void _smooth_gaussian( UBYTE_image* img, float sigma, UBYTE_image* res, int n_thread ); 27 | 28 | 29 | 30 | 31 | 32 | /* * * * * * GRADIENT COMPUTATIONS * * * * * * */ 33 | 34 | /* Compute the dx,dy gradient on the image based on a [-1,0,1] mask. 35 | method 36 | =0 : no prior smoothing 37 | =1 : sobel smoothing 38 | */ 39 | void _compute_grad_101( UBYTE_image* img, int method, float_layers* grad, int n_thread ); 40 | 41 | 42 | 43 | 44 | 45 | /* * * * * * pixel-HOG COMPUTATIONS * * * * * * */ 46 | 47 | /* Compute the Histogram of oriented gradient for each pixel. 48 | Number of orientations is determined by hog->tz; 49 | method determines orientation bining: 50 | =0 : atan + linear interpolation 51 | =1 : fast cos projection 52 | */ 53 | void _compute_hog( float_layers* grad, int method, float_layers* hog, int n_thread ); 54 | 55 | /* Compute per-pixel HOG of 8 directions using a different pipeline. 56 | The method uses 4 oriented filters extremely simple ([-1,1]) 57 | */ 58 | void _compute_hog_8_direct( UBYTE_image* image, float_layers* hog_out, int n_thread ); 59 | 60 | 61 | /* Post-processing of the HOG: cross-orientation inhibition. 62 | for one pixel i and orientation o: hog[i,o] = max(0, hog[i,o] - coef*hog[i,:].mean()) 63 | This is useful for HOGs computed from cosinus projection. 64 | */ 65 | void subtract_mean_ori( float_layers* hog, float coef, int n_thread ); 66 | 67 | 68 | /* Pass the gradient image through a sigmoid 69 | lambda v: 2/(1 + exp(-coef*v + offset)) - 1 70 | */ 71 | void sigmoid_array( float_array* img, float coef, float offset, int n_thread ); 72 | 73 | 74 | /* Compute a spatially smoothed version of the HOG. 75 | */ 76 | void smooth_hog_gaussian( float_layers* hog, float smoothing, int n_thread ); 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/image.cpp: -------------------------------------------------------------------------------- 1 | #include "image.h" 2 | #include "std.h" 3 | 4 | 5 | /********** Create/Delete **********/ 6 | 7 | /* allocate a new image of size width x height */ 8 | image_t *image_new(int width, int height) 9 | { 10 | image_t *image = NEW(image_t); 11 | if(image == NULL) 12 | { 13 | err_printf( "Error: image_new() - not enough memory !\n"); 14 | exit(1); 15 | } 16 | image->width = width; 17 | image->height = height; 18 | image->stride = ( (width+3) / 4 ) * 4; 19 | image->data = NEWA(float, image->stride*height*sizeof(float)); 20 | if(image->data == NULL) 21 | { 22 | err_printf( "Error: image_new() - not enough memory !\n"); 23 | exit(1); 24 | } 25 | return image; 26 | } 27 | 28 | /* allocate a new image and copy the content from src */ 29 | image_t *image_cpy(const image_t *src) 30 | { 31 | image_t *dst = image_new(src->width, src->height); 32 | memcpy(dst->data, src->data, src->stride*src->height*sizeof(float)); 33 | return dst; 34 | } 35 | 36 | /* set all pixels values to zeros */ 37 | void image_erase(image_t *image) 38 | { 39 | memset(image->data, 0, image->stride*image->height*sizeof(float)); 40 | } 41 | 42 | 43 | /* multiply an image by a scalar */ 44 | void image_mul_scalar(image_t *image, float scalar) 45 | { 46 | int i; 47 | for( i=0 ; istride*image->height ; i++) 48 | image->data[i] *= scalar; 49 | } 50 | 51 | /* free memory of an image */ 52 | void image_delete(image_t *image) 53 | { 54 | if(image == NULL) 55 | { 56 | //err_printf( "Warning: Delete image --> Ignore action (image not allocated)\n"); 57 | } 58 | else 59 | { 60 | free(image->data); 61 | free(image); 62 | } 63 | } 64 | 65 | 66 | /* allocate a new color image of size width x height */ 67 | color_image_t *color_image_new(int width, int height) 68 | { 69 | size_t stride_channel = width*height*sizeof(float); 70 | char *buffer = NEWA(char, sizeof(color_image_t) + 3*stride_channel); 71 | if(buffer == NULL) 72 | { 73 | err_printf( "Error: color_image_new() - not enough memory !\n"); 74 | exit(1); 75 | } 76 | color_image_t *image = (color_image_t*) buffer; 77 | image->width = width; 78 | image->height = height; 79 | image->c1 = (float*) (buffer + sizeof(color_image_t)); 80 | image->c2 = (float*) (buffer + sizeof(color_image_t) + stride_channel); 81 | image->c3 = (float*) (buffer + sizeof(color_image_t) + 2*stride_channel); 82 | return image; 83 | } 84 | 85 | /* allocate a new color image and copy the content from src */ 86 | color_image_t *color_image_cpy(const color_image_t *src) 87 | { 88 | color_image_t *dst = color_image_new(src->width, src->height); 89 | memcpy(dst->c1, src->c1, 3*src->width*src->height*sizeof(float)); 90 | return dst; 91 | } 92 | 93 | /* set all pixels values to zeros */ 94 | void color_image_erase(color_image_t *image) 95 | { 96 | memset(image->c1, 0, 3*image->width*image->height*sizeof(float)); 97 | } 98 | 99 | /* free memory of a color image */ 100 | void color_image_delete(color_image_t *image) 101 | { 102 | if(image) 103 | { 104 | free(image); // the image is allocated such that the data is stored just after the pointer 105 | } 106 | } 107 | 108 | /* convert a color image to a gray-scale image */ 109 | image_t* image_gray_from_color( color_image_t* img ) 110 | { 111 | image_t* res = image_new(img->width, img->height); 112 | 113 | int n=0; 114 | for(int j=0; jheight; j++) 115 | for(int i=0; iwidth; i++,n++) 116 | res->data[i+j*res->stride] = (img->c1[n] + img->c2[n] + img->c3[n])/3; 117 | 118 | return res; 119 | } 120 | 121 | 122 | /* reallocate the memory of an image to fit the new width height */ 123 | void resize_if_needed_newsize(image_t *im, int w, int h) 124 | { 125 | if(im->width != w || im->height != h) 126 | { 127 | im->width = w; 128 | im->height = h; 129 | im->stride = ((w+3)/4)*4; 130 | float *data = NEWA(float,im->stride*h*sizeof(float)); 131 | if(data == NULL) 132 | { 133 | err_printf( "Error: resize_if_needed_newsize() - not enough memory !\n"); 134 | exit(1); 135 | } 136 | free(im->data); 137 | im->data = data; 138 | } 139 | } 140 | 141 | 142 | /************ Resizing *********/ 143 | 144 | /* resize an image to a new size (assumes a difference only in width) */ 145 | void image_resize_horiz(image_t *dst, const image_t *src) 146 | { 147 | int i; 148 | float real_scale = ((float) src->width-1) / ((float) dst->width-1); 149 | for(i = 0; i < dst->height; i++) 150 | { 151 | int j; 152 | for(j = 0; j < dst->width; j++) 153 | { 154 | float dx; 155 | int x; 156 | x = floor((float) j * real_scale); 157 | dx = j * real_scale - x; 158 | if(x >= (src->width - 1)) 159 | { 160 | dst->data[i * dst->stride + j] = 161 | src->data[i * src->stride + src->width - 1]; 162 | } 163 | else 164 | { 165 | dst->data[i * dst->stride + j] = 166 | (1.0f - dx) * src->data[i * src->stride + x ] + 167 | ( dx) * src->data[i * src->stride + x + 1]; 168 | } 169 | } 170 | } 171 | } 172 | 173 | /* resize an image to a new size (assumes a difference only in height) */ 174 | void image_resize_vert(image_t *dst, const image_t *src) 175 | { 176 | int i; 177 | float real_scale = ((float) src->height-1) / ((float) dst->height-1); 178 | for(i = 0; i < dst->width; i++) 179 | { 180 | int j; 181 | for(j = 0; j < dst->height; j++) 182 | { 183 | int y; 184 | float dy; 185 | y = floor((float) j * real_scale); 186 | dy = j * real_scale - y; 187 | if(y >= (src->height - 1)) 188 | { 189 | dst->data[j * dst->stride + i] = 190 | src->data[i + (src->height - 1) * src->stride]; 191 | } 192 | else 193 | { 194 | dst->data[j * dst->stride + i] = 195 | (1.0f - dy) * src->data[i + (y ) * src->stride] + 196 | ( dy) * src->data[i + (y + 1) * src->stride]; 197 | } 198 | } 199 | } 200 | } 201 | 202 | /* resize an image with bilinear interpolation to fit the new weidht, height ; reallocation is done if necessary */ 203 | void image_resize_bilinear_newsize(image_t *dst, const image_t *src, int new_width, int new_height) 204 | { 205 | resize_if_needed_newsize(dst,new_width,new_height); 206 | if(new_width < new_height) 207 | { 208 | image_t *tmp = image_new(new_width,src->height); 209 | image_resize_horiz(tmp,src); 210 | image_resize_vert(dst,tmp); 211 | image_delete(tmp); 212 | } 213 | else 214 | { 215 | image_t *tmp = image_new(src->width,new_height); 216 | image_resize_vert(tmp,src); 217 | image_resize_horiz(dst,tmp); 218 | image_delete(tmp); 219 | } 220 | } 221 | 222 | /* resize an image with bilinear interpolation */ 223 | image_t *image_resize_bilinear_scale(const image_t *src, float scale) { 224 | const int new_width = int(0.5 + src->width * scale); 225 | const int new_height = int(0.5 + src->height * scale); 226 | 227 | image_t *res = image_new(new_width,src->height); 228 | image_resize_bilinear_newsize(res, src, new_width, new_height); 229 | return res; 230 | } 231 | 232 | 233 | /* crop an image (in-place) */ 234 | void image_crop(image_t* img, int width, int height) 235 | { 236 | assert(width<=img->width); 237 | img->width = width; 238 | assert(height<=img->height); 239 | img->height = height; 240 | } 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /src/image.h: -------------------------------------------------------------------------------- 1 | #ifndef ___IMAGE_H___ 2 | #define ___IMAGE_H___ 3 | 4 | /********** STRUCTURES *********/ 5 | 6 | /* structure for 1-channel image */ 7 | typedef struct image_s 8 | { 9 | int width; /* Width of the image */ 10 | int height; /* Height of the image */ 11 | int stride; /* Width of the memory (width + paddind such that it is a multiple of 4) */ 12 | float *data; /* Image data */ 13 | } image_t; 14 | 15 | /* structure for 3-channels image stored with one layer per color, it assumes that c2 = c1+width*height and c3 = c2+width*height. */ 16 | typedef struct color_image_s 17 | { 18 | int width; /* Width of the image */ 19 | int height; /* Height of the image */ 20 | float *c1; /* Color 1 */ 21 | float *c2; /* Color 2 */ 22 | float *c3; /* Color 3 */ 23 | } color_image_t; 24 | 25 | 26 | /********** Create/Delete **********/ 27 | 28 | /* allocate a new image of size width x height */ 29 | image_t *image_new(int width, int height); 30 | 31 | /* allocate a new image and copy the content from src */ 32 | image_t *image_cpy(const image_t *src); 33 | 34 | /* set all pixels values to zeros */ 35 | void image_erase(image_t *image); 36 | 37 | /* free memory of an image */ 38 | void image_delete(image_t *image); 39 | 40 | /* multiply an image by a scalar */ 41 | void image_mul_scalar(image_t *image, float scalar); 42 | 43 | /* allocate a new color image of size width x height */ 44 | color_image_t *color_image_new(int width, int height); 45 | 46 | /* allocate a new color image and copy the content from src */ 47 | color_image_t *color_image_cpy(const color_image_t *src); 48 | 49 | /* set all pixels values to zeros */ 50 | void color_image_erase(color_image_t *image); 51 | 52 | /* free memory of a color image */ 53 | void color_image_delete(color_image_t *image); 54 | 55 | /* convert a color image to a gray-scale image */ 56 | image_t* image_gray_from_color( color_image_t* img ) ; 57 | 58 | /* reallocate the memory of an image to fit the new width height */ 59 | void resize_if_needed_newsize(image_t *im, int w, int h); 60 | 61 | 62 | /************ Resizing *********/ 63 | 64 | /* resize an image with bilinear interpolation */ 65 | image_t *image_resize_bilinear_scale(const image_t *src, float scale); 66 | 67 | /* resize an image with bilinear interpolation to fit the new weidht, height ; reallocation is done if necessary */ 68 | void image_resize_bilinear_newsize(image_t *dst, const image_t *src, int new_width, int new_height); 69 | 70 | /* resize a color image with bilinear interpolation */ 71 | color_image_t *color_image_resize_bilinear(const color_image_t *src, float scale); 72 | 73 | /* crop an image (in-place) */ 74 | void image_crop(image_t* img, int width, int height); 75 | 76 | #endif 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include "std.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "io.h" 23 | 24 | 25 | void output_correspondences( const char* out_filename, const corres_t* corres, int nb, float fx, float fy ) 26 | { 27 | assert(0x0,fy*r->y0,fx*r->x1,fy*r->y1,r->maxima,r->score); 33 | } 34 | if(out_filename) 35 | fclose(f); 36 | } 37 | 38 | /* IMAGE */ 39 | 40 | // PPM 41 | 42 | typedef struct 43 | { 44 | int magic; 45 | int width; 46 | int height; 47 | int pixmax; 48 | } ppm_hdr_t; 49 | 50 | static void get_magic(FILE *fp, ppm_hdr_t *ppm_hdr) 51 | { 52 | char str[1024]; 53 | fgets(str, 1024, fp); 54 | if(str[0] == 'P' && (str[1] <= '6' || str[1] >= '1')) 55 | { 56 | ppm_hdr->magic = str[1] - '0'; 57 | } 58 | } 59 | 60 | static int skip_comment(FILE *fp) 61 | { 62 | char c; 63 | do 64 | { 65 | c = (char) fgetc(fp); 66 | } 67 | while (c == ' ' || c == '\t' || c == '\n'); 68 | if(c == '#') 69 | { 70 | do 71 | { 72 | c = (char) fgetc(fp); 73 | 74 | } while(c != 0x0A); 75 | return 1; 76 | } 77 | else 78 | { 79 | ungetc(c, fp); 80 | } 81 | return 0; 82 | } 83 | 84 | /*----------------------------------------------------------------------------*/ 85 | 86 | static void skip_comments(FILE *fp) 87 | { 88 | while(skip_comment(fp)); 89 | } 90 | 91 | /*----------------------------------------------------------------------------*/ 92 | 93 | static int get_image_size(FILE *fp, ppm_hdr_t *ppm_hdr) 94 | { 95 | skip_comments(fp); 96 | if(fscanf(fp, "%d %d", &ppm_hdr->width, &ppm_hdr->height) != 2) 97 | { 98 | err_printf( "Warning: PGM --> File currupted\n"); 99 | return 0; 100 | } 101 | return 1; 102 | } 103 | 104 | /*----------------------------------------------------------------------------*/ 105 | 106 | static int get_pixmax(FILE *fp, ppm_hdr_t *ppm_hdr) 107 | { 108 | skip_comments(fp); 109 | ppm_hdr->pixmax = 1; 110 | if(ppm_hdr->magic == 2 || ppm_hdr->magic == 3 || ppm_hdr->magic == 5 || ppm_hdr->magic == 6) 111 | { 112 | if(fscanf(fp, "%d", &ppm_hdr->pixmax) != 1) 113 | { 114 | err_printf( "Warning: PGM --> pixmax not valid\n"); 115 | return 0; 116 | } 117 | } 118 | fgetc(fp); 119 | return 1; 120 | } 121 | 122 | /*----------------------------------------------------------------------------*/ 123 | 124 | static int get_ppm_hdr(FILE *fp, ppm_hdr_t *ppm_hdr) 125 | { 126 | get_magic(fp, ppm_hdr); 127 | if(!get_image_size(fp, ppm_hdr)) 128 | { 129 | return 0; 130 | } 131 | if(!get_pixmax(fp, ppm_hdr)) 132 | { 133 | return 0; 134 | } 135 | return 1; 136 | } 137 | 138 | static void raw_read_color(FILE *fp, color_image_t *image) 139 | { 140 | int i, size=image->height*image->width; 141 | for(i=0;ic1[i]=(float) fgetc(fp); 144 | image->c2[i]=(float) fgetc(fp); 145 | image->c3[i]=(float) fgetc(fp); 146 | } 147 | } 148 | 149 | color_image_t *color_image_pnm_load(FILE *fp) 150 | { 151 | color_image_t *image = NULL; 152 | ppm_hdr_t ppm_hdr; 153 | if(!get_ppm_hdr(fp, &ppm_hdr)) 154 | { 155 | return NULL; 156 | } 157 | switch(ppm_hdr.magic) 158 | { 159 | case 1: /* PBM ASCII */ 160 | case 2: /* PGM ASCII */ 161 | case 3: /* PPM ASCII */ 162 | case 4: /* PBM RAW */ 163 | case 5: /* PGM RAW */ 164 | err_printf( "color_image_pnm_load: only PPM raw with maxval 255 supported\n"); 165 | break; 166 | case 6: /* PPM RAW */ 167 | image = color_image_new(ppm_hdr.width, ppm_hdr.height); 168 | raw_read_color(fp, image); 169 | break; 170 | } 171 | return image; 172 | } 173 | 174 | // JPG 175 | 176 | color_image_t *color_image_jpeg_load(FILE *fp) 177 | { 178 | struct jpeg_decompress_struct cinfo; 179 | struct jpeg_error_mgr jerr; 180 | JSAMPARRAY buffer; 181 | int row_stride; 182 | int index = 0; 183 | color_image_t *image = NULL; 184 | float *r_p, *g_p, *b_p; 185 | JSAMPROW buffer_p; 186 | cinfo.err = jpeg_std_error(&jerr); 187 | jpeg_create_decompress(&cinfo); 188 | jpeg_stdio_src(&cinfo, fp); 189 | jpeg_read_header(&cinfo, TRUE); 190 | cinfo.out_color_space = JCS_RGB; 191 | cinfo.quantize_colors = FALSE; 192 | image = color_image_new(cinfo.image_width, cinfo.image_height); 193 | if(image == NULL) 194 | { 195 | return NULL; 196 | } 197 | jpeg_start_decompress(&cinfo); 198 | row_stride = cinfo.output_width * cinfo.output_components; 199 | buffer = (*cinfo.mem->alloc_sarray) 200 | ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); 201 | 202 | r_p = image->c1; 203 | g_p = image->c2; 204 | b_p = image->c3; 205 | 206 | while (cinfo.output_scanline < cinfo.output_height) 207 | { 208 | jpeg_read_scanlines(&cinfo, buffer, 1); 209 | buffer_p = buffer[0]; 210 | index = cinfo.output_width; 211 | while(index--) 212 | { 213 | *r_p++ = (float) *buffer_p++; 214 | *g_p++ = (float) *buffer_p++; 215 | *b_p++ = (float) *buffer_p++; 216 | } 217 | } 218 | jpeg_finish_decompress(&cinfo); 219 | jpeg_destroy_decompress(&cinfo); 220 | return image; 221 | } 222 | 223 | color_image_t * color_image_png_load( FILE* fp, const char* file_name ) 224 | { 225 | // read the header 226 | png_byte header[8]; 227 | fread(header, 1, 8, fp); 228 | 229 | if (png_sig_cmp(header, 0, 8)) 230 | { 231 | err_printf( "error: %s is not a PNG.\n", file_name); 232 | fclose(fp); 233 | return 0; 234 | } 235 | 236 | png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 237 | if (!png_ptr) 238 | { 239 | err_printf( "error: png_create_read_struct returned 0.\n"); 240 | fclose(fp); 241 | return 0; 242 | } 243 | 244 | // create png info struct 245 | png_infop info_ptr = png_create_info_struct(png_ptr); 246 | if (!info_ptr) 247 | { 248 | err_printf( "error: png_create_info_struct returned 0.\n"); 249 | png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); 250 | fclose(fp); 251 | return 0; 252 | } 253 | 254 | // create png info struct 255 | png_infop end_info = png_create_info_struct(png_ptr); 256 | if (!end_info) 257 | { 258 | err_printf( "error: png_create_info_struct returned 0.\n"); 259 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); 260 | fclose(fp); 261 | return 0; 262 | } 263 | 264 | // the code in this if statement gets called if libpng encounters an error 265 | if (setjmp(png_jmpbuf(png_ptr))) { 266 | err_printf( "error from libpng\n"); 267 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 268 | fclose(fp); 269 | return 0; 270 | } 271 | 272 | // init png reading 273 | png_init_io(png_ptr, fp); 274 | 275 | // let libpng know you already read the first 8 bytes 276 | png_set_sig_bytes(png_ptr, 8); 277 | 278 | // read all the info up to the image data 279 | png_read_info(png_ptr, info_ptr); 280 | 281 | // variables to pass to get info 282 | int bit_depth, color_type; 283 | png_uint_32 temp_width, temp_height; 284 | 285 | // get info about png 286 | png_get_IHDR(png_ptr, info_ptr, &temp_width, &temp_height, &bit_depth, &color_type, 287 | NULL, NULL, NULL); 288 | 289 | // Update the png info struct. 290 | png_read_update_info(png_ptr, info_ptr); 291 | 292 | // Row size in bytes. 293 | int rowbytes = png_get_rowbytes(png_ptr, info_ptr); 294 | 295 | // Allocate the image_data as a big block, to be given to opengl 296 | png_byte * image_data; 297 | image_data = NEWA(png_byte, rowbytes * temp_height); 298 | assert(image_data!=NULL); 299 | 300 | // row_pointers is for pointing to image_data for reading the png with libpng 301 | png_bytep * row_pointers = NEWA(png_bytep, temp_height); 302 | assert(row_pointers!=NULL); 303 | 304 | // set the individual row_pointers to point at the correct offsets of image_data 305 | unsigned int i; 306 | for (i = 0; i c1[i] = image->c2[i] = image->c3[i] = image_data[i]; 318 | 319 | } 320 | else if( color_type == 2 ) { 321 | assert((unsigned)rowbytes == 3*temp_width || !"error: not a proper color png image"); 322 | for(i=0; ic1[i] = image_data[3*i+0]; 324 | image->c2[i] = image_data[3*i+1]; 325 | image->c3[i] = image_data[3*i+2]; 326 | } 327 | } else 328 | assert(!"error: unknown PNG color type" ); 329 | 330 | // clean up 331 | png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); 332 | free(row_pointers); 333 | free(image_data); 334 | 335 | return image; 336 | } 337 | 338 | // GENERAL LOAD 339 | 340 | /* load a color image from a file */ 341 | color_image_t *color_image_load(const char *fname) 342 | { 343 | FILE *fp; 344 | char magic[2]; 345 | unsigned short *magic_short = (unsigned short *) magic; 346 | color_image_t *image = NULL; 347 | if((fp = fopen(fname, "rb")) == NULL) 348 | { 349 | err_printf( "Warning: color_image_load() - can not open file `%s' !\n", fname); 350 | return NULL; 351 | } 352 | fread(magic, sizeof(char), 2, fp); 353 | rewind(fp); 354 | if(magic_short[0] == 0xd8ff) 355 | { 356 | image = color_image_jpeg_load(fp); 357 | } 358 | else if(magic[0]=='P' && (magic[1]=='6' || magic[1]=='5')) 359 | { /* PPM raw */ 360 | image = color_image_pnm_load(fp); 361 | } 362 | else if( magic[0]==-119 && magic[1]=='P' ) 363 | { 364 | image = color_image_png_load( fp, fname ); 365 | } 366 | else 367 | { 368 | err_printf( "Warning: color_image_load(%s) - image format not recognized\n",fname); 369 | } 370 | fclose(fp); 371 | return image; 372 | } 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /src/io.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___IO_H___ 18 | #define ___IO_H___ 19 | #include 20 | 21 | #include "image.h" 22 | #include "deep_matching.h" 23 | 24 | // output correspondences to a file or on the stdout 25 | void output_correspondences( const char* out_filename, const corres_t* corres, int nb, float fx, float fy ); 26 | 27 | /* load a color image from a file */ 28 | color_image_t *color_image_load(const char *fname); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include "mainwindow.h" 18 | #include 19 | #include 20 | 21 | int main (int argc, char **argv) 22 | { 23 | QApplication a (argc, argv); 24 | MainWindow w; 25 | w.show (); 26 | return a.exec (); 27 | } 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/main.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___MAIN_H___ 18 | #define ___MAIN_H___ 19 | 20 | #define EXE_OPTIONS 0 21 | #define MATLAB_OPTIONS 1 22 | #define PYTHON_OPTIONS 2 23 | 24 | #include "deep_matching.h" 25 | 26 | void usage(const int language); 27 | 28 | const char* parse_options(dm_params_t *params, scalerot_params_t *sr_params, \ 29 | bool *use_scalerot, float *fx, float *fy, const int argc, \ 30 | const char **argv, const int language, \ 31 | image_t **im1, image_t **im2); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | #include 5 | #include 6 | #define IS_PUNC(c) ((c) == ' ' /*|| (c) == '.' || (c) == ','*/) 7 | 8 | MainWindow::MainWindow(QWidget *parent) : 9 | QMainWindow(parent), 10 | ui(new Ui::MainWindow) 11 | { 12 | init(); 13 | ui->setupUi(this); 14 | } 15 | void MainWindow::init() 16 | { 17 | dmPkg = new str_dmPkg; 18 | dmPkg->argc = 0; 19 | dmPkg->argv = NULL; 20 | dmPkg->configDM = QString(); 21 | dmPkg->fx = 1.0; 22 | dmPkg->fy = 1.0; 23 | dmPkg->nameImgRef = QString(); 24 | dmPkg->nameImgMtch = QString(); 25 | dmPkg->imRef = NULL; 26 | dmPkg->imMtch = NULL; 27 | dmPkg->out_filename = NULL; 28 | dmPkg->use_scalerot = false; 29 | set_default_dm_params(&dmPkg->params); 30 | set_default_scalerot_params(&dmPkg->sr_params); 31 | } 32 | MainWindow::~MainWindow() 33 | { 34 | delete ui; 35 | } 36 | 37 | int word_count(const char *str) 38 | { 39 | int count = 0; 40 | const char *s; 41 | 42 | // Skip leading punctuation and white-space 43 | while(IS_PUNC(*str)) 44 | { 45 | str++; 46 | } 47 | s = str; 48 | 49 | while(*str) 50 | { 51 | while(*s && !IS_PUNC(*s)) 52 | { s++; } 53 | if(str - s) { count++;} 54 | while(IS_PUNC(*s)) { s++; } 55 | str = s; 56 | } 57 | return count; 58 | } 59 | void copyString(char* target, std::string source) 60 | { 61 | for(int i=0; i> word; ) 72 | { 73 | argv[i] = new char[word.length()]; 74 | std::strcpy(argv[i], word.c_str()); 75 | // copyString(argv[i], word); 76 | // printf("%s\n",argv[i]); 77 | i++; 78 | } 79 | } 80 | void MainWindow::on_exeDeepMtch_clicked() 81 | { 82 | dmPkg->argc = word_count(dmPkg->configDM.toStdString().c_str()); 83 | dmPkg->argv = new char*[dmPkg->argc]; 84 | stringToDyn2dArray(dmPkg->configDM.toStdString(), dmPkg->argv); 85 | // for(int i=0; iargc; i++) 86 | // { printf("%s\n",dmPkg->argv[i]); } 87 | 88 | // parse options 89 | deepMtchInfo configDM; 90 | configDM.initialization(&dmPkg->params, &dmPkg->sr_params, \ 91 | &dmPkg->use_scalerot, &dmPkg->fx, &dmPkg->fy,\ 92 | dmPkg->argc, dmPkg->argv, EXE_OPTIONS, \ 93 | &dmPkg->imRef, &dmPkg->imMtch, dmPkg->out_filename); 94 | // (dm_params_t *params, scalerot_params_t *sr_params,\ 95 | // bool *use_scalerot, float *fx, float *fy, \ 96 | // int argc, char **argv, const int language,\ 97 | // image_t **im1, image_t **im2) 98 | // // compute deep matching 99 | // float_image* corres = use_scalerot ? 100 | // deep_matching_scale_rot( im1, im2, ¶ms, &sr_params ) : 101 | // deep_matching ( im1, im2, ¶ms, NULL ); // standard call 102 | 103 | // // save result 104 | // output_correspondences( out_filename, (corres_t*)corres->pixels, corres->ty, fx, fy ); 105 | 106 | // free_image(corres); 107 | // image_delete(im1); 108 | // image_delete(im2); 109 | } 110 | 111 | void MainWindow::on_loadImgRef_clicked() 112 | { 113 | dmPkg->nameImgRef = QFileDialog::getOpenFileName(this, QObject::tr("Open Reference Image"), 114 | "/home/jiang/CvDataset/KITTI/tracking_module/training/image_02/0000", \ 115 | QObject::tr("Image Files (*.png *.jpg *.bmp)")); 116 | 117 | if(dmPkg->nameImgRef.size()<1) 118 | { std::cout<<"Reference image was not loaded ...\n"; return; } 119 | 120 | color_image_t *cimRef = color_image_load(dmPkg->nameImgRef.toStdString().c_str()); 121 | dmPkg->imRef = image_gray_from_color(cimRef); 122 | ui->imgRefDisp->setPixmap(QPixmap(dmPkg->nameImgRef.toStdString().c_str(), 0, Qt::AutoColor)); 123 | color_image_delete(cimRef); 124 | dmPkg->argv; 125 | } 126 | 127 | void MainWindow::on_loadImgMtch_clicked() 128 | { 129 | dmPkg->nameImgMtch = QFileDialog::getOpenFileName(this, QObject::tr("Open Match Image"), 130 | "/home/jiang/CvDataset/KITTI/tracking_module/training/image_02/0000", \ 131 | QObject::tr("Image Files (*.png *.jpg *.bmp)")); 132 | 133 | if(dmPkg->nameImgMtch.size()<1) 134 | { std::cout<<"Reference image was not loaded ...\n"; return; } 135 | 136 | color_image_t *cimMtch = color_image_load(dmPkg->nameImgMtch.toStdString().c_str()); 137 | dmPkg->imMtch = image_gray_from_color(cimMtch); 138 | ui->imgMtchDisp->setPixmap(QPixmap(dmPkg->nameImgMtch.toStdString().c_str(), 0, Qt::AutoColor)); 139 | color_image_delete(cimMtch); 140 | } 141 | 142 | void MainWindow::on_help_clicked() 143 | { 144 | deepMtchInfo displayInfo; 145 | displayInfo.usage(0); 146 | } 147 | 148 | 149 | void MainWindow::on_configDM_clicked() 150 | { 151 | bool ok; 152 | QString defaultConfig = "-nt 0 | python viz.py"; 153 | dmPkg->configDM = QInputDialog::getText(this, tr("QInputDialog::getText()"), 154 | tr("Configuration"), QLineEdit::Normal, 155 | defaultConfig, &ok); 156 | 157 | if ( ok && dmPkg->configDM.compare(defaultConfig, Qt::CaseSensitive) ) { 158 | std::cout<<"User setting configured...\n"; 159 | } else { 160 | std::cout<<"Use default setting...\n"; 161 | } 162 | QString imgNames = dmPkg->nameImgRef.right(10) + " " + dmPkg->nameImgMtch.right(10); 163 | dmPkg->configDM = imgNames + " "+ dmPkg->configDM + " " + imgNames; 164 | } 165 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "image.h" 18 | #include "io.h" 19 | #include "deepmtchinfo.h" 20 | #include 21 | 22 | struct str_dmPkg 23 | { 24 | QString nameImgRef; 25 | QString nameImgMtch; 26 | QString configDM; 27 | image_t *imRef; 28 | image_t *imMtch; 29 | dm_params_t params; 30 | scalerot_params_t sr_params; 31 | int argc; 32 | char **argv; 33 | float fx; 34 | float fy; 35 | bool use_scalerot; 36 | char *out_filename; 37 | }; 38 | 39 | namespace Ui { 40 | class MainWindow; 41 | } 42 | 43 | class MainWindow : public QMainWindow 44 | { 45 | Q_OBJECT 46 | 47 | public: 48 | explicit MainWindow(QWidget *parent = 0); 49 | ~MainWindow(); 50 | void init(); 51 | 52 | private slots: 53 | void on_exeDeepMtch_clicked(); 54 | 55 | void on_loadImgRef_clicked(); 56 | 57 | void on_loadImgMtch_clicked(); 58 | 59 | void on_help_clicked(); 60 | 61 | void on_configDM_clicked(); 62 | 63 | protected: 64 | str_dmPkg *dmPkg; 65 | protected: 66 | Ui::MainWindow *ui; 67 | }; 68 | 69 | #endif // MAINWINDOW_H 70 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 960 10 | 713 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 250 23 | 0 24 | 25 | 26 | 27 | 28 | 250 29 | 16777215 30 | 31 | 32 | 33 | 0 34 | 35 | 36 | 37 | Img Info 38 | 39 | 40 | 41 | 42 | 10 43 | 30 44 | 220 45 | 27 46 | 47 | 48 | 49 | 50 | 220 51 | 0 52 | 53 | 54 | 55 | 56 | 16777215 57 | 16777215 58 | 59 | 60 | 61 | Qt::Horizontal 62 | 63 | 64 | 65 | Load Img Ref 66 | 67 | 68 | 69 | 70 | Load Img Mtch 71 | 72 | 73 | 74 | 75 | 76 | 77 | 130 78 | 70 79 | 111 80 | 27 81 | 82 | 83 | 84 | Deep Matching 85 | 86 | 87 | 88 | 89 | 90 | 140 91 | 570 92 | 99 93 | 27 94 | 95 | 96 | 97 | help 98 | 99 | 100 | 101 | 102 | 103 | 10 104 | 70 105 | 99 106 | 27 107 | 108 | 109 | 110 | config. DM 111 | 112 | 113 | 114 | 115 | 116 | Tab 2 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Reference Image 125 | 126 | 127 | 128 | 129 | 130 | 131 | Match Image 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 0 141 | 0 142 | 960 143 | 25 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/maxfilter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include "std.h" 18 | #include "maxfilter.h" 19 | #include "omp.h" 20 | 21 | void _max_filter_3_horiz( float_image* img, float_image* res, int n_thread ) { 22 | ASSERT_SAME_SIZE(img,res); 23 | int j; 24 | const int tx = img->tx; 25 | const int ty = img->ty; 26 | 27 | #if defined(USE_OPENMP) 28 | #pragma omp parallel for num_threads(n_thread) 29 | #endif 30 | for(j=0; jpixels + j*tx; 33 | float *r = res->pixels + j*tx; 34 | 35 | float m = MAX(p[0],p[1]); 36 | *r++ = m; 37 | 38 | for(i=1; itx; 51 | const int ty = img->ty; 52 | int j; 53 | 54 | for(j=0; jpixels + j*tx; 57 | float *r = res->pixels + j*tx; 58 | 59 | for(i=0; ipixels+(ty-1)*tx,res->pixels+(ty-2)*tx,tx*sizeof(float)); // copy last row 64 | 65 | for(j=ty-2; j>0; j--) { 66 | int i; 67 | float *p = res->pixels + (j-1)*tx; 68 | float *r = res->pixels + j*tx; 69 | 70 | for(i=0; ity>128? n_thread : 1); 80 | } 81 | 82 | void _max_filter_3_layers( float_layers* img, float_layers* res, int n_thread ) { 83 | ASSERT_SAME_LAYERS_SIZE(img,res); 84 | const long npix = img->tx*img->ty; 85 | 86 | int l; 87 | #if defined(USE_OPENMP) 88 | omp_set_nested(0); 89 | omp_set_dynamic(0); 90 | #pragma omp parallel for num_threads(n_thread) 91 | #endif 92 | for(l=0; ltz; l++) { 93 | float_image img2 = {img->pixels + l*npix,img->tx,img->ty}; 94 | float_image res2 = {res->pixels + l*npix,res->tx,res->ty}; 95 | _max_filter_3( &img2, &res2, n_thread ); 96 | } 97 | } 98 | 99 | 100 | /* Subsample an array, equivalent to res = img[:,::2,::2] 101 | */ 102 | void _subsample2( float_layers* img, float_layers* res, int n_thread ) { 103 | const int n_layers = res->tz; 104 | assert( img->tz==n_layers ); 105 | const int tx = res->tx; 106 | const int ty = res->ty; 107 | assert( (img->tx+1)/2 == tx ); 108 | assert( (img->ty+1)/2 == ty ); 109 | 110 | long l; 111 | #if defined(USE_OPENMP) 112 | #pragma omp parallel for num_threads(n_thread) 113 | #endif 114 | for(l=0; lpixels + (l*img->ty + (2*y))*img->tx ; 118 | float* r = res->pixels + (l*ty + y)*tx; 119 | for(x=0; xtz; 129 | assert( img->tz==n_layers ); 130 | const int tx = res->tx; 131 | const int ty = res->ty; 132 | assert( tx>=2 && ty>=2 ); 133 | const int tx2 = img->tx; 134 | const int ty2 = img->ty; 135 | assert( (tx2+1)/2 == tx ); // tx2=3 => tx=2 136 | assert( (ty2+1)/2 == ty ); 137 | 138 | long l; 139 | #if defined(USE_OPENMP) 140 | #pragma omp parallel for num_threads(n_thread) 141 | #endif 142 | for(l=0; lpixels + l*tx*ty, 0, tx*ty*sizeof(float)); 145 | 146 | int x,y; 147 | for(y=0; ypixels + (l*ty2 + (2*y))*tx2 ; 149 | float* r = res->pixels + (l*ty + y)*tx; 150 | float* r2 = (y+1v) ? m : v 153 | 154 | // even rows of img 155 | for(x=0; xpixels)%tx2 == 0); 170 | 171 | // odd rows of img 172 | if (ypixels)%tx2 == 0); 196 | 197 | #undef maxEq 198 | } 199 | } 200 | } 201 | 202 | 203 | 204 | /* Subsample an array, equivalent to res = trueimg[:,offset_y::2,offset_x::2] 205 | except at boundaries, where the rules are a bit more complex: 206 | if img->tx % 2 == 0: 207 | if offset_x % 2 == 0: 208 | trueimg[offset_x+img->tx-1] is also sampled 209 | else: 210 | trueimg[offset_x] is also sampled 211 | elif img->tx % 2 == 1: 212 | trueimg[offset_x] is also sampled 213 | 214 | ...and likewise for y dimension. 215 | */ 216 | void _subsample2_offset( float_layers* img, int_image* offsets, float_layers* res, int n_thread ) { 217 | const int n_layers = res->tz; 218 | assert( img->tz==n_layers ); 219 | assert( offsets->tx==2 && offsets->ty==n_layers ); 220 | const int tx = res->tx; 221 | const int ty = res->ty; 222 | assert( (img->tx+2)/2 == tx ); 223 | assert( (img->ty+2)/2 == ty ); 224 | 225 | long l; 226 | #if defined(USE_OPENMP) 227 | #pragma omp parallel for num_threads(n_thread) 228 | #endif 229 | for(l=0; lpixels[2*l]+0x10000) % 2; 232 | const int oy = (offsets->pixels[2*l+1]+0x10000) % 2; 233 | assert(ox>=0 && oy>=0); 234 | #define get_img_2pos(x,tx,ox) MAX(0, MIN(img->tx-1, 2*x-ox)) 235 | 236 | for(y=0; ypixels + (l*img->ty + get_img_2pos(y,ty,oy))*img->tx; 238 | float* r = res->pixels + (l*ty + y)*tx; 239 | r[0] = i[get_img_2pos(0,tx,ox)]; // first is special case 240 | for(x=1; xtz; 257 | assert( img->tz==n_layers ); 258 | const int tx = res->tx; 259 | const int ty = res->ty; 260 | assert( (img->tx)/2 == tx ); 261 | assert( (img->ty)/2 == ty ); 262 | 263 | long l; 264 | #if defined(USE_OPENMP) 265 | #pragma omp parallel for num_threads(n_thread) 266 | #endif 267 | for(l=0; lpixels + (l*img->ty + (2*y))*img->tx ; 271 | float* j = i + img->tx; 272 | float* r = res->pixels + (l*ty + y)*tx; 273 | for(x=0; xtz; 287 | assert( img->tz==n_layers ); 288 | const int tx = res->tx; 289 | const int ty = res->ty; 290 | assert( (img->tx)/2 == tx ); 291 | assert( (img->ty)/2 == ty ); 292 | 293 | long l; 294 | #if defined(USE_OPENMP) 295 | #pragma omp parallel for num_threads(n_thread) 296 | #endif 297 | for(l=0; lpixels + (l*img->ty + (2*y))*img->tx ; 301 | float* j = i + img->tx; 302 | float* r = res->pixels + (l*ty + y)*tx; 303 | for(x=0; x 325 | static pthread_mutex_t mutex0, mutex1; 326 | 327 | 328 | static inline void add_one_max( maxima* list, int scale, int layer, int x, int y, float score ) { 329 | pthread_mutex_lock (&mutex0); 330 | if( list->n_alloc <= list->n_elems ) { 331 | list->n_alloc = 3*(list->n_alloc+64)/2; 332 | list->list = (one_max*)realloc(list->list, sizeof(one_max)*list->n_alloc); 333 | } 334 | one_max* m = &list->list[list->n_elems++]; 335 | m->scale = scale; 336 | m->layer = layer; 337 | m->x = x; 338 | m->y = y; 339 | m->score = score; 340 | pthread_mutex_unlock (&mutex0); 341 | } 342 | 343 | 344 | 345 | void _get_list_parents( int_cube* children, int_image* res ) { 346 | const int np2 = children->tz; 347 | assert( np2 == res->tx ); 348 | const int n_cells_at_prev_scale = res->ty; 349 | int* parents = res->pixels; 350 | memset(parents,0xFF,n_cells_at_prev_scale*np2*sizeof(int)); // =-1 by default 351 | int i,j,ncells=children->tx*children->ty; 352 | int* cur = children->pixels; 353 | for(i=0; itz; 363 | int_image res = {NEWA(int, n_cells_at_prev_scale*np2 ), np2, n_cells_at_prev_scale}; 364 | _get_list_parents( children, &res ); 365 | return res.pixels; 366 | } 367 | 368 | 369 | /* Return a list of local maxima in the scale-space of scores 370 | */ 371 | void _extract_maxima( res_scale* scales, int n_scales, float_array* sc_factor, float th, int min_scale, float nlpow, 372 | int check_parents, int check_children, int nobordure, int_image* res_out, int n_thread ) { 373 | 374 | assert( sc_factor->tx == n_scales ); 375 | assert( min_scale>=0 && min_scalepixels; 377 | 378 | maxima res = {NULL,0,0}; 379 | int s; 380 | 381 | // compute the maximum filter for each scale separately 382 | const int min_scale_max = MAX(0,min_scale); 383 | for(s=min_scale_max; sres_map; 386 | assert(sc->max_map.pixels==NULL); // not already allocated 387 | sc->max_map = r; // initialize tx,ty,tz 388 | sc->max_map.pixels = NEWA(float, r.tx*r.ty*r.tz ); 389 | _max_filter_3_layers( &r, &sc->max_map, n_thread ); 390 | } 391 | 392 | // then localize the local maxima in the scale-space 393 | for(s=min_scale; sres_map.tx; 396 | const int ty = sc->res_map.ty; 397 | const long npix = tx*ty; 398 | const int n_layers = sc->assign.tx; 399 | 400 | // helpful values... 401 | const int f = sc->f; 402 | 403 | const int upper_tx = (s+1grid.tx*sc->grid.ty) : NULL; 414 | 415 | const int down_tx = (s>min_scale_max) ? sc[-1].res_map.tx : 0; 416 | const int down_ty = (s>min_scale_max) ? sc[-1].res_map.ty : 0; 417 | const int down_npix = down_tx*down_ty; 418 | const float down_scf= (s>min_scale_max) ? scf[s]/scf[s-1] : 0; 419 | const int nc2 = (s>min_scale_max) ? sc->children.tz : 0; 420 | const int nc = (int)sqrt(nc2); 421 | const int down_gap = sc->patch_size/4; 422 | const int down_f = (s>min_scale_max) ? sc[-1].f : 0; 423 | const float* down_layers = (s>min_scale_max) ? sc[-1].max_map.pixels : NULL; 424 | const int* down_assign = (s>min_scale_max) ? sc[-1].assign.pixels : NULL; 425 | 426 | int l; 427 | #if defined(USE_OPENMP) 428 | #pragma omp parallel for num_threads(n_thread) 429 | #endif 430 | for(l=0; lassign.pixels[l]<0) continue; // no layer for this 433 | float* res_map = sc->res_map.pixels + sc->assign.pixels[l]*npix; 434 | float* max_map = sc->max_map.pixels + sc->assign.pixels[l]*npix; 435 | 436 | // for each point which is a local maxima, check 437 | int i; 438 | for(i=0; ith && res_map[i]==max_map[i] ) { 440 | // ok, we have a maxima at this scale 441 | const float val = res_map[i]; 442 | int x = i%tx; 443 | int y = i/tx; 444 | if( nobordure && (x<1 || y<1 || x>=tx-1 || y>=ty-1) ) continue; // not interested in maxima on image bordures 445 | 446 | //if(s==2 && l==344 && x==41 && y==4) getchar(); 447 | 448 | // now compare with lower scale 449 | if( check_children && s>min_scale_max ) { 450 | float valref = down_scf*val; 451 | int* children = sc->children.pixels + l*nc2; 452 | int u,v,ok=1; 453 | for(v=0; ok && v=0 && uy=0 && ux and eliminate non-maxima 470 | if( check_parents && list_parents ) { 471 | float valref = upper_scf*val; 472 | const int* parents = list_parents + l*np2; 473 | int u,v,ok=1; 474 | for(v=0; ok && v=0 && uy=0 && uxtx = 5; 502 | res_out->ty = res.n_elems; 503 | res_out->pixels = (int*)res.list; 504 | } 505 | 506 | 507 | /* Return the best local children assignement in a 3x3 neigborhood 508 | l,u,v is the approximate position of the children in the corresponding response map[l,v,u] 509 | */ 510 | static inline float _local_argmax( long l, int u, int v, const float_layers* map, int extended, /*float reg,*/ int* x, int* y ) { 511 | assert(0<=l && ltz); 512 | int umin = MAX(0, u-1); 513 | int vmin = MAX(0, v-1); 514 | const int etx = map->tx-extended; // because of extended response map 515 | const int ety = map->ty-extended; 516 | int umax = MIN(etx, u+2); 517 | int vmax = MIN(ety, v+2); 518 | 519 | // determine best children in the neighborhood (argmax) 520 | const int tx = map->tx; 521 | int i,j,bestx=0,besty=0; float m=0.f; 522 | const float *r = map->pixels + l*tx*map->ty; 523 | for(j=vmin; jm) {m=r[p]; bestx=i; besty=j;} 527 | } 528 | *x = bestx; 529 | *y = besty; 530 | return m; 531 | } 532 | 533 | /* Return the best assignment (= list of correspondences) for a given maxima 534 | from a pyramid top, this function returns 535 | a list of weigthed correspondences (matches) between 536 | img0 pixels and img1 pixels 537 | */ 538 | void _argmax_correspondences_rec( res_scale* scales, int s, int l, int x, int y, 539 | float_cube* res0, int step0, float_cube* res1, int step1, 540 | int index, float score ) { 541 | res_scale* sc = scales + s; 542 | 543 | if(s==0) { 544 | const int x0 = sc->grid.pixels[2*l]; 545 | const int y0 = sc->grid.pixels[2*l+1]; 546 | const int x1 = sc->f * x; 547 | const int y1 = sc->f * y; 548 | 549 | const int qx0 = x0/step0; 550 | const int qy0 = y0/step0; 551 | //assert(0<=l && lres_map.tz); 552 | 553 | if( qx0tx && qy0ty ) { 554 | assert(qx0>=0 && qy0>=0); 555 | float* r0 = res0->pixels + ((qy0*res0->tx + qx0))*res0->tz; 556 | //assert(res0->pixels<=r0 && r0+5pixels+res0->tx*res0->ty*res0->tz); 557 | 558 | pthread_mutex_lock (&mutex0); 559 | if( score > r0[4] ) { 560 | // r[0:2] = pos in img0 561 | r0[0] = x0; 562 | r0[1] = y0; 563 | // r[2:4] = pos in img1 564 | r0[2] = x1; 565 | r0[3] = y1; 566 | // r[4] = score 567 | r0[4] = score; 568 | r0[5] = index; 569 | } 570 | pthread_mutex_unlock (&mutex0); 571 | 572 | const int qx1 = x1/step1; 573 | const int qy1 = y1/step1; 574 | assert(qx1>=0 && qy1>=0); 575 | if( qx1tx && qy1ty ) { 576 | float* r1 = res1->pixels + ((qy1)*res1->tx + (qx1))*res1->tz; 577 | //assert(res1->pixels<=r1 && r1+5pixels+res1->tx*res1->ty*res1->tz); 578 | pthread_mutex_lock (&mutex1); 579 | if( score > r1[4] ) { 580 | // r[0:2] = pos in img0 581 | r1[0] = x0; 582 | r1[1] = y0; 583 | // r[2:4] = pos in img1 584 | r1[2] = x1; 585 | r1[3] = y1; 586 | // r[4] = score 587 | r1[4] = score; 588 | r1[5] = index; 589 | } 590 | pthread_mutex_unlock (&mutex1); 591 | } 592 | } 593 | } else { 594 | // mark this maximum as already processed 595 | assert(0<=l && lassign.tx); 596 | if( sc->passed.pixels ) { 597 | const long truel = sc->assign.pixels[l]; 598 | const long offset = ((truel*sc->true_shape[1] + MAX(0,y))*sc->true_shape[0] + MAX(0,x)) % sc->passed.tx; 599 | //pthread_mutex_lock (&mutex); 600 | int useless = ( sc->passed.pixels[offset] >= score ); 601 | if(!useless) sc->passed.pixels[offset] = score; 602 | //pthread_mutex_unlock (&mutex); 603 | if(useless) return; // this maximum was already investigated with a better score 604 | } 605 | 606 | const int f = sc->f; 607 | const res_scale* lower = &scales[s-1]; 608 | const int lower_f = lower->f; 609 | // position in lower response map 610 | x *= f/lower_f; 611 | y *= f/lower_f; 612 | const int lower_gap = sc->patch_size/(4*lower_f); // gap is equal to patch_size/4 in absolute size 613 | const int nc2 = sc->children.tz; 614 | const int nc = (nc2==4) ? 2 : 3; 615 | const int* children = sc->children.pixels + l*nc2; 616 | const int* lower_ass = lower->assign.pixels; 617 | 618 | // for all children 619 | int u,v,c=0; 620 | for(v=0; voffsets.pixels ) { 633 | // take offsets into account 634 | xc -= lower->offsets.pixels[2*l+0]; 635 | yc -= lower->offsets.pixels[2*l+1]; 636 | ex = 0; // no extension... maybe 637 | } 638 | 639 | // position of children in child1 = parent1 - (parent0-child0) 640 | int xb, yb; 641 | float child_score = _local_argmax( lower_ass[ch], xc, yc, &lower->res_map, ex, &xb, &yb ); 642 | 643 | if( lower->offsets.pixels ) { 644 | // back to real image coordinates 645 | xb += lower->offsets.pixels[2*l+0]; 646 | yb += lower->offsets.pixels[2*l+1]; 647 | } 648 | 649 | if( child_score ) 650 | _argmax_correspondences_rec( scales, s-1, ch, xb, yb, res0, step0, res1, step1, index, score + child_score ); 651 | } 652 | } 653 | } 654 | } 655 | 656 | 657 | void _argmax_correspondences( res_scale* scales, int s, int l, int x, int y, float score, 658 | float_cube* res0, int step0, float_cube* res1, int step1, 659 | int index ) { 660 | assert(res0->tz==6); 661 | if(res1) assert(res0->tz==6); 662 | _argmax_correspondences_rec( scales, s, l, x, y, res0, step0, res1, step1, index, score ); 663 | } 664 | 665 | 666 | void _argmax_correspondences_rec_v1( res_scale* scales, int s, int l, int x, int y, 667 | float_cube* res0, int step0, float_cube* res1, int step1, 668 | int index, float top_score ) { 669 | res_scale* sc = scales + s; 670 | const int f = sc->f; 671 | 672 | if(s==0) { 673 | const int* ass = sc->assign.pixels; 674 | const float score = top_score * sc->res_map.pixels[(ass[l]*sc->res_map.ty + y)*sc->res_map.tx + x]; 675 | const int x0 = sc->grid.pixels[2*l]; 676 | const int y0 = sc->grid.pixels[2*l+1]; 677 | const int x1 = f * x; 678 | const int y1 = f * y; 679 | 680 | const int qx0 = x0/step0; 681 | const int qy0 = y0/step0; 682 | if( qx0tx && qy0ty ) { 683 | float* r0 = res0->pixels + ((qy0*res0->tx + qx0))*res0->tz; 684 | 685 | pthread_mutex_lock (&mutex0); 686 | if( score > r0[4] ) { 687 | // r[0:2] = pos in img0 688 | r0[0] = x0; 689 | r0[1] = y0; 690 | // r[2:4] = pos in img1 691 | r0[2] = x1; 692 | r0[3] = y1; 693 | // r[4] = score 694 | r0[4] = score; 695 | r0[5] = index; 696 | } 697 | pthread_mutex_unlock (&mutex0); 698 | 699 | if( res1 ) { 700 | const int qx1 = x1/step1; 701 | const int qy1 = y1/step1; 702 | // if( qx1tx && qy1ty ) { // useless check 703 | float* r1 = res1->pixels + ((qy1)*res1->tx + (qx1))*res1->tz; 704 | pthread_mutex_lock (&mutex1); 705 | if( score > r1[4] ) { 706 | // r[0:2] = pos in img0 707 | r1[0] = x0; 708 | r1[1] = y0; 709 | // r[2:4] = pos in img1 710 | r1[2] = x1; 711 | r1[3] = y1; 712 | // r[4] = score 713 | r1[4] = score; 714 | r1[5] = index; 715 | } 716 | pthread_mutex_unlock (&mutex1); 717 | }} 718 | 719 | } else { 720 | const res_scale* lower = &scales[s-1]; 721 | const int lower_f = lower->f; 722 | // position in lower response map 723 | x *= f/lower_f; 724 | y *= f/lower_f; 725 | const int lower_gap = sc->patch_size/(4*lower_f); // gap is equal to patch_size/4 in absolute size 726 | const int nc2 = sc->children.tz; 727 | const int nc = (nc2==4) ? 2 : 3; 728 | const int* children = sc->children.pixels + l*nc2; 729 | const int* lower_ass = lower->assign.pixels; 730 | 731 | // remember all scores for all children 732 | int u,v,c=0; 733 | for(v=0; vres_map, 1, &xb, &yb ); 744 | 745 | if( child_score>0 ) 746 | _argmax_correspondences_rec_v1( scales, s-1, ch, xb, yb, res0, step0, res1, step1, index, top_score ); 747 | } 748 | } 749 | } 750 | } 751 | 752 | void _argmax_correspondences_v1( res_scale* scales, int s, int l, int x, int y, float top_score, 753 | float_cube* res0, int step0, float_cube* res1, int step1, 754 | int index ) { 755 | assert(res0->tz==6); 756 | if(res1) assert(res0->tz==6); 757 | _argmax_correspondences_rec_v1( scales, s, l, x, y, res0, step0, res1, step1, index, top_score ); 758 | } 759 | 760 | 761 | 762 | static float** get_list_corres( const float_cube* map, int* nb ) { 763 | const int tz = map->tz; 764 | float* m = map->pixels; 765 | const long npix = map->tx*map->ty; 766 | float** res = NEWA(float*,npix); 767 | 768 | int i,n=0; 769 | for(i=0; itz==tz && map1->tz==tz ); 788 | 789 | // build the list of triplets 790 | int n0,n1; 791 | float** const corres0 = get_list_corres(map0,&n0); 792 | float** const corres1 = get_list_corres(map1,&n1); 793 | 794 | // arg-sort the lists 795 | qsort( corres0, n0, sizeof(float*), cmp_corres ); 796 | qsort( corres1, n1, sizeof(float*), cmp_corres ); 797 | 798 | // remove all correspondences from map0/map1 that is not shared 799 | float** c0 = corres0; 800 | float** c1 = corres1; 801 | float** const c0max = corres0 + n0; 802 | float** const c1max = corres1 + n1; 803 | float* res = NEWA(float, tz*MIN(n1,n0) ); 804 | float* r = res; 805 | while(c00) { // corres0 > corres1 811 | c1++; 812 | } else { // corres0 == corres1 813 | if( r==res || memcmp( r-tz, *c0, tz*sizeof(float) ) ) { // if not already copied 814 | memcpy( r, *c0, tz*sizeof(float) ); 815 | r += tz; 816 | } 817 | c0++; 818 | c1++; 819 | } 820 | } 821 | 822 | free(corres0); 823 | free(corres1); 824 | *nres = (r-res)/tz; 825 | return res; 826 | } 827 | 828 | 829 | /* erase corres in the first array that are not in the second one 830 | */ 831 | void transfer_corres_score( const float_image* ref, float_cube* map0 ) { 832 | const int tz = 6; 833 | assert( map0->tz==tz && ref->tx==tz ); 834 | 835 | // build the list of triplets 836 | int n0,n1; 837 | float** const corres0 = get_list_corres(map0,&n0); 838 | float_cube map1 = {ref->pixels,1,ref->ty,ref->tx}; 839 | float** const corres1 = get_list_corres(&map1,&n1); 840 | 841 | // arg-sort the lists 842 | qsort( corres0, n0, sizeof(float*), cmp_corres ); 843 | qsort( corres1, n1, sizeof(float*), cmp_corres ); 844 | 845 | // remove all correspondences from map0/map1 that is not shared 846 | float** c0 = corres0; 847 | float** c1 = corres1; 848 | float** const c0max = corres0 + n0; 849 | float** const c1max = corres1 + n1; 850 | while(c00) { // corres0 > corres1 856 | assert(!"error: 'ref in map0' is not verified"); 857 | c1++; 858 | } else { // corres0 == corres1 859 | (*c0)[4] = (*c1)[4]; // copy score from ref 860 | c0++; 861 | c1++; 862 | } 863 | } 864 | 865 | while(c0tz==6 && all_corres->tz==6 ); 881 | const float* corres_pix = corres->pixels; 882 | assert(tol>=1); 883 | tol*=tol; // squared 884 | float dmax = 2*step / sqrt( aff[0]*aff[4] - aff[1]*aff[3] ); 885 | dmax*=dmax; // squared 886 | 887 | // for each bin of the final histograms, we get the nearest-neighbour bin in corres0 and corres1 888 | int i,j; 889 | for(j=0; jty; j++) 890 | for(i=0; itx; i++) { 891 | float* all_cor = all_corres->pixels + (j*all_corres->tx + i)*corres->tz; 892 | 893 | // center of the bin in the reference frame 894 | float x = i*all_step + all_step/2; 895 | float y = j*all_step + all_step/2; 896 | 897 | // center of the bin on the rescaled+rotated image 898 | float xr = ptdot( aff + 0, x, y ); 899 | float yr = ptdot( aff + 3, x, y ); 900 | 901 | // iterate on the nearby bins 902 | int xb = (int)(0.5+ xr/step); // rescaled+rotated image is binned with size 903 | int yb = (int)(0.5+ yr/step); 904 | int u,v; 905 | float best = 9e9f; 906 | for(v=MAX(0,yb-1); vty,yb+2); v++) 907 | for(u=MAX(0,xb-1); utx,xb+2); u++) { 908 | const float* cor = corres_pix + (v*corres->tx + u)*corres->tz; 909 | float d = pow2(cor[offset]-x) + pow2(cor[offset+1]-y); 910 | if( d < best && dty,yb+2); v++) 914 | for(u=MAX(0,xb-1); utx,xb+2); u++) { 915 | const float* cor = corres_pix + (v*corres->tx + u)*corres->tz; 916 | float d = pow2(cor[offset]-x) + pow2(cor[offset+1]-y); 917 | if( d <= tol*best ) { // spatially close 918 | // merge correspondence if score is better than actual 919 | if( cor[4] > all_cor[4] ) 920 | memcpy( all_cor, cor, 6*sizeof(float) ); 921 | } 922 | } 923 | 924 | } 925 | } 926 | 927 | 928 | /* merge correspondences from several rotated/scaled version of an image into a single common reference frame 929 | rot0 = 2x3 rotation matrix: (pt in rotated img0) = rot0 * (pt in ref frame) 930 | rot1 = 2x3 rotation matrix: (pt in rotated img1) = rot1 * (pt in ref frame) 931 | step0 and step1 are bin size of correspondences histograms 932 | tol >= 1 is the tolerance to grid rotation (default = 2) 933 | corres0, corres1: correspondences histograms of rotated image 934 | all_corres0, all_corres1: correspondences histograms of reference frame (result) 935 | */ 936 | void merge_corres( float rot0[6], float rot1[6], int step0, int step1, 937 | float_cube* corres0, float_cube* corres1, float tol, 938 | int all_step0, int all_step1, float_cube* all_corres0, float_cube* all_corres1 ) { 939 | 940 | merge_one_side( rot0, step0, corres0, tol, all_step0, all_corres0, 0 ); 941 | merge_one_side( rot1, step1, corres1, tol, all_step1, all_corres1, 2 ); 942 | } 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | -------------------------------------------------------------------------------- /src/maxfilter.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___MAXFILTER_H___ 18 | #define ___MAXFILTER_H___ 19 | #include "array_types.h" 20 | #include "deep_matching.h" 21 | 22 | 23 | /* compute the 3x3 maximum filter on an image and store the result in 24 | */ 25 | void _max_filter_3( float_image* img, float_image* res, int n_thread ); 26 | 27 | 28 | /* Same as above for float_layers* images 29 | */ 30 | void _max_filter_3_layers( float_layers* img, float_layers* res, int n_thread ); 31 | 32 | 33 | /* Subsample an array, equivalent to res = img[:,1::2,1::2] 34 | */ 35 | void _subsample2( float_layers* img, float_layers* res, int n_thread ); 36 | 37 | /* joint max-pooling and subsampling 38 | */ 39 | void _max_filter_3_and_subsample_layers( float_layers* img, float_layers* res, int n_thread ); 40 | 41 | 42 | /* Subsample an array, equivalent to res = trueimg[:,offset_y::2,offset_x::2] 43 | except at boundaries, where the rules are a bit more complex (see code) 44 | */ 45 | void _subsample2_offset( float_layers* img, int_image* offsets, float_layers* res, int n_thread ); 46 | 47 | /* Max-pool in 2x2 px non-overlapping cells 48 | */ 49 | void _maxpool2( float_layers* img, float_layers* res, int n_thread ); 50 | 51 | 52 | /* average-pool in 2x2 px non-overlapping cells 53 | */ 54 | void _avgpool2( float_layers* img, float_layers* res, int n_thread ); 55 | 56 | 57 | /* Return the list of parent cells of all cells of a given scale (parents are from the upper scale) 58 | children: list of children of the parent cells 59 | res: result matrix, n_cells_at_current_scale x n_max_parents 60 | res == -1 when there is no parent 61 | */ 62 | void _get_list_parents( int_cube* children, int_image* res ); 63 | 64 | 65 | /* Return a list of local maxima in the scale-space of scores 66 | */ 67 | void _extract_maxima( res_scale* scales, int n_scales, float_array* sc_factor, float th, int min_scale, float nlpow, 68 | int check_parents, int check_children, int nobordure, int_image* res_out, int n_thread ); 69 | 70 | 71 | /* Return the best assignment (= list of correspondences) for a given maxima 72 | from a pyramid top, this function returns 73 | a list of weigthed correspondences (matches) between 74 | img0 pixels and img1 pixels 75 | index = index of the maxima (s,l,x,y), so that it can be linked to the correspondences it generated 76 | */ 77 | void _argmax_correspondences( res_scale* scales, int s, int l, int x, int y, float score, 78 | float_cube* res0, int step0, float_cube* res1, int step1, 79 | int index ); 80 | 81 | void _argmax_correspondences_v1( res_scale* scales, int s, int l, int x, int y, float score, 82 | float_cube* res0, int step0, float_cube* res1, int step1, 83 | int index ); 84 | 85 | 86 | 87 | 88 | 89 | 90 | /* Intersect 2 mappings: erase all correspondences that are not reciprocal 91 | */ 92 | float* _intersect_corres( const float_cube* map0, const float_cube* map1, int* nres ); 93 | 94 | 95 | /* erase corres in the first array that are not in the second one 96 | */ 97 | void transfer_corres_score( const float_image* ref, float_cube* map0 ); 98 | 99 | 100 | 101 | 102 | /* merge correspondences from several rotated/scaled version of an image into a single common reference frame 103 | rot0 = 2x3 rotation matrix: (pt in rotated img0) = rot0 * (pt in ref frame) 104 | rot1 = 2x3 rotation matrix: (pt in rotated img1) = rot1 * (pt in ref frame) 105 | step0 and step1 are bin size of correspondences histograms 106 | tol >= 1 is the tolerance to grid rotation (default = 2) 107 | corres0, corres1: correspondences histograms of rotated image 108 | all_corres0, all_corres1: correspondences histograms of reference frame (result) 109 | */ 110 | void merge_corres( float rot0[6], float rot1[6], int step0, int step1, 111 | float_cube* corres0, float_cube* corres1, float tol, 112 | int all_step0, int all_step1, float_cube* all_corres0, float_cube* all_corres1 ); 113 | 114 | 115 | #endif 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/pixel_desc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #include "pixel_desc.h" 18 | #include "std.h" 19 | #include "image.h" 20 | #include "hog.h" 21 | #include "conv.h" 22 | 23 | 24 | /* convert a float image to a consecutive array 25 | a bit stupid but well 26 | */ 27 | UBYTE_image* image_to_arraytype( image_t* img ) { 28 | UBYTE_image* res = NEW(UBYTE_image); 29 | *res = empty_image(UBYTE,img->width,img->height); 30 | 31 | for(int j=0; jheight; j++) 32 | for(int i=0; iwidth; i++) 33 | res->pixels[i+j*res->tx] = (UBYTE)img->data[i+j*img->stride]; 34 | 35 | return res; 36 | } 37 | 38 | 39 | // set default params 40 | void set_default_desc_params( desc_params_t* params ) 41 | { 42 | // default = jpg settings, 43 | // better in almost all cases 44 | params->presmooth_sigma = 1.0; 45 | params->mid_smoothing = 1.0; 46 | params->post_smoothing = 1.0; 47 | params->hog_sigmoid = 0.2; 48 | params->ninth_dim = 0.3; 49 | params->norm_pixels = false; 50 | } 51 | 52 | 53 | /* extract pixel descriptors (pixel-wise HOG) 54 | */ 55 | float_layers* extract_desc( image_t* _img, const desc_params_t* params, int nt ) 56 | { 57 | // verify parameters 58 | assert(between(0,params->presmooth_sigma,3)); 59 | assert(between(0,params->mid_smoothing,3)); 60 | assert(between(0,params->post_smoothing,3)); 61 | assert(between(0.05,params->hog_sigmoid,0.8)); 62 | assert(between(0,params->ninth_dim,1)); 63 | assert(between(0,params->norm_pixels,1)); 64 | 65 | UBYTE_image* img = image_to_arraytype(_img); // could be optimized but well 66 | const int npix = img->tx*img->ty; 67 | //hash_image(img)D(img->tx)D(img->ty) 68 | 69 | // pre-smooth image 70 | assert( params->presmooth_sigma>=0 ); 71 | if( params->presmooth_sigma>0 ) 72 | _smooth_gaussian( img, params->presmooth_sigma, img, nt ); 73 | //hash_image(img) 74 | 75 | // extract HOG 76 | float_layers grad = {NEWA(float,npix*2),img->tx,img->ty,2}; 77 | _compute_grad_101( img, 0, &grad, nt ); 78 | //hash_cube(&grad) 79 | float_layers* hog = NEW(float_layers); 80 | *hog = {NEWA(float,9*npix),img->tx,img->ty,8}; 81 | _compute_hog( &grad, 1, hog, nt ); 82 | free(grad.pixels); 83 | free_image(img); 84 | //hash_layers(hog) 85 | 86 | // mid smoothing 87 | assert( params->mid_smoothing>=0 ); 88 | if( params->mid_smoothing ) 89 | smooth_hog_gaussian( hog, params->mid_smoothing, nt ); 90 | //hash_layers(hog) 91 | 92 | // apply non-linearity 93 | assert( params->hog_sigmoid>=0 ); 94 | if( params->hog_sigmoid ) { 95 | float_array hog_ravel = {hog->pixels,npix*hog->tz}; 96 | sigmoid_array( &hog_ravel, params->hog_sigmoid, 0, nt); 97 | } 98 | //hash_layers(hog) 99 | 100 | // final smoothing 101 | assert( params->post_smoothing>=0 ); 102 | if( params->post_smoothing ) 103 | smooth_hog_gaussian( hog, params->post_smoothing, nt ); 104 | //hash_layers(hog) 105 | 106 | // add ninth dimension and normalize per-pixel 107 | float* ninth_layer = hog->pixels + hog->tz*npix; 108 | for(int i=0; ininth_dim; 110 | hog->tz++; 111 | //hash_layers(hog) 112 | if( params->norm_pixels ) 113 | norm_layers( hog, 1, nt ); 114 | //hash_layers(hog);D(0)getchar(); 115 | 116 | return hog; 117 | } 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /src/pixel_desc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___PIXEL_DESC_H___ 18 | #define ___PIXEL_DESC_H___ 19 | #include "image.h" 20 | #include "array_types.h" 21 | 22 | 23 | // pixel descriptor params 24 | typedef struct { 25 | float presmooth_sigma; // image pre-smoothing 26 | float mid_smoothing; // smoothing of oriented gradients (before sigmoid) 27 | float post_smoothing; // smoothing of oriented gradients (after sigmoid) 28 | float hog_sigmoid; // sigmoid strength 29 | float ninth_dim; // small constant for gradient-less area 30 | bool norm_pixels; // 1: normalize pixels separately / 0: normalize atomic patches 31 | 32 | } desc_params_t; 33 | 34 | 35 | // set default params 36 | void set_default_desc_params( desc_params_t* params ); 37 | 38 | 39 | /* extract pixel descriptors (pixel-wise HOG) 40 | */ 41 | float_layers* extract_desc( image_t* _img, const desc_params_t* params, int nt ); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/rescore.py: -------------------------------------------------------------------------------- 1 | import sys, Image 2 | from numpy import * 3 | import scipy.ndimage 4 | 5 | def score_from_autocorr(img0, img1, corres): 6 | # Code by Philippe Weinzaepfel 7 | # Compute autocorrelation 8 | # parameters 9 | sigma_image = 0.8 # for the gaussian filter applied to images before computing derivatives 10 | sigma_matrix = 3.0 # for the integration gaussian filter 11 | derivfilter = array([-0.5,0,0.5]) # function to compute the derivatives 12 | # smooth_images 13 | tmp = scipy.ndimage.filters.gaussian_filter1d(img0.astype(float32), sigma_image, axis=0, order=0, mode='nearest') 14 | img0_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_image, axis=1, order=0, mode='nearest') 15 | # compute the derivatives 16 | img0_dx = scipy.ndimage.filters.convolve1d(img0_smooth, derivfilter, axis=0, mode='nearest') 17 | img0_dy = scipy.ndimage.filters.convolve1d(img0_smooth, derivfilter, axis=1, mode='nearest') 18 | # compute the auto correlation matrix 19 | dx2 = sum(img0_dx*img0_dx,axis=2) 20 | dxy = sum(img0_dx*img0_dy,axis=2) 21 | dy2 = sum(img0_dy*img0_dy,axis=2) 22 | # integrate it 23 | tmp = scipy.ndimage.filters.gaussian_filter1d(dx2, sigma_matrix, axis=0, order=0, mode='nearest') 24 | dx2_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_matrix, axis=1, order=0, mode='nearest') 25 | tmp = scipy.ndimage.filters.gaussian_filter1d(dxy, sigma_matrix, axis=0, order=0, mode='nearest') 26 | dxy_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_matrix, axis=1, order=0, mode='nearest') 27 | tmp = scipy.ndimage.filters.gaussian_filter1d(dy2, sigma_matrix, axis=0, order=0, mode='nearest') 28 | dy2_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_matrix, axis=1, order=0, mode='nearest') 29 | # compute minimal eigenvalues: it is done by computing (dx2+dy2)/2 - sqrt( ((dx2+dy2)/2)^2 + (dxy)^2 - dx^2*dy^2) 30 | tmp = 0.5*(dx2_smooth+dy2_smooth) 31 | small_eigen = tmp - sqrt( maximum(0,tmp*tmp + dxy_smooth*dxy_smooth - dx2_smooth*dy2_smooth)) # the numbers can be negative in practice due to rounding errors 32 | large_eigen = tmp + sqrt( maximum(0,tmp*tmp + dxy_smooth*dxy_smooth - dx2_smooth*dy2_smooth)) 33 | # Compute weight as flow score: preparing variable 34 | #parameters 35 | sigma_image = 0.8 # gaussian applied to images 36 | derivfilter = array([1.0,-8.0,0.0,8.0,-1.0])/12.0 # filter to compute the derivatives 37 | sigma_score = 50.0 # gaussian to convert dist to score 38 | mul_coef = 10.0 # multiplicative coefficients 39 | # smooth images 40 | tmp = scipy.ndimage.filters.gaussian_filter1d(img0.astype(float32), sigma_image, axis=0, order=0, mode='nearest') 41 | img0_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_image, axis=1, order=0, mode='nearest') 42 | tmp = scipy.ndimage.filters.gaussian_filter1d(img1.astype(float32), sigma_image, axis=0, order=0, mode='nearest') 43 | img1_smooth = scipy.ndimage.filters.gaussian_filter1d(tmp, sigma_image, axis=1, order=0, mode='nearest') 44 | # compute derivatives 45 | img0_dx = scipy.ndimage.filters.convolve1d(img0_smooth, derivfilter, axis=0, mode='nearest') 46 | img0_dy = scipy.ndimage.filters.convolve1d(img0_smooth, derivfilter, axis=1, mode='nearest') 47 | img1_dx = scipy.ndimage.filters.convolve1d(img1_smooth, derivfilter, axis=0, mode='nearest') 48 | img1_dy = scipy.ndimage.filters.convolve1d(img1_smooth, derivfilter, axis=1, mode='nearest') 49 | # compute it 50 | res = [] 51 | for pos0, pos1, score in corres: 52 | p0, p1 = tuple(pos0)[::-1], tuple(pos1)[::-1] # numpy coordinates 53 | dist = sum( abs(img0_smooth[p0]-img1_smooth[p1]) + abs(img0_dx[p0]-img1_dx[p1]) + abs(img0_dy[p0]-img1_dy[p1]) ) 54 | score = mul_coef * sqrt( max(0,small_eigen[p0])) / (sigma_score*sqrt(2*pi))*exp(-0.5*square(dist/sigma_score)); 55 | res.append((pos0,pos1,score)) 56 | return res 57 | 58 | 59 | if __name__=='__main__': 60 | args = sys.argv[1:] 61 | img0 = array(Image.open(args[0]).convert('RGB')) 62 | img1 = array(Image.open(args[1]).convert('RGB')) 63 | out = open(args[2]) if len(args)>=3 else sys.stdout 64 | 65 | ty0, tx0 = img0.shape[:2] 66 | ty1, tx1 = img1.shape[:2] 67 | rint = lambda s: int(0.5+float(s)) 68 | 69 | retained_matches = [] 70 | for line in sys.stdin: 71 | line = line.split() 72 | if not line or len(line)!=6 or not line[0][0].isdigit(): continue 73 | x0, y0, x1, y1, score, index = line 74 | retained_matches.append(((min(tx0-1,rint(x0)),min(ty0-1,rint(y0))), 75 | (min(tx1-1,rint(x1)),min(ty1-1,rint(y1))),0)) 76 | 77 | assert retained_matches, 'error: no matches piped to this program' 78 | 79 | for p0, p1, score in score_from_autocorr(img0, img1, retained_matches): 80 | print >>out, '%d %d %d %d %f' %(p0[0],p0[1],p1[0],p1[1],score) 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /src/runDeepMatching.m: -------------------------------------------------------------------------------- 1 | 2 | help deepmatching 3 | deepmatching() % show some help about options 4 | img1 = single(imread('liberty1.png')); 5 | img2 = single(imread('liberty2.png')); 6 | matches = deepmatching( img1, img2, '-downscale 2 -v'); 7 | matches % print matches, should be as the listing shown below -------------------------------------------------------------------------------- /src/std.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include 3 | #include "stdio.h" 4 | 5 | void std_printf(const char* format, ... ) { 6 | va_list arglist; 7 | va_start( arglist, format ); 8 | vprintf( format, arglist ); 9 | va_end(arglist); 10 | } 11 | 12 | void err_printf(const char* format, ... ) { 13 | va_list arglist; 14 | va_start( arglist, format ); 15 | vfprintf( stderr, format, arglist ); 16 | va_end(arglist); 17 | } 18 | -------------------------------------------------------------------------------- /src/std.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2014 Jerome Revaud 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see 16 | */ 17 | #ifndef ___STD_H___ 18 | #define ___STD_H___ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define MIN(a,b) (((a)<(b)) ? (a) : (b)) 28 | #define MAX(a,b) (((a)>(b)) ? (a) : (b)) 29 | #define SWAP(a,b,type) {type _t = a; a = b; b = _t;} 30 | #define between(min,val,max) (min<=val && val<=max) 31 | 32 | #define NEWA(type,n) (type*)malloc(sizeof(type)*long(n)) 33 | #define NEWAC(type,n) (type*)calloc(sizeof(type),(n)) 34 | #define NEW(type) NEWA(type,1) 35 | #define REALLOC(ptr,type,n) ptr = (type*)realloc(ptr, sizeof(type)*long(n)) 36 | 37 | /* debugging macros */ 38 | #define P(x) printf(#x " = %g\n",(double)(x)); 39 | #define D(x) P(x) 40 | #define DA(x,nb) {int _iter; printf(#x " = {"); for(_iter=0; _iter=sizeof(double)) { 51 | double tmp = *(double*)a; 52 | *((double*&)a)++ = *(double*)b; 53 | *((double*&)b)++ = tmp; 54 | nbytes -= sizeof(double); 55 | } 56 | while(nbytes) { 57 | char tmp = *(char*)a; 58 | *((char*&)a)++ = *(char*)b; 59 | *((char*&)b)++ = tmp; 60 | nbytes--; 61 | } 62 | } 63 | 64 | static inline float pow2( float f ) { 65 | return f*f; 66 | } 67 | static inline bool ispowerof2( long n ) { 68 | return (n & (n-1))==0; 69 | } 70 | 71 | 72 | inline float min_array_f(const float* a, int n) { 73 | int i=n; 74 | float res = FLOAT_MAX; 75 | while(i--) if(a[i]res) res=a[i]; 83 | return res; 84 | } 85 | 86 | // override printf because matlab can't use it as such 87 | void std_printf(const char* fmt, ... ); 88 | void err_printf(const char* fmt, ... ); 89 | 90 | 91 | //#include 92 | //inline double now() 93 | //{ 94 | // struct timeval tv; 95 | // gettimeofday (&tv,NULL); 96 | // return (tv.tv_sec*1e3 +tv.tv_usec*1e-3)/1000; 97 | //} 98 | //#define tic {double t = now(); 99 | //#define toc t=now()-t; printf("elapsed time = %g ms\n",1000*t);} 100 | 101 | #endif 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/viz.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PIL import Image 3 | from numpy import * 4 | from matplotlib.pyplot import * 5 | 6 | 7 | def show_correspondences( img0, img1, corr ): 8 | assert corr.shape[-1]==6 9 | corr = corr[corr[:,4]>0,:] 10 | 11 | # make beautiful colors 12 | center = corr[:,[1,0]].mean(axis=0) # array(img0.shape[:2])/2 # 13 | corr[:,5] = arctan2(*(corr[:,[1,0]] - center).T) 14 | corr[:,5] = int32(64*corr[:,5]/pi) % 128 15 | 16 | set_max = set(corr[:,5]) 17 | colors = {m:i for i,m in enumerate(set_max)} 18 | colors = {m:cm.hsv(i/float(len(colors))) for m,i in colors.items()} 19 | 20 | def motion_notify_callback(event): 21 | if event.inaxes==None: return 22 | numaxis = event.inaxes.numaxis 23 | if numaxis<0: return 24 | x,y = event.xdata, event.ydata 25 | ax1.lines = [] 26 | ax2.lines = [] 27 | n = sum((corr[:,2*numaxis:2*(numaxis+1)] - [x,y])**2,1).argmin() # find nearest point 28 | print "\rdisplaying #%d (%d,%d) --> (%d,%d), score=%g from maxima %d" % (n, 29 | corr[n,0],corr[n,1],corr[n,2],corr[n,3],corr[n,4],corr[n,5]),;sys.stdout.flush() 30 | x,y = corr[n,0:2] 31 | ax1.plot(x,y,'+',ms=10,mew=2,color='blue',scalex=False,scaley=False) 32 | x,y = corr[n,2:4] 33 | ax2.plot(x,y,'+',ms=10,mew=2,color='red',scalex=False,scaley=False) 34 | # we redraw only the concerned axes 35 | renderer = fig.canvas.get_renderer() 36 | ax1.draw(renderer) 37 | ax2.draw(renderer) 38 | fig.canvas.blit(ax1.bbox) 39 | fig.canvas.blit(ax2.bbox) 40 | 41 | def noticks(): 42 | xticks([]) 43 | yticks([]) 44 | clf() 45 | ax1 = subplot(221) 46 | ax1.numaxis = 0 47 | imshow(img0,interpolation='nearest') 48 | noticks() 49 | ax2 = subplot(222) 50 | ax2.numaxis = 1 51 | imshow(img1,interpolation='nearest') 52 | noticks() 53 | 54 | ax = subplot(223) 55 | ax.numaxis = -1 56 | imshow(img0/2+64,interpolation='nearest') 57 | for m in set_max: 58 | plot(corr[corr[:,5]==m,0],corr[corr[:,5]==m,1],'+',ms=10,mew=2,color=colors[m],scalex=0,scaley=0) 59 | noticks() 60 | 61 | ax = subplot(224) 62 | ax.numaxis = -1 63 | imshow(img1/2+64,interpolation='nearest') 64 | for m in set_max: 65 | plot(corr[corr[:,5]==m,2],corr[corr[:,5]==m,3],'+',ms=10,mew=2,color=colors[m],scalex=0,scaley=0) 66 | noticks() 67 | 68 | subplots_adjust(left=0.01, bottom=0.01, right=0.99, top=0.99, 69 | wspace=0.02, hspace=0.02) 70 | 71 | fig = get_current_fig_manager().canvas.figure 72 | cid_move = fig.canvas.mpl_connect('motion_notify_event',motion_notify_callback) 73 | print "Move your mouse over the top images to visualize individual matches" 74 | show() 75 | fig.canvas.mpl_disconnect(cid_move) 76 | 77 | 78 | 79 | if __name__=='__main__': 80 | args = sys.argv[1:] 81 | img0 = array(Image.open(args[0]).convert('RGB')) 82 | img1 = array(Image.open(args[1]).convert('RGB')) 83 | 84 | retained_matches = [] 85 | for line in sys.stdin: 86 | line = line.split() 87 | if not line or len(line)!=6 or not line[0][0].isdigit(): continue 88 | x0, y0, x1, y1, score, index = line 89 | retained_matches.append((float(x0),float(y0),float(x1),float(y1),float(score),float(index))) 90 | 91 | assert retained_matches, 'error: no matches piped to this program' 92 | show_correspondences(img0, img1, array(retained_matches)) 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | --------------------------------------------------------------------------------