Gkisi27
Gkisi27

Reputation: 177

Extract specific member of k-mean cluster of an image

I have an image (front facing man) with 4 different colors (background, hair, skin-tone, and cloth). I used k-mean with k=4, and image is segmented. Now what I want to do is extract out the hair out of the image.

I used canny edge detection to detect edge, which helped to detect the point in hair area(Pointed out by red dot). Now, I want to extract hair area, as the member of k-mean pointed out by the red dot. Is it possible?

Or is there any other way to extract out hair area from image of a person?

Code done till now is:

import cv2
import numpy as np

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

#Resizing Image for fixed width
def image_resize(image1, width = None, height = None, inter = 
cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image1.shape[:2]

    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image1

    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)

    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image1, dim, interpolation = inter)

    # return the resized image
    return resized


img1 = image_resize(image1, width = 500)

cv2.imshow("Resized", img1)
cv2.waitKey(0)

#Detecting Edge of image
canny = cv2.Canny(img1, 100, 150)

cv2.imshow("Edge", canny)
cv2.waitKey(0)

coords = np.nonzero(canny)

topmost_y = np.min(coords[0])


#Blurring effect

img2 = cv2.medianBlur(img1, 5)

cv2.imshow("Blurred", img2)
cv2.waitKey(0)

#K-mean approach
Z = img2.reshape((-1,3))
Z = np.float32(Z)

criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)

K=4
ret, label1, center1 = cv2.kmeans(Z, K, None,
                                          criteria, 10, 
cv2.KMEANS_RANDOM_CENTERS)
center1 = np.uint8(center1)
res1 = center1[label1.flatten()]
output1 = res1.reshape((img2.shape))

cv2.circle(output1, (250, topmost_y + 20), 5, (0,0,255), -1)

cv2.imshow("k = 4", output1)
cv2.waitKey(0)
cv2.destroyAllWindows()

Images:

Original Image, Result after resizing, Canny Edge detection, Blurred, K-mean

Upvotes: 4

Views: 1387

Answers (2)

tel
tel

Reputation: 13999

Given the code you already have you can get the xy coordinates of the cluster to which the hair belongs with just a few extra lines. You can also create an image that shows only the hair's cluster:

# find the index of the cluster of the hair
mask = label1.reshape(output1.shape[:-1])
khair = mask[(topmost_y + 20, 250)]

# get a mask that's True at all of the indices of hair's group
hairmask = mask==khair

# get the hair's cluster's xy coordinates
xyhair = hairmask.nonzero()

# plot an image with only the hair's cluster on a white background
cv2.imwrite("khair.jpg", np.where(hairmask[..., None], img1, [255,255,255]))

Here's what the hair's cluster looks like:

enter image description here

Once you have the hair's cluster, you can then find the blob that represents "just the hair". Here's how you'd do that:

import scipy.ndimage as snd

# label all connected blobs in hairmask
bloblab = snd.label(hairmask, structure=np.ones((3,3)))[0]

# create a mask for only the hair
haironlymask = bloblab == bloblab[topmost_y + 20, 250]

# get an image with just the hair and then crop it
justhair = np.where(haironlymask[..., None], img1, [255,255,255])
nz = haironlymask.nonzero()
justhair = justhair[nz[0].min():nz[0].max(), nz[1].min():nz[1].max()]

# save the image of just the hair on a white background
cv2.imwrite("justhair.jpg", justhair)

and here's the image of your hair by itself:

enter image description here

Upvotes: 1

Matthieu Brucher
Matthieu Brucher

Reputation: 22023

Now that you have one point in this hair region, propagate this point to all the other points.

The pseudo code would be:

set = red point while set of hair doesn't change: add all points (i-1, j) (i+1, j) (i, j-1) (i, j+1) to the set intersect the set with the mask of brown points

At the end, you will have a mask with the hair.

You can do that easily in numpy by starting with a Boolean image with just one True element at the red dot and then use |= and &= operators. I suspect OpenCV also has this kind of morphological dilation operator.

Upvotes: 0

Related Questions