Python 使用kmeans聚类分割目标

Python 使用kmeans聚类分割目标

关于背景

  • 在复杂变化的背景中,识别运动目标并跟踪是一件很困难的事情。简单使用灰度阈值或HSV颜色空间阈值分割在特定环境下效果不错,但是在现实环境下(水草、淤泥变化)导致阈值的选择会非常不容易。
  • 当对象受到光影变化或者背景颜色从黑转化到白等,都会导致目标残缺。
  • 最重要的问题在于,由于使用阈值分割,不但需要手动设定阈值,不同的背景阈值差别很大,效率非常低。
  • 生成的轮廓数量会大于目标轮廓数量,需要条件检索目标轮廓,在特定情况下时,会导致目标丢失。
  • 不像汽车、人脸等跟踪,因为鱼没有特定的特征,又需要精确获取目标轮廓来计算速度、尾频等参数,目前大部分办法都是使用离线的模式:即录像、分析、分割目标、计算参数。

在视频的实时跟踪中,直接对rgb图像进行聚类,比如设定类数为4-5类,这样可以使复杂背景在光影下寻找目标变简单快速。

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def find_fish_kmeans(image):
kernel = np.ones((5, 5), np.uint8)
img = image.copy()
if image is None:
print("图像加载错误")
sys.exit()
else:
if len(np.shape(image)) != 3:
print("不是多通道图片,请确认图像类型")
sys.exit()
else:
m, n, _ = np.shape(img)
# kmeans聚类
Z = np.float32(img.reshape((-1, 3))) # 把图像拉伸成一列
print("Z的尺寸为[{}]".format(np.shape(Z)))
criteria = (cv2.TERM_CRITERIA_EPS +
cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 4
ret, label, center = cv2.kmeans(
Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
center = np.uint8(center)
print("中心点坐标为")
print(center)
print("label的尺寸为[{}]".format(np.shape(label.flatten())))
img1 = center[label.flatten()]
img1_kmeans = img1.reshape((img.shape)) # 获得聚类的图像
center_zero = np.float32(center)
red_max = (center_zero[0][2] - (center_zero[0]
[0] + center_zero[0][1]) / 2.0)
print red_max
idmax = 0
for i in range(1, K):
print(center_zero[i][2] - (center_zero[i]
[0] + center_zero[i][1]) / 2.0)
if (center_zero[i][2] - (center_zero[i][0] + center_zero[i][1]) / 2.0) > red_max:
red_max = (center_zero[i][
2] - (center_zero[i][0] + center_zero[i][1]) / 2.0)
# print red_max
idmax = i
zero = np.zeros(np.shape(center))
zero[idmax] = center[idmax]
# zero = np.uint8(zero) #如果不转化回uint8,图像显示为黑白色。
print("选取第[{}]类为鱼".format(idmax + 1))
print("坐标值为")
print(zero)
img2 = zero[label.flatten()]
img2_kmeans = img2.reshape((img.shape))
return img1_kmeans, img2_kmeans, center, label

结果:

鱼-光影下 鱼-光影下
鱼-阴影下 鱼-阴影下

优点:

直接调用opencv的 cv2.kmeans函数,速度快,视频的实时跟踪不容易丢失轮廓,对鱼的速度、尾频计算非常有用。

另一大难点在于,2个目标A与B重合时识别到各自的轮廓,AB分离后,继续跟踪到对应的目标AB而不是BA