直方圖
何為直方圖?沒那么高大上,其實就是二維統計圖。每個照片都是有像素點所組成,當然也是[0,255],直方圖就是統計每個值所對應的像素點有幾個。
直方圖橫坐標表示0-255這些像素點值;縱坐標表示對應像素點值的個數有多少個,例如:像素為55的像素點有多少個
cv2.calcHist(images,channels,mask,histSize,ranges)
cv2.calcHist([img],[0],None,[256],[0,256])
參數一:images:原圖像格式為uint8或float32;當傳入函數時應該用中括號括住,通常情況下都是輸入的是灰度圖
參數二:同樣用中括號括起來,它會告訴函數我們圖像的直方圖;如果輸入圖像時灰度圖它的值就是[0];如果時彩色圖像傳入的參數可以是[0][1][2],分別對應BGR
參數三:掩模圖像,說白了就是取部分圖像而已;統計整個圖像的直方圖時就把它設置為None;當然也可以通過掩模來統計圖像的某部分的直方圖
參數四:BIN的數目,也就是橫坐標的總量程而已,一般都是256,也就是0-255這256個像素點值,也需要用中括號括起來
參數五:像素值的范圍,一般設置為[0,256]
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#圖像顯示函數cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png',0)#這里的參數0表示以灰度圖進行讀取
hist = cv2.calcHist([img],[0],None,[256],[0,256])#當然由于是灰度圖通道數也只有一個即參數二[0];參數三None表示沒有使用掩模直接輸出整體圖像的直方圖;參數四[256]直方圖的橫坐標量程;參數五[0,256]像素值的范圍
hist.shape#結果為:(256, 1) 其中256表示這個圖像中有0-255這256個取值,1表示得到的直方圖是二維的,即每個像素出現多少個plt.hist(img.ravel(),256)
plt.show()img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png')#第二個參數不填表示原圖輸入,這個圖像為彩色圖也就是彩色圖輸入
color = ('b','g','r')
for i,col in enumerate(color):#枚舉格式遍歷每個顏色通道histr = cv2.calcHist([img],[i],None,[256],[0,256])plt.plot(histr,color = col)plt.xlim([0,256])
plt.show()
原圖:
原圖對應的灰度圖的直方圖:
原圖的BGR直方圖:
掩模mask
np.zeros(img.shape[:2],np.uint8)
上面的函數中有掩模的操作,接下來介紹一下掩模mask的定義的操作
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#圖像顯示函數cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()#創建mast,掩模是由黑白兩部分組成的,然后與原圖重疊,掩模白色區域對應原圖區域不變,黑色區域對應原圖區域變黑
mask = np.zeros(img.shape[:2],np.uint8)#這里面的掩碼實際上就是邊緣黑中間白,此時的mask就和原圖片大小相同,uint8表示無符號八位整數0-255之間
print(mask.shape)#結果為:(321, 287)
mask[50:200,50:200] = 255#要保存的東西是白色的,白色區域為要保存的地方,所以將選取地方置為255
show_photo('mask',mask)img = cv2.imread('E:\Jupyter_workspace\study\data/cat.png',0)#灰度圖讀取照片
print(img.shape)#結果為:(321, 287)
show_photo('img',img)masked_img = cv2.bitwise_and(img,img,mask=mask)#與操作,也就是有0則0,黑色為0所以說掩碼黑色地區都為黑;參數一表示原圖像,參數三表示掩模圖像
show_photo('masked_img',masked_img)hist_full = cv2.calcHist([img],[0],None,[256],[0,256])#不帶掩模的圖像進行統計直方圖
hist_mask = cv2.calcHist([img],[0],mask,[256],[0,256])#帶掩模mask的時候統計圖像的部分掩模區域的直方圖plt.subplot(221),plt.imshow(img,'gray')
plt.subplot(222),plt.imshow(mask,'gray')
plt.subplot(223),plt.imshow(masked_img,'gray')
plt.subplot(224),plt.plot(hist_full),plt.plot(hist_mask)
plt.xlim([0,256])
plt.show()
掩模圖像:
原圖:
掩模操作后的圖像:
上一:原圖
上二:自定義的掩模
下一:掩模對應的原圖部分
下二:藍線對應原圖的直方圖,橙線對應掩模處理的部分原圖的直方圖
直方圖均衡化
假設某圖片部分像素值為:
255 | 128 | 200 | 50 |
50 | 200 | 255 | 50 |
255 | 200 | 128 | 128 |
200 | 200 | 255 | 50 |
下面表格中的函數映射中(255-0)表示設置的橫軸的量程這里設置的是0-255
灰度值 | 像素個數 | 概率 | 累積概率 | 根據函數映射后灰度值 | 取整 |
---|---|---|---|---|---|
50 | 4 | 4/16 = 0.25 | 0.25 | 0.25*(255-0)=63.75 | 64 |
128 | 3 | 3/16 = 0.1875 | 0.25+0.1875=0.4375 | 0.4375*(255-0)=111.5625 | 112 |
200 | 5 | 5/16 = 0.3125 | 0.25+0.1875+0.3125=0.75 | 0.75*(255-0)=191.25 | 191 |
255 | 4 | 4/16 = 0.25 | 0.25+0.1875+0.3125+0.25=1 | 1*(255-0)=255 | 255 |
均衡化后的像素值為:
255 | 112 | 191 | 64 |
64 | 191 | 255 | 64 |
255 | 191 | 112 | 112 |
192 | 191 | 255 | 255 |
均衡化之后發現了這16個數相差的并不是特別大了
cv2.equalizeHist(img)
傳入圖像對象名稱即可進行整體均衡化
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#圖像顯示函數cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/people.jpg',0)
plt.hist(img.ravel(),256)#原圖像的直方圖
plt.show()equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)#均衡化后的直方圖
plt.show()res = np.hstack((img,equ))
show_photo('img_equ',res)#顯示原圖和均衡化后的圖片
原圖直方圖:
均衡化后的直方圖:
原圖和均衡化后的圖片對比:
均衡化后的圖像臉上的細節變得更加模糊了,尷尬不???
這時候就需要自定義均衡化
自定義均衡化
直方圖的均衡化也就是整體的均衡化,其他像素點值大的地方給平均給其他地方了導致一下細節會丟失
若將原圖分成塊進行均衡化,每塊進行自己塊的均衡化效果會比全局整體均衡化更好些
當然,若圖像里面噪音太大,局部反而沒有整體均衡化好,需要自己事先去衡量一下
cv2.createCLAHE(clipLimit = 2.0,tileGridSize = (8,8))
(8,8)表示分塊均衡化中塊的大小
import cv2
import numpy as np
from matplotlib import pyplot as pltdef show_photo(name,picture):#圖像顯示函數cv2.imshow(name,picture)cv2.waitKey(0)cv2.destroyAllWindows()img = cv2.imread('E:\Jupyter_workspace\study\data/people.jpg',0)
plt.hist(img.ravel(),256)#原圖像的直方圖equ = cv2.equalizeHist(img)
plt.hist(equ.ravel(),256)#均衡化后的直方圖clahe = cv2.createCLAHE(clipLimit = 2.0,tileGridSize = (8,8))#自定義均衡化,每8*8分成塊,按塊進行均衡化
res_clahe = clahe.apply(img)res = np.hstack((img,equ,res_clahe))
show_photo('img_equ_clahe',res)
原圖-整體均衡化-自定義均衡化