直方图阈值分割
一般图像都有双峰性,一个峰是前景一个是后景。
一般取这两个峰值之间的最小值为图片的二值化的阈值
从而二值化图片
值得一提的是,对于任何一张图像,它的直方图中如果存在较为明显的双峰,用直方图分割技术法可以达到很好的效果,否则,达到的效果会很不理想。
所以建议先绘制直方图看看。
import numpy as np
import cv2
import os
def calcGrayHist(image):
'''
统计像素值
:param image:
:return:
'''
# 灰度图像的高,宽
rows, cols = image.shape
# 存储灰度直方图
grayHist = np.zeros([256], np.uint64)
for r in range(rows):
for c in range(cols):
grayHist[image[r][c]] += 1
return grayHist
def threshTwoPeaks(image,image_name):
# 计算灰度直方图
histogram = calcGrayHist(image)
# 找到灰度直方图的最大峰值对应的灰度值
maxLoc = np.where(histogram == np.max(histogram))
firstPeak = maxLoc[0][0]
# 寻找灰度直方图的第二个峰值对应的灰度值
measureDists = np.zeros([256], np.float32)
for k in range(256):
measureDists[k] = pow(k - firstPeak, 2) * histogram[k]
maxLoc2 = np.where(measureDists == np.max(measureDists))
secondPeak = maxLoc2[0][0]
# 找两个峰值之间的最小值对应的灰度值,作为阈值
thresh = 0
if firstPeak > secondPeak:
temp = histogram[int(secondPeak): int(firstPeak)]
minLoc = np.where(temp == np.min(temp))
thresh = secondPeak + minLoc[0][0] + 1
else:
temp = histogram[int(firstPeak): int(secondPeak)]
minLoc = np.where(temp == np.min(temp))
thresh = firstPeak + minLoc[0][0] + 1
# 找到阈值,我们进行处理
img = image.copy()
print(type(img))
img[img > thresh] = 255.0
img[img <= thresh] = 0.0
cv2.imshow('deal_image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
print(type(img))
cv2.imwrite(image_name,img)
if __name__ == '__main__':
for img_name in os.listdir("rect"):
image = cv2.imread("rect/"+img_name, cv2.IMREAD_GRAYSCALE)
threshTwoPeaks(image,"result/"+img_name)
如果直方图无明显双峰则分割效果明显不行
有双峰分割效果才勉强能看
自适应阈值算法
自适应二值化就是在单个阈值不够分割整个图片时,把整张图片分为多个小块,每块采用各自的阈值进行分割。
import os
import matplotlib.pyplot as plt
import cv2
import numpy as np
def adaptiveThresh(I, winSize, ratio=0.15):
# 第一步:对图像矩阵进行均值平滑
I_mean = cv2.boxFilter(I, cv2.CV_32FC1, winSize)
# 第二步:原图像矩阵与平滑结果做差
out = I - (1.0 - ratio) * I_mean
# 第三步:当差值大于或等于0时,输出值为255;反之,输出值为0
out[out >= 0] = 255
out[out < 0] = 0
out = out.astype(np.uint8)
return out
if __name__ == '__main__':
for img_name in os.listdir("rect"):
image = cv2.imread("rect/"+img_name, cv2.IMREAD_GRAYSCALE)
img = adaptiveThresh(image, (10, 10))
# cv2.imshow('origin', image)
# cv2.imshow('deal_image', img)
cv2.imwrite("result/"+img_name,img)
cv2.waitKey(0)
cv2.destroyAllWindows()
在虾分割的测试中效果明显不佳,因为虾和周围环境颜色还是较为相似的
滑窗大小为10x10
滑窗大小为50x50
熵算法
熵是用来衡量信息量多少的标准
熵算法取得是让前景与背景熵的和最大的阈值,因此相对而言获取到的信息也是最多的。
def threshEntroy(image):
rows, cols = image.shape
# 求灰度直方图
grayHist = calcGrayHist(image)
# 归一化灰度直方图,即概率直方图
normGrayHist = grayHist / float(rows * cols)
# 第一步:计算累加直方图,也称零阶累积矩
zeroCumuMoment = np.zeros([256], np.float32)
for k in range(256):
if k == 0:
zeroCumuMoment[k] = normGrayHist[k]
else:
zeroCumuMoment[k] = zeroCumuMoment[k - 1] + normGrayHist[k]
# 第二步:计算各个灰度级的熵
entropy = np.zeros([256], np.float32)
for k in range(256):
if k == 0:
if normGrayHist[k] == 0:
entropy[k] = 0
else:
entropy[k] = -normGrayHist[k] * math.log10(normGrayHist[k])
else:
if normGrayHist[k] == 0:
entropy[k] = entropy[k - 1]
else:
entropy[k] = entropy[k - 1] - normGrayHist[k] * math.log10(normGrayHist[k])
# 第三步:找阈值
fT = np.zeros([256], np.float32)
ft1, ft2 = 0.0, 0.0
totalEntropy = entropy[255]
for k in range(255):
# 找最大值
maxFront = np.max(normGrayHist[0: k + 1])
maxBack = np.max(normGrayHist[k + 1: 256])
if (maxFront == 0 or zeroCumuMoment[k] == 0
or maxFront == 1 or zeroCumuMoment[k] == 1 or totalEntropy == 0):
ft1 = 0
else:
ft1 = entropy[k] / totalEntropy * (math.log10(zeroCumuMoment[k]) / math.log10(maxFront))
if (maxBack == 0 or 1 - zeroCumuMoment[k] == 0
or maxBack == 1 or 1 - zeroCumuMoment[k] == 1):
ft2 = 0
else:
if totalEntropy == 0:
ft2 = (math.log10(1 - zeroCumuMoment[k]) / math.log10(maxBack))
else:
ft2 = (1 - entropy[k] / totalEntropy) * (math.log10(1 - zeroCumuMoment[k]) / math.log10(maxBack))
fT[k] = ft1 + ft2
# 找最大值的索引,作为得到的阈值
threshLoc = np.where(fT == np.max(fT))
thresh = threshLoc[0][0]
# 阈值处理
threshold = np.copy(image)
threshold[threshold > thresh] = 255
threshold[threshold <= thresh] = 0
return threshold
if __name__ == '__main__':
for img_name in os.listdir("rect"):
image = cv2.imread("rect/"+img_name, cv2.IMREAD_GRAYSCALE)
img = threshEntroy(image)
cv2.imshow("img",img)
cv2.imwrite("result/"+img_name,img)
最终实验版本:熵算法加骨架算法
import os
import math
import cv2
import numpy as np
from skimage import morphology, data, color
import matplotlib.pyplot as plt
def get_skeleton(image,original_img,img_name):
image = color.rgb2gray(image)
print(image)
image = 1 - image # 反相
# 实施骨架算法
skeleton = morphology.skeletonize(image)
# 显示结果
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(8, 4))
ax1.imshow(original_img, cmap=plt.cm.gray)
ax1.axis('off')
ax1.set_title('original', fontsize=20)
ax2.imshow(skeleton, cmap=plt.cm.gray)
ax2.axis('off')
ax2.set_title('skeleton', fontsize=20)
fig.tight_layout()
plt.savefig(img_name)
plt.show()
def calcGrayHist(image):
'''
统计像素值
:param image:
:return:
'''
# 灰度图像的高,宽
rows, cols = image.shape
# 存储灰度直方图
grayHist = np.zeros([256], np.uint64)
for r in range(rows):
for c in range(cols):
grayHist[image[r][c]] += 1
return grayHist
def threshEntroy(image):
rows, cols = image.shape
# 求灰度直方图
grayHist = calcGrayHist(image)
# 归一化灰度直方图,即概率直方图
normGrayHist = grayHist / float(rows * cols)
# 第一步:计算累加直方图,也称零阶累积矩
zeroCumuMoment = np.zeros([256], np.float32)
for k in range(256):
if k == 0:
zeroCumuMoment[k] = normGrayHist[k]
else:
zeroCumuMoment[k] = zeroCumuMoment[k - 1] + normGrayHist[k]
# 第二步:计算各个灰度级的熵
entropy = np.zeros([256], np.float32)
for k in range(256):
if k == 0:
if normGrayHist[k] == 0:
entropy[k] = 0
else:
entropy[k] = -normGrayHist[k] * math.log10(normGrayHist[k])
else:
if normGrayHist[k] == 0:
entropy[k] = entropy[k - 1]
else:
entropy[k] = entropy[k - 1] - normGrayHist[k] * math.log10(normGrayHist[k])
# 第三步:找阈值
fT = np.zeros([256], np.float32)
ft1, ft2 = 0.0, 0.0
totalEntropy = entropy[255]
for k in range(255):
# 找最大值
maxFront = np.max(normGrayHist[0: k + 1])
maxBack = np.max(normGrayHist[k + 1: 256])
if (maxFront == 0 or zeroCumuMoment[k] == 0
or maxFront == 1 or zeroCumuMoment[k] == 1 or totalEntropy == 0):
ft1 = 0
else:
ft1 = entropy[k] / totalEntropy * (math.log10(zeroCumuMoment[k]) / math.log10(maxFront))
if (maxBack == 0 or 1 - zeroCumuMoment[k] == 0
or maxBack == 1 or 1 - zeroCumuMoment[k] == 1):
ft2 = 0
else:
if totalEntropy == 0:
ft2 = (math.log10(1 - zeroCumuMoment[k]) / math.log10(maxBack))
else:
ft2 = (1 - entropy[k] / totalEntropy) * (math.log10(1 - zeroCumuMoment[k]) / math.log10(maxBack))
fT[k] = ft1 + ft2
# 找最大值的索引,作为得到的阈值
threshLoc = np.where(fT == np.max(fT))
thresh = threshLoc[0][0]
# 阈值处理
threshold = np.copy(image)
threshold[threshold > thresh] = 1
threshold[threshold <= thresh] = 0
return threshold
if __name__ == '__main__':
for img_name in os.listdir("rect"):
image = cv2.imread("rect/"+img_name, cv2.IMREAD_GRAYSCALE)
img = threshEntroy(image)
get_skeleton(img,image,"result/"+img_name)
效果很一般,果然还是得深度学习