1. 引言
欢迎回来,我的图像处理爱好者们!本文我们将直接进入传统图像分析的新领域——图像分割,这是指将图像分成若干具有相似性质的区域的过程,从数学角度来看,图像分割是将图像划分成互不相交的区域的过程。
闲话少说,我们直接开始吧!
2. 基于阈值的分割
首先介绍的是基于阈值和基于Otsu的分割方法,这是我们进行分割实验的第一步,我们将了解像这样简单而有效的方法是如何根据图像像素的强度值将图像划分为前景和背景两部分的。
但是我们如何科学地决定分割的阈值呢?这就是Otsu方法派上用场的地方。简单地说,这种巧妙的方法通过计算出最大化类间方差的最佳阈值,使其成为自适应阈值选择的优秀工具。
首先从我们的准备工作开始, 导入我们需要的图像处理库:
# Import libraries
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
import numpy as np
from skimage.color import rgb2gray, rgb2hsv
from skimage.filters import threshold_otsu
加载本节测试图像样例,如下:
# Display the image that we will be playing with
original_image = imread('plants.jpg')
plt.figure(figsize=(20,20))
plt.imshow(original_image)
plt.title('Original Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()
结果如下:
3. 固定阈值分割
当我们想要对图像进行二值化时,我们执行两步过程,将图像转换为灰度图,然后设置我们想要的任何阈值(通常为0.50),进而将其转换为二值图像:
# Convert the image to grayscale
gray_image = rgb2gray(original_image)
plt.figure(figsize=(20,20))
plt.imshow(gray_image, cmap='gray')
plt.title('Grayscale Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()
得到灰度图,结果如下:
选择固定阈值,进行二值化,代码如下:
# Convert the grayscale image to binary image using threshold=0.50
threshold = 0.5
binary_image = gray_image<threshold
plt.figure(figsize=(20,20))
plt.imshow(binary_image, cmap='gray')
plt.title('Binary Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()
得到二值图效果如下:
观察上图,可以发现对于前景背景区分度比较明显的图像,通过设置固定的阈值也可以方便的得到我们需要的前景物体。
4. 自适应阈值分割
请注意分析上述图像中原本较亮的部分是如何变黑的,这是因为我们使用了0.50作为阈值。但在Otsu的方法中,我们不必手动设置阈值,我们可以通过调用函数threshold_otsu
让Otsu为我们处理这项工作,如下所示:
# Use threshold_otsu to automatically calculate the optimal threshold
threshold = threshold_otsu(gray_image)
print(f"Otsu's Threshold: {threshold:.2f}")
binary_image_otsu = gray_image < threshold
plt.figure(figsize=(20,20))
plt.imshow(binary_image_otsu, cmap='gray')
plt.title("Binary Image using Otsu's Method", fontsize=20, weight='bold')
plt.axis('off')
plt.show()
结果如下:
Output:
Otsu's Threshold: 0.67
得到二值图的效果如下:
看到区别了吗?我们不必为了获得更好的二值化图像而手动地对阈值进行试错。多亏了otsu的方法,为我们节省了大量试错时间!
5. 彩色图像分割
假设我们只想在图像中隔离青色的植物,那么是时候采用另一种令人兴奋的技术了——彩色图像分割!这种方法是二值化的扩展,但这里有一个注意项:我们使用颜色信息来分离对象,而不是像素值强度。当图像中感兴趣的对象拥有独特不同的颜色时,该方法特别有用。
按照惯例,我们还是首先导入我们的样例图像,代码如下:
# Display original image
original_image = imread('plants.jpg')
plt.figure(figsize=(20,20))
imshow(original_image)
plt.title('Original Image', fontsize=20, weight='bold')
plt.axis('off')
plt.show()
结果如下:
6. 基于RGB颜色空间的图像分割
观察上图,为了只隔离茂盛的绿色植物,我们可以执行简单的分割操作。如果绿色通道值大于红色和蓝色通道值,我们可以使用下面的比较运算符来表示像素为绿色,代码如下:
# Read image using skimage
original_image = imread('plants.jpg')
# Create subplot of 1x2
fig, ax = plt.subplots(1, 2, figsize=(20, 20))
# Plot original image
ax[0].imshow(original_image)
ax[0].set_title('Original Image', fontsize=20, weight='bold')
ax[0].axis('off')
# Get red, green, and blue channels
r = original_image[:, :, 0]
g = original_image[:, :, 1]
b = original_image[:, :, 2]
# Create a mask for green color
mask = (g > r) & (g > b) # adjust these values depending on what you consider to be 'green'
# Create a new image
new_img = original_image.copy()
# Apply mask to all channels
new_img[:, :, 0] = new_img[:, :, 0] * mask
new_img[:, :, 1] = new_img[:, :, 1] * mask
new_img[:, :, 2] = new_img[:, :, 2] * mask
# Plot the green segmented image
ax[1].imshow(new_img)
ax[1].set_title('Green Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off')
# Display the subplot
plt.tight_layout()
plt.show()
结果如下:
7. 美化效果
观察上述右图,虽然我们可以大致得到我们想要的分割结果,但是仔细观察上图中留下了很多无用的白色标记。实质上白色是红色、绿色和蓝色都达到顶峰的颜色。因此,为了阻止这些不想要的因素,我们可以在代码中添加一个白色掩膜,代码如下:
# Create a mask for white color
white_threshold = 180 # adjust this depending on what you consider to be 'white'
white_mask = (r > white_threshold) & (g > white_threshold) & (b > white_threshold)
# Combine the green and white masks
mask = green_mask & ~white_mask # ~ is the NOT operator
# Create a new image and apply mask
new_img = original_image.copy()
# Apply mask to all channels
new_img[:,:,0] = new_img[:,:,0] * mask
new_img[:,:,1] = new_img[:,:,1] * mask
new_img[:,:,2] = new_img[:,:,2] * mask
# Plot the green segmented image
ax[1].imshow(new_img)
ax[1].set_title('Green Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off');
得到结果如下:
8. 基于HSV颜色空间的图像分割
如上所述,有时在其他颜色空间(如HSV(色调、饱和度、值))中分割颜色更容易。在HSV颜色空间中,不同的颜色排列在一个圆上(色调),因此挑选特定的颜色可能更容易。
让我们显示原始图像的色调、饱和度和值:
# Read image using skimage
original_image = imread('plants.jpg')
# Convert the image to HSV color space
hsv_img = rgb2hsv(original_image)
fig, ax = plt.subplots(1, 3, figsize=(20,20))
ax[0].imshow(hsv_img[:,:,0], cmap='hsv')
ax[0].set_title('Hue', fontsize=20)
ax[1].imshow(hsv_img[:,:,1], cmap='hsv')
ax[1].set_title('Saturation', fontsize=20)
ax[2].imshow(hsv_img[:,:,2], cmap='hsv')
ax[2].set_title('Value', fontsize=20);
结果如下:
9. 基于H色调的图像分割
由于使用上面的图很难看到强度值的差异,所以让我们使用colorbar
函数来分析,代码如下:
plt.imshow(hsv_img[:,:,0], cmap='hsv')
plt.colorbar()
结果如下:
正如你所看到的,橙色介于0到0.05之间,所以让我们这些值作为阈值:
# Read image using skimage
original_image = imread('plants.jpg')
# Convert the image to HSV color space
hsv_img = rgb2hsv(original_image)
# Create a mask for orange color
# Hue for orange is roughly in the range of 0 - 0.05
# We can play around these values to adapt to our specific color requirement
mask = (hsv_img[:,:,0] > 0) & (hsv_img[:,:,0] < 0.05)
# create a new image and apply mask
new_img = original_image.copy()
new_img[:,:,0] = new_img[:,:,0] * mask
new_img[:,:,1] = new_img[:,:,1] * mask
new_img[:,:,2] = new_img[:,:,2] * mask
# plot the original and segmented images side by side
fig, ax = plt.subplots(1, 2, figsize=(20,10))
ax[0].imshow(original_image)
ax[0].set_title('Original Image', fontsize=20, weight='bold')
ax[0].axis('off')
ax[1].imshow(new_img)
ax[1].set_title('Orange Segmented Image', fontsize=20, weight='bold')
ax[1].axis('off')
plt.show()
结果如下:
观察上图,我们已经成功地在植物图像中分割出了具有鲜艳色调的橙色植物。
10.总结
哇,我们今天的旅程简直太棒了!我们对图像分割的深入研究无疑为我们如何从图像中提取有意义的信息提供了有效的方法。 希望大家可以通过上述案例,进入到图像处理的神秘领域,感叹相关算法的魅力。