OpenCv學習筆記3(圖像識別:檢測直線和圓, 圖像分割,SURF特徵匹配)

1.用霍夫變換檢測直線和圓

霍爾夫變換是圖像處理中從圖像中識別幾何形狀的基本方法之一。
原理:在原始圖像座標系下的一個點(直線)對應了參數座標系下的一條直線(點)。
在這裏插入圖片描述
OpenCV提供瞭如下三種霍夫變換相關的函數:
HoughLines:檢測圖像中的直線。
HoughLinesP:檢測圖像中的直線段。
HoughCircles:檢測圖像中的圓。

HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])

image參數爲進行直線檢測的圖像
rho和theta參數分別爲累加器中每個點所表示的r和θ的大小。其中rho的單位是像素點,而theta是以弧度表示的角度。
值越小則累加器 的尺寸越大,最後找出的直線的參數的精度越高,但是運算時間也越長。
threshold參數是 在累加器中尋找峯值時所使用的閾值,即只有大於此值的峯值點才被當作與某條直線相對 應。
由於HoughLinesP()檢測的是圖像中的線段,因此minLineLength參數指定線段的最小長度。
maxLineGap參數則指定線段的最大間隙。當有多條線段共線時,間隙小於此值 線段將被合併爲一條線段。

下面是檢測圖像中的直線

import cv2
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體

img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/building.jpg",0)
#輸入圖像進行邊緣檢測,得到一個二值圖像
#(輸入圖像,閾值1,閾值2)閾值越小,從圖像中檢測的細節越多
img_binary = cv2.Canny(img, 100, 255)
#(被檢測的圖像,累加器參數,,在累加器中尋找峯值時所使用的閾值,線段的最小長度,線段的最大間隙)
#HoughLinesP()的返回值是一個形狀爲(N,1,4)的數組,其中N爲線段數,第二軸的4個元素爲線段的起點和終點:x0、y0、x1、y1。
lines = cv2.HoughLinesP(img_binary, rho=1, theta=np.deg2rad(0.1),#度數轉爲弧度
        threshold=96, minLineLength=33,maxLineGap=4)#(85,1,4)

fig, ax = pl.subplots(figsize=(8, 6))
pl.imshow(img, cmap="gray")

from matplotlib.collections import LineCollection
lc = LineCollection(lines.reshape(-1, 2, 2))
ax.add_collection(lc)
ax.axis("off")

在這裏插入圖片描述
檢測圓形的HoughCircles()的參數如下:

HoughCircles(image, method, dp, minDist [, circles[, param1[, param2[, minRadius[, maxRadius]]]]])

method參數爲圓形檢測的算法,目前OpenCV中只實現了一種檢測算法:CV_HOUGH_GRADIENTP
dp參數和直線檢測中的rho參數類似,決定了檢測的精度, dp=1時累加器的分辨率和輸入圖像相同,而dp=2時累加器的分辨率爲輸入圖像的一半。
minDist參數是檢測到的所有圓的圓心之間的最小距離,當它過小時會檢測出很多近似的圓形,若過大則可能會漏掉一些結果。
param1參數相當於邊緣檢測Canny()的第二個閾值,Canny()的第一個閾值自動設置爲它的一半。
param2參數是累加器上的閾值,它的值越小檢測出的圓形越多。
minRadius和maxRadius參數指定圓形的半徑範圍,缺省都爲0表示範圍不限。

下面是檢測圖像中的圓

import cv2
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體

img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/coins.png",0)
#爲了獲得較好的邊緣檢測結果,調用GaussianBlur()對圖像進行模糊處理。
img_blur = cv2.GaussianBlur(img, (0, 0), 1.8) 
circles = cv2.HoughCircles(img_blur, cv2.HOUGH_GRADIENT,dp=2.0, minDist=20.0,
            param1=170, param2=44, minRadius=16, maxRadius=40)#(1,24,3)
x, y, r = circles[0].T#(1,24,3)變成(24,3)。第一列給x,2-y,3-r

fig, ax = pl.subplots(figsize=(8, 6))
pl.imshow(img, cmap="gray")

from matplotlib.collections import EllipseCollection
#使用EllipseCollection快速繪製多個圓形
ec = EllipseCollection(widths=2*r, heights=2*r, angles=0, units="xy", 
        facecolors="none", edgecolors="red",transOffset=ax.transData, offsets=np.c_[x, y])
ax.add_collection(ec)
ax.axis("off")

在這裏插入圖片描述

2.圖像分割

一般的圖像中顏色豐富、信息繁雜,不利於計算機進行圖像識別。因此通常會使用圖像分割技術,將圖像中相似的區域進行合併,使得圖像更容易理解和分析。下面介紹OpenCV中提供的兩種常見的圖像分割算法。

2.1Mean-Shift算法

pyrMeanShiftFiltering()使用Mean-Shift算法對圖像進行分割。它的調用參數如下:

pyrMeanShiftFiltering(src, sp, sr[, dst[, maxLevel[, termcrit]]])

fig, axes = pl.subplots(1, 3, figsize=(9, 3))
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/fruits.jpg")
srs = [20, 40, 80]
for ax, sr in zip(axes, srs):
    img2 = cv2.pyrMeanShiftFiltering(img, sp=20, sr=sr, maxLevel=1)
    ax.imshow(img2[:,:,::-1])
    ax.set_axis_off()
    ax.set_title("sr = {}".format(sr))

fig.subplots_adjust(0.02, 0, 0.98, 1, 0.02, 0)

在這裏插入圖片描述

2.2分水嶺算法(重要)

分水嶺算法(Watershed)的基本思想是將圖像的梯度當作地形圖。圖像中變化小的區域相當於地形圖中的山谷,而變化大的區域相當於山峯。從指定的幾個初始區域同時開始向地形灌不同數值的水,水面上升逐漸淹沒山谷,並且範圍逐漸擴大。
watershed()實現此算法,它的調用形式如下:
watershed(image, markers)

   image參數是需要進行分割處理的圖像,它必須是一個3通道8位圖像。
   markers參數是一個32位整數數組,其大小必須和image相同。 markers中值大於0的點構成初始灌溉區域,其值可以理解爲水的顏色。
   調用watershed()之後,markers中幾乎所有的點都將被賦值爲某個初始區域的值,而在兩個區域的境界線上的點將被賦值爲-1。
import cv2
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
fig, axes = pl.subplots(1, 2, figsize=(10, 6))
a0,a1=axes[0],axes[1]
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/pills.png")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
a0.imshow(img_gray,cmap='gray')
#先調用blur()對藥丸的灰度圖像進行模糊處理,這樣可以有效消除噪聲,減少局域最值的個數。
img_gray = cv2.blur(img_gray, (15, 15)) 

_, img_binary = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY) 

#局域最亮像素就是比周邊臨近的像素都亮的像素,使用dilate()對灰度圖像進行膨脹處
#理,將每個像素都設置爲鄰近像素中的最大值,然後與原始圖像中的值比較,如果值保持
#不變,該像素即爲局域最亮像素。
peaks = img_gray == cv2.dilate(img_gray, np.ones((7, 7)), 1) 
peaks =peaks*img_binary#非零值爲局部最亮,其餘爲0值

peaks[1, 1] = True #設置初始值

from scipy.ndimage import label
markers, count = label(peaks) #對區域進行編號
marks=cv2.watershed(img, markers)#邊界值爲-1
img2=img
img2[marks == -1] = [0,0,255]
a1.imshow(img2)

在這裏插入圖片描述
還在繼續學習相關編程。

3.SURF特徵匹配

SURF是一種快速提取圖像特徵點的算法,它所提取的圖像特徵具有縮放和旋轉的不變性,而且它對圖像的灰度變化也不敏感。對SURF算法的詳細介紹還有待研究,這裏只簡單地介紹OpenCV中SURF類的用法。

對一副圖像找關鍵點

import cv2
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體

img_gray1 = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/lena.jpg",0)
#然後創建SURF()對象,並設置hessianThreshold和nOctaves參數,這兩個參數爲計算關鍵點的參數,
#hessianThreshold越大則關鍵點的個數越少,nOctaves越大則關鍵點的尺寸範圍越大。
surf = cv2.xfeatures2d.SURF_create (2000, 2)
#調用detect()方法從灰度圖像中檢測出關鍵點。
#它返回一個列表key_points,其中每個元素都是一個保存關鍵點信息的KeyPoint對象。
key_points1 = surf.detect(img_gray1)
#根據關鍵點的size屬性按照從大到小的順序排列關鍵點
key_points1.sort(key=lambda x:x.size, reverse=True)

#灰度圖像通過cvtColor()轉換成灰色的RGB格式的圖像
img_color1 = cv2.cvtColor(img_gray1, cv2.COLOR_GRAY2RGB)
#調用drawKeypoints()可以在圖像上繪製關鍵點
#(繪製的圖像,關鍵點(前25個),輸出圖像,顏色設置,繪圖標誌)
#繪圖標誌:。圖中紅色圓圈的大小顯示關鍵點的size屬性,而半徑線段的方向顯示了關鍵點的方向屬性angle。
cv2.drawKeypoints(img_color1, key_points1[:25], img_color1, color=(255, 0, 0), 
                  flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
pl.imshow(img_color1)

#調用SURF.compute()計算關鍵點列表key_points1對應的特徵向量features1
_, features1 = surf.compute(img_gray1, key_points1)

在這裏插入圖片描述
如果關鍵點數很多,可以使用OpenCV提供的FlannBasedMatcher尋找匹配的特徵向量。
在這裏插入圖片描述

"關鍵點數很多,可以使用OpenCV提供的FlannBasedMatcher尋找匹配的特徵向量。"
import cv2
import numpy as np
import matplotlib.pyplot as pl
pl.rcParams["font.family"] = "SimHei"#直接修改配置字典,設置默認字體
"1.準備兩幅圖像"
img_gray1 = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/lena.jpg")
img_gray2 = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/lena1.jpg")


"2.直接計算關鍵點以及與之對應的特徵向量"
key_points1, features1 = surf.detectAndCompute(img_gray1, None)
key_points2, features2 = surf.detectAndCompute(img_gray2, None)

"3.調用匹配算法"
FLANN_INDEX_KDTREE = 1
#創建FlannBasedMatcher對象時傳遞兩個字典index_params和search_params,分別設置其索引參數和搜索參數。
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=100)
fbm = cv2.FlannBasedMatcher(index_params, search_params) 
#調用knnMatch()對features1中的每個特徵向量在features2中搜索k個最近的特徵向量。
#這裏設置參數k爲1,只搜索最接近的特徵向量。
match_list = fbm.knnMatch(features1, features2, k=2) #嵌套列表
matchesMask = [[0,0] for i in range(len(match_list))]
# ratio test as per Lowe's paper
for i,(m,n) in enumerate(match_list):
    if m.distance < 0.8*n.distance:
        matchesMask[i]=[1,0]

draw_params = dict(matchColor = (0,255,0), matchesMask = matchesMask, flags = 0)
img = cv2.drawMatchesKnn(img_gray1,key_points1,img_gray2,key_points2,match_list,None,**draw_params)
fig, axes = pl.subplots(1, 1, figsize=(15, 8))
axes.imshow(img[:,:,::-1])

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章