OpenCV Python教程(3)(4)(5): 直方圖的計算與顯示 形態學處理 初級濾波內
OpenCV Python教程(3、直方圖的計算與顯示)
本篇文章介紹如何用OpenCV Python來計算直方圖,並簡略介紹用NumPy和Matplotlib計算和繪製直方圖
直方圖的背景知識、用途什麼的就直接略過去了。這裡直接介紹方法。
計算並顯示直方圖
與C++中一樣,在Python中呼叫的OpenCV直方圖計算函式為cv2.calcHist。
cv2.calcHist的原型為:
- cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate ]]) #返回hist
- #coding=utf-8
- import cv2
- import numpy as np
- image = cv2.imread("D:/histTest.jpg", 0)
- hist = cv2.calcHist([image],
- [0], #使用的通道
- None, #沒有使用mask
- [256], #HistSize
- [0.0,255.0]) #直方圖柱的範圍
第二個引數是用於計算直方圖的通道,這裡使用灰度圖計算直方圖,所以就直接使用第一個通道;
第三個引數是Mask,這裡沒有使用,所以用None。
第四個引數是histSize,表示這個直方圖分成多少份(即多少個直方柱)。第二個例子將繪出直方圖,到時候會清楚一點。
第五個引數是表示直方圖中各個畫素的值,[0.0, 256.0]表示直方圖能表示畫素值從0.0到256的畫素。
最後是兩個可選引數,由於直方圖作為函式結果返回了,所以第六個hist就沒有意義了(待確定)
最後一個accumulate是一個布林值,用來表示直方圖是否疊加。
彩色影象不同通道的直方圖
彩色影象不同通道的直方圖
下面來看下彩色影象的直方圖處理。以最著名的lena.jpg為例,首先讀取並分離各通道:
- import cv2
- import numpy as np
- img = cv2.imread("D:/lena.jpg")
- b, g, r = cv2.split(img)
- def calcAndDrawHist(image, color):
- hist= cv2.calcHist([image], [0], None, [256], [0.0,255.0])
- minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(hist)
- histImg = np.zeros([256,256,3], np.uint8)
- hpt = int(0.9* 256);
- for h in range(256):
- intensity = int(hist[h]*hpt/maxVal)
- cv2.line(histImg,(h,256), (h,256-intensity), color)
- return histImg;
接著在主函式中使用:
- if __name__ == '__main__':
- img = cv2.imread("D:/lena.jpg")
- b, g, r = cv2.split(img)
- histImgB = calcAndDrawHist(b, [255, 0, 0])
- histImgG = calcAndDrawHist(g, [0, 255, 0])
- histImgR = calcAndDrawHist(r, [0, 0, 255])
- cv2.imshow("histImgB", histImgB)
- cv2.imshow("histImgG", histImgG)
- cv2.imshow("histImgR", histImgR)
- cv2.imshow("Img", img)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
更進一步
這樣做有點繁瑣,參考abid rahman的做法,無需分離通道,用折線來描繪直方圖的邊界可在一副圖中同時繪製三個通道的直方圖。方法如下:
- #coding=utf-8
- import cv2
- import numpy as np
- img = cv2.imread('D:/lena.jpg')
- h = np.zeros((256,256,3)) #建立用於繪製直方圖的全0影象
- bins = np.arange(256).reshape(256,1) #直方圖中各bin的頂點位置
- color = [ (255,0,0),(0,255,0),(0,0,255) ] #BGR三種顏色
- for ch, col in enumerate(color):
- originHist = cv2.calcHist([img],[ch],None,[256],[0,256])
- cv2.normalize(originHist, originHist,0,255*0.9,cv2.NORM_MINMAX)
- hist=np.int32(np.around(originHist))
- pts = np.column_stack((bins,hist))
- cv2.polylines(h,[pts],False,col)
- h=np.flipud(h)
- cv2.imshow('colorhist',h)
- cv2.waitKey(0)
程式碼說明:
這裡的for迴圈是對三個通道遍歷一次,每次繪製相應通道的直方圖的折線。for迴圈的第一行是計算對應通道的直方圖,經過上面的介紹,應該很容易就能明白。
這裡所不同的是沒有手動的計算直方圖的最大值再乘以一個係數,而是直接呼叫了OpenCV的歸一化函式。該函式將直方圖的範圍限定在0-255×0.9之間,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個元素四捨六入五湊偶取整(cv2.calcHist函式得到的是float32型別的陣列),接著將整數部分轉成np.int32型別。即61.123先轉成61.0,再轉成61。注意,這裡必須使用np.int32(...)進行轉換,numpy的轉換函式可以對陣列中的每個元素都進行轉換,而Python的int(...)只能轉換一個元素,如果使用int(...),將導致only length-1 arrays can be converted to Python scalars錯誤。
下面的pts = np.column_stack((bins,hist))是將直方圖中每個bin的值轉成相應的座標。比如hist[0] =3,...,hist[126] = 178,...,hist[255] = 5;而bins的值為[[0],[1],[2]...,[255]]。使用np.column_stack將其組合成[0, 3]、[126, 178]、[255, 5]這樣的座標作為元素組成的陣列。
最後使用cv2.polylines函式根據這些點繪製出折線,第三個False引數指出這個折線不需要閉合。第四個引數指定了折線的顏色。
當所有完成後,別忘了用h = np.flipud(h)反轉繪製好的直方圖,因為繪製時,[0,0]在影象的左上角。這在直方圖視覺化一節中有說明。
NumPy版的直方圖計算
在查閱abid rahman的資料時,發現他用NumPy的直方圖計算函式np.histogram也實現了相同的效果。如下:
- #coding=utf-8
- import cv2
- import numpy as np
- img = cv2.imread('D:/lena.jpg')
- h = np.zeros((300,256,3))
- bins = np.arange(257)
- bin = bins[0:-1]
- color = [ (255,0,0),(0,255,0),(0,0,255) ]
- for ch,col in enumerate(color):
- item = img[:,:,ch]
- N,bins = np.histogram(item,bins)
- v=N.max()
- N = np.int32(np.around((N*255)/v))
- N=N.reshape(256,1)
- pts = np.column_stack((bin,N))
- cv2.polylines(h,[pts],False,col)
- h=np.flipud(h)
- cv2.imshow('img',h)
- cv2.waitKey(0)
另外,通過NumPy和matplotlib可以更方便的繪製出直方圖,下面的程式碼供大家參考,如果有機會,再寫的專門介紹matplotlib的文章。
- import matplotlib.pyplot as plt
- import numpy as np
- import cv2
- img = cv2.imread('D:/lena.jpg')
- bins = np.arange(257)
- item = img[:,:,1]
- hist,bins = np.histogram(item,bins)
- width = 0.7*(bins[1]-bins[0])
- center = (bins[:-1]+bins[1:])/2
- plt.bar(center, hist, align = 'center', width = width)
- plt.show()
這裡顯示的是綠色通道的直方圖。
OpenCV-Python教程(4、形態學處理)
提示:
- 轉載請詳細註明原作者及出處,謝謝!
- 本文介紹使用OpenCV-Python進行形態學處理
- 本文不介紹形態學處理的基本概念,所以讀者需要預先對其有一定的瞭解。
定義結構元素
形態學處理的核心就是定義結構元素,在OpenCV-Python中,可以使用其自帶的getStructuringElement函式,也可以直接使用NumPy的ndarray來定義一個結構元素。首先來看用getStructuringElement函式定義一個結構元素:
- element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
也可以用NumPy來定義結構元素,如下:
- NpKernel = np.uint8(np.zeros((5,5)))
- for i in range(5):
- NpKernel[2, i] = 1 #感謝chenpingjun1990的提醒,現在是正確的
- NpKernel[i, 2] = 1
- [[0 0 1 0 0]
- [0 0 1 0 0]
- [1 1 1 1 1]
- [0 0 1 0 0]
- [0 0 1 0 0]]
這裡可以看出,用OpenCV-Python內建的常量定義橢圓(MORPH_ELLIPSE)和十字形結構(MORPH_CROSS)元素要簡單一些,如果定義矩形(MORPH_RECT)和自定義結構元素,則兩者差不多。
本篇文章將用參考資料1中的相關章節的圖片做測試:
腐蝕和膨脹
下面先以腐蝕影象為例子介紹如何使用結構元素:
- #coding=utf-8
- import cv2
- import numpy as np
- img = cv2.imread('D:/binary.bmp',0)
- #OpenCV定義的結構元素
- kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
- #腐蝕影象
- eroded = cv2.erode(img,kernel)
- #顯示腐蝕後的影象
- cv2.imshow("Eroded Image",eroded);
- #膨脹影象
- dilated = cv2.dilate(img,kernel)
- #顯示膨脹後的影象
- cv2.imshow("Dilated Image",dilated);
- #原影象
- cv2.imshow("Origin", img)
- #NumPy定義的結構元素
- NpKernel = np.uint8(np.ones((3,3)))
- Nperoded = cv2.erode(img,NpKernel)
- #顯示腐蝕後的影象
- cv2.imshow("Eroded by NumPy kernel",Nperoded);
- cv2.waitKey(0)
- cv2.destroyAllWindows()
如上所示,腐蝕和膨脹的處理很簡單,只需設定好結構元素,然後分別呼叫cv2.erode(...)和cv2.dilate(...)函式即可,其中第一個引數是需要處理的影象,第二個是結構元素。返回處理好的影象。
結果如下:
開運算和閉運算
瞭解形態學基本處理的同學都知道,開運算和閉運算就是將腐蝕和膨脹按照一定的次序進行處理。但這兩者並不是可逆的,即先開後閉並不能得到原先的影象。程式碼示例如下:
- #coding=utf-8
- import cv2
- import numpy as np
- img = cv2.imread('D:/binary.bmp',0)
- #定義結構元素
- kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
- #閉運算
- closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
- #顯示腐蝕後的影象
- cv2.imshow("Close",closed);
- #開運算
- opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
- #顯示腐蝕後的影象
- cv2.imshow("Open", opened);
- cv2.waitKey(0)
- cv2.destroyAllWindows()
閉運算用來連線被誤分為許多小塊的物件,而開運算用於移除由影象噪音形成的斑點。因此,某些情況下可以連續運用這兩種運算。如對一副二值圖連續使用閉運算和開運算,將獲得影象中的主要物件。同樣,如果想消除影象中的噪聲(即影象中的“小點”),也可以對影象先用開運算後用閉運算,不過這樣也會消除一些破碎的物件。
對原始影象進行開運算和閉運算的結果如下:
用形態學運算檢測邊和角點
這裡通過一個較複雜的例子介紹如何用形態學運算元檢測影象中的邊緣和拐角(這裡只是作為介紹形態學處理例子,實際使用時請用Canny或Harris等演算法)。
檢測邊緣
形態學檢測邊緣的原理很簡單,在膨脹時,影象中的物體會想周圍“擴張”;腐蝕時,影象中的物體會“收縮”。比較這兩幅影象,由於其變化的區域只發生在邊緣。所以這時將兩幅影象相減,得到的就是影象中物體的邊緣。這裡用的依然是參考資料1中相關章節的圖片:
程式碼如下:
- #coding=utf-8
- import cv2
- import numpy
- image = cv2.imread("D:/building.jpg",0);
- #構造一個3×3的結構元素
- element = cv2.getStructuringElement(cv2.MORPH_RECT,(3, 3))
- dilate = cv2.dilate(image, element)
- erode = cv2.erode(image, element)
- #將兩幅影象相減獲得邊,第一個引數是膨脹後的影象,第二個引數是腐蝕後的影象
- result = cv2.absdiff(dilate,erode);
- #上面得到的結果是灰度圖,將其二值化以便更清楚的觀察結果
- retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY);
- #反色,即對二值圖每個畫素取反
- result = cv2.bitwise_not(result);
- #顯示影象
- cv2.imshow("result",result);
- cv2.waitKey(0)
- cv2.destroyAllWindows()
檢測拐角
與邊緣檢測不同,拐角的檢測的過程稍稍有些複雜。但原理相同,所不同的是先用十字形的結構元素膨脹畫素,這種情況下只會在邊緣處“擴張”,角點不發生變化。接著用菱形的結構元素腐蝕原影象,導致只有在拐角處才會“收縮”,而直線邊緣都未發生變化。
第二步是用X形膨脹原影象,角點膨脹的比邊要多。這樣第二次用方塊腐蝕時,角點恢復原狀,而邊要腐蝕的更多。所以當兩幅影象相減時,只保留了拐角處。示意圖如下(示意圖來自參考資料1):
程式碼如下:
- #coding=utf-8
- import cv2
- image = cv2.imread("D:/building.jpg", 0)
- origin = cv2.imread("D:/building.jpg")
- #構造5×5的結構元素,分別為十字形、菱形、方形和X型
- cross = cv2.getStructuringElement(cv2.MORPH_CROSS,(5, 5))
- #菱形結構元素的定義稍麻煩一些
- diamond = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
- diamond[0, 0] = 0
- diamond[0, 1] = 0
- diamond[1, 0] = 0
- diamond[4, 4] = 0
- diamond[4, 3] = 0
- diamond[3, 4] = 0
- diamond[4, 0] = 0
- diamond[4, 1] = 0
- diamond[3, 0] = 0
- diamond[0, 3] = 0
- diamond[0, 4] = 0
- diamond[1, 4] = 0
- square = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))
- x = cv2.getStructuringElement(cv2.MORPH_CROSS,(5, 5))
- #使用cross膨脹影象
- result1 = cv2.dilate(image,cross)
- #使用菱形腐蝕影象
- result1 = cv2.erode(result1, diamond)
- #使用X膨脹原影象
- result2 = cv2.dilate(image, x)
- #使用方形腐蝕影象
- result2 = cv2.erode(result2,square)
- #result = result1.copy()
- #將兩幅閉運算的影象相減獲得角
- result = cv2.absdiff(result2, result1)
- #使用閾值獲得二值圖
- retval, result = cv2.threshold(result, 40, 255, cv2.THRESH_BINARY)
- #在原圖上用半徑為5的圓圈將點標出。
- for j in range(result.size):
- y = j / result.shape[0]
- x = j % result.shape[0]
- if result[x, y] == 255:
- cv2.circle(image, (y, x), 5, (255,0,0))
- cv2.imshow("Result", image)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
大家可以驗證一下,比如在程式碼中插入這兩行程式碼,就能知道結果了:
- cv2.circle(image, (5, 10), 5, (255,0,0))
- image[5, 10] = 0
當然,這只是個形態學處理示例,檢測結果並不好。
未完待續...
在將來的某一篇文章中將做個總結,介紹下OpenCV中常用的函式,如threshold、bitwise_xxx,以及繪製函式等。
參考資料:
1、《Opencv2 Computer Vision Application Programming Cookbook》
2、《OpenCV References Manule》
如果覺得本文寫的還可以的話,請輕點“頂”,方便讀者、以及您的支援是我寫下去的最大的兩個動力。
OpenCV-Python教程(5、初級濾波內容)
本篇文章介紹如何用OpenCV-Python來實現初級濾波功能。
提示:
- 轉載請詳細註明原作者及出處,謝謝!
- 本文介紹使用OpenCV-Python實現基本的濾波處理
- 本文不介紹濾波處理的詳細概念,所以讀者需要預先對其有一定的瞭解。
簡介
過濾是訊號和影象處理中基本的任務。其目的是根據應用環境的不同,選擇性的提取影象中某些認為是重要的資訊。過濾可以移除影象中的噪音、提取感興趣的可視特徵、允許影象重取樣,等等。其源自於一般的訊號和系統理論,這裡將不介紹該理論的細節。但本章會介紹關於過濾的基本概念,以及如何在影象處理程式中使用濾波器。首先,簡要介紹下頻率域分析的概念。
當我們觀察一張圖片時,我們觀察的是影象中有多少灰度級(或顏色)及其分佈。根據灰度分佈的不同來區分不同的影象。但還有其他方面可以對影象進行分析。我們可以觀察影象中灰度的變化。某些影象中包含大量的強度不變的區域(如藍天),而在其他影象中的灰度變化可能會非常快(如包含許多小物體的擁擠的影象)。因此,觀察影象中這些變化的頻率就構成了另一條分類影象的方法。這個觀點稱為頻域。而通過觀察影象灰度分佈來分類影象稱為空間域。
頻域分析將影象分成從低頻到高頻的不同部分。低頻對應影象強度變化小的區域,而高頻是影象強度變化非常大的區域。目前已存在若干轉換方法,如傅立葉變換或餘弦變換,可以用來清晰的顯示影象的頻率內容。注意,由於影象是一個二維實體,所以其由水平頻率(水平方向的變化)和豎直頻率(豎直方向的變化)共同組成。
在頻率分析領域的框架中,濾波器是一個用來增強影象中某個波段或頻率並阻塞(或降低)其他頻率波段的操作。低通濾波器是消除影象中高頻部分,但保留低頻部分。高通濾波器消除低頻部分
本篇文章介紹在OpenCV-Python中實現的初級的濾波操作,下一篇文章介紹更加複雜的濾波原理及其實現。
本篇文章使用傳統的lena作為實驗影象。
用低通濾波來平滑影象
低通濾波器的目標是降低影象的變化率。如將每個畫素替換為該畫素周圍畫素的均值。這樣就可以平滑並替代那些強度變化明顯的區域。在OpenCV中,可以通過blur函式做到這一點:
- dst = cv2.blur(image,(5,5));
其中dst是blur處理後返回的影象,引數一是輸入的待處理影象,引數2是低通濾波器的大小。其後含有幾個可選引數,用來設定濾波器的細節,具體可查閱參考資料2。不過這裡,這樣就夠了。下面是一個簡單的示例程式碼:
- #coding=utf-8
- import cv2
- img = cv2.imread("D:/lena.jpg", 0)
- result = cv2.blur(img, (5,5))
- cv2.imshow("Origin", img)
- cv2.imshow("Blur", result)
- cv2.waitKey(0)
- cv2.destroyAllWindows()
這種濾波器又稱為boxfilter(注,這與化學上的箱式過濾器是兩碼事,所以這裡就不翻譯了)。所以也可通過OpenCV的cv2.bofxfilter(...)函式來完成相同的工作。如下:
- result1 = cv2.boxFilter(img, -1, (5, 5))
高斯模糊
在某些情況下,需要對一個畫素的周圍的畫素給予更多的重視。因此,可通過分配權重來重新計算這些周圍點的值。這可通過高斯函式(鐘形函式,即喇叭形數)的權重方案來解決。cv::GaussianBlur函式可作為濾波器用下面的方法呼叫:
- gaussianResult = cv2.GaussianBlur(img,(5,5),1.5)
區別
低通濾波與高斯濾波的不同之處在於:低通濾波中,濾波器中每個畫素的權重是相同的,即濾波器是線性的。而高斯濾波器中畫素的權重與其距中心畫素的距離成比例。關於高斯模糊的詳細內容,抽空將寫一篇獨立的文章介紹。
使用中值濾波消除噪點
前面介紹的是線性過濾器,這裡介紹非線性過濾器——中值濾波器。由於中值濾波器對消除椒鹽現象特別有用。所以我們使用第二篇教程中椒鹽函式先對影象進行處理,將處理結果作為示例圖片。
呼叫中值濾波器的方法與呼叫其他濾波器的方法類似,如下:
- result = cv2.medianBlur(image,5)
如果在某個畫素周圍有白色或黑色的畫素,這些白色或黑色的畫素不會選擇作為中值(最大或最小值不用),而是被替換為鄰域值。程式碼如下:
- #coding=utf-8
- import cv2
- import numpy as np
- def salt(img, n):
- for k in range(n):
- i = int(np.random.random() * img.shape[1]);
- j = int(np.random.random() * img.shape[0]);
- if img.ndim == 2:
- img[j,i] = 255
- elif img.ndim == 3:
- img[j,i,0]= 255
- img[j,i,1]= 255
- img[j,i,2]= 255
- return img
- img = cv2.imread("D:/lena.jpg", 0)
- result = salt(img, 500)
- median = cv2.medianBlur(result, 5)
- cv2.imshow("Salt", result)
- cv2.imshow("Median", median)
- cv2.waitKey(0)
由於中值濾波不會處理最大和最小值,所以就不會受到噪聲的影響。相反,如果直接採用blur進行均值濾波,則不會區分這些噪聲點,濾波後的影象會受到噪聲的影響。
中值濾波器在處理邊緣也有優勢。但中值濾波器會清除掉某些區域的紋理(如背景中的樹)。
其他
由於方向濾波器與這裡的原理有較大的出入,所以將用獨立的一篇文章中介紹其原理以及實現。
參考資料:
1、《Opencv2 Computer Vision Application Programming Cookbook》
2、《OpenCV References Manule》
from: http://blog.csdn.net/sunny2038/article/category/904451
相關文章
- OpenCV計算機視覺學習(4)——影像平滑處理(均值濾波,高斯濾波,中值濾波,雙邊濾波)OpenCV計算機視覺
- Python 影像處理 OpenCV (16):影像直方圖PythonOpenCV直方圖
- [Python影象處理] 十一.灰度直方圖概念及OpenCV繪製直方圖Python直方圖OpenCV
- Python 影像處理 OpenCV (7):影像平滑(濾波)處理PythonOpenCV
- Python 影像處理 OpenCV (10):影像處理形態學之頂帽運算與黑帽運算PythonOpenCV
- OpenCV計算機視覺學習(9)——影像直方圖 & 直方圖均衡化OpenCV計算機視覺直方圖
- Python 影像處理 OpenCV (9):影像處理形態學開運算、閉運算以及梯度運算PythonOpenCV梯度
- Python-OpenCV 處理影象(四):影象直方圖和反向投影PythonOpenCV直方圖
- 【影像處理】基於OpenCV實現影像直方圖的原理OpenCV直方圖
- OpenCV成長之路(4):影象直方圖OpenCV直方圖
- opencv——影像直方圖與反向投影OpenCV直方圖
- 顯示網路圖片變形的處理
- 精通Python自然語言處理 3 :形態學Python自然語言處理
- 計算機圖形學-線性過濾計算機
- [Python影象處理] 四.影象平滑之均值濾波、方框濾波、高斯濾波及中值濾波Python
- OpenCV計算機視覺學習(10)——影像變換(傅立葉變換,高通濾波,低通濾波)OpenCV計算機視覺
- OpenCV成長之路(5):影象直方圖的應用OpenCV直方圖
- Python 影像處理 OpenCV (2):畫素處理與 Numpy 操作以及 Matplotlib 顯示影像PythonOpenCV
- Python-OpenCV 處理影象(二):濾鏡和影象運算PythonOpenCV
- Python影象處理庫:Pillow 初級教程Python
- Python影像處理庫:Pillow 初級教程Python
- OpenCV計算機視覺學習(5)——形態學處理(腐蝕膨脹,開閉運算,禮帽黑帽,邊緣檢測)OpenCV計算機視覺
- python 圖形初學Python
- 【Python_Demo_5】Python中條形重疊直方圖的繪製Python直方圖
- 【OpenCV學習】影象卷積濾波OpenCV卷積
- 形態學影像處理
- 等頻直方圖,計算COST直方圖
- OpenCV 線性濾波OpenCV
- OpenCV之影象直方圖均衡化OpenCV直方圖
- 計算機圖形學之紋理的作用計算機
- [Python影象處理] 九.形態學之影象開運算、閉運算、梯度運算Python梯度
- 請教大家,java如何顯示直方圖、餅圖等等?Java直方圖
- 形態學影像處理(二)
- python資料視覺化-matplotlib入門(4)-條形圖和直方圖Python視覺化直方圖
- 計算機圖形學----DDA、Bresenham直線演算法計算機演算法
- 個人實驗程式碼記錄 | 數字影像處理實驗3·影像直方圖與均衡化處理直方圖
- 計算機圖形學(一) 視訊顯示裝置_1_CRT原理計算機
- 【OpenCV】鄰域濾波:方框、高斯、中值、雙邊濾波OpenCV