天天看点

图像处理------简单脸谱检测算法

介绍基于皮肤检测之后的,寻找最大连通区域,完成脸谱检测的算法。大致的算法步骤如下:

图像处理------简单脸谱检测算法

原图如下:

图像处理------简单脸谱检测算法

每步处理以后的效果:

图像处理------简单脸谱检测算法

程序运行,加载选择图像以后的截屏如下:

图像处理------简单脸谱检测算法

截屏中显示图片,是适当放缩以后,代码如下:

image scaledimage = rawimg.getscaledinstance(200, 200, image.scale_fast); // java image api, rawimage is source image  

g2.drawimage(scaledimage, 0, 0, 200, 200, null);  

第一步:图像预处理,预处理的目的是为了减少图像中干扰像素,使得皮肤检测步骤可以得

到更好的效果,最常见的手段是调节对比度与亮度,也可以高斯模糊。

这里调节对比度的算法很简单,源代码如下:

package com.gloomyfish.face.detection;  

import java.awt.image.bufferedimage;  

public class contrastfilter extends abstractbufferedimageop {  

    private double ncontrast = 30;  

    public contrastfilter() {  

        system.out.println("contrast filter");  

    }  

    @override  

    public bufferedimage filter(bufferedimage src, bufferedimage dest) {  

        int width = src.getwidth();  

        int height = src.getheight();  

        double contrast = (100.0 + ncontrast) / 100.0;  

        contrast *= contrast;  

        if ( dest == null )  

            dest = createcompatibledestimage( src, null );  

        int[] inpixels = new int[width*height];  

        int[] outpixels = new int[width*height];  

        getrgb( src, 0, 0, width, height, inpixels );  

        int index = 0;  

        int ta = 0, tr = 0, tg = 0, tb = 0;  

        for(int row=0; row<height; row++) {  

            for(int col=0; col<width; col++) {  

                index = row * width + col;  

                ta = (inpixels[index] >> 24) & 0xff;  

                tr = (inpixels[index] >> 16) & 0xff;  

                tg = (inpixels[index] >> 8) & 0xff;  

                tb = inpixels[index] & 0xff;  

                // adjust contrast - red, green, blue  

                tr = adjustcontrast(tr, contrast);  

                tg = adjustcontrast(tg, contrast);  

                tb = adjustcontrast(tb, contrast);  

                // output rgb pixel  

                outpixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;  

            }  

        }  

        setrgb( dest, 0, 0, width, height, outpixels );  

        return dest;  

    public int adjustcontrast(int color, double contrast) {  

        double result = 0;  

        result = color / 255.0;  

        result -= 0.5;  

        result *= contrast;  

        result += 0.5;  

        result *=255.0;  

        return clamp((int)result);  

    public static int clamp(int c) {  

        if (c < 0)  

            return 0;  

        if (c > 255)  

            return 255;  

        return c;  

}  

注意:第一步不是必须的,如果图像质量已经很好,可以直接跳过。

第二步:皮肤检测,采用的是基于rgb色彩空间的统计结果来判断一个像素是否为skin像

素,如果是皮肤像素,则设置像素为黑色,否则为白色。给出基于rgb色彩空间的五种皮

肤检测统计方法,最喜欢的一种源代码如下:

/** 

 * this skin detection is absolutely good skin classification, 

 * i love this one very much 

 *  

 * this one should be always primary skin detection  

 * from all five filters 

 * @author zhigang 

 * 

 */  

public class skinfilter4 extends abstractbufferedimageop {  

            int ta = 0, tr = 0, tg = 0, tb = 0;  

                // detect skin method...  

                double sum = tr + tg + tb;  

                if (((double)tb/(double)tg<1.249) &&  

                    ((double)sum/(double)(3*tr)>0.696) &&  

                    (0.3333-(double)tb/(double)sum>0.014) &&  

                    ((double)tg/(double)(3*sum)<0.108))  

                {  

                    tr = tg = tb = 0;  

                } else {  

                    tr = tg = tb = 255;  

                }  

        setrgb(dest, 0, 0, width, height, outpixels);  

第三步:寻找最大连通区域

使用连通组件标记算法,寻找最大连通区域,关于什么是连通组件标记算法,可以参见这里

以这里我完成了一个更具效率的版本,主要思想是对像素数据进行八邻域寻找连通,然后合

并标记。源代码如下:

import java.util.arrays;  

import java.util.hashmap;  

 * fast connected component label algorithm 

 * @date 2012-05-23 

public class fastconnectedcomponentlabelalg {  

    private int bgcolor;  

    private int[] labels;  

    private int[] outdata;  

    private int dw;  

    private int dh;  

    public fastconnectedcomponentlabelalg() {  

        bgcolor = 255; // black color  

    public int[] dolabel(int[] inpixels, int width, int height) {  

        dw = width;  

        dh = height;  

        int nextlabel = 1;  

        int result = 0;  

        labels = new int[dw * dh/2];  

        outdata = new int[dw * dh];  

        for(int i=0; i<labels.length; i++) {  

            labels[i] = i;  

        // we need to define these two variable arrays.  

        int[] fourneighborhoodpixels = new int[8];  

        int[] fourneighborhoodlabels = new int[8];  

        int[] knownlabels = new int[4];  

        int srcrgb = 0, index = 0;  

        boolean existedlabel = false;  

        for(int row = 0; row < height; row ++) {  

            for(int col = 0; col < width; col++) {  

                srcrgb = inpixels[index] & 0x000000ff;  

                if(srcrgb == bgcolor) {  

                    result = 0; // which means no labeled for this pixel.  

                    // we just find the eight neighborhood pixels.  

                    fourneighborhoodpixels[0] = getpixel(inpixels, row-1, col); // upper cell  

                    fourneighborhoodpixels[1] = getpixel(inpixels, row, col-1); // left cell  

                    fourneighborhoodpixels[2] = getpixel(inpixels, row+1, col); // bottom cell  

                    fourneighborhoodpixels[3] = getpixel(inpixels, row, col+1); // right cell  

                    // four corners pixels  

                    fourneighborhoodpixels[4] = getpixel(inpixels, row-1, col-1); // upper left corner  

                    fourneighborhoodpixels[5] = getpixel(inpixels, row-1, col+1); // upper right corner  

                    fourneighborhoodpixels[6] = getpixel(inpixels, row+1, col-1); // left bottom corner  

                    fourneighborhoodpixels[7] = getpixel(inpixels, row+1, col+1); // right bottom corner  

                    // get current possible existed labels  

                    fourneighborhoodlabels[0] = getlabel(outdata, row-1, col); // upper cell  

                    fourneighborhoodlabels[1] = getlabel(outdata, row, col-1); // left cell  

                    fourneighborhoodlabels[2] = getlabel(outdata, row+1, col); // bottom cell  

                    fourneighborhoodlabels[3] = getlabel(outdata, row, col+1); // right cell  

                    // four corners labels value  

                    fourneighborhoodlabels[4] = getlabel(outdata, row-1, col-1); // upper left corner  

                    fourneighborhoodlabels[5] = getlabel(outdata, row-1, col+1); // upper right corner  

                    fourneighborhoodlabels[6] = getlabel(outdata, row+1, col-1); // left bottom corner  

                    fourneighborhoodlabels[7] = getlabel(outdata, row+1, col+1); // right bottom corner  

                    knownlabels[0] = fourneighborhoodlabels[0];  

                    knownlabels[1] = fourneighborhoodlabels[1];  

                    knownlabels[2] = fourneighborhoodlabels[4];  

                    knownlabels[3] = fourneighborhoodlabels[5];  

                    existedlabel = false;  

                    for(int k=0; k<fourneighborhoodlabels.length; k++) {  

                        if(fourneighborhoodlabels[k] != 0) {  

                            existedlabel = true;  

                            break;  

                        }  

                    }  

                    if(!existedlabel) {  

                        result = nextlabel;  

                        nextlabel++;  

                    } else {  

                        int found = -1, count = 0;  

                        for(int i=0; i<fourneighborhoodpixels.length; i++) {  

                            if(fourneighborhoodpixels[i] != bgcolor) {  

                                found = i;  

                                count++;  

                            }  

                        if(count == 1) {  

                            result = (fourneighborhoodlabels[found] == 0) ? nextlabel : fourneighborhoodlabels[found];  

                        } else {  

                            for(int j=0; j<knownlabels.length; j++) {  

                                if(knownlabels[j] != 0 && knownlabels[j] != result &&  

                                        knownlabels[j] < result) {  

                                    result = knownlabels[j];  

                                }  

                            boolean needmerge = false;  

                            for(int mm = 0; mm < knownlabels.length; mm++ ) {  

                                if(knownlabels[0] != knownlabels[mm] && knownlabels[mm] != 0) {  

                                    needmerge = true;  

                            // merge the labels now....  

                            if(needmerge) {  

                                int minlabel = knownlabels[0];  

                                for(int m=0; m<knownlabels.length; m++) {  

                                    if(minlabel > knownlabels[m] && knownlabels[m] != 0) {  

                                        minlabel = knownlabels[m];  

                                    }  

                                // find the final label number...  

                                result = (minlabel == 0) ? result : minlabel;  

                                // re-assign the label number now...  

                                if(knownlabels[0] != 0) {  

                                    setdata(outdata, row-1, col, result);  

                                if(knownlabels[1] != 0) {  

                                    setdata(outdata, row, col-1, result);  

                                if(knownlabels[2] != 0) {  

                                    setdata(outdata, row-1, col-1, result);  

                                if(knownlabels[3] != 0) {  

                                    setdata(outdata, row-1, col+1, result);  

                outdata[index] = result; // assign to label  

        // post merge each labels now  

                mergelabels(index);  

        // labels statistic  

        hashmap<integer, integer> labelmap = new hashmap<integer, integer>();  

        for(int d=0; d<outdata.length; d++) {  

            if(outdata[d] != 0) {  

                if(labelmap.containskey(outdata[d])) {  

                    integer count = labelmap.get(outdata[d]);  

                    count+=1;  

                    labelmap.put(outdata[d], count);  

                    labelmap.put(outdata[d], 1);  

        // try to find the max connected component  

        integer[] keys = labelmap.keyset().toarray(new integer[0]);  

        arrays.sort(keys);  

        int maxkey = 1;  

        int max = 0;  

        for(integer key : keys) {  

            if(max < labelmap.get(key)){  

                max = labelmap.get(key);  

                maxkey = key;  

            system.out.println( "number of " + key + " = " + labelmap.get(key));  

        system.out.println("maxkey = " + maxkey);  

        system.out.println("max connected component number = " + max);  

        return outdata;  

    private void mergelabels(int index) {  

        int row = index / dw;  

        int col = index % dw;  

        // get current possible existed labels  

        int min = getlabel(outdata, row, col);  

        if(min == 0) return;  

        if(min > getlabel(outdata, row-1, col) && getlabel(outdata, row-1, col) != 0) {  

            min = getlabel(outdata, row-1, col);  

        if(min > getlabel(outdata, row, col-1) && getlabel(outdata, row, col-1) != 0) {  

            min = getlabel(outdata, row, col-1);  

        if(min > getlabel(outdata, row+1, col) && getlabel(outdata, row+1, col) != 0) {  

            min = getlabel(outdata, row+1, col);  

        if(min > getlabel(outdata, row, col+1) && getlabel(outdata, row, col+1) != 0) {  

            min = getlabel(outdata, row, col+1);  

        if(min > getlabel(outdata, row-1, col-1) && getlabel(outdata, row-1, col-1) != 0) {  

            min = getlabel(outdata, row-1, col-1);  

        if(min > getlabel(outdata, row-1, col+1) && getlabel(outdata, row-1, col+1) != 0) {  

            min = getlabel(outdata, row-1, col+1);  

        if(min > getlabel(outdata, row+1, col-1) && getlabel(outdata, row+1, col-1) != 0) {  

            min = getlabel(outdata, row+1, col-1);  

        if(min > getlabel(outdata, row+1, col+1) && getlabel(outdata, row+1, col+1) != 0) {  

            min = getlabel(outdata, row+1, col+1);  

        if(getlabel(outdata, row, col) == min)  

            return;  

        outdata[index] = min;  

        // eight neighborhood pixels  

        if((row -1) >= 0) {  

            mergelabels((row-1)*dw + col);  

        if((col-1) >= 0) {  

            mergelabels(row*dw+col-1);  

        if((row+1) < dh) {  

            mergelabels((row + 1)*dw+col);  

        if((col+1) < dw) {  

            mergelabels((row)*dw+col+1);  

        if((row-1)>= 0 && (col-1) >=0) {  

            mergelabels((row-1)*dw+col-1);  

        if((row-1)>= 0 && (col+1) < dw) {  

            mergelabels((row-1)*dw+col+1);  

        if((row+1) < dh && (col-1) >=0) {  

            mergelabels((row+1)*dw+col-1);  

        if((row+1) < dh && (col+1) < dw) {  

            mergelabels((row+1)*dw+col+1);  

    private void setdata(int[] data, int row, int col, int value) {  

        if(row < 0 || row >= dh) {  

        if(col < 0 || col >= dw) {  

        int index = row * dw + col;  

        data[index] = value;  

    private int getlabel(int[] data, int row, int col) {  

        // handle the edge pixels  

        return (data[index] & 0x000000ff);  

    private int getpixel(int[] data, int row, int col) {  

            return bgcolor;  

    /** 

     * binary image data: 

     *  

     * 255, 0,   0,   255,   0,   255, 255, 0,   255, 255, 255, 

     * 255, 0,   0,   255,   0,   255, 255, 0,   0,   255, 0, 

     * 255, 0,   0,   0,     255, 255, 255, 255, 255, 0,   0, 

     * 255, 255, 0,   255,   255, 255, 0,   255, 0,   0,   255 

     * 255, 255, 0,   0,     0,   0,   255, 0,   0,   0,   0 

     * height = 5, width = 11 

     * @param args 

     */  

    public static int[] imagedata = new int[]{  

         255, 0,   0,   255,   0,   255, 255, 0,   255, 255, 255,  

         255, 0,   0,   255,   0,   255, 255, 0,   0,   255, 0,  

         255, 0,   0,   0,     255, 255, 255, 255, 255, 0,   0,  

         255, 255, 0,   255,   255, 255, 0,   255, 0,   0,   255,  

         255, 255, 0,   0,     0,   0,   255, 0,   0,   0,   0  

    };  

    public static void main(string[] args) {  

        fastconnectedcomponentlabelalg ccl = new fastconnectedcomponentlabelalg();  

        int[] outdata = ccl.dolabel(imagedata, 11, 5);  

        for(int i=0; i<5; i++) {  

            system.out.println("--------------------");  

            for(int j = 0; j<11; j++) {  

                int index = i * 11 + j;  

                if(j != 0) {  

                    system.out.print(",");  

                system.out.print(outdata[index]);  

            system.out.println();  

找到最大连通区域以后,对最大连通区域数据进行扫描,找出最小点,即矩形区域左上角坐

标,找出最大点,即矩形区域右下角坐标。知道这四个点坐标以后,在原图上打上红色矩形

框,标记出脸谱位置。寻找四个点坐标的实现代码如下:

private void getfacerectangel() {  

    int width = resultimage.getwidth();  

       int height = resultimage.getheight();  

       int[] inpixels = new int[width*height];  

       getrgb(resultimage, 0, 0, width, height, inpixels);  

       int index = 0;  

       int ta = 0, tr = 0, tg = 0, tb = 0;  

       for(int row=0; row<height; row++) {  

        for(int col=0; col<width; col++) {  

            index = row * width + col;  

            ta = (inpixels[index] >> 24) & 0xff;  

               tr = (inpixels[index] >> 16) & 0xff;  

               tg = (inpixels[index] >> 8) & 0xff;  

               tb = inpixels[index] & 0xff;  

               if(tr == tg && tg == tb && tb == 0) { // face skin  

                if(miny > row) {  

                    miny = row;  

                if(minx > col) {  

                    minx = col;  

                if(maxy < row) {  

                    maxy = row;  

                if(maxx < col) {  

                    maxx = col;  

               }  

       }  

缺点:

此算法不支持多脸谱检测,不支持裸体中的脸谱检测,但是根据人脸的

生物学特征可以进一步细化分析,支持裸体人脸检测