OpenCV教程(29)-- 图像特征提取与匹配

 

有时候,图像的像素大小对角点存在一定的影响。比如图像越小,角点看上去趋向近似一条直线,这样很容易造成角点的丢失。如果按照上述的检测方法,会造成角点检测结果不相符,因此引入DoG和SIFT算法进行检测Opencv的SIFT类是DoG和SIFT算法组合。

DoG是对同一图像使用不同高斯滤波器所得的结果。

SIFT是通过一个特征向量来描述关键点周围区域的情况。

对比SURF和SIFT算法,ORB算法更处于起步阶段,在2011年才首次发布。但比前两者的速度更快。ORB基于FAST关键点检测和BRIEF的描述符技术相结合,因此我们先了解FAST和BRIEF。

FAST:特征检测算法。

BRIEF:只是一个描述符,这是图像一种表示方式,可以比较两个图像的关键点描述符,可作为特征匹配的一种方法。

暴力匹配:比较两个描述符并产生匹配结果。

K-最近邻匹配(KNN)

FLANN匹配,相对暴力匹配BFMatcher来讲,这匹配算法比较准确、快速和使用方便。FLANN具有一种内部机制,可以根据数据本身选择最合适的算法来处理数据集。值得注意的是,FLANN匹配器只能使用SURF和SIFT算法。
在上述的例子中,我们只是将检测的关键点进行勾画,在这例子中,将使用SIFT SURF检测关键点之外,还将两图进行匹配,匹配的图像如下:

SIFT FLANN 0.7

SIFT FLANN 0.6

 

SIFT FLANN 0.4

SURF FLANN 0.4

 

#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@Copyright:  Jihua Lab 2021.
@File Name   :  1. SIFT_Feature.py
@Description:  


@Create Time   :   2021/1/28 0028  10:04
@Author :   HaoWANG, Foshan,China
@Email  :   haowanghk@163.com

@Software : Pycharm
@Version: V1.0

"""
import os
from os import walk
from os.path import join

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt


class imgFeature(object):
    def __init__(self, img_path):
        self.__img_path = img_path
        self.__file_info = os.path.split(os.path.abspath(img_path))
        self.__file_folder = self.__file_info[0]
        self.__file_name = self.__file_info[1]

    def get_img_path(self):
        # 返回图像路径
        return self.__img_path

    def get_img_folder(self):
        # 返回图像存放绝对路径文件件
        return self.__file_folder

    def img_path_check(self):
        # 读取图像路径合法性检查:输出False or True
        img_path = self.get_img_path()
        if not (os.path.exists(img_path) and
                os.access(img_path, os.R_OK)):
            # 判断文件路径是否存在; 检查文件是否可读
            print("ERROR : File in path {} is not exists or un-accessible to read"
                  .format(img_path))
            return False
        else:
            print("--------------------------")
            print("File in path {} is accessible to read"
                  .format(img_path))
            return True
        # end of img_path_check(img_path)

    def cv_img_read(self, check = True):
        if check:
            if not self.img_path_check():
                print("Image Read ERROR : from img_path_check( ) !")
                return -1;
            else:
                # 读取图像数据
                return cv.imread(self.get_img_path())
        else:
            return cv.imread(self.get_img_path())

    def create_descriptors(self):
        path = self.get_img_path()
        files = []
        for (dirpath, dirnames, filenames) in walk(path):
            files.extend(filenames)
        for f in files:
            if '.jpg' in f:
                self.save_descriptor(path, f, cv.xfeatures2d.SIFT_create())
                print("--------------------------")
                print("File in path {} is accessible to read"
                      .format(join(dirnames,filenames)))
        return True

    def save_descriptor(self,feature_detector):
        # 判断图片是否为npy格式
        img_path = self.get_img_path()
        if img_path.endswith("npy"):
            return
        # 读取图片并检查特征
        img = self.cv_img_read(check=False)
        keypoints, descriptors = feature_detector.detectAndCompute(img, None)
        # 设置文件名并将特征数据保存到npy文件
        descriptor_file = img_path.replace("jpg", "npy")
        np.save(descriptor_file, descriptors)
        print("--------------------------")
        print("Descriptor File in path {} is saved".format(descriptor_file))


def sift_feature(img_src1, img_src2):
    '''
    :param img_src1: RGB 三通道彩色图像
    :param img_src2: RGB三通道彩色图像
    :return:  None
    '''
    # 彩色图像转灰度图
    img_gray1 = cv.cvtColor(img_src1, cv.COLOR_BGR2GRAY)
    img_gray2 = cv.cvtColor(img_src2, cv.COLOR_BGR2GRAY)
    # 创建sift特征点对象
    # sift = cv.xfeatures2d.SIFT_create()
    sift = cv.xfeatures2d.SURF_create(float(4000))
    # 将图片进行SURF计算,并找出角点keypoints,keypoints是检测关键点
    # descriptor是描述符,这是图像一种表示方式,可以比较两个图像的关键点描述符,可作为特征匹配的一种方法。
    keypoints_1, descriptor_1 = sift.detectAndCompute(img_gray1, None)
    keypoints_2, descriptor_2 = sift.detectAndCompute(img_gray2, None)
    dst_1 = cv.drawKeypoints(image=img_src1,
                             outImage=img_src1,
                             keypoints=keypoints_1,
                             flags=cv.DRAW_MATCHES_FLAGS_DEFAULT,
                             color=(100, 13, 236))
    dst_2 = cv.drawKeypoints(image=img_src2,
                             outImage=img_src2,
                             keypoints=keypoints_2,
                             flags=cv.DRAW_MATCHES_FLAGS_DEFAULT,
                             color=(51, 163, 236))
    # cv2.drawKeypoints() 函数主要包含五个参数:
    # image: 原始图片
    # keypoints:从原图中获得的关键点,这也是画图时所用到的数据
    # outputimage:输出
    # color:颜色设置,通过修改(b,g,r)的值,更改画笔的颜色,b=蓝色,g=绿色,r=红色。
    # flags:绘图功能的标识设置,标识如下:
    # cv2.DRAW_MATCHES_FLAGS_DEFAULT  默认值
    # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS
    # cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG
    # cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS

    # 设置FLANN匹配器参数
    # algorithm设置可参考https://docs.opencv.org/3.1.0/dc/d8c/namespacecvflann.html
    indexParams = dict(algorithm=0, trees=5)
    searchParams = dict(checks=50)
    # 定义FLANN匹配器
    flann = cv.FlannBasedMatcher(indexParams, searchParams)
    # 使用 KNN 算法实现匹配
    matches = flann.knnMatch(descriptor_1, descriptor_2, k=2)

    # 根据matches生成相同长度的matchesMask列表,列表元素为[0,0]
    matchesMask = [[0, 0] for i in range(len(matches))]

    # 去除错误匹配
    for i, (m, n) in enumerate(matches):
        if m.distance < 0.4 * n.distance:
            matchesMask[i] = [1, 0]
    # 将图像显示
    # matchColor是两图的匹配连接线,连接线与matchesMask相关
    # singlePointColor是勾画关键点
    drawParams = dict(matchColor=(0, 255, 0),
                      singlePointColor=(255, 0, 0),
                      matchesMask=matchesMask,
                      flags=0)
    resultImage = cv.drawMatchesKnn(img_src1,
                                    keypoints_1,
                                    img_src2,
                                    keypoints_2,
                                    matches, None, **drawParams)
    return resultImage

    # end of sift_feature(()

def cv_img_show( resultImage):
    # 调用cv.imshow()
    window_name = "Input Image"
    cv.namedWindow(window_name, cv.WINDOW_NORMAL)
    cv.imshow(window_name, resultImage)
    while True:
        if cv.waitKey(0) & 0xff == ord("q"):
            break
    cv.destroyAllWindows()
    return 0

def plt_img_show(resultImage):
    plt.imshow(resultImage)
    plt.show()


def main():
    # 图像处理主函数
    img_path1 = "../pictures/right.jpg"  # 路径1
    img_path2 = "../pictures/left.jpg"  # 路径2
    img1 = imgFeature(img_path1)
    img2 = imgFeature(img_path2)

    if (img1.img_path_check() == False or
        img2.img_path_check() == False):
        print("ERROR : img_path_check(img_path) !")
        return -1;
    else:
        # 读取图像数据
        img_src1 = img1.cv_img_read(check=False)
        img_src2 = img2.cv_img_read(check=False)

        # 提取并绘制特征点

        resultImage = sift_feature(img_src1, img_src2)
        cv_img_show(resultImage=resultImage)
        # plt_img_show(resultImage)
        img1.create_descriptors()
        img1.save_descriptor(feature_detector=cv.xfeatures2d.SURF_create(float(4000)))

        img2.create_descriptors()
        img2.save_descriptor(feature_detector=cv.xfeatures2d.SURF_create(float(4000)))

        return 0
    # end of main(()


if __name__ == '__main__':
    main()

 

参考:

1.博客以代码实例详述SIFT 、 SURF、 Harris、ORB等常见特征点提取与匹配方法, https://xyhuang.blog.csdn.net/article/details/80660688?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-3.control

 

 

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Techblog of HaoWANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值