第六章 圖像檢索以及基於圖像描述符的搜索python
經過提取特徵進行圖像的匹配與搜索算法
1 特徵檢測算法數組
常見的特徵和提取算法:app
Harris 檢測角點ui
Sift 檢測斑點(blob) 有專利保護spa
Surf 檢測斑點 有專利保護rest
Fast 檢測角點orm
Brief 檢測斑點對象
Orb 帶方向的fast算法和具備旋轉不變性的brief算法索引
特徵的定義
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2016/12/5 12:30 # @Author : Retacn # @Site : 檢測圖像的角點 # @File : cornerHarris.py # @Software: PyCharm __author__ = "retacn" __copyright__ = "property of mankind." __license__ = "CN" __version__ = "0.0.1" __maintainer__ = "retacn" __email__ = "zhenhuayue@sina.com" __status__ = "Development" import cv2 import numpy as np # 讀入圖像 img = cv2.imread('../test1.jpg') # 轉換顏色空間 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = np.float32(gray) # 檢測圖像角點 dst = cv2.cornerHarris(gray, 2, 23, # sobel算子的中孔,3-31之間的奇數 0.04) # 將檢測到有角點標記爲紅色 img[dst > 0.01 * dst.max()] = [0, 0, 255] while (True): cv2.imshow("corners", img) if cv2.waitKey(33) & 0xFF == ord('q'): break cv2.destroyAllWindows()
使用dog和sift進行特徵提取和描述
示例代碼以下:
import cv2 import sys import numpy as py # 讀入圖像 # imgpath=sys.argv[1] imgpath = '../test1.jpg' img = cv2.imread(imgpath) # 更換顏色空間 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 建立sift對象,計算灰度圖像,會使用dog檢測角點 sift = cv2.xfeatures2d.SIFT_create() keypoints, descriptor = sift.detectAndCompute(gray, None) # print(keypoints) # 關鍵點有如下幾個屬性 # angle 表示特徵的方向 # class_id 關鍵點的id # octave 特徵所在金字塔的等級 # pt 圖像中關鍵點的座標 # response 表示關鍵點的強度 # size 表示特徵的直徑 img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints, color=(51, 163, 236), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) # 顯示圖像 cv2.imshow('sift_keypoints', img) while (True): if cv2.waitKey(int(1000 / 12)) & 0xFF == ord('q'): break cv2.destroyAllWindows()
使用心有快速hessian算法和SURF來提取特徵
示例代碼發以下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2016/12/10 17:30 # @Author : Retacn # @Site : sift用於檢測斑點 # @File : sift.py # @Software: PyCharm __author__ = "retacn" __copyright__ = "property of mankind." __license__ = "CN" __version__ = "0.0.1" __maintainer__ = "retacn" __email__ = "zhenhuayue@sina.com" __status__ = "Development" import cv2 import sys import numpy as py # 讀入圖像 # imgpath=sys.argv[1] # alg=sys.argv[2] # threshold=sys.argv[3] imgpath = '../test1.jpg' img = cv2.imread(imgpath) # alg = 'SURF' alg = 'SIFT' # threshold = '8000' # 閾值越小特徵點越多 threshold = '4000' def fd(algorithm): if algorithm == 'SIFT': return cv2.xfeatures2d.SIFT_create() if algorithm == 'SURF': # return cv2.xfeatures2d.SURF_create(float(threshold) if len(sys.argv) == 4 else 4000) return cv2.xfeatures2d.SURF_create(float(threshold)) # 更換顏色空間 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 建立sift對象,計算灰度圖像,會使用dog檢測角點 fd_alg = fd(alg) keypoints, descriptor = fd_alg.detectAndCompute(gray, None) # print(keypoints) # 關鍵點有如下幾個屬性 # angle 表示特徵的方向 # class_id 關鍵點的id # octave 特徵所在金字塔的等級 # pt 圖像中關鍵點的座標 # response 表示關鍵點的強度 # size 表示特徵的直徑 img = cv2.drawKeypoints(image=img, outImage=img, keypoints=keypoints, color=(51, 163, 236), flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) # 顯示圖像 cv2.imshow('keypoints', img) while (True): if cv2.waitKey(int(1000 / 12)) & 0xFF == ord('q'): break cv2.destroyAllWindows()
基於ORB的特徵檢測和特徵匹配
ORB是基於
FAST(featuresfrom accelerated segment test)關鍵點檢測技術
在像素周圍繪製一個圓,包含16個像素
BRIEF(binaryrobust independent elementary features) 描述符
暴力(brute-force)匹配法
比較兩個描述符,併產生匹配結果
ORB特徵匹配
示例代碼以下:
import numpy as np import cv2 from matplotlib import pyplot as plt cv2.ocl.setUseOpenCL(False) # 讀入灰度圖像 img1 = cv2.imread('../test2_part.jpg', cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('../test2.jpg', cv2.IMREAD_GRAYSCALE) # 建立orb特徵檢測器和描述符 orb = cv2.ORB_create() kp1, des1 = orb.detectAndCompute(img1, None) kp2, des2 = orb.detectAndCompute(img2, None) # 暴力匹配 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) matches = bf.match(des1, des2) matches = sorted(matches, key=lambda x: x.distance) # 顯示圖像 img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:40], img2, flags=2) plt.imshow(img3), plt.show()
報以下錯誤:
cv2.error: D:\Build\OpenCV\opencv-3.1.0\modules\python\src2\cv2.cpp:163:error: (-215) The data should normally be NULL! in functionNumpyAllocator::allocate
解決辦法,添加以下代碼 :
cv2.ocl.setUseOpenCL(False)
k最鄰近配匹
import numpy as np import cv2 from matplotlib import pyplot as plt cv2.ocl.setUseOpenCL(False) # 讀入灰度圖像 img1 = cv2.imread('../test2_part.jpg', cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('../test2.jpg', cv2.IMREAD_GRAYSCALE) # 建立orb特徵檢測器和描述符 orb = cv2.ORB_create() kp1, des1 = orb.detectAndCompute(img1, None) kp2, des2 = orb.detectAndCompute(img2, None) # knn匹配,返回k個匹配 bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=False) matches = bf.knnMatch(des1, des2, k=2) # 顯示圖像 img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, matches, img2, flags=2) plt.imshow(img3), plt.show()
Flann匹配法
Fast library for approximate nearestneighbors 近似最近鄰的快速庫
import numpy as np import cv2 from matplotlib import pyplot as plt # 讀入圖像 queryImage = cv2.imread('../test2_part.jpg', cv2.IMREAD_GRAYSCALE) trainingImage = cv2.imread('../test2.jpg', cv2.IMREAD_GRAYSCALE) # 建立sift對象 sift = cv2.xfeatures2d.SIFT_create() kp1, des1 = sift.detectAndCompute(queryImage, None) kp2, des2 = sift.detectAndCompute(trainingImage, None) FLANN_INDEX_KDTREE = 0 # 建立字典參數 indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) # 處理索引 searchParams = dict(checks=50) # 建立對象,用來指定索引樹的遍歷次數 flann = cv2.FlannBasedMatcher(indexParams, searchParams) matches = flann.knnMatch(des1, des2, k=2) matchesMask = [[0, 0] for i in range(len(matches))] for i, (m, n) in enumerate(matches): if m.distance < 0.7 * n.distance: matchesMask[i] = [1, 0] drawParams = dict(matchColor=(0, 255, 0), singlePointColor=(255, 0, 0), matchesMask=matchesMask, flags=0) resultImage = cv2.drawMatchesKnn(queryImage, kp1, trainingImage, kp2, matches, None, **drawParams) plt.imshow(resultImage), plt.show()
運行結果以下:
Flann單應性匹配
示例代碼以下:
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2016/12/11 12:02 # @Author : Retacn # @Site : flann的單應性匹配 # @File : flann_homography.py # @Software: PyCharm __author__ = "retacn" __copyright__ = "property of mankind." __license__ = "CN" __version__ = "0.0.1" __maintainer__ = "retacn" __email__ = "zhenhuayue@sina.com" __status__ = "Development" import numpy as np import cv2 from matplotlib import pyplot as plt MIN_MATCH_COUNT = 10 # 讀入圖像 img1 = cv2.imread('../test3_part.jpg', cv2.IMREAD_GRAYSCALE) img2 = cv2.imread('../test3.jpg', cv2.IMREAD_GRAYSCALE) # 建立sift對象 sift = cv2.xfeatures2d.SIFT_create() # 查詢特徵點和描述符 kp1, des1 = sift.detectAndCompute(img1, None) kp2, des2 = sift.detectAndCompute(img2, None) FLANN_INDEX_KDTREE = 0 # 建立字典參數 indexParams = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) # 處理索引 searchParams = dict(checks=50) # 建立對象,用來指定索引樹的遍歷次數 flann = cv2.FlannBasedMatcher(indexParams, searchParams) 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) # 單應性 M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0) matchesMask = mask.ravel().tolist() # 對第二張圖片計算相對於原始圖像的投影畸變,並繪製邊框 h, w = img1.shape 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 = cv2.polylines(img2, [np.int32(dst)], True, 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), # 綠線 singlePointColor=None, matchesMask=matchesMask, flags=2) img3 = cv2.drawMatches(img1, kp1, img2, kp2, good, None, **draw_params) plt.imshow(img3, 'gray'), plt.show()
運行結果以下:
基於紋身取證的應用程序示例
A 將圖像描述符保存到文件中
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2016/12/11 13:52 # @Author : Retacn # @Site : 將圖像描述符保存到文件中 # @File : generate_descriptors.py # @Software: PyCharm __author__ = "retacn" __copyright__ = "property of mankind." __license__ = "CN" __version__ = "0.0.1" __maintainer__ = "retacn" __email__ = "zhenhuayue@sina.com" __status__ = "Development" import cv2 import numpy as np from os import walk from os.path import join import sys # 建立描述符 def create_descriptors(folder): files = [] for (dirpath, dirnames, filenames) in walk(folder): files.extend(filenames) for f in files: save_descriptor(folder, f, cv2.xfeatures2d.SIFT_create()) # 保存描述符 def save_descriptor(folder, image_path, feature_detector): print("reading %s" % image_path) if image_path.endswith("npy") or image_path.endswith("avi"): return img = cv2.imread(join(folder, image_path), cv2.IMREAD_GRAYSCALE) keypoints, descriptors = feature_detector.detectAndCompute(img, None) descriptor_file = image_path.replace("jpg", "npy") np.save(join(folder, descriptor_file), descriptors) # 從執行參數中取得文件目錄 dir = sys.argv[1] create_descriptors(dir)
B 掃描匹配
#!/usr/bin/env python # -*- coding: utf-8 -*- # @Time : 2016/12/11 14:05 # @Author : Retacn # @Site : 掃描匹配 # @File : scan4matches.py # @Software: PyCharm __author__ = "retacn" __copyright__ = "property of mankind." __license__ = "CN" __version__ = "0.0.1" __maintainer__ = "retacn" __email__ = "zhenhuayue@sina.com" __status__ = "Development" from os.path import join from os import walk import numpy as np import cv2 from sys import argv from matplotlib import pyplot as plt # 建立文件名數組 folder = argv[1] query = cv2.imread(join(folder, 'part.jpg'), cv2.IMREAD_GRAYSCALE) # 建立全局的文件,圖片,描述符 files = [] images = [] descriptors = [] for (dirpath, dirnames, filenames) in walk(folder): files.extend(filenames) for f in files: if f.endswith('npy') and f != 'part.npy': descriptors.append(f) print(descriptors) # 建立sift檢測器 sift = cv2.xfeatures2d.SIFT_create() query_kp, query_ds = sift.detectAndCompute(query, None) # 建立flann匹配 FLANN_INDEX_KDTREE = 0 index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) search_params = dict(checks=50) flann = cv2.FlannBasedMatcher(index_params, search_params) # 最小匹配數 MIN_MATCH_COUNT = 10 potential_culprits = {} print(">> Initiating picture scan...") for d in descriptors: print("--------- analyzing %s for matches ------------" % d) matches = flann.knnMatch(query_ds, np.load(join(folder, d)), k=2) good = [] for m, n in matches: if m.distance < 0.7 * n.distance: good.append(m) if len(good) > MIN_MATCH_COUNT: print('%s is a match! (%d)' % (d, len(good))) else: print('%s is not a match ' % d) potential_culprits[d] = len(good) max_matches = None potential_suspect = None for culprit, matches in potential_culprits.items(): if max_matches == None or matches > max_matches: max_matches = matches potential_suspect = culprit print("potential suspect is %s" % potential_suspect.replace("npy", "").upper())