一、什么是opencv?
Open Source Computer Vision Library.OpenCV於1999年由Intel建立,如今由Willow Garage提供支持。OpenCV是一個基於BSD許可(開源)發行的跨平台計算機視覺庫,可以運行在Linux、Windows、MacOS操作系統上。它輕量級而且高效——由一系列 C 函數和少量C++類構成,同時提供了Python、Ruby、MATLAB等語言的接口,實現了圖像處理和計算機視覺方面的很多通用算法。最新版本是3.1 ,2016年1月29日發布。(引自百度百科openCV)
簡言之,通過openCV可實現計算機圖像、視頻的編輯。廣泛應用於圖像識別、運動跟蹤、機器視覺等領域。
二、安裝
直接使用pip安裝
pip install numpy Matplotlib #由於opencv依賴numpy pip install opencv-python #或者使用國內鏡像 pip install opencv-python -i https://pypi.douban.com/simple
安裝完成之后再命令行解釋器輸入:
import cv2
若沒有提示no module錯誤,則表示安裝成功
測試腳本:
import cv2 #導入模塊,opencv的python模塊叫cv2 imgobj = cv2.imread('pho.jpg') #讀取圖像 cv2.namedWindow("image") #創建窗口並顯示的是圖像類型 cv2.imshow("image",imgobj) cv2.waitKey(0) #等待事件觸發,參數0表示永久等待 cv2.destroyAllWindows() #釋放窗口
三、學習網站:
https://www.cnblogs.com/lclblack/p/6377710.html opencv環境搭建
http://www.opencv.org.cn 官網中文教程
https://blog.csdn.net/Notzuonotdied/article/details/77876080 Python Matplotlib簡易教程
http://python.jobbole.com/87471/ 給深度學習入門者的Python快速教程 - Numpy和Matplotlib篇
https://blog.csdn.net/qq_32211827/article/details/56854985 單通道與三通道的區別
四、圖像對比相關
相關背景
要識別兩張相似圖像,我們從感性上來談是怎么樣的一個過程?首先我們會區分這兩張相片的類型,例如是風景照,還是人物照。風景照中,是沙漠還是海洋,人物照中,兩個人是不是都是國字臉,還是瓜子臉(還是倒瓜子臉……哈哈……)。
那么從機器的角度來說也是這樣的,先識別圖像的特征,然后再相比。
很顯然,在沒有經過訓練的計算機(即建立模型),那么計算機很難區分什么是海洋,什么是沙漠。但是計算機很容易識別到圖像的像素值。
因此,在圖像識別中,顏色特征是最為常用的。(其余常用的特征還有紋理特征、形狀特征和空間關系特征等)
其中又分為
直方圖
顏色集
顏色矩
聚合向量
相關圖
1、直方圖
在Python中利用opencv中的calcHist()方法獲取其直方圖數據,返回的結果是一個列表,使用matplotlib,畫出了這兩張圖的直方圖數據圖
import cv2 import numpy from matplotlib import pyplot if __name__ == '__main__': imgobj1 = cv2.imread('pho.jpg') imgobj2 = cv2.imread('ph1.jpg') hist1 = cv2.calcHist([imgobj1], [0], None, [256], [0.0, 255.0]) hist2 = cv2.calcHist([imgobj2], [0], None, [256], [0.0, 255.0]) pyplot.plot(range(256), hist1, 'r') pyplot.plot(range(256), hist2, 'b') pyplot.show() cv2.imshow('img1',imgobj1) cv2.imshow('img2',imgobj2) cv2.waitKey(0)
2.灰度圖的作用?
(一):單通道圖,
俗稱灰度圖,每個像素點只能有有一個值表示顏色,它的像素值在0到255之間,0是黑色,255是白色,中間值是一些不同等級的灰色。(也有3通道的灰度圖,3通道灰度圖只有一個通道有值,其他兩個通道的值都是零)。
(二):三通道圖,每個像素點都有3個值表示 ,所以就是3通道。也有4通道的圖。例如RGB圖片即為三通道圖片,RGB色彩模式是工業界的一種顏色標准,是通過對紅(R)、綠(G)、藍(B)三個顏色通道的變化以及它們相互之間的疊加來得到各式各樣的顏色的,RGB即是代表紅、綠、藍三個通道的顏色,這個標准幾乎包括了人類視力所能感知的所有顏色,是目前運用最廣的顏色系統之一。總之,每一個點由三個值表示。
3、利用直方圖判斷兩張圖片的是否相似的方法就是,計算其直方圖的重合程度即可。
計算方法如下:
其中gi和si是分別指兩條曲線的第i個點。
最后計算得出的結果就是就是其相似程度。
不過,這種方法有一個明顯的弱點,就是他是按照顏色的全局分布來看的,無法描述顏色的局部分布和色彩所處的位置。
也就是假如一張圖片以藍色為主,內容是一片藍天,而另外一張圖片也是藍色為主,但是內容卻是妹子穿了藍色裙子,那么這個算法也很可能認為這兩張圖片的相似的。
緩解這個弱點有一個方法就是利用Image的crop方法把圖片等分,然后再分別計算其相似度,最后綜合考慮。
圖像指紋與漢明距離
在介紹下面其他判別相似度的方法前,先補充一些概念。第一個就是圖像指紋
圖像指紋和人的指紋一樣,是身份的象征,而圖像指紋簡單點來講,就是將圖像按照一定的哈希算法,經過運算后得出的一組二進制數字。
說到這里,就可以順帶引出漢明距離的概念了。
假如一組二進制數據為101,另外一組為111,那么顯然把第一組的第二位數據0改成1就可以變成第二組數據111,所以兩組數據的漢明距離就為1
簡單點說,漢明距離就是一組二進制數據變成另一組數據所需的步驟數,顯然,這個數值可以衡量兩張圖片的差異,漢明距離越小,則代表相似度越高。漢明距離為0,即代表兩張圖片完全一樣。
如何計算得到漢明距離,請看下面三種哈希算法
平均哈希法(aHash)
此算法是基於比較灰度圖每個像素與平均值來實現的
一般步驟:
1.縮放圖片,一般大小為8*8,64個像素值。
2.轉化為灰度圖
3.計算平均值:計算進行灰度處理后圖片的所有像素點的平均值,直接用numpy中的mean()計算即可。
4.比較像素灰度值:遍歷灰度圖片每一個像素,如果大於平均值記錄為1,否則為0.
5.得到信息指紋:組合64個bit位,順序隨意保持一致性。
最后比對兩張圖片的指紋,獲得漢明距離即可。
感知哈希算法(pHash)
平均哈希算法過於嚴格,不夠精確,更適合搜索縮略圖,為了獲得更精確的結果可以選擇感知哈希算法,它采用的是DCT(離散余弦變換)來降低頻率的方法
一般步驟:
縮小圖片:32 * 32是一個較好的大小,這樣方便DCT計算
轉化為灰度圖
計算DCT:利用Opencv中提供的dct()方法,注意輸入的圖像必須是32位浮點型,所以先利用numpy中的float32進行轉換
縮小DCT:DCT計算后的矩陣是32 * 32,保留左上角的8 * 8,這些代表的圖片的最低頻率
計算平均值:計算縮小DCT后的所有像素點的平均值。
進一步減小DCT:大於平均值記錄為1,反之記錄為0.
得到信息指紋:組合64個信息位,順序隨意保持一致性。
最后比對兩張圖片的指紋,獲得漢明距離即可。
dHash算法
相比pHash,dHash的速度要快的多,相比aHash,dHash在效率幾乎相同的情況下的效果要更好,它是基於漸變實現的。
步驟:
縮小圖片:收縮到9*8的大小,以便它有72的像素點
轉化為灰度圖
計算差異值:dHash算法工作在相鄰像素之間,這樣每行9個像素之間產生了8個不同的差異,一共8行,則產生了64個差異值
獲得指紋:如果左邊的像素比右邊的更亮,則記錄為1,否則為0.
最后比對兩張圖片的指紋,獲得漢明距離即可。
import cv2 import numpy as np from matplotlib import pyplot #最簡單的以huidu直方圖作為相似比較的實現 def classify_gray_hist(image1,image2,size = (256,256)): #先調整大小 image1 = cv2.resize(image1,size) image2 = cv2.resize(image2,size) hist1 = cv2.calcHist([image1],[0],None,[256],[0.0,255.0]) hist2 = cv2.calcHist([image2],[0],None,[256],[0.0,255.0]) #比較直方圖 pyplot.plot(range(256),hist1,'r') pyplot.plot(range(256),hist2,'y') pyplot.show() #計算直方圖的重合度 degree = 0 for i in range(len(hist1)): if hist1[i] != hist2[i]: degree = degree + (1-(abs(hist1[i]-hist2[i])/max(hist1[i],hist2[i]))) else: degree = degree + 1 degree = degree/len(hist1) return degree def classify_hist_with_split(image1,image2,size = (256,256)): #將圖像resize后,分類為三個通道,再計算每個通道的相似值 image1 = cv2.resize(image1,size) image2 = cv2.resize(image2,size) sub_image1 = cv2.split(image1) sub_image2 = cv2.split(image2) sub_data = 0 for im1,im2 in zip(sub_image1,sub_image2): sub_data += calculate(im1,im2) sub_data = sub_data/3 return sub_data def calculate(im1,im2): hist1 = cv2.calcHist([im1],[0],None,[256],[0.0,255.0]) hist2 = cv2.calcHist([im2],[0],None,[256],[0.0,255.0]) pyplot.plot(range(256),hist1,'r') pyplot.plot(range(256),hist2,'y') pyplot.show() degree = 0 for i in range(len(hist1)): if hist1[i] != hist2[i]: degree = degree + (1 - (abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))) else: degree = degree + 1 degree = degree / len(hist1) return degree # 平均哈希算法計算 def classify_aHash(image1,image2): image1 = cv2.resize(image1,(8,8)) image2 = cv2.resize(image2,(8,8)) gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY) #切換至灰度圖 gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY) hash1 = getHash(gray1) hash2 = getHash(gray2) return Hamming_distance(hash1,hash2) def classify_pHash(image1,image2): image1 = cv2.resize(image1,(32,32)) image2 = cv2.resize(image2,(32,32)) gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY) #切換至灰度圖 gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY) # 將灰度圖轉為浮點型,再進行dct變換 dct1 = cv2.dct(np.float32(gray1)) dct2 = cv2.dct(np.float32(gray2)) # 取左上角的8*8,這些代表圖片的最低頻率 # 這個操作等價於c++中利用opencv實現的掩碼操作 # 在python中進行掩碼操作,可以直接這樣取出圖像矩陣的某一部分 dct1_roi = dct1[0:8,0:8] dct2_roi = dct2[0:8,0:8] print(dct1) hash1 = getHash(dct1_roi) hash2 = getHash(dct2_roi) return Hamming_distance(hash1,hash2) # 輸入灰度圖,返回hash def getHash(image): avreage = np.mean(image) #計算像素平均值 hash = [] for i in range(image.shape[0]): for j in range(image.shape[1]): if image[i, j] > avreage: hash.append(1) else: hash.append(0) return hash # 計算漢明距離 def Hamming_distance(hash1, hash2): num = 0 for index in range(len(hash1)): if hash1[index] != hash2[index]: num += 1 return num #差值感知算法 def dhash(image1,image2): image1 = cv2.resize(image1,(9,8)) image2 = cv2.resize(image2,(9,8)) gray1 = cv2.cvtColor(image1,cv2.COLOR_BGR2GRAY) #切換至灰度圖 gray2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY) hash1 = dhashcaulate(gray1) hash2 = dhashcaulate(gray2) return Hamming_distance(hash1,hash2) def dhashcaulate(gray): hash_str = '' for i in range(8): for j in range(8): if gray[i, j] > gray[i, j + 1]: hash_str = hash_str + '1' else: hash_str = hash_str + '0' return hash_str if __name__ == '__main__': imgobj1 = cv2.imread('one.jpg') imgobj2 = cv2.imread('three.jpg') cv2.imshow('img1',imgobj1) cv2.imshow('img2',imgobj2) #degree = classify_gray_hist(imgobj1,imgobj2) #單通道直方圖 #degree = classify_hist_with_split(imgobj1,imgobj2) #三通道直方圖 #degree = classify_aHash(imgobj1,imgobj2) #平均哈希法 #degree = classify_pHash(imgobj1,imgobj2) #感知哈希法 degree = dhash(imgobj1,imgobj2) #差值感知哈希法 print(degree) cv2.waitKey(0)
方差的計算方式:
import cv2 import matplotlib.pyplot as plt #計算方差 def getss(list): #計算平均值 avg=sum(list)/len(list) #定義方差變量ss,初值為0 ss=0 #計算方差 for l in list: ss+=(l-avg)*(l-avg)/len(list) #返回方差 return ss #獲取每行像素平均值 def getdiff(img): #定義邊長 Sidelength=30 #縮放圖像 img=cv2.resize(img,(Sidelength,Sidelength),interpolation=cv2.INTER_CUBIC) #灰度處理 gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #avglist列表保存每行像素平均值 avglist=[] #計算每行均值,保存到avglist列表 for i in range(Sidelength): avg=sum(gray[i])/len(gray[i]) avglist.append(avg) #返回avglist平均值 return avglist #讀取測試圖片 img1=cv2.imread("one.jpg") diff1=getdiff(img1) print('img1:',getss(diff1)) #讀取測試圖片 img11=cv2.imread("two.jpg") diff11=getdiff(img11) print('img11:',getss(diff11)) x=range(30) plt.figure("avg") plt.plot(x,diff1,marker="*",label="$walk01$") plt.plot(x,diff11,marker="*",label="$walk03$") plt.title("avg") plt.legend() plt.show() cv2.waitKey(0) cv2.destroyAllWindows()