I think the easiest path IS opencv for what you want to do.
There's a link about 'simpler' stuff:
http://media.packetlife.net/media/blog/attachments/413/nbar_flesh_tone.html
(If you got a Cisco server where you want to prioritize non-flesh colored images.)
This thread
Nude image detection - OPENCV
Links to : https://csel.cs.colorado.edu/~xingx/project/privacy.html
(No code, research on video 'nude' detection.)
You can look at O'reilly Opencv book. There's a hsv color space example for flesh color explained there. This book explains it well. (There are some google book pages available if you search.)
You can also look at the camshift opencv example.
This is a link to the problem space in opencv. (If you want some code, or read a bit about using the color space HSV or CIE Lab* color space)
http://tech.groups.yahoo.com/group/OpenCV/message/45158
Some code copied from that opencv group that gives a mask for the 'flesh color':
src_RGB = RGB-Image (IPL_DEPTH_8U , 3); // Source
mask_BW = GRAY-Image (IPL_DEPTH_8U , 3);// Resulting Mask same size as source !!
//after GetSkinMask you can use cvAnd between src_RGB and mask_BW.
void GetSkinMask(IplImage * src_RGB, IplImage * mask_BW, int
erosions=1, int dilations=7)
{
CvSize size;
CvSize sz = cvSize( src_RGB->width & -2, src_RGB->height & -2);
//get the size of input_image (src_RGB)
IplImage* pyr = cvCreateImage( cvSize(sz.width/2, sz.height/2), 8, 3 ); //create 2 temp-images
IplImage* src = cvCreateImage(cvGetSize(src_RGB), IPL_DEPTH_8U ,3);
cvCopyImage(src_RGB, src);
IplImage* tmpYCR = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U , 3);
cvPyrDown( src, pyr, 7 );
//remove noise from input
cvPyrUp( pyr, src, 7 );
cvCvtColor(src ,tmpYCR , CV_RGB2YCrCb);
uchar Y;
uchar Cr;
uchar Cb;
CvPixelPosition8u pos_src;
CvPixelPosition8u pos_dst;
int x =0;
int y =0;
CV_INIT_PIXEL_POS(pos_src,(unsigned char *) tmpYCR->imageData,
tmpYCR->widthStep, cvGetSize(tmpYCR), x,y, tmpYCR->origin);
CV_INIT_PIXEL_POS(pos_dst, (unsigned char *) mask_BW->imageData,
mask_BW->widthStep, cvGetSize(mask_BW), x,y, mask_BW->origin);
uchar * ptr_src;
uchar * ptr_dst;
for( y=0;y<src-> height; y++)
{
for ( x=0; x<src->width; x++)
{
ptr_src = CV_MOVE_TO(pos_src,x,y,3);
ptr_dst = CV_MOVE_TO(pos_dst,x,y,3);
Y = ptr_src[0];
Cb= ptr_src[1];
Cr= ptr_src[2];
if( Cr > 138 && Cr < 178 &&
Cb + 0.6 * Cr >200 && Cb + 0.6 * Cr <215)
{
ptr_dst[0] = 255;
ptr_dst[1] = 255;
ptr_dst[2] = 255;
}
else
{
ptr_dst[0] = 0;
ptr_dst[1] = 0;
ptr_dst[2] = 0;
}
}
}
if(erosions>0) cvErode(mask_BW,mask_BW,0,erosions);
if (dilations>0) cvDilate(mask_BW,mask_BW,0,dilations);
cvReleaseImage(&pyr);
cvReleaseImage(&tmpYCR);
cvReleaseImage(&src);
}