专栏名称: OpenCV学堂
一个致力于计算机视觉OpenCV原创技术传播的公众号!OpenCV计算机视觉与tensorflow深度学习相关算法原创文章分享、函数使用技巧、源码分析与讨论、,计算机视觉前沿技术介绍,技术专家经验分享,人才交流,学习交流。
目录
相关文章推荐
中国舞台美术学会  ·  各省市丨湖南省舞台美术学会观展团参加2025 ... ·  昨天  
中国舞台美术学会  ·  各省市丨山西省舞台美术学会观展团赴广州参加2 ... ·  昨天  
中国舞台美术学会  ·  盘点丨2月全球21个份新媒体艺术作品 ·  3 天前  
中国舞台美术学会  ·  资讯丨2024杭州演出16786场,票房收入 ... ·  3 天前  
51好读  ›  专栏  ›  OpenCV学堂

干货 | 基于特征的图像配准用于缺陷检测

OpenCV学堂  · 公众号  ·  · 2024-06-26 23:26

正文



点击上方 蓝字 关注我们

微信公众号: OpenCV学堂

投稿作者: 小黄弟

来自:中国电科智慧城市建模仿真与智能技术重点实验室

文字编辑:gloomyfish

特征提取

基于特征的图像配准,具有非常广泛的应用,大致流程可以如下:

经典的特征匹配算法有SIFT、SURF、ORB等,这三种方法在OpenCV里面都已实现。SURF基本就是SIFT的全面升级版,有 SURF基本就不用考虑SIFT,而ORB的强点在于计算时间,以下具体比较:

计算速度:ORB>>SURF>>SIFT(各差一个量级)
旋转鲁棒性:SURF>ORB~SIFT(~表示差不多)
模糊鲁棒性:SURF>ORB~SIFT
尺度变换鲁棒性:SURF>SIFT>ORB(ORB并不具备尺度变换性)

所以结论就是,如果对计算实时性要求非常高,可选用ORB算法,但基本要保证正对拍摄;如果对稳定性要求稍高,可以选择SURF;基本不用SIFT。此外补充一点,自从OpenCV3.x开始,受到SIFT跟SURF专利授权的影响,OpenCV正式的发布版本中已经移除了SIFT跟SURF算法。ORB特征提取算法是基于FAST跟BRIEF算法改进的组合算法,其中FAST实现关键点/特征点的检测,在此基础上基于几何矩添加方向属性,BRIEF实现描述子生成,添加旋转不变性支持。


ORB特征匹配速度快的一个原因之一就是使用字符串向量的描述子,避免了浮点数计算。字符串描述子匹配上可以采用汉明距离或者LSH改进算法实现,相比浮点数计算L2距离进一步降低了计算量。所以在一般情况下建议使用ORB特征匹配,如果效果不好再尝试AKAZE/SURF/SIFT等其它特征匹配算法。

特征对齐/配准

两幅图像之间的基于特征匹配的透视变换矩阵求解通常被称为图像对齐或者配准。基于特征的匹配可以很好实现图像对齐或者配准,首先需要获取两张图像的特征关键点与特征描述子,然后通过暴力匹配或者FLANN匹配寻找匹配度高的相关特征点。最后基于这些相关特征点估算它们之间的单应性矩阵,通过单应性矩阵实现透视变换,完成图像对齐与配准。OpenCV中有两个函数可以获得单映射变换矩阵,分别为:

- findHomography- getPerspectiveTransform

两者之间的区别在于getPerspectiveTransform只会拿4个点去计算,findHomography则会拿一堆点(>=4)去计算。

应用代码演示

下面是一个简单的代码演示,基于特征对齐,实现基于分差的缺陷检测。

用基于ORB特征的匹配结果,如下图所示,可以看到有一些错误的匹配点


基于ORB特征实现图像相关特征点匹配的代码实现如下:

constint MAX_FEATURES = 5000;
constfloat GOOD_MATCH_PERCENT = 0.45f;
//im1为待配准图片
//im2为模板图片
//im1Reg为配准后的图片
//h为单应性矩阵
void alignImages(Mat&im1, Mat&im2, Mat&im1Reg, Mat&h)
{
    // 将图像转为灰度图
    Mat im1Gray, im2Gray;
    cvtColor(im1, im1Gray, COLOR_BGR2GRAY);
    cvtColor(im2, im2Gray, COLOR_BGR2GRAY);

    // 存储特征与特征描述子的变量
    std::vector keypoints1, keypoints2;
    Mat descriptors1, descriptors2;

    // 检测ORB特征计算特征描述子.
    Ptr orb = ORB::create(MAX_FEATURES);
    orb->detectAndCompute(im1Gray, Mat(), keypoints1, descriptors1);
    clock_t start, end;
    start = clock();
    orb->detectAndCompute(im2Gray, Mat(), keypoints2, descriptors2);  //77ms

    // 特征匹配.
    std::vector matches;
    Ptr matcher = DescriptorMatcher::create("BruteForce-Hamming");
    matcher->match(descriptors1, descriptors2, matches, Mat());
    // Sort matches by score
    std::sort(matches.begin(), matches.end());

    //基于GMS的特征匹配算法
    //vector matchesAll, matchesGMS;
    //BFMatcher matcher(NORM_HAMMING);
    //std::vector matches;
    //matcher.match(descriptors1, descriptors2, matchesAll);
    //cout << "matchesAll: " << matchesAll.size() << endl;
    //matchGMS(im1.size(), im2.size(), keypoints1, keypoints2, matchesAll, matches);
    //std::sort(matches.begin(), matches.end());

    end = clock();
    cout << (float)(end - start) * 1000 / CLOCKS_PER_SEC<<"ms"<< endl;

    // 移除不好的匹配点
    constint numGoodMatches = matches.size() * GOOD_MATCH_PERCENT;
    matches.erase(matches.begin() + numGoodMatches, matches.end());
    // 画匹配点
    Mat imMatches;
    drawMatches(im1, keypoints1, im2, keypoints2, matches, imMatches);
    imwrite("matches.jpg", imMatches);

    // 存储好的匹配点
    std::vector points1, points2;

    for (size_t i = 0; i < matches.size(); i++)
    {
        points1.push_back(keypoints1[matches[i].queryIdx].pt);
        points2.push_back(keypoints2[matches[i].trainIdx].pt);
    }

    // 找出最优单映射变换矩阵h
    h= findHomography(points1, points2, RANSAC);

    // 利用h矩阵进行透视变换
    warpPerspective(im1, im1Reg, h, im2.size());
}


Grid-based Motion Statistics(GMS)通过网格划分、运动统计特性的方法可以迅速剔除错误匹配,以此来提高匹配的稳定性。ORB+GMS的匹配效果如下,可见错误的匹配点少了很多。

配准后的图如下图所示:

将配准后的图与基准模板图做差分,效果如下:

进行形态学操作,

找出缺陷,比较大的缺陷可以找出来,较小的缺陷还是不能找出来。


这部分的代码实现如下:

int main(intargc, char **argv)
{
    // Read reference image
    string refFilename("8.jpg");
    cout <<"Reading reference image : "<< refFilename << endl;
    Mat imReference = imread(refFilename);

    // Read image to be aligned
    string imFilename("7.jpg");
    cout <<"Reading image to align : "<< imFilename << endl;
    Mat im = imread(imFilename);

    // Registered image will be resotred in imReg. 
    // The estimated homography will be stored in h. 
    Mat imReg, h;

    // Align images
    cout <<"Aligning images ..."<< endl;
    alignImages(im, imReference, imReg, h);

    // Write aligned image to disk. 
    string outFilename("aligned.jpg");
    cout <<"Saving aligned image : "<< outFilename << endl;
    imwrite(outFilename, imReg);

    // Print estimated homography
    cout <<"Estimated homography : \n"<< h << endl;
    Mat currentframe, previousframe;
    cvtColor(imReference, previousframe, COLOR_BGR2GRAY);
    cvtColor(imReg, currentframe, COLOR_BGR2GRAY);  //转化为单通道灰度图

    absdiff(currentframe, previousframe, currentframe);//做差求绝对值
    imshow("1", currentframe);
    imwrite("re.jpg", currentframe);
    threshold(currentframe, currentframe, 120255.0, THRESH_BINARY);
    imwrite("re11.jpg", currentframe);

    erode(currentframe, currentframe, Mat());//腐蚀






请到「今天看啥」查看全文