久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

OpenCV計(jì)算機(jī)視覺(jué)學(xué)習(xí)(9)圖像直方圖&直方圖均衡化

 山峰云繞 2023-03-19 發(fā)布于貴州

https://m.toutiao.com/is/STMYtYW/ 

1,如何提高圖像像素

對(duì)曝光過(guò)度或者逆光拍攝的圖片可以通過(guò)直方圖均衡化的方法用來(lái)增強(qiáng)局部或者整體的對(duì)比度,。

對(duì)于相機(jī)采集的原始圖像經(jīng)常會(huì)出現(xiàn)一種現(xiàn)象,,即圖像所有像素的灰度值分布不均勻,,而是集中在某一特定的小區(qū)域,,導(dǎo)致圖像中的所有信息的灰度值都很接近,,即對(duì)比度差,,很難從圖像中分辨出某一特征的信息,。而質(zhì)量較高的圖像中,像素的強(qiáng)度應(yīng)該均衡的分布,。

為了提高圖像處理的效果,,經(jīng)常會(huì)在圖像處理之前進(jìn)行直方圖均衡化,即將圖像的直方圖灰度級(jí)別由集中在某一小部分灰度級(jí)分散成在所有灰度級(jí)別都有一定的覆蓋,,所以通過(guò)直方圖均衡化的方法用來(lái)增強(qiáng)局部或整體的對(duì)比度,。

具體的思路就是通過(guò)找出圖像中最亮和最暗的像素值將之映射到純黑和純白之后再將其他的像素值按照某種算法映射到純黑和純白之間的值。另外一種方法就是尋找圖像中像素的平均值作為中間灰度值,,然后擴(kuò)展范圍以達(dá)到盡量充滿可顯示的值。

一個(gè)好的圖像會(huì)有來(lái)自圖像的所有區(qū)域的像素,。所以你需要把這個(gè)直方圖拉伸到兩端(如上圖所給出的),,這就是直方圖均衡的作用(用簡(jiǎn)單的話說(shuō))。這通常會(huì)改善圖像的對(duì)比度,。

2,,直方圖均衡化的原理

2.1 直方圖的介紹

具體直方圖實(shí)現(xiàn)的原理是什么呢?請(qǐng)看下圖:

左圖是一個(gè)圖像的像素組合,,我們拿到的是一個(gè)12*20 大小的圖像像素,;右圖就是他的直方圖展示,橫軸表示在0~255之間的區(qū)間塊,,我們將其分為16個(gè)bin,,統(tǒng)計(jì)圖像中每個(gè)像素的個(gè)數(shù),右圖反映的時(shí)圖像中每個(gè)像素出現(xiàn)的頻率,,橫軸是像素區(qū)間,,縱坐標(biāo)是像素出現(xiàn)的頻率。

看到上面兩個(gè)圖,,大概直方圖的解釋?xiě)?yīng)該很明顯了,。

2.2 用實(shí)驗(yàn)數(shù)據(jù)展示什么是直方圖?

我們可以把直方圖看做一個(gè)圖,,它給我們一個(gè)關(guān)于圖像的強(qiáng)度分布的總體思路,。它是一個(gè)帶有像素值的圖(從0到255, 不總是)在X軸上,,在y軸上的圖像對(duì)應(yīng)的像素個(gè)數(shù),。

這只是理解圖像的另一種方式,通過(guò)觀察圖像的直方圖,我們可以直觀的了解圖像的對(duì)比度,,亮度,,亮度分布等。(下圖來(lái)至于Cambridge in Color website的圖片,,建議去訪問(wèn)這個(gè)網(wǎng)站,,了解更多細(xì)節(jié)。)

你可以看到圖像和它的直方圖,。(這個(gè)直方圖是用灰度圖像繪制的,,而不是彩色圖像)。在直方圖中,,橫坐標(biāo)表示圖像中各個(gè)像素點(diǎn)的灰度級(jí),,縱坐標(biāo)表示具有該灰度級(jí)的像素個(gè)數(shù)。直方圖的左邊部分顯示了圖像中較暗像素的數(shù)量,,右邊區(qū)域顯示了更明亮的像素,。從直方圖中可以看到,深色區(qū)域的像素?cái)?shù)量比亮色區(qū)域更多,,而中間色調(diào)的數(shù)量(中值大約在127左右)則少得多,。

2.3 直方圖均衡化的原理

有時(shí)圖像的視覺(jué)上的缺陷并不在強(qiáng)度值集中在很窄的范圍內(nèi)。而是某些強(qiáng)度值的使用頻率很大,。在完美均衡的直方圖中,,每個(gè)柱的值都應(yīng)該相等。即50%的像素值應(yīng)該小于128,25%的像素值應(yīng)該小于64.總結(jié)出的經(jīng)驗(yàn)可定義為:在標(biāo)準(zhǔn)的直方圖中 p% 的像素?fù)碛械膹?qiáng)度值一定小于或等于 255*p%,,將該規(guī)律用于均衡直方圖中:強(qiáng)度 i 的灰度值應(yīng)該在對(duì)應(yīng)的像素強(qiáng)度低于 i 的百分比的強(qiáng)度中,。因此,所需要的查詢(xún)表可由下面的式子建立:

lut[i] = int(255.0 *p[i]) #p[i]是是強(qiáng)度值小于或等于i的像素的數(shù)目,。

p[i] 即直方圖累計(jì)值,,這是包含小于給點(diǎn)強(qiáng)度值的像素的直方圖,以代替包含指定強(qiáng)度值像素的數(shù)目,。比如第一幅圖像的累計(jì)直方圖如下圖中的藍(lán)線:

而完美均衡的直方圖,,其累積直方圖應(yīng)為一條斜線,如上圖中均衡化之后的紅線,。

更專(zhuān)業(yè)一點(diǎn),,這種累計(jì)直方圖應(yīng)該稱(chēng)為累計(jì)分布(cumulative distribition)。在Numpy中有一個(gè)專(zhuān)門(mén)的函數(shù)來(lái)計(jì)算,。這個(gè)在后面說(shuō),。

所以直方圖均衡化就是對(duì)圖像使用一種特殊的查詢(xún)表。通常來(lái)說(shuō),,直方圖均衡化大大增加了圖像的表象,。但是根據(jù)圖像可視內(nèi)容的不同,不同圖像的直方圖均衡化產(chǎn)生的效果不盡相同。下面我們具體學(xué)習(xí)一下,。

比如下圖小狗,,我們畫(huà)出原圖,并展示出其像素直方圖分布范圍:

我們對(duì)直方圖進(jìn)行均衡化,,均衡化的圖,,如下:

最終我們得到了小狗直方圖均衡化后的圖像。那其計(jì)算原理如下圖:

簡(jiǎn)單解釋一下,,上面兩張圖是我們?nèi)×藞D片中一點(diǎn)像素,,是我們直方圖均衡化前后的兩張表的對(duì)比。那么如何進(jìn)行直方圖均衡化的計(jì)算,,也就是將左圖的像素點(diǎn)轉(zhuǎn)換為右圖呢,,我們就需要下圖的計(jì)算過(guò)程了。圖很明顯,,我就不再贅述了,。

下面首先對(duì)直方圖的計(jì)算進(jìn)行學(xué)習(xí),然后學(xué)習(xí)直方圖均衡化,。

3,,直方圖的繪制

3.1 使用OpenCV統(tǒng)計(jì)繪制直方圖

OpenCV提供了cv.calcHist()函數(shù)來(lái)獲取直方圖,與C++中一樣,,都是cv.calcHist()。讓我們熟悉一下這個(gè)函數(shù)及其參數(shù):

def calcHist(images, channels, mask, histSize, ranges, hist=None, accumulate=None):# real signature unknown; restored from __doc__'''calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) -> hist. @overload'''pass
  • images:它是uint8類(lèi)型或float32的源圖像,。當(dāng)傳入函數(shù)時(shí),,它要用方括號(hào)括起來(lái),也就是”[img]”,。

  • channels:它也用方括號(hào)括起來(lái),。它是我們計(jì)算直方圖的信道的索引。例如,,如果輸入是灰度圖像,,它的值是0。對(duì)于顏色圖像,,您可以通過(guò)0,、1或2來(lái)分別計(jì)算藍(lán)色、綠色或紅色通道的直方圖,,即BGR通道,。

  • mask:遮罩圖。為了找到完整圖像的直方圖,,它被指定為“None”,。但如果你想找到圖像的特定區(qū)域的直方圖,你必須為它創(chuàng)建一個(gè)遮罩圖,并將其作為遮罩,。

  • histSize:這代表了我們的BINS數(shù),。需要用方括號(hào)來(lái)表示。在整個(gè)范圍內(nèi),,我們通過(guò)了256,。

  • ranges:強(qiáng)度值范圍,通常是 [ 0,,256 ]

讓我們從一個(gè)樣本圖像開(kāi)始,,只需要在灰度模式下加載圖像并找出其完整的直方圖:

#_*_coding:utf-8_*_import cv2 # opencv讀取的格式是BGRimport numpy as npimport matplotlib.pyplot as plt # Matplotlib讀取的格式是RGBimg = cv2.imread('cat.jpg', 0) #0 表示灰度圖hist = cv2.calcHist([img], [0], None, [256], [0, 256])print(hist.shape) # (256, 1)

hist是一個(gè)256x1陣列,每個(gè)值對(duì)應(yīng)于該圖像中的像素值機(jī)器對(duì)應(yīng)的像素值,。

3.2 使用Numpy計(jì)算直方圖

Numpy中提供了np.histogram()方法,,用于對(duì)一位數(shù)組進(jìn)行直方圖統(tǒng)計(jì),其參數(shù)列表入下:

Histogram(a,bins=10,range=None,normed=False,weights=None)
  • a:是保存待統(tǒng)計(jì)的數(shù)組

  • bins:指定統(tǒng)計(jì)的區(qū)間個(gè)數(shù),,即對(duì)統(tǒng)計(jì)范圍的等分?jǐn)?shù)

  • range:是一個(gè)長(zhǎng)度為2的元組,,表示統(tǒng)計(jì)范圍的最大值和最小值,默認(rèn)值為None,,表示范圍由數(shù)據(jù)的范圍決定,,即(a.min(), a.max))。

  • normed:當(dāng)normed參數(shù)為False時(shí),,函數(shù)返回?cái)?shù)組a中的數(shù)據(jù)在每個(gè)區(qū)間的個(gè)數(shù),,否則對(duì)個(gè)數(shù)進(jìn)行正規(guī)化處理,使它等于每個(gè)區(qū)間的概率密度,。

  • weights:weights參數(shù)和 bincount()的類(lèi)似

返回值(有兩個(gè))

  • hist : hist和之前計(jì)算的一樣,,每個(gè)區(qū)間的統(tǒng)計(jì)結(jié)果。

  • bins : 數(shù)組,,存儲(chǔ)每個(gè)統(tǒng)計(jì)區(qū)間的起點(diǎn),。range為[0,256]時(shí),bins有257個(gè)元素,,因?yàn)镹umpy計(jì)算bins是以0-0.99,1-1.99等,,所以最后一個(gè)是255-255.99。為了表示這一點(diǎn),,他們還在bins的末端添加了256,。但我們不需要256。到255就足夠了,。

讓我們從一個(gè)樣本圖像開(kāi)始,。只需在灰度模式下加載圖像并找到其完整的直方圖

hist, bins = np.histogram(img.ravel(), 255, [0,256])

Numpy還有另一個(gè)函數(shù),np.bincount(),,比np.histograme()要快得多(大約10X),。對(duì)于一維直方圖,,你可以試一下。不要忘記在np.bincount中設(shè)置minlength=256,。例如,,hist=np.bincount(img.ravel(),minlength=256)

OpenCV函數(shù)比np.histogram()快(大約40X),。所以考慮效率的時(shí)候堅(jiān)持用OpenCV函數(shù),。

3.3 使用matplotlib繪制直方圖

Matplotlib中有一個(gè)繪制直方圖的函數(shù):

matplotlib.pyplot.hist()

參數(shù):數(shù)據(jù)源必須是一維數(shù)組,通常要通過(guò)函數(shù) ravel()拉直圖像,,像素一般是256,,表示[0, 256]。

函數(shù)ravel() 將多維數(shù)組降為一維,,格式為:一維數(shù)組 = 多維數(shù)組.ravel()

hist()直接找到直方圖繪制,。您不需要使用calcHist()或np.histogram()函數(shù)來(lái)找到直方圖??聪旅娴拇a:

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('cat.jpg', 0)plt.hist(img.ravel(), 256, [0,256])plt.show()

效果如下:

或者我們可以用常規(guī)的matplotlib的plot函數(shù)繪制直方圖,,適合繪制BGR圖像直方圖。為此,,我們需要首先找到直方圖的數(shù)據(jù),,代碼如下:

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('cat.jpg')color = ('b', 'g', 'r')for i, col in enumerate(color):histr = cv.calcHist([img], [i], None, [256], [0,256])plt.plot(histr, color=col)plt.xlim([0,256])plt.show()

效果如下:

4 彩色圖像不同通道的直方圖

下面來(lái)看下彩色圖像的直方圖處理,首先讀取并分離各通道,,接著計(jì)算每個(gè)通道的直方圖,,這里將其封裝成函數(shù),接著調(diào)用,,代碼如下:

import cv2import numpy as npdef 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 histImgdef show_histphoto(photo_path):image = cv2.imread(photo_path)b, g, r = cv2.split(image)histImgB = calcAndDrawHist(b, [255, 0, 0])histImgG = calcAndDrawHist(b, [0, 255, 0])histImgR = calcAndDrawHist(b, [0, 0, 255])cv2.imshow('histImgB', histImgB)cv2.imshow('histImgG', histImgG)cv2.imshow('histImgR', histImgR)cv2.imshow('Img', image)cv2.waitKey(0)cv2.destroyAllWindows()if __name__ == '__main__':photo_path = 'cat.jpg'show_histphoto(photo_path)

三個(gè)通道的直方圖如下:

圖如下:

4.1 更進(jìn)一步

這樣做有些繁瑣,,參考(
https://plus.google.com/118298613334549762938)的做法,無(wú)需分離通道,,用折現(xiàn)來(lái)描繪直方圖的邊界可在一副圖中同時(shí)繪制三個(gè)通道的直方圖。方法如下:

def Line_chart(photo_path):image = cv2.imread(photo_path)# 創(chuàng)建用于繪制直方圖的全0 圖像h = np.zeros((256, 256, 3))# 直方圖中各bin的頂點(diǎn)位置bins = np.arange(256).reshape(256, 1)# BGR 三種顏色color = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]for ch, col in enumerate(color):originHist = cv2.calcHist([image], [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)if __name__ == '__main__':photo_path = 'cat.jpg'# show_histphoto(photo_path)Line_chart(photo_path)

結(jié)果如下:

代碼說(shuō)明:

這里的for循環(huán)是對(duì)三個(gè)通道遍歷一次,,每次繪制相應(yīng)通道的直方圖的折線,。for循環(huán)的第一行是計(jì)算對(duì)應(yīng)通道的直方圖,經(jīng)過(guò)上面的介紹,,應(yīng)該很容易就能明白,。

這里所不同的是沒(méi)有手動(dòng)的計(jì)算直方圖的最大值再乘以一個(gè)系數(shù),而是直接調(diào)用了OpenCV的歸一化函數(shù),。該函數(shù)將直方圖的范圍限定在0-255×0.9之間,,與之前的一樣。下面的hist= np.int32(np.around(originHist))先將生成的原始直方圖中的每個(gè)元素四舍六入五湊偶取整(cv2.calcHist函數(shù)得到的是float32類(lèi)型的數(shù)組),,接著將整數(shù)部分轉(zhuǎn)成np.int32類(lèi)型,。即61.123先轉(zhuǎn)成61.0,,再轉(zhuǎn)成61。注意,,這里必須使用np.int32(...)進(jìn)行轉(zhuǎn)換,,numpy的轉(zhuǎn)換函數(shù)可以對(duì)數(shù)組中的每個(gè)元素都進(jìn)行轉(zhuǎn)換,而Python的int(...)只能轉(zhuǎn)換一個(gè)元素,,如果使用int(...),,將導(dǎo)致only length-1 arrays can be converted to Python scalars錯(cuò)誤。

下面的pts = np.column_stack((bins,hist))是將直方圖中每個(gè)bin的值轉(zhuǎn)成相應(yīng)的坐標(biāo),。比如hist[0] =3,,...,hist[126] = 178,,...,,hist[255] = 5;而bins的值為[[0],[1],[2]...,[255]],。使用np.column_stack將其組合成[0, 3],、[126, 178]、[255, 5]這樣的坐標(biāo)作為元素組成的數(shù)組,。

最后使用cv2.polylines函數(shù)根據(jù)這些點(diǎn)繪制出折線,,第三個(gè)False參數(shù)指出這個(gè)折線不需要閉合。第四個(gè)參數(shù)指定了折線的顏色,。

當(dāng)所有完成后,,別忘了用h = np.flipud(h)反轉(zhuǎn)繪制好的直方圖,因?yàn)槔L制時(shí),,[0,0]在圖像的左上角,。這在直方圖可視化一節(jié)中有說(shuō)明。

5,,直方圖均衡化

5.1 使用OpenCV繪制直方圖均衡化

我們可以調(diào)整直方圖的值和它的bin值,,讓它看起來(lái)像x,y坐標(biāo),,這樣你就可以用cv.line()或cv.polyline()函數(shù)來(lái)繪制它,,從而生成與上面相同的圖像。這已經(jīng)是OpenCV-Python2官方的樣本了,。查看sampl/python/hist.py的代碼,。我們用cv.calcHist()函數(shù)來(lái)找一張完整的圖片的直方圖。但是我們只要圖片的一部分的直方圖呢,?在你想要找到的區(qū)域中,,創(chuàng)建一個(gè)帶有白色的遮罩圖像。然后把它作為遮罩

我們可以拿到一幅圖像進(jìn)行mask操作,,并且可以看一下其直方圖分布:

#_*_coding:utf-8_*_import cv2 # opencv讀取的格式是BGRimport numpy as npimport matplotlib.pyplot as plt # Matplotlib讀取的格式是RGBimg = cv2.imread('cat.jpg', 0)# 創(chuàng)建 maskmask = np.zeros(img.shape[:2], np.uint8)print(mask.shape) # (414, 500)mask[100:300, 100:400] = 255masked_img = cv2.bitwise_and(img, img, mask=mask) # 與操作hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])plt.subplot(221), plt.imshow(img, 'gray'), plt.title('gray image')plt.subplot(222), plt.imshow(mask, 'gray'), plt.title('mask image')plt.subplot(223), plt.imshow(masked_img, 'gray'), plt.title('image bitwise and mask')plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask), plt.title('hist image')plt.xlim([0, 256])plt.show()

效果如下:

OpenCV有一個(gè)函數(shù)可以這樣做,,cv.equalizeHist(),,它封裝好了計(jì)算cdf和cdf重映射以及根據(jù)cdf表生成直方圖均衡圖像的過(guò)程。它的輸入只是灰度圖像,,輸出是我們的直方圖均衡圖像,。

img = cv.imread('cat,jpg', 0)equ = cv.equalizeHist(img)res = np.hstack((img, equ)) # 并排疊加圖片cv.imwrite('res.png', res)

所以現(xiàn)在你可以用不同的光條件來(lái)拍攝不同的圖像,平衡它,,并檢查結(jié)果,。

當(dāng)圖像的直方圖被限制在一個(gè)特定的區(qū)域時(shí),直方圖均衡是很好的,。在那些有很大強(qiáng)度變化的地方,,直方圖覆蓋了一個(gè)大區(qū)域,比如明亮的和暗的像素,,這樣的地方就不好用了,。

我們可以看看直方圖均衡化之前和之后的直方圖分布圖。

補(bǔ)充代碼如下:

plt.hist(img.ravel(), 256)plt.hist(equ.ravel(), 256)plt.show()

展示在一個(gè)直方圖,,效果如下:

5.2 使用OpenCV繪制自適應(yīng)直方圖均衡化

局部直方圖均衡化,,即把圖像分成許多小塊(比如按 8*8 作為一個(gè)小塊),那么對(duì)每個(gè)小塊進(jìn)行均衡化,。這種方法主要對(duì)圖像直方圖不是那么單一的(比如存在多峰情況)的圖像比較實(shí)用,。

直方圖自適應(yīng)直方圖均衡化的源碼如下:

def createCLAHE(clipLimit=None, tileGridSize=None): # real signature unknown; restored from __doc__'''createCLAHE([, clipLimit[, tileGridSize]]) -> retval. @brief Creates a smart pointer to a cv::CLAHE class and initializes it... @param clipLimit Threshold for contrast limiting.. @param tileGridSize Size of grid for histogram equalization. Input image will be divided into. equally sized rectangular tiles. tileGridSize defines the number of tiles in row and column.'''pass

參數(shù)說(shuō)明:

  • clipLimit:顏色對(duì)比度的閾值

  • titleGridSize:進(jìn)行像素均衡化的網(wǎng)格大小,即在多少網(wǎng)格下進(jìn)行直方圖的均衡化操作

代碼如下:

#_*_coding:utf-8_*_import cv2 # opencv讀取的格式是BGRimport numpy as npimport matplotlib.pyplot as plt # Matplotlib讀取的格式是RGBimg = cv2.imread('cat.jpg', 0)equ = cv2.equalizeHist(img)clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))res_clahe = clahe.apply(img)res = np.hstack((img, equ, res_clahe))cv2.imshow('res', res)cv2.waitKey(0)cv2.destroyAllWindows()

效果如下:

示例1:?jiǎn)瓮ǖ赖幕译A圖的直方圖均衡化

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltim = cv.imread('test.png', 0)cv.imshow('before', im)# Histogram Equalizationim2 = cv.equalizeHist(im)print(im2)cv.imshow('after', im2)plt.show()cv.waitKey(0)

示例2:彩圖的直方圖均衡

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltim = cv.imread('test.jpg')cv.imshow('before', im)# split g,b,rg = im[:,:,0]b = im[:,:,1]r = im[:,:,2]# Histogram Equalizationr2 = cv.equalizeHist(r)g2 = cv.equalizeHist(g)b2 = cv.equalizeHist(b)im2 = im.copy()im2[:,:,0] = g2im2[:,:,1] = b2im2[:,:,2] = r2print(im2)cv.imshow('after', im2)plt.show()cv.waitKey(0)

示例3:帶遮罩的直方圖均衡化

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltim = cv.imread('test.png', 0)cv.imshow('before', im)mask = cv.imread('test_mask2.png', 0)cv.imshow('mask', mask)# calculate histogram with maskhist_mask = cv.calcHist([im], [0], mask, [256], [0,256])# calculate cdf with maskcdf = hist_mask.cumsum()# Histogram Equalizationcdf = (cdf-cdf[0])*255/(cdf[-1]-1)cdf = cdf.astype(np.uint8)# Transform from float64 back to unit8# generate img after Histogram Equalizationim2 = np.zeros((384, 495, 1), dtype =np.uint8)im2 = cdf[im]# im2 = cv.equalizeHist(im)print(im2)cv.imshow('after', im2)plt.show()cv.waitKey(0)

5.2,,使用Numpy進(jìn)行直方圖均衡化

計(jì)算累積和的 cumsun()函數(shù)

numpy.cumsum(a, axis=None, dtype=None, out=None)

這個(gè)函數(shù)的功能是返回給定axis上的累積和

>>>import numpy as np>>> b=[1,2,3,4,5,6,7]>>> np.cumsum(a)array([ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 75, 105])

直方圖均衡化

import numpy as npimport cv2 as cvfrom matplotlib import pyplot as pltimg = cv.imread('wiki.jpg', 0)hist, bins = np.histogram(img.flatten(), 256, [0,256])cdf = hist.cumsum()cdf_normalized = cdf*float(hist.max())/cdf.max()plt.plot(cdf_normalized, color = 'b')plt.hist(img.flatten(),256,[0,256], color = 'r')plt.xlim([0,256])plt.legend(('cdf','histogram'), loc = 'upper left')plt.show()

你可以看到直方圖位于更亮的區(qū)域,。我們需要讓它充滿整個(gè)頻譜。為此,,我們需要一個(gè)轉(zhuǎn)換函數(shù),,它將更亮區(qū)域的輸入像素映射到全區(qū)域的輸出像素。這就是直方圖均衡所做的,。

現(xiàn)在我們找到了最小的直方圖值(不包括0),,并應(yīng)用了在wiki頁(yè)面中給出的直方圖均衡等式。

cdf = (cdf-cdf[0]) *255/ (cdf[-1]-1)cdf = cdf.astype(np.uint8)

現(xiàn)在我們有了一個(gè)查找表,,它提供了關(guān)于每個(gè)輸入像素值的輸出像素值的信息,。所以我們只要應(yīng)用變換。

img2 = cdf[img]

另一個(gè)重要的特征是,,即使圖像是一個(gè)較暗的圖像(而不是我們使用的更亮的圖像),在均衡之后,,我們將得到幾乎相同的圖像,。因此,它被用作一種“參考工具”,,使所有的圖像都具有相同的光照條件,。這在很多情況下都很有用,。例如,在人臉識(shí)別中,,在對(duì)人臉數(shù)據(jù)進(jìn)行訓(xùn)練之前,,人臉的圖像是均勻的,使它們具有相同的光照條件,。

示例1:?jiǎn)瓮ǖ赖幕译A圖的直方圖均衡化

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltimg = cv.imread('test.png', 0)cv.imshow('before', img)# calculate histhist, bins = np.histogram(img, 256)# calculate cdfcdf = hist.cumsum()# plot histplt.plot(hist,'r')# remap cdf to [0,255]cdf = (cdf-cdf[0])*255/(cdf[-1]-1)cdf = cdf.astype(np.uint8)# Transform from float64 back to unit8# generate img after Histogram Equalizationimg2 = np.zeros((384, 495, 1), dtype =np.uint8)img2 = cdf[img]hist2, bins2 = np.histogram(img2, 256)cdf2 = hist2.cumsum()plt.plot(hist2, 'g')cv.imshow('after', img2)plt.show()cv.waitKey(0)

我們可以看到,,直方圖均衡化后的圖像對(duì)比度增強(qiáng)了。

示例2:彩圖的直方圖均衡化

首先是分離通道,,對(duì)比三個(gè)通道分別進(jìn)行處理,,合并三通道顏色到圖片

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltimg = cv.imread('test.png')cv.imshow('before', img)# split g,b,rg = img[:,:,0]b = img[:,:,1]r = img[:,:,2]# calculate histhist_r, bins_r = np.histogram(r, 256)hist_g, bins_g = np.histogram(g, 256)hist_b, bins_b = np.histogram(b, 256)# calculate cdfcdf_r = hist_r.cumsum()cdf_g = hist_g.cumsum()cdf_b = hist_b.cumsum()# remap cdf to [0,255]cdf_r = (cdf_r-cdf_r[0])*255/(cdf_r[-1]-1)cdf_r = cdf_r.astype(np.uint8)# Transform from float64 back to unit8cdf_g = (cdf_g-cdf_g[0])*255/(cdf_g[-1]-1)cdf_g = cdf_g.astype(np.uint8)# Transform from float64 back to unit8cdf_b = (cdf_b-cdf_b[0])*255/(cdf_b[-1]-1)cdf_b = cdf_b.astype(np.uint8)# Transform from float64 back to unit8# get pixel by cdf tabler2 = cdf_r[r]g2 = cdf_g[g]b2 = cdf_b[b]# merge g,b,r channelimg2 = img.copy()img2[:,:,0] = g2img2[:,:,1] = b2img2[:,:,2] = r2# show img after histogram equalizationcv.imshow('img2', img2)cv.waitKey(0)

示例三:帶遮罩的直方圖均衡化

如果想要在做直方圖均衡化的時(shí)候不考慮圖像的某一部分,比如我們不想考慮圖片右上角的云彩,,那么可以使用遮罩在計(jì)算hist和cdf時(shí)不考慮這一部分像素,。

import cv2 as cvimport numpy as npfrom matplotlib import pyplot as pltimg = cv.imread('test.png', 0)cv.imshow('src', img)# load mask imgmask = cv.imread('test_mask2.png', 0)cv.imshow('mask', mask)# apply mask to srcmasked_img = np.ma.masked_array(img, mask = mask)masked_img = np.ma.filled(masked_img,0).astype('uint8')# print(masked_img)masked_img = np.ma.masked_equal(masked_img,0)# print(masked_img)cv.imshow('masked_img', masked_img)# calculate histhist, bins = np.histogram(masked_img.compressed(), 256) # img have to be compressed() to let mask work# calculate cdfcdf = hist.cumsum()print(cdf)# plot histplt.plot(hist,'r')# remap cdf to [0,255]cdf = (cdf-cdf[0])*255/(cdf[-1]-1)cdf = cdf.astype(np.uint8)# Transform from float64 back to unit8# generate img after Histogram Equalizationimg2 = np.zeros((384, 495, 1), dtype =np.uint8)img2 = cdf[img]hist2, bins2 = np.histogram(img2, 256)cdf2 = hist2.cumsum()plt.plot(hist2, 'g')cv.imshow('dst', img2)plt.show()cv.waitKey(0)

6,使用查找表(LUT)來(lái)拉伸直方圖

在圖像處理中,,直方圖均衡化一般用來(lái)均衡圖像的強(qiáng)度,,或增加圖像的對(duì)比度。在介紹使用直方圖均衡化來(lái)拉伸圖像的直方圖之前,,先學(xué)習(xí)使用查詢(xún)表的方法,。

直方圖的繪制代碼如下:

import cv2import numpy as npimport matplotlib.pyplot as pltdef show_histphoto(photo_path):image = cv2.imread(photo_path, 0)print(image.shape)hist = cv2.calcHist([image], [0], None, [256], [0.0, 256.0])# print(hist.shape)plt.plot(hist)def Line_chart(photo_path):image = cv2.imread(photo_path)# 創(chuàng)建用于繪制直方圖的全0 圖像h = np.zeros((256, 256, 3))# 直方圖中各bin的頂點(diǎn)位置bins = np.arange(256).reshape(256, 1)# BGR 三種顏色color = [(255, 0, 0), (0, 255, 0), (0, 0, 255)]for ch, col in enumerate(color):originHist = cv2.calcHist([image], [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)if __name__ == '__main__':photo_path = 'test.jpg'# show_histphoto(photo_path)Line_chart(photo_path)

(一種借助matplotlib,代碼簡(jiǎn)單,,一種使用opencv,,需要轉(zhuǎn)化)

觀察上圖中原始圖像的直方圖,很容易發(fā)現(xiàn)大部分強(qiáng)度值范圍都沒(méi)有用到,。因此先檢測(cè)圖像非0的最低(imin)強(qiáng)度值和最高(imax)強(qiáng)度值,。將最低強(qiáng)度值imin設(shè)置為0,最高值 imax 設(shè)為255,。中間的按照255.0*(i - imin)/(imax - imin)+ 0.5)的形式設(shè)置,。

實(shí)現(xiàn)的任務(wù)主要集中在查詢(xún)表的創(chuàng)建中,代碼如下:

minBinNo, maxBinNo = 0, 255# 計(jì)算從左起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(hist):if binValue != 0:minBinNo = binNobreak# 計(jì)算從右起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(reversed(hist)):if binValue != 0:maxBinNo = 255 - binNobreak# 生成查找表for i, v in enumerate(lut):if i < minBinNo:lut[i] = 0elif i > maxBinNo:lut[i] = 255else:lut[i] = int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)

查詢(xún)表創(chuàng)建完成后,,就直接調(diào)用相應(yīng)的OpenCV函數(shù),,這里調(diào)用的時(shí) cv2.LUT函數(shù):

#計(jì)算result = cv2.LUT(image, lut)

cv2.LUT 函數(shù)只有兩個(gè)參數(shù),分別為輸入圖像和查找表,,其返回處理的結(jié)果,。

完整代碼如下:

import cv2import numpy as npimage = cv2.imread('wiki.jpg', 0)# 創(chuàng)建空的查找表lut = np.zeros(256, dtype=image.dtype)# OpenCV提供了cv.calcHist()函數(shù)來(lái)獲取直方圖hist = cv2.calcHist([image], # 計(jì)算圖像的直方圖[0], # 使用的通道None, # 沒(méi)有使用mask[256], # it is a 2D histogram[0.0, 255.0])# print(hist.shape) # (256, 1)minBinNo, maxBinNo = 0, 255# 計(jì)算從左起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(hist):if binValue != 0:minBinNo = binNobreak# 計(jì)算從右起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(reversed(hist)):if binValue != 0:maxBinNo = 255 - binNobreak# 生成查找表for i, v in enumerate(lut):if i < minBinNo:lut[i] = 0elif i > maxBinNo:lut[i] = 255else:lut[i] = int(255.0 * (i - minBinNo) / (maxBinNo - minBinNo) + 0.5)# 計(jì)算result = cv2.LUT(image, lut)print(result.shape) # (534, 800)cv2.imshow('result', result)cv2.waitKey(0)cv2.destroyAllWindows()

效果如下:

7,直方圖均衡化OpenCV實(shí)現(xiàn)和Numpy實(shí)現(xiàn)的對(duì)比

圖像為下圖:

7.1 使用OpenCV函數(shù)實(shí)現(xiàn)

用OpenCV函數(shù)實(shí)現(xiàn)直方圖均衡化很簡(jiǎn)單,,只需要調(diào)用一個(gè)函數(shù)即可:

def opencv_equalizeHist(image_path):img = cv2.imread(image_path, 0)equ = cv2.equalizeHist(img)cv2.imshow('equ', equ)cv2.waitKey(0)cv2.destroyAllWindows()

這樣就圖像均衡化了,。效果如下:

7.2,使用Numpy函數(shù)實(shí)現(xiàn)

通過(guò)前面的介紹,,可以明白直方圖均衡化就是用一種特殊的查詢(xún)表來(lái)實(shí)現(xiàn)的,,所以這里用Numpy函數(shù),,以查找表的方式手動(dòng)來(lái)實(shí)現(xiàn)圖像直方圖均衡化:

def numpy_equalizeHist(image_path):img = cv2.imread(image_path, 0)# 創(chuàng)建空的查詢(xún)表lut = np.zeros(256, dtype=img.dtype)hist, bins = np.histogram(img.flatten(), 256, [0, 256])# 計(jì)算累計(jì)直方圖cdf = hist.cumsum()# 除以直方圖中的0值cdf_m = np.ma.masked_equal(cdf, 0)#等同于前面介紹的lut[i] = int(255.0 *p[i])公式cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())#將掩模處理掉的元素補(bǔ)為0cdf = np.ma.filled(cdf_m, 0).astype('uint8')# 計(jì)算result2 = cdf[img]# result = cv2.LUT(img, cdf)# cv2.imshow('OpenCVLUT', result)cv2.imshow('NumPyLUT', result2)cv2.waitKey(0)cv2.destroyAllWindows()

結(jié)果如下:

7.3,三種方法的直方圖對(duì)比

這里對(duì)比了使用查找表,,使用OpenCV,,使用numpy直方圖均衡化生成的直方圖:

(注意:這里有將三種方法生成的直方圖均衡化的圖片保存下來(lái),然后對(duì)此均衡化的圖片畫(huà)直方圖)

import cv2import numpy as npimport matplotlib.pyplot as pltdef LookUpTable(photo_path):image = cv2.imread(photo_path, 0)# 創(chuàng)建空的查找表lut = np.zeros(256, dtype=image.dtype)# OpenCV提供了cv.calcHist()函數(shù)來(lái)獲取直方圖hist = cv2.calcHist([image], # 計(jì)算圖像的直方圖[0], # 使用的通道None, # 沒(méi)有使用mask[256], # it is a 2D histogram[0.0, 255.0])# print(hist.shape) # (256, 1)minBinNo, maxBinNo = 0, 255# 計(jì)算從左起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(hist):if binValue != 0:minBinNo = binNobreak# 計(jì)算從右起第一個(gè)不為0的直方圖位置for binNo, binValue in enumerate(reversed(hist)):if binValue != 0:maxBinNo = 255 - binNobreak# 生成查找表for i, v in enumerate(lut):if i < minBinNo:lut[i] = 0elif i > maxBinNo:lut[i] = 255else:lut[i] = int(255.0 * (i - minBinNo) / (maxBinNo - minBinNo) + 0.5)# 計(jì)算lut = cv2.LUT(image, lut)cv2.imwrite('lut.jpg', lut)# cv2.imshow('lut', lut)# cv2.waitKey(0)# cv2.destroyAllWindows()return lutdef opencv_equalizeHist(image_path):img = cv2.imread(image_path, 0)equ = cv2.equalizeHist(img)cv2.imwrite('equ.jpg', equ)# cv2.imshow('equ', equ)# cv2.waitKey(0)# cv2.destroyAllWindows()return equdef numpy_equalizeHist(image_path):img = cv2.imread(image_path, 0)hist, bins = np.histogram(img.flatten(), 256, [0, 256])# 計(jì)算累計(jì)直方圖cdf = hist.cumsum()# 除以直方圖中的0值cdf_m = np.ma.masked_equal(cdf, 0)# 等同于前面介紹的lut[i] = int(255.0 *p[i])公式cdf_m = (cdf_m - cdf_m.min()) * 255 / (cdf_m.max() - cdf_m.min())# 將掩模處理掉的元素補(bǔ)為0cdf = np.ma.filled(cdf_m, 0).astype('uint8')# 計(jì)算numpy_lut = cdf[img]cv2.imwrite('numpy_lut.jpg', numpy_lut)# cv2.imshow('NumPyLUT', numpy_lut)# cv2.waitKey(0)# cv2.destroyAllWindows()return numpy_lutdef show_allphoto():lut = cv2.imread('lut.jpg', 0)np_equ = cv2.imread('numpy_lut.jpg', 0)opencv_equ = cv2.imread('equ.jpg', 0)print(lut.shape, np_equ.shape, opencv_equ.shape)lut = cv2.calcHist([lut], [0], None, [256], [0.0, 256.0])np_equ = cv2.calcHist([np_equ], [0], None, [256], [0.0, 256.0])opencv_equ = cv2.calcHist([opencv_equ], [0], None, [256], [0.0, 256.0])plt.subplot(311), plt.plot(lut)plt.subplot(312), plt.plot(np_equ)plt.subplot(313), plt.plot(opencv_equ)plt.show()if __name__ == '__main__':photo_path = 'wiki.jpg'# lut = LookUpTable(photo_path)# np_equ = numpy_equalizeHist(photo_path)# opencv_equ = opencv_equalizeHist(photo_path)show_allphoto()

結(jié)構(gòu)如下:

lut計(jì)算出來(lái)的和opencv和numpy計(jì)算的結(jié)果還是不太一樣,。但是 opencv和numpy計(jì)算的結(jié)果相似,。具體原因不知道,再學(xué)習(xí),。

轉(zhuǎn)載于:
https://www.cnblogs.com/wj-1314/p/9444056.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多