暂时记录一下
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 used
Ptr<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 test
const 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 matches
Mat 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 object
std::vector<Point2f> obj;
std::vector<Point2f> scene;
for (size_t i = 0; i < good_matches.size(); i++)
{
//-- Get the keypoints from the good matches
obj.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 matches
imshow("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
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210314181942339.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,tex
_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MTY2NTIyNQ==,size_16,color_FFFFFF,t_70#pic_center)
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 test
const 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 matches
Mat 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;
}
没有大变化的话,匹配效果还可以。