2

I tried to to write the same as written down here in python but my code doesn't produce good results. my goal is to take an RGB image, resize and convert to YCbCr and then set the background pixel value as 0 and the hand pixel value as 1. can someone help me write this code in python using PIL please?

(the code I'm trying to replicate, I have some trouble with steps 3-6)

function image_out = processSkinImage(filename)
    Step 1...
    % Read the image
    original = imread(filename);
    ...
    Step 2...
    % Resize the image to 50x50
    image_resized = imresize(original, scale);
    [M N Z] = size(image_resized);

    % Initialize the output image
    image_out = zeros(height,width);
    image_out = zeros(M,N);
    ...
    Step 3...
    % Convert the image from RGB to YCbCr
    img_ycbcr = rgb2ycbcr(image_resized);
    Cb = img_ycbcr(:,:,2);
    Cr = img_ycbcr(:,:,3);
    ...
    Step 4...
    % Get the central color of the image
    % Expected the hand to be in the central of the image
    central_color = img_ycbcr(int32(M/2),int32(N/2),:);
    Cb_Color = central_color(:,:,2);
    Cr_Color = central_color(:,:,3);
    % Set the range
    Cb_Difference = 15;
    Cr_Difference = 10;
    ...
    Step 5...
    % Detect skin pixels
    [r,c,v] = find(Cb>=Cb_Color-Cr_Difference & Cb<=Cb_Color+Cb_Difference & Cr>=Cr_Color-Cr_Difference & Cr<=Cr_Color+Cr_Difference);
    ...
    Step 6...
    % Mark detected pixels
    for i=1:match_count
        image_out(r(i),c(i)) = 1;
    end
end

that's the code I wrote:

from PIL import Image as im

image = im.open('/Users/eitan/Desktop/eell.jpg')
image = image.resize((50,50), im.NEAREST)
grayScale = image.convert(mode='L')

width, height = grayScale.size
mid_pixel=grayScale.getpixel((width/2,height/2))
print (mid_pixel)

pixels = grayScale.load()

for i in range(grayScale.size[0]):    # for every col:
    for j in range(grayScale.size[1]):    # For every row

        if grayScale.getpixel((i,j)) < mid_pixel+40 and grayScale.getpixel((i,j)) > mid_pixel-15:
            pixels[i,j] = 255

        else:
            pixels[i, j] = 0

grayScale.show()

This is an example of an image the code would get

And this is what the result should look like

It would be great if someone could help me write this code in python!

Eitanbl
  • 41
  • 4
  • As I said in a comment to your related [question](https://stackoverflow.com/questions/58030104/how-to-convert-image-to-one-dimension-array-using-pil), I think you're going to need to use pixel _color_, not just grayscale intensity (because that throws away too much information), to determine which ones are part of the hand and which are not. The question [Is there an easy way to compare how close two colors are to each other?](https://stackoverflow.com/questions/492211/is-there-an-easy-way-to-compare-how-close-two-colors-are-to-each-other) might help. – martineau Sep 22 '19 at 19:27
  • 1
    As @martineau says, throwing away the colour is maybe not the best approach for finding pixels similar to a specific skin tone/colour. Maybe consider finding the HSL values of the central pixel then locate ones with similar Hue and Saturation, for example. – Mark Setchell Sep 22 '19 at 19:59

1 Answers1

2

You could approach it like this, where I have used HSV colourspace rather than YCbCr colourspace:

#!/usr/bin/env python3

import numpy as np
from PIL import Image

# Open image and convert to HSV colourspace
im = Image.open('hand.png').convert('HSV')

# Convert to Numpy array
ni = np.array(im)

# Get H, S and V of central pixel - consider taking a median of a larger area here
h,s,v = ni[int(ni.shape[0]/2), int(ni.shape[1]/2)]

# Separate each channel to own array
H = ni[:,:,0]
S = ni[:,:,1]
V = ni[:,:,2]

# Permissible +/- tolerances on each channel
deltah = 20
deltas = 80
deltav = 50

# Make masks of pixels with acceptable H, S and V
hmask = np.where((H > h-deltah) & (H < h+deltah), 255, 0).astype(np.uint8)
smask = np.where((S > s-deltas) & (S < s+deltas), 255, 0).astype(np.uint8)
vmask = np.where((V > v-deltav) & (V < v+deltav), 255, 0).astype(np.uint8)

# Save as images for inspection
Image.fromarray(hmask).save('hmask.png')
Image.fromarray(smask).save('smask.png')
Image.fromarray(vmask).save('vmask.png')

Resulting Hue mask:

enter image description here

Resulting Saturation mask:

enter image description here

Resulting Value mask:

enter image description here

You could then AND or OR the masks together to get more complex combinations of masks.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432