图像直方图(histogram)是图像的统计学特征,常用于了解图像的基本特征以便分析。不过图像的直方图不具有空间特征。
图像的灰度直方图(histogram),就是将图像转化成灰度图像之后,统计各个像素点的灰度值,绘制成直方图,其横轴是灰度值(0,255),纵轴是该灰度值所对应的像素的数目。对灰度直方图做积分=图像的size。
灰度直方图
1 defplt_hist(img):2 plt.hist(img.ravel(), 256, [0, 256])3 plt.show()
三色折线图(可以直观的看出三原色的占比分布)
1 defimg_hist(img):2 color = ("blue", "green", "red")3 for i, color inenumerate(color):4 hist = cv.calcHist([img], [i], None, [256], [0, 256])5 plt.plot(hist, color=color)6 plt.xlim([0, 25])7 plt.show()
一般来说,直方图有双峰性,会有两个峰值以区分前景和背景,但并不是每张图像都如此。分析图像的变化,进而确定最优二值化值。在两个峰值之间的最小值,经常是我们需要找的二值化值。我们可以发现,两个峰值之间的波谷是两个峰值的叠加区。有了灰度直方图,我们可以给图片做二值化。确定哪些pixel是我们需要关注的哪些是不重要的。二值化有两种主要形式:一种是全局阈值二值化,另外一种是局部阈值二值化。
选择二值化的值(找到两个双峰的最低点,但不是都有多峰可能存在多个最小值,可以用局部阈值)
经典算法:isodata algorithm 预设初始的二值化值,然后不断迭代,效果不是最好的
Otsu algorithm 研究双峰的关联性求二值化值
Entropy algorithm 熵算法,前景熵hb和背景熵hw。0-255个灰度值分别求熵,最大的h值就是我们要找的二值化值。
其他算法:triangle algorithm 灰度值的最大值和最左侧的最小值连接,然后做垂线,当垂线距离最大时,所对应的灰度值就是我们需要的二值化值,不过这个算法效果不是很好,应用于细胞分离比较多。
使用opencvAPI:(otsu和triangle)
1 defthreshold_binary(src):2 #把BGR图像转化成灰度图像
3 gray =cv.cvtColor(src, cv.COLOR_BGR2GRAY)4 #获得灰度直方图以便调整算法的使用
5 plt.hist(src.ravel(), 256, [0, 256])6 plt.show()7 #几种二值化方法
8 ret, binary = cv.threshold(gray, 50, 255, cv.THRESH_BINARY)#指定阈值50
9 print("二值阈值: %s" %ret)10 cv.imshow("threshold_binary", binary)11 ret_otsu, binary_otsu = cv.threshold(gray, 0, 255, cv.THRESH_BINARY |cv.THRESH_OTSU)12 print("二值阈值_otsu: %s" %ret_otsu)13 cv.imshow("threshold_binary_otsu", binary_otsu)14 ret_tri, binary_tri = cv.threshold(gray, 0, 255, cv.THRESH_BINARY |cv.THRESH_TRIANGLE)15 print("二值阈值_tri: %s" % ret_tri)
运行示例:
二值阈值: 50.0
二值阈值_otsu: 173.0
二值阈值_tri: 250.0
以上是全局阈值二值化的结果,下面还有局部阈值二值化。个人觉得局部阈值更好的描绘了图片的轮廓。
1 deflocal_threshold_binary(src):2 gray =cv.cvtColor(src, cv.COLOR_BGR2GRAY)3 binary_mean = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 25, 10)4 cv.imshow("binary_mean", binary_mean)5
6 binary_gau = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 25, 10)7 cv.imshow("binary_gau", binary_gau)
两种方法,
超大图像及其二值化
经常会遇到一些比较大的图像,我们可以把图片拆分成很多个小方块分别进行全局二值化或者使用局部阈值。这时候可以用opencv处理图像显示不全,需要把图片保存到本地才行。
1 defbig_image_binary(src):2 print(src.shape)3 cw = 256
4 ch = 256
5 h, w, c =src.shape6 gray =cv.cvtColor(src, cv.COLOR_BGR2GRAY)7 for row inrange(0, h, ch):8 for col inrange(0, w, cw):9 roi = gray[row:row+ch, col:col+cw]10 dst = cv.adaptiveThreshold(roi, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 25, 10)11 #ret_otsu, dst = cv.threshold(roi, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
12 gray[row:row+ch, col:col+cw] =dst13 #计算方差和平均值,当这两个数值小于某一值时,可以认为图像为空白,做空白图像过滤
14 print(np.std(dst), np.mean(dst))15 cv.imwrite("E:/photo/big_img_binary.jpg", gray)