Python 進行目標檢測

1、前言

  從學單片機開始鼓搗C語言,到如今爲了學CV鼓搗Python,期間在CSDN、簡書、博客園和github這些地方獲得了不少幫助,因此也想把本身作的一些小東西分享給你們,但願能幫助到別人。記錄人生的第一篇博客,mark。html

2、圖像檢測步驟

1. 讀取兩張圖片

  第一張是須要檢測的小物體,第二章圖片是小物體放置在大場景中。代碼與輸出結果以下所示:git

import numpy as np
import matplotlib.pyplot as plt
import cv2

def my_show(img):
    plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
    return 

# 讀取圖片
img_small=cv2.imread('small.jpg',1)
img_big=cv2.imread('big.jpg',1)

# 顯示圖片
plt.figure(figsize=(10,10))
plt.subplot(121)
my_show(img_small)
plt.subplot(122)
my_show(img_big)

 

2. 提取圖片中的特徵點

  這一步就像咱們在區分不一樣的人的時候,一眼看到外貌就知道此人是誰,而外貌就是這我的的特徵。咱們但願提取該物體的特徵點,以便在不一樣的場景中識別出來。圖片是由像素點構成,可是像素點包含的信息太零散了,通常是識別物體的邊緣或者角點做爲特徵信息。經常使用的特徵描述算法以下:github

  SIFThttps://blog.csdn.net/zddblog/article/details/7521424
算法

  harris corner detection: https://www.jianshu.com/p/efc81fdb8afb數組

  Hog: https://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdfapp

  SURF: https://www.vision.ee.ethz.ch/~surf/eccv06.pdfdom

  BRISK: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.371.1343&rep=rep1&type=pdf函數

  Orb: http://www.willowgarage.com/sites/default/files/orb_final.pdfspa

  咱們項目中用到的是SIFT算法,SIFT精確度高,對尺度、亮度以及旋轉的魯棒性強,不過計算時間長,所需的計算資源較多。SURF是SIFT的加速版本,有興趣的小夥伴能夠了解一下。SIFT算法運行後,能夠獲得的特徵點的位置以及特徵向量。(PS:SIFT算法申請了專利,因此在opencv3.4.2版本後不能使用了,須要用SIFT的請安裝3.4.2的opencv).net

import numpy as np
import matplotlib.pyplot as plt
import cv2

def my_show(img):
    plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
    return 

# 讀取圖片
img_small=cv2.imread('small.jpg',1)
img_big=cv2.imread('big.jpg',1)

# 提取特徵點
sift=cv2.xfeatures2d.SIFT_create()
kp1,des1=sift.detectAndCompute(img_small,None)
kp2,des2=sift.detectAndCompute(img_big,None)

# 在圖中顯示特徵點
img_small_sift=cv2.drawKeypoints(img_small,kp1,outImage=np.array([]),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
img_big_sift=cv2.drawKeypoints(img_big,kp2,outImage=np.array([]),flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

plt.figure(figsize=(15,15))
plt.subplot(121)
my_show(img_small_sift)
plt.subplot(122)
my_show(img_big_sift)

 

3.特徵點匹配

  在分別提取了兩張圖的特徵點後,就須要進行特徵點匹配啦。這裏須要注意的是,兩張圖的特徵點數量通常狀況下是不同的,opencv算法裏默認用第一張圖的特徵點來匹配,因此匹配矩陣的行數與第一張圖特徵點的行數一致。經常使用的匹配方法:

  Brute-Force: 暴力搜索,用圖1的特徵點逐一與圖二的特徵點比較,找到歐式距離最小的點做爲匹配點。不過這樣匹配的錯誤點就會不少,因此經常使用交叉匹配法消除錯誤匹配。交叉匹配法指的是,反過來匹配一次,用匹配到圖2的點反過來匹配圖1的點,若是匹配結果與原來相同,則認爲是正確匹配。

  KNN: K近鄰匹配,在匹配的時候選擇K個和特徵點最類似的點,若是這K個點之間的區別足夠大,則選擇最類似的那個點做爲匹配點,一般選擇K = 2。KNN匹配也會出現一些誤匹配,這時候須要對比第一鄰近與第二鄰近之間的距離大小,假如 distance_1< (0.5~0.7)*distance_2, 則認爲是正確匹配。

  FLANN: FLANN是快速最近鄰搜索包(Fast Library for Approximate Nearest Neighbors)的簡稱,是最近鄰搜索的算法的集合。

  通常咱們會選擇調用cv2.Brute-Force或者cv2.FlannBasedMatcher來進行特徵點匹配,FLANN裏邊就包含的KNN、KD樹還有其餘的最近鄰算法。

 

4.計算單應性矩陣

  這裏咱們須要在大場景中用矩形框出匹配的小物體,因此就要計算單應性矩陣,而後作投影變換。RANSAC(Random Sample Consensus隨機抽樣一致性算法是計算單應性矩陣的有效方法,而且在尋找單應性矩陣的過程當中能夠進一步剔除錯誤匹配點。RANSAC算法的步驟以下:

  •  隨機抽取四個匹配點對計算投影矩陣,須要檢驗四個點是否共線
  •  圖片1特徵點座標齊次變換後(3,1)乘上投影矩陣(3,3),而後計算變換後的特徵點座標與圖片2特徵點座標的歐氏距離,小於設定的閾值則記錄爲內點,反之則爲內點
  •  判斷這次內點的數量是否比以往記錄的內點最大值多,若是是,則更新投影矩陣,若是不是,則不更新
  •  斷定循環次數是夠達到設定次數或內點數佔所有匹配點的比例達到設定比例,則跳出循環,不然跳到步驟1繼續循環

  尋找到最優的單應性矩陣後,將框住物體一的矩陣經投影變換後在圖片二上畫出來,完成目標檢測框選。如下代碼摘自Brook@CV的博客並修改,若有侵權,請通知刪除

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

MIN_MATCH_COUNT = 10

img1 = cv2.imread('small.jpg',1)
img2 = cv2.imread('big.jpg',1)

# 使用SIFT檢測角點
sift = cv2.xfeatures2d.SIFT_create()
# 獲取關鍵點和描述符
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

# 定義FLANN匹配器
index_params = dict(algorithm = 1, trees = 5)
search_params = dict(checks = 50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 使用KNN算法匹配
matches = flann.knnMatch(des1,des2,k=2)

# 去除錯誤匹配
good = []
for m,n in matches:
    if m.distance <= 0.7*n.distance:
        good.append(m)

# 單應性
if len(good)>MIN_MATCH_COUNT:
    # 改變數組的表現形式,不改變數據內容,數據內容是每一個關鍵點的座標位置
    src_pts = np.float32([ kp1[m.queryIdx].pt for m in good ]).reshape(-1,1,2)
    dst_pts = np.float32([ kp2[m.trainIdx].pt for m in good ]).reshape(-1,1,2)
    # findHomography 函數是計算變換矩陣
    # 參數cv2.RANSAC是使用RANSAC算法尋找一個最佳單應性矩陣H,即返回值M
    # 返回值:M 爲變換矩陣,mask是掩模
    M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC,5.0)
    # ravel方法將數據降維處理,最後並轉換成列表格式
    matchesMask = mask.ravel().tolist()
    # 獲取img1的圖像尺寸
    h,w,dim = img1.shape
    # pts是圖像img1的四個頂點
    pts = np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
    # 計算變換後的四個頂點座標位置
    dst = cv2.perspectiveTransform(pts,M)

    # 根據四個頂點座標位置在img2圖像畫出變換後的邊框
    img2 = cv2.polylines(img2,[np.int32(dst)],True,(0,0,255),3, cv2.LINE_AA)

else:
    print("Not enough matches are found - %d/%d") % (len(good),MIN_MATCH_COUNT)
    matchesMask = None

# 顯示匹配結果
draw_params = dict(matchColor = (0,255,0), # draw matches in green color
                   singlePointColor = None,
                   matchesMask = matchesMask, # draw only inliers
                   flags = 2)
img3 = cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
plt.figure(figsize=(20,20))
plt.imshow(cv2.cvtColor(img3,cv2.COLOR_BGR2RGB))
plt.show()

最後給你們放上原圖能夠跑一下程序!

相關文章
相關標籤/搜索