在之前的笔记《OpenCV4学习笔记(38)》中,整理记录了我本人对于经典特征算法——SIFT算法一些流程的理解,而今天要整理的是SIFT算法的其中一种改进算法,也即AKAZE特征算法。
AKAZE特征算法是SIFT特征算法的一种改进版本,但不使用高斯模糊来构建尺度空间,因为高斯模糊具有丢失边缘信息的缺点,进而采用非线性扩散滤波来构建尺度空间,从而保留图像更多的边缘特征。
在特征点提取阶段,AKAZE算法采用与SIFT算法类似的提取特征点方式,即在同一金字塔层内的不同尺度的一组图像中寻找最大特征点。
然后在特征描述子生成阶段,采用与ORB特征算法类似的方法生成描述子(可参阅《OpenCV4学习笔记(40)》),但ORB算法中采用LDB特征描述算法来生成特征描述子,而AKAZE采用 M-LDB特征描述算法来生成描述子,使得最终得到的特征具有旋转不变性。M-LDB特征描述算法是基于LDB特征描述算法并针对图像的旋转和缩放进行改进,相比LDB具有旋转不变性和尺度不变性,进一步提高了特征的鲁棒性。
通过AKAZE特征算法得到的描述子具有旋转不变性、尺度不变性、光照不变性、空间不变性等,而且其鲁棒性、特征独特性和特征精度相比起ORB、SIFT算法提取出的特征要更好。
AKAZE算法作为KAZE算法的性能提升版本,利用Fast Explicit Diffusion(FED)来构建尺度空间。因为盒子滤波可以很好地近似高斯核,并且能够提升速度、易于实现,所以其主要思想是循环执行M次的盒子滤波,每次循环都有N个步长的显式扩散,每一步的步长是非线性的、也即是不断变化的,初始步长起源于盒子滤波的因式分解。
虽然其速度有了一定的提升,但是仍然不足以胜任实时处理的需求。
在OpenCV中已经实现并封装好了AKAZE算法的特征检测器,我们只需要使用auto akaze = AKAZE::create()
就可以直接创建一个AKAZE特征检测器了,然后就可以对图像进行特征提取、描述子生成等操作。下面看一下演示代码:
Mat tem_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\tem.jpg");
Mat dected_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\miao.jpeg");
resize(tem_image, tem_image, Size(160,120));
resize(dected_image, dected_image, Size(600, 800));
auto akaze = AKAZE::create();
vector<KeyPoint> keyPoints_tem, keyPoints_dected;
Mat descriptors_tem, descriptors_dected;
akaze->detectAndCompute(tem_image, Mat(), keyPoints_tem, descriptors_tem, false);
akaze->detectAndCompute(dected_image, Mat(), keyPoints_dected, descriptors_dected, false);
auto matcher = DescriptorMatcher::create(DescriptorMatcher::MatcherType::BRUTEFORCE);
vector<DMatch> matches;
matcher->match(descriptors_tem, descriptors_dected, matches);
float maxdist = matches[0].distance;
for (int i = 0; i < matches.size(); i++)
{
if (maxdist < matches[i].distance)
{
maxdist = matches[i].distance;
}
}
float thresh = 0.6;
vector<DMatch> good_Matches;
vector<Point2f> temPoints, dectedPoints;
for (int j = 0; j < matches.size(); j++)
{
if (matches[j].distance < thresh * maxdist)
{
good_Matches.push_back(matches[j]);
temPoints.push_back(keyPoints_tem[matches[j].queryIdx].pt);
dectedPoints.push_back(keyPoints_dected[matches[j].trainIdx].pt);
}
}
if (0 == good_Matches.size())
{
cout << "不存在最佳匹配特征点" << endl;
return 0;
}
Mat result;
drawMatches(tem_image, keyPoints_tem, dected_image, keyPoints_dected, good_Matches, result, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
Mat H;
H = findHomography(temPoints, dectedPoints, RHO);
int tem_width = tem_image.cols;
int tem_height = tem_image.rows;
vector<Point2f> tem_points(4), transform_points(4);
tem_points[0] = Point(tem_width, tem_height);
tem_points[1] = Point(0, tem_height);
tem_points[2] = Point(0, 0);
tem_points[3] = Point(tem_width, 0);
perspectiveTransform(tem_points, transform_points, H);
for (int k = 0; k < transform_points.size(); k++)
{
line(result, transform_points[k % 4] + Point2f(tem_width, 0), transform_points[ (k+1) % 4] + Point2f(tem_width, 0), Scalar(0, 255, 0), 1, 8, 0);
}
imshow("result", result);
在上述代码中,分别对一张模板图像和一张待检测图像进行AKAZE特征提取和描述,然后将两幅图像的特征进行匹配,从而得到两幅图像之间高度匹配的特征点,进而定位出模板图像中的目标物体在待检测图像中的位置。
下面是演示效果图:
拿自己脑袋做样本。。。不说啥了。。。
ε=ε=ε=┏(゜ロ゜;)┛
好的那本次笔记到此结束~
PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!