CS585 Assignment 1: Skin Detection
January, 2002
Jennifer Wortman




Description of Goals

This assignment focuses on methods of skin detection based on various algorithms for determining whether or not pixels are skin colored. I have based my tests for skin pixels on a combination of hue, saturation, intensity, and RGB values at individual pixels.

The main program I have developed for this assignment takes as input a .ppm or .pnm ASCII color image. It outputs two images which are based on the original. The first is a copy of the input with all of the pixels determined to be "skin pixels" marked in black. The second is a copy of the input with the determined "center" of the skin marked. The mode of the skin values (i.e. the row and column with more skin pixels than any other row or column respectively) is marked with a red cross. The mean of the skin values (i.e. the row and column which are the mathematical averages of the rows and columns where all skin pixels are located) is marked with a green cross.

My goal has been to determine the best combination of color factors to differentiate skin pixels from non-skin pixels on the three input images I have been using in my tests. I have tried to have as many skin pixels as possible identified correctly, while keeping the number of non-skin pixels identified incorrectly as skin pixels to a minimum. In doing so, I have also tried to find a method of determining the "center" of an area of pixels determined to be skin.




Assumptions

Clearly it would be impossible to find a precise definition of "skin" based on attributes of color alone which would correctly identify skin pixels and non-skin pixels in all cases. The color of skin can vary greatly from one person to the next. In addition, the background lighting in a picture can have a huge effect on what color the skin appears to be.

Because of these factors, my program is not designed to work well on all input pictures. I have used three main images in my testing. One is an image of professor Betke, betke.ppm. Two are images of myself, jenn.pnm and jenn2.pnm. The lighting in these two pictures is different, and as a result the hue of my skin is not the same. My program was designed to work well on betke.pnm and jenn.pnm. I have included the results on jenn2.pnm in order to illustrate the extreme difference which lighting can make.

Since these three images were the main focus of my research, I realize that my skin tests are somewhat biased towards them. I do not claim that these tests will work well on other images.

One further note I should make is that all .jpg images which are referenced from this page were converted directly from .ppm and .pnm images using the GIMP, and have not been altered in any other way. I have convered them to .jpg images so that they will display on this page. The .ppm and .pnm versions of these images are also included in my project tar file. The original images of Professor Betke and myself are in the origimages directory. The skin sample images are in the data directory. The images output by my program are in the outfiles directory.




Methods

In order to find a starting point for my experiments, I created a program skintest.cpp which is analyzes the pixels in an image and outputs information about the colors used in the image. I then ran this program on three skin images, skin.ppm which is a patch of skin from betke.ppm, skin2.pnm which is a patch of skin from jenn.pnm, and skin3.pnm which is a patch of skin from jenn2.pnm. I planned on using information collected from these skin samples to determine my tests for what constitutes a skin pixel.

My first version of skintest.cpp looked at nothing but the red, green, and blue values of pixels. It output the average red, green, and blue values over the image. These values, taken seperately, were not similar enough in the three pictures to determine anything. However, I did notice that the ratios of the average red values to the average green values in the three pictures were close. To find out how close, I added the red to green ratio to the output of skintest.cpp. The results were as follows, with the color values ranging from 0 to 255.

Image: Average Red Value: Average Green Value: Average Blue Value: Per Pixel Ave Red/Green: Combined Ave Red/Green:
skin1.ppm 170.205882 146.421569 115.722222 1.162028 1.162437
skin2.pnm 214.843175 183.222857 175.265397 1.174104 1.172578
skin3.pnm 133.149767 105.522145 134.152681 1.261755 1.261818


Based on these results, I made my first attempt at skin detection. This version of the skin test checked the ratio of red to green in a pixel to see if it fell into a certain range. The range I had the best luck with on the particular images I used was between 1.1 and 1.3. However, even with the best range I could find, this method was lacking. Many real skin pixels were marked as skin pixels, but a good amount were missed, and a fair amount of background pixels were also marked as skin pixels. (The result of this attempt and the others can be viewed in the Image Results section below.)

Here is the facePixel function used in my implementation of the Red/Green Ratio Method.

This ratio seemed like a good starting point even though I knew that on its own, it was not enough, so I decided to move further in the same direction by checking the hue, saturation, and/or intensity values of pixels. To convert to an HSI system, I used the procedure provided in class by Professor Betke. In order to figure out the appropriate range of HSI values, I added average hue, saturation, and intensity to the output of skintest. These were the results on the same three patches of skin used before. The average HSI values over the skin samples were as follows.

Image: Average Hue: Average Saturation: Average Intensity:
skin1.ppm 5.998102 0.321071 170.205882
skin2.pnm 6.021008 0.186734 214.843175
skin3.pnm 3.838065 0.234735 138.148601


For my second attempt at skin detection, I defined a span of values which would pick up most of the skin pixels from the first two images and some from the third. I started with estimates from the data I had collected. After some experimentation, the spans I found to work the best were

5.75 <= hue <= 6.30
0.10 <= sat <= 0.40
100 <= int <= 250


Using these ranges, my program could correctly identify the majority of skin pixels but still incorrectly marked too many background pixels as skin pixels to be effective on its own.

Here is the facePixel function used in my implementation of the HSI Method.

I felt that my first two attempts were leading me in the right direction. However, both attempts labeled too many background pixels as skin pixels. I based my third attempt at identifying skin pixels on a combination of these two methods. I hoped that more background pixels would be weeded out if skin pixels were required to pass tests on their red to green ratio in addition to their hue, saturation, and intensity. Again I experimented with the ranges I used. I found the best to be

5.70 <= hue <= 6.50
0.10 <= saturation <= 0.40
100 <= intensity <= 250
1.0 < red/green < 1.3


Here is the facePixel function used in my implementation of the Combination Red/Green Ratio and HSI Method.

I was satisfied with the results of this final method on the first two images. However, it worked poorly on the third image. These results will be discussed in more detail below.




Source Code

The following are the source code files which implement the face detection methods as described above. These are the most recent versions of the files, with the Combination Red/Green and HSI facePixel function in place.

vision.h -- This is the header file for vision.cpp and main.cpp. It is based on the header file provided by Margrit Betke for use with the assignment. Some changes have been made to expand it to cover main.cpp and the conversions to HSI color values.

vision.cpp -- This file was taken virtually as-is from a sample file provided by and written by Margrit Betke for Image and Video Computing. It contains functions for manipulating .ppm files. The functions included can scan and write .ppm files. In addition I have added some functionality for converting RGB values of pixels to HSI values.

main.cpp -- This is the main program of my face detection project. Two output files will be produced when this program is run. The first will display the image with the face pixels in black. The second will display the image with the center of the face marked in two ways. The mode of the face pixels will be marked with a red cross, the mean with a green cross.

In addition, the Makefile I used to compile the main program is included in the code directory. I compiled everything in Mandrake Linux 8.1. I believe that the Makefile should work on any unix platform.




Image Results

Here are the three original images I used in my tests as well as the results of the three skin detection methods used above on each of the images.


Image 1:

The Original:


Red/Green Method:


HSI Method:


Combination of Red/Green and HSI Methods:





Image 2:

(These images are displayed smaller than they actually are to fit on the page. Click on an image to see the full version.)

The Original:


Red/Green Method:


HSI Method:


Combination of Red/Green and HSI Methods:





Image 3:

(Again, these images are displayed smaller than they actually are to fit on the page. Click on an image to see the full version.)

The Original:


Red/Green Method:


HSI Method:


Combination of Red/Green and HSI Methods:







Analysis and Conclusions

The Red/Green Ratio Method proved to be ineffective by itself for determining skin pixels. Although it found some of the skin pixels in Image 1, it missed the area above Professor Betke's eyes, and all of her neck. In Image 2, it picked up most of the skin pixels but also incorrectly identified features in the background such as the border of the poster and parts of the printer as skin. It is interesting to note that this method performed best of the three methods I tried on Image 3. However, as mentioned in my Assumptions, I was looking for the method which would perform best on the first two images.

The HSI Method picked up almost all of the skin pixels in Image 1. In addition, it correctly excluded areas on Professor Betke's neck where her necklace was. Unfortunately, this method identified the entire lighter portion of the background and Professor Betke's shirt as skin. Clearly this method is not precise enough to be useful on this particular image. The method did perform well on Image 2. Almost all of the skin was identified, and much less of the background was identified incorrectly than with the first method. The method performed poorly on Image 3, missing most of the skin entirely as well as identifying part of the background as skin.

The Red/Green and HSI Combination Method clearly performed the best on Image 1. The output from Image 1 is quite satisfying. Almost all of the face skin is correctly identified and the eyes are propperly excluded. However, this method still included Professor Betke's teeth and a small amount of her hair as skin. This method performed reasonably well on Image 2 also, although not as well as method 2. More skin pixels have been left out and there is still a small ammount of background noise which has been marked as skin. Like method 2, this method did particularly poorly on Image 3, although less of the background is identified incorrectly this time.

The Combination Method seems to determine the "center" of the skin areas best in each image as well. In Images 1 and 2, the mean center and the mode center as specified by this Method are more or less accurate representations of the centers of the skin areas. In Image 3, the mode center is off due to the large ammount of background which has been identified as skin. However, the mean center is still fairly accurate.

My overall conclusion is that skin detection algorithms should not be based on the color of pixels alone. There are too many outside factors such as lighting that can change the apparent color of skin, and of course, different people have different colored skin. In addition, objects in the background may be the same color as a person's skin and there is no clear way of telling the difference using these methods.

However, if these methods must be used, it is wise to use a combination of color-related factors. The red, green, and blue values of a pixel are not enough. By adding hue, saturation, and intensity, a more defined idea of skin color can be formed.




All content on this page was written by Jennifer Wortman in January 2002, except where noted.