数字图像处理——最大类间方差法(OTSU)图像阈值分割实例
图像阈值分割是指通过以某个确定的图像灰度值(灰度级)将图像分为不同的部分,其目的通常是将需要的目标从图像中分割出来,图像分割在数字图像处理领域具有重要作用。
最大类间方差法,也称为Otsu方法,是应用广泛的图像分割法之一,该方法是通过计算图像灰度级的类间方差最大值或者类内方差最小值来确定分割阈值的标准。具体操作方法是通过遍历数字图像的灰度图像灰度级直方图的全部灰度级作为图像灰度级分割阈值,在每个阈值下将图像灰度按阈值划分为两类,如果在某个阈值下,两类灰度值的类间方差达到最大,或者类内方差达到最小,则该阈值即为最佳分割阈值。
待分割的图像文件名:inkpad2.jpg,该图像是彩色图像,每个像素包含RGB三种颜色,本实例的任务是将印台(红色)部分图像与背景(绿色)部分图像通过最大类间方差法进行阈值分割。
图片在我电脑中的保存位置:D:\ZJPythonFiles\opencv45564\inkpad2.jpg,该位置应根据实际进行修改,如果不想用这张图片,可以换成其他图片,最大类间方差法是二分类
图1 待处理的原始图像最大类间方差法分割图像的步骤:
1.将真彩色图像转换为灰度图像。
2.绘制灰度级直方图。
3.遍历灰度级,计算出各灰度级对应的目标和背景的类间方差。计算目标和背景的最大类间方差,此时对应的灰度值即为最佳阈值。用该阈值分割图像的灰度值即可实现图像的阈值分割。
以下代码是实例的完整代码,其中使用了名剑求瑕的python灰度图中的代码,用于将RGB真彩色图像转换为灰度图像,使用了mormal的OTSU算法(大津法—最大类间方差法)原理及实现中的代码用于实现将灰度图像进行最大类间方差法进行阈值分割。
#otsu
# 彩色图像转换成灰度图像
import numpy as np
from matplotlib import pyplot as plt
from PIL import Image
# 将一个RGB颜色转换成灰度值,结果保留整数
def RGBtoGray(r, g, b):
########## Begin ##########
gray = round(r*0.299 + g*0.587 + b*0.114)
########## End ##########
return gray
# 将真彩色图像转换成灰度图
# 真彩色和灰度图的文件路径分别为path1和path2
def toGrayImage(path1, path2):
img1 = Image.open(path1) # 真彩色图像,像素中是RGB颜色
w, h = img1.size
img2 = Image.new('L', (w, h)) # 新建一个灰度图像,像素中是灰度值
########## Begin ##########
# 此部分功能:依次取出img1中每个像素的RGB颜色,转换成灰度值,再放到img2的对应位置
for x in range(w):
for y in range(h):
r, g, b = img1.getpixel((x, y)) # 取出颜色
gray = RGBtoGray(r, g, b) # 转成灰度值
img2.putpixel((x, y), gray) # 放回像素
########## End ##########
img2.save(path2)
# otsu算法
def otsu(gray):
pixel_number = gray.shape[0] * gray.shape[1]
mean_weigth = 1.0/pixel_number
# #统计各灰度级的像素个数,灰度级分为256级
# bins必须写到257,否则255这个值只能分到[254,255)区间
his, bins = np.histogram(gray, np.arange(0,257))
# 绘制直方图
plt.figure(figsize=(12,8))
plt.hist(gray,256,[0,256],label='灰度级直方图') #运行比较慢,如果电脑卡顿,可以将本行代码注释掉
plt.show()
final_thresh = -1
final_value = -1
intensity_arr = np.arange(256) #灰度分为256级,0级到255级
for t in bins[1:-1]: # 遍历1到254级 (一定不能有超出范围的值)
pcb = np.sum(his[:t])
pcf = np.sum(his[t:])
Wb = pcb * mean_weigth#像素被分类为背景的概率
Wf = pcf * mean_weigth#像素被分类为目标的概率
mub = np.sum(intensity_arr[:t]*his[:t]) / float(pcb)#分类为背景的像素均值
muf = np.sum(intensity_arr[t:]*his[t:]) / float(pcf)#分类为目标的像素均值
#print mub, muf
value = Wb * Wf * (mub - muf) ** 2 #计算目标和背景类间方差
if value > final_value:
final_thresh = t
final_value = value
final_img = gray.copy()
print(final_thresh)
final_img[gray > final_thresh] = 255
final_img[gray < final_thresh] = 0
return final_img
path1 = r'D:\ZJPythonFiles\opencv45564\inkpad2.jpg' # 真彩色图像
path2 = r'D:\ZJPythonFiles\opencv45564\inkpad2_grayA.jpg' # 灰度图像
toGrayImage(path1, path2)
imgcolor=plt.imread(path1)
imggray=plt.imread(path2)
plt.imshow(imgcolor)
plt.show()
plt.imshow(imggray,cmap='gray')
plt.show()
plt.imshow(otsu(imggray),cmap='gray')
print(otsu(imggray))
plt.show()
参考:
1.normal的Otsu算法原理与python实现,https://blog.csdn.net/u011285477/article/details/52004513
2.名剑求瑕的python灰度图,https://blog.csdn.net/qq_42833469/article/details/121575782