暂时记录一下
OpenCV版本:
#include<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/core.hpp>
#include<opencv2/features2d.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;int main(int argc, char* argv[]) {Mat img_object = imread("a.jpg", IMREAD_GRAYSCALE);Mat img_scene = imread("b.jpg", IMREAD_GRAYSCALE);Ptr<SIFT> detector = SIFT::create(argc > 1 ? atoi(argv[1]) : 400);if (img_object.empty() || img_scene.empty()) {cout << "Could not open or find the image!\n" << endl;return -1;}std::vector<KeyPoint> keypoints_object, keypoints_scene;Mat descriptors_object, descriptors_scene;detector->detectAndCompute(img_object, noArray(), keypoints_object, descriptors_object);detector->detectAndCompute(img_scene, noArray(), keypoints_scene, descriptors_scene);//-- Step 2: Matching descriptor vectors with a FLANN based matcher// Since SURF is a floating-point descriptor NORM_L2 is usedPtr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);std::vector< std::vector<DMatch> > knn_matches;matcher->knnMatch(descriptors_object, descriptors_scene, knn_matches, 2);//-- Filter matches using the Lowe's ratio testconst float ratio_thresh = 0.85f;std::vector<DMatch> good_matches;for (size_t i = 0; i < knn_matches.size(); i++){if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance){good_matches.push_back(knn_matches[i][0]);}}img_object = imread("a.jpg");img_scene = imread("b.jpg");for (auto& k : keypoints_object) {circle(img_object, k.pt, k.size, CV_RGB(255, 0, 0));}for (auto& k : keypoints_scene) {circle(img_scene, k.pt, k.size, CV_RGB(0, 255, 0));}//-- Draw matchesMat img_matches;drawMatches(img_object, keypoints_object, img_scene, keypoints_scene, good_matches, img_matches, Scalar::all(-1),Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);//-- Localize the objectstd::vector<Point2f> obj;std::vector<Point2f> scene;for (size_t i = 0; i < good_matches.size(); i++){//-- Get the keypoints from the good matchesobj.push_back(keypoints_object[good_matches[i].queryIdx].pt);scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);}/*Mat H = findHomography(obj, scene, RANSAC);//-- Get the corners from the image_1 ( the object to be "detected" )std::vector<Point2f> obj_corners(4);obj_corners[0] = Point2f(0, 0);obj_corners[1] = Point2f((float)img_object.cols, 0);obj_corners[2] = Point2f((float)img_object.cols, (float)img_object.rows);obj_corners[3] = Point2f(0, (float)img_object.rows);std::vector<Point2f> scene_corners(4);perspectiveTransform(obj_corners, scene_corners, H);//-- Draw lines between the corners (the mapped object in the scene - image_2 )line(img_matches, scene_corners[0] + Point2f((float)img_object.cols, 0),scene_corners[1] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4);line(img_matches, scene_corners[1] + Point2f((float)img_object.cols, 0),scene_corners[2] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4);line(img_matches, scene_corners[2] + Point2f((float)img_object.cols, 0),scene_corners[3] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4);line(img_matches, scene_corners[3] + Point2f((float)img_object.cols, 0),scene_corners[0] + Point2f((float)img_object.cols, 0), Scalar(0, 255, 0), 4);*///-- Show detected matchesimshow("Good Matches & Object detection", img_matches);waitKey();return 0;
}
如果要用SURF,改这行代码即可:
//Ptr<SIFT> detector = SIFT::create(argc > 1 ? atoi(argv[1]) : 400);
Ptr<SURF> detector = SURF::create(argc > 1 ? atoi(argv[1]) : 400);
SURF是受专利保护的,所以编译OpenCV的时候需要把Contrib包一起编译,还要勾选OPENCV_ENABLE_NONFREE

SURF比较慢,精度怎样不做比较。
VLFeat 的SIFT版本据说比OpenCV好。
官网:https://www.vlfeat.org/
代码下载来后,为Visual Studio 2019建立工程文件 libvlfeat_sift.vcxproj 和vl目录同一级。
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><ItemGroup Label="ProjectConfigurations"> <ProjectConfiguration Include="Debug|x64"><Configuration>Debug</Configuration><Platform>x64</Platform></ProjectConfiguration><ProjectConfiguration Include="Release|x64"><Configuration>Release</Configuration><Platform>x64</Platform></ProjectConfiguration></ItemGroup><PropertyGroup Label="Globals"><VCProjectVersion>16.0</VCProjectVersion><Keyword>Win32Proj</Keyword><ProjectGuid>{dfd18de8-c6e6-45dc-88a3-91545155cac8}</ProjectGuid><RootNamespace>libvlfeatsift</RootNamespace><WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion></PropertyGroup><Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"><ConfigurationType>DynamicLibrary</ConfigurationType><UseDebugLibraries>true</UseDebugLibraries><PlatformToolset>v142</PlatformToolset><CharacterSet>MultiByte</CharacterSet></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"><ConfigurationType>DynamicLibrary</ConfigurationType><UseDebugLibraries>false</UseDebugLibraries><PlatformToolset>v142</PlatformToolset><WholeProgramOptimization>true</WholeProgramOptimization><CharacterSet>MultiByte</CharacterSet></PropertyGroup><Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /><ImportGroup Label="ExtensionSettings"></ImportGroup><ImportGroup Label="Shared"></ImportGroup><ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"><Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /></ImportGroup><ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"><Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /></ImportGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"><LinkIncremental>true</LinkIncremental><TargetName>$(ProjectName)d</TargetName></PropertyGroup><PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"><LinkIncremental>false</LinkIncremental></PropertyGroup><ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"><ClCompile><WarningLevel>Level3</WarningLevel><SDLCheck>false</SDLCheck><PreprocessorDefinitions>VL_BUILD_DLL;_LIB;_CRT_SECURE_NO_WARNINGS;VL_DISABLE_SSE2;VL_DISABLE_AVX;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions><ConformanceMode>true</ConformanceMode><PrecompiledHeader>NotUsing</PrecompiledHeader><PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile></ClCompile><Link><SubSystem></SubSystem><GenerateDebugInformation>true</GenerateDebugInformation></Link></ItemDefinitionGroup><ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"><ClCompile><WarningLevel>Level3</WarningLevel><FunctionLevelLinking>true</FunctionLevelLinking><IntrinsicFunctions>true</IntrinsicFunctions><SDLCheck>false</SDLCheck><PreprocessorDefinitions>VL_BUILD_DLL;_LIB;_CRT_SECURE_NO_WARNINGS;VL_DISABLE_SSE2;VL_DISABLE_AVX;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions><ConformanceMode>true</ConformanceMode><PrecompiledHeader>NotUsing</PrecompiledHeader><PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile></ClCompile><Link><SubSystem></SubSystem><EnableCOMDATFolding>true</EnableCOMDATFolding><OptimizeReferences>true</OptimizeReferences><GenerateDebugInformation>true</GenerateDebugInformation></Link></ItemDefinitionGroup><ItemGroup><ClInclude Include="vl\aib.h" /><ClInclude Include="vl\array.h" /><ClInclude Include="vl\covdet.h" /><ClInclude Include="vl\dsift.h" /><ClInclude Include="vl\fisher.h" /><ClInclude Include="vl\framework.h" /><ClInclude Include="vl\generic.h" /><ClInclude Include="vl\getopt_long.h" /><ClInclude Include="vl\gmm.h" /><ClInclude Include="vl\heap-def.h" /><ClInclude Include="vl\hikmeans.h" /><ClInclude Include="vl\hog.h" /><ClInclude Include="vl\homkermap.h" /><ClInclude Include="vl\host.h" /><ClInclude Include="vl\ikmeans.h" /><ClInclude Include="vl\imopv.h" /><ClInclude Include="vl\imopv_sse2.h" /><ClInclude Include="vl\kdtree.h" /><ClInclude Include="vl\kmeans.h" /><ClInclude Include="vl\lbp.h" /><ClInclude Include="vl\liop.h" /><ClInclude Include="vl\mathop.h" /><ClInclude Include="vl\mathop_avx.h" /><ClInclude Include="vl\mathop_sse2.h" /><ClInclude Include="vl\mser.h" /><ClInclude Include="vl\pch.h" /><ClInclude Include="vl\pgm.h" /><ClInclude Include="vl\qsort-def.h" /><ClInclude Include="vl\quickshift.h" /><ClInclude Include="vl\random.h" /><ClInclude Include="vl\rodrigues.h" /><ClInclude Include="vl\scalespace.h" /><ClInclude Include="vl\shuffle-def.h" /><ClInclude Include="vl\sift.h" /><ClInclude Include="vl\slic.h" /><ClInclude Include="vl\stringop.h" /><ClInclude Include="vl\svm.h" /><ClInclude Include="vl\svmdataset.h" /><ClInclude Include="vl\vlad.h" /></ItemGroup><ItemGroup><ClCompile Include="vl\aib.c" /><ClCompile Include="vl\array.c" /><ClCompile Include="vl\covdet.c" /><ClCompile Include="vl\dsift.c" /><ClCompile Include="vl\fisher.c" /><ClCompile Include="vl\generic.c" /><ClCompile Include="vl\getopt_long.c" /><ClCompile Include="vl\gmm.c" /><ClCompile Include="vl\hikmeans.c" /><ClCompile Include="vl\hog.c" /><ClCompile Include="vl\homkermap.c" /><ClCompile Include="vl\host.c" /><ClCompile Include="vl\ikmeans.c" /><ClCompile Include="vl\imopv.c" /><ClCompile Include="vl\imopv_sse2.c" /><ClCompile Include="vl\kdtree.c" /><ClCompile Include="vl\kmeans.c" /><ClCompile Include="vl\lbp.c" /><ClCompile Include="vl\liop.c" /><ClCompile Include="vl\mathop.c" /><ClCompile Include="vl\mathop_avx.c" /><ClCompile Include="vl\mathop_sse2.c" /><ClCompile Include="vl\mser.c" /><ClCompile Include="vl\pgm.c" /><ClCompile Include="vl\quickshift.c" /><ClCompile Include="vl\random.c" /><ClCompile Include="vl\rodrigues.c" /><ClCompile Include="vl\scalespace.c" /><ClCompile Include="vl\sift.c" /><ClCompile Include="vl\slic.c" /><ClCompile Include="vl\stringop.c" /><ClCompile Include="vl\svm.c" /><ClCompile Include="vl\svmdataset.c" /><ClCompile Include="vl\vlad.c" /></ItemGroup><Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /><ImportGroup Label="ExtensionTargets"></ImportGroup>
</Project>
使用vl_sift
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>using namespace cv;
using namespace std;
#define PI 3.1415926
extern "C" {
#include <vl/generic.h>
#include <vl/stringop.h>
#include <vl/sift.h>
#include <vl/getopt_long.h>
};
struct QKeyPoint {float o;float x;float y;float r;float main_angle;int angle_cnt;float desc[4][128];};void vl_sift_extract(VlSiftFilt* vl_sift, vl_sift_pix* data, vector<KeyPoint>& kpts, Mat& desc) {if (vl_sift_process_first_octave(vl_sift, data) == VL_ERR_EOF) return;double angles[4];vector<float*> temp;//// float* d = reinterpret_cast<float*>(desc.data);do {vl_sift_detect(vl_sift);VlSiftKeypoint* p = vl_sift->keys;for (int i = 0; i < vl_sift->nkeys; i++, p++) {KeyPoint kp(Point2f(p->x, p->y), p->sigma);int angle_cnt = vl_sift_calc_keypoint_orientations(vl_sift, angles, p);if (angle_cnt > 0) {kp.angle = angles[0] / PI * 360;float* d = new float[128];vl_sift_calc_keypoint_descriptor(vl_sift, d , p, angles[0]);temp.push_back(d);}kp.octave = p->o; kpts.push_back(kp);}} while (vl_sift_process_next_octave(vl_sift) != VL_ERR_EOF);desc = Mat::zeros(temp.size(), 128, CV_32FC1);float* d = reinterpret_cast<float*>(desc.data);for (int i = 0; i < temp.size(); i++ , d+= 128) {memcpy(d, temp[i], 128 * sizeof(float));delete[]temp[i];}}
const char* name1 = "a.jpg";
const char* name2 = "b.jpg";
int main(int argc, char* argv[]) {Mat img = imread(name1, IMREAD_GRAYSCALE);Mat float_img;img.convertTo(float_img, CV_32F);Mat color_img_a = imread(name1);Mat color_img_b = imread(name2);VlSiftFilt* vl_sift = vl_sift_new(img.cols, img.rows, 4, 3, 0);vl_sift_set_peak_thresh(vl_sift, atof(argv[1]));vl_sift_set_edge_thresh(vl_sift, atof(argv[2]));vector<KeyPoint> kpts_a, kpts_b;Mat desc_a, desc_b;vl_sift_extract(vl_sift, (vl_sift_pix*)(float_img.data), kpts_a, desc_a);img = imread(name2, IMREAD_GRAYSCALE);img.convertTo(float_img, CV_32F);VlSiftFilt* vl_sift2 = vl_sift_new(img.cols, img.rows, 4, 3, 0);vl_sift_set_peak_thresh(vl_sift2, atof(argv[1]));vl_sift_set_edge_thresh(vl_sift2, atof(argv[2]));vl_sift_extract(vl_sift2, (vl_sift_pix*)(float_img.data), kpts_b, desc_b);cout << " Features : " << kpts_a.size() << ", " << kpts_b.size() << endl;for (auto& k : kpts_a) {circle(color_img_a, k.pt, k.size, CV_RGB(255, 0, 0)); }for (auto& k : kpts_b) {circle(color_img_b, k.pt, k.size, CV_RGB(0, 255, 0)); }Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);vector< vector<DMatch> > knn_matches;matcher->knnMatch(desc_a, desc_b, knn_matches, 2);//-- Filter matches using the Lowe's ratio testconst float ratio_thresh = 0.75f;std::vector<DMatch> good_matches;for (size_t i = 0; i < knn_matches.size(); i++){if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance){good_matches.push_back(knn_matches[i][0]);}}//-- Draw matchesMat img_matches;drawMatches(color_img_a, kpts_a, color_img_b, kpts_b, good_matches, img_matches, Scalar::all(-1),Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);vl_sift_delete(vl_sift);vl_sift_delete(vl_sift2);//imshow("SIFT Match Testing", img_matches);imwrite("match-result.jpg", img_matches); return 0;
}
没有大变化的话,匹配效果还可以。