什么是直方圖呢?通過(guò)直方圖你可以對(duì)整幅圖像的灰度分布有一個(gè)整體的了解。直方圖的x 軸是灰度值(0 到255),y 軸是圖片中具有同一個(gè)灰度值的點(diǎn)的數(shù)目。
直方圖其實(shí)就是對(duì)圖像的另一種解釋。一下圖為例,通過(guò)直方圖我們可以對(duì)圖像的對(duì)比度,亮度,灰度分布等有一個(gè)直觀的認(rèn)識(shí)。幾乎所有的圖像處理軟件都提供了直方圖分析功能。下圖來(lái)自Cambridge in Color website,強(qiáng)烈推薦你到這個(gè)網(wǎng)站了解更多知識(shí)。

讓我們來(lái)一起看看這幅圖片和它的直方圖吧。(要記住,直方圖是根據(jù)灰度圖像繪制的,而不是彩色圖像)。直方圖的左邊區(qū)域像是了暗一點(diǎn)的像素?cái)?shù)量,右側(cè)顯示了亮一點(diǎn)的像素的數(shù)量。從這幅圖上你可以看到灰暗的區(qū)域比兩的區(qū)域要大,而處于中間部分的像素點(diǎn)很少。
一、使用opencv計(jì)算直方圖
方法:cv2.calcHist(images,channels,mask,histSize,ranges)
1. images: 原圖像(圖像格式為uint8 或float32)。當(dāng)傳入函數(shù)時(shí)應(yīng)該
用中括號(hào)[] 括起來(lái),例如:[img]。
2. channels: 同樣需要用中括號(hào)括起來(lái),它會(huì)告訴函數(shù)我們要統(tǒng)計(jì)那幅圖
像的直方圖。如果輸入圖像是灰度圖,它的值就是[0];如果是彩色圖像
的話(huà),傳入的參數(shù)可以是[0],[1],[2] 它們分別對(duì)應(yīng)著通道B,G,R。
3. mask: 掩模圖像。要統(tǒng)計(jì)整幅圖像的直方圖就把它設(shè)為None。但是如
果你想統(tǒng)計(jì)圖像某一部分的直方圖的話(huà),你就需要制作一個(gè)掩模圖像,并
使用它。(后邊有例子)
4. histSize:BIN 的數(shù)目。也應(yīng)該用中括號(hào)括起來(lái),例如:[256]。
5. ranges: 像素值范圍,通常為[0,256]
BINS:上面的直方圖顯示了每個(gè)灰度值對(duì)應(yīng)的像素?cái)?shù)。如果像素值為0到255,你就需要256 個(gè)數(shù)來(lái)顯示上面的直方圖。但是,如果你不需要知道
每一個(gè)像素值的像素點(diǎn)數(shù)目的,而只希望知道兩個(gè)像素值之間的像素點(diǎn)數(shù)目怎么辦呢?舉例來(lái)說(shuō),我們想知道像素值在0 到15 之間的像素點(diǎn)的數(shù)目,接著
是16 到31,….,240 到255。我們只需要16 個(gè)值來(lái)繪制直方圖。OpenCV Tutorials on histograms中例子所演示的內(nèi)容。
那到底怎么做呢?你只需要把原來(lái)的256 個(gè)值等分成16 小組,取每組的總和。而這里的每一個(gè)小組就被成為BIN。第一個(gè)例子中有256 個(gè)BIN,第二個(gè)例子中有16 個(gè)BIN。在OpenCV 的文檔中用histSize 表示BINS。
DIMS:表示我們收集數(shù)據(jù)的參數(shù)數(shù)目。在本例中,我們對(duì)收集到的數(shù)據(jù)只考慮一件事:灰度值。所以這里就是1。RANGE:就是要統(tǒng)計(jì)的灰度值范圍,一般來(lái)說(shuō)為[0,256],也就是說(shuō)所有的灰度值
import numpy as np
import argparse
import cv2
from matplotlib import pyplot as plt
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required = True,help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
cv2.imshow("Original",image)
hist = cv2.calcHist([image],[0],None,[256],[0,256])
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
plt.plot(hist)
plt.xlim([0,256])
plt.show()
cv2.waitKey(0)

from __future__ import print_function
import numpy as np
import argparse
import cv2
from matplotlib import pyplot as plt
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required = True,help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
chans = cv2.split(image)
colors = ("b","g","r")
plt.figure()
plt.title("Grayscale Histogram")
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
for (chan,color) in zip(chans,colors):
hist = cv2.calcHist([chan],[0],None,[256],[0,256])
plt.plot(hist,color = color)
plt.xlim([0,256])
plt.show()
cv2.waitKey(0)

from __future__ import print_function
import numpy as np
import argparse
import cv2
from matplotlib import pyplot as plt
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required = True,help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
chans = cv2.split(image)
colors = ("b","g","r")
fig=plt.figure()
ax = fig.add_subplot(131)
hist= cv2.calcHist([chans[1],chans[0]],[0,1],None,[32,32],[0,256,0,256])
p = ax.imshow(hist,interpolation = "nearest")
ax.set_title("2D color Historgram for G and B")
plt.colorbar(p)
ax = fig.add_subplot(132)
hist= cv2.calcHist([chans[1],chans[2]],[0,1],None,[32,32],[0,256,0,256])
p = ax.imshow(hist,interpolation = "nearest")
ax.set_title("2D color Historgram for G and R")
plt.colorbar(p)
ax = fig.add_subplot(133)
hist= cv2.calcHist([chans[0],chans[2]],[0,1],None,[32,32],[0,256,0,256])
p = ax.imshow(hist,interpolation = "nearest")
ax.set_title("2D color Historgram for B and R")
plt.colorbar(p)
plt.show()
print("2D histogram shape:{},with {} values".format(hist.shape,hist.flatten().shape[0]))
為了同時(shí)在一個(gè)窗口中顯示多個(gè)圖像,我們使用函數(shù)plt.subplot()。你
可以通過(guò)查看Matplotlib 的文檔獲得更多詳細(xì)信息
藍(lán)色陰影表示低像素計(jì)數(shù),而紅色陰影表示大像素計(jì)數(shù)(即,2D圖形中的峰值)

二、直方圖均衡化 -histogram equalization
直方圖均衡通過(guò)“拉伸”像素的分布來(lái)改善圖像的對(duì)比度。
應(yīng)用直方圖均衡會(huì)將峰值拉向圖像的角落,從而改善圖像的全局對(duì)比度。直方圖均衡適用于灰度圖像

執(zhí)行直方圖均衡僅使用單個(gè)函數(shù)完成:cv2.equalizeHist(image)它接受一個(gè)單一的參數(shù),灰度圖像,我們要執(zhí)行直方圖均衡。
import numpy as np
import argparse
import cv2
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required = True,help = "Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
eq = cv2.equalizeHist(image)
cv2.imshow("Histogram Equalization",np.vstack([image,eq]))
#hstack 橫向堆疊圖像 vstack縱向
cv2.waitKey(0)
三、使用掩模
from matplotlib import pyplot as plt
import numpy as np
import argparse
import cv2
def plot_histogram(image,title,mask=None):
chans = cv2.split(image)
colors = ("b","g","r")
plt.figure()
plt.title(title)
plt.xlabel("Bins")
plt.ylabel("# of Pixels")
for (chan,color) in zip(chans,colors):
hist = cv2.calcHist([chan],[0],mask,[256],[0,256])
plt.plot(hist,color = color)
plt.xlim([0,256])
ap = argparse.ArgumentParser()
ap.add_argument("-i","--image",required =True, help="Path to the image")
args = vars(ap.parse_args())
image = cv2.imread(args["image"])
cv2.imshow("Original",image)
mask = np.zeros(image.shape[:2],dtype ="uint8")
(cx,cy) = (image.shape[1]//2,image.shape[0]//2)
cv2.rectangle(mask,(cx-250,cy-150),(cx+200 ,cy+150),255,-1)
cv2.imshow("Mask",mask)
masked = cv2.bitwise_and(image,image,mask=mask)
cv2.imshow("Applying the mask",masked)
plot_histogram(image,"Histogram for Masked Image",mask = mask)
plt.show()

人生最大的痛苦不是失敗,而是沒(méi)有經(jīng)歷自己想要經(jīng)歷的一切.
更多文章請(qǐng)關(guān)注我的博客:https://harveyyeung.github.io