Python + OpenCV使用Kmeans进行颜色分割

3

我正在尝试使用OpenCV中的kmeans算法来在HSV颜色空间中对图像进行分割。

def leftOffset(src, p_countours):
    height, width, size = src.shape

    p_width = width/p_countours
    o_left = src[0:height, 0:p_width]

    HSV_img = cv2.cvtColor(o_left, cv2.COLOR_BGR2HSV)
    hue = HSV_img[0]
    hue = np.float32(HSV_img)

    # Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

    # Set flags (Just to avoid line break in the code)
    flags = cv2.KMEANS_RANDOM_CENTERS

    # Apply KMeans
    compactness,labels,centers = cv2.kmeans(hue,2,criteria,10,flags)

    centers = np.uint8(centers)
    res = centers[labels.flatten()]
    res2 = res.reshape((hue.shape))
    cv2.imshow("o_left", hue)
    cv2.waitKey(0)

我现在能够使用K=2的kmeans算法对HSVImage[0]进行处理,那么我如何根据结果得到类似于阈值的图像呢?
为了澄清问题: 我有基于颜色的验证码,我想要分割出每个数字。
图片如下:6 1 我打算使用k-means方法找出主要颜色并分割其中的数字。

我可能没有完全理解问题。您能否请附上一张图片来澄清? - tfv
@tfv 我已经添加了2张测试图片。 - Mix
你使用的OpenCV版本是什么? - Yonatan Simson
这是2.4.8 @YonatanSimson。 - Mix
那么我下面的答案中的代码应该适用于您。 - Yonatan Simson
2个回答

5

1) 如果你只需要找到主导颜色,为什么不找出每个颜色通道的直方图呢? 找到占优势的通道,然后使用otsu算法仅对该通道进行分割?例如,如果我仅限于阈值处理色调,就可以得到很好的结果。 对于这个任务来说,K-means可能有点过头了。

import cv2
import numpy as np
import matplotlib.pylab as plt

## Simple Otsu over hue
six = cv2.imread('7zovC.jpg')

##convert to hsv
hsv = cv2.cvtColor(six, cv2.COLOR_BGR2HSV)
hue = hsv[:, :, 0]

binary_img = cv2.threshold(hue, 128, 255, cv2.THRESH_OTSU)

plt.figure()
plt.imshow(binary_img*255)
plt.show()

2) 为什么不使用所有通道进行聚类,而只使用色调?您需要的是聚类 -> 色彩量化,请参考此链接。这适用于OpenCV版本> 3.0.0。

备注:对于Python 2.4.11,cv2.kmeans具有略有不同的接口,您可以使用以下内容:

def color_quantize(img, K):
    Z = img.reshape((-1, 3))

    # convert to np.float32
    Z = np.float32(Z)

    # define criteria, number of clusters(K) and apply kmeans()
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    ret, label, center = cv2.kmeans(Z, 2, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)

    # Now convert back into uint8, and make original image
    center = np.uint8(center)
    res = center[label.flatten()]
    quantized_img = res.reshape((img.shape))

    label_img = label.reshape((img.shape[:2]))
    return label_img, quantized_img



six = cv2.imread('7zovC.jpg')


##convert to hsv
hsv = cv2.cvtColor(six, cv2.COLOR_BGR2HSV)

K = 2
label_img, six_q = color_quantize(hsv, K)



plt.figure()
plt.imshow(label_img)

plt.show()

我的颜色量化结果并不令人印象深刻。


3

我可以建议一种常规的替代方案吗?如果你首先消除非常暗和非常亮的区域,你可能只需要依靠从直方图计算出的色相分量的最频繁值。

请注意,由于周围颜色的相似性,数字边界永远不会完全精确。

此外,您可以仅选择最大的斑块(按大小)以抑制外部剩余的小斑块。

结果:

enter image description here

代码:

import cv2
import numpy as np
from matplotlib import pyplot as plt


img = cv2.imread('image1.jpg')

#get rid of very bright and very dark regions
delta=30
lower_gray = np.array([delta, delta,delta])
upper_gray = np.array([255-delta,255-delta,255-delta])
# Threshold the image to get only selected
mask = cv2.inRange(img, lower_gray, upper_gray)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)

#Convert to HSV space
HSV_img = cv2.cvtColor(res, cv2.COLOR_BGR2HSV)
hue = HSV_img[:, :, 0]

#select maximum value of H component from histogram
hist = cv2.calcHist([hue],[0],None,[256],[0,256])
hist= hist[1:, :] #suppress black value
elem = np.argmax(hist)
print np.max(hist), np.argmax(hist)

tolerance=10
lower_gray = np.array([elem-tolerance, 0,0])
upper_gray = np.array([elem+tolerance,255,255])
# Threshold the image to get only selected
mask = cv2.inRange(HSV_img, lower_gray, upper_gray)
# Bitwise-AND mask and original image
res2 = cv2.bitwise_and(img,img, mask= mask)


titles = ['Original Image', 'Selected Gray Values', 'Hue', 'Result']
images = [img, res, hue, res2]
for i in xrange(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接