图像处理之简单脸谱检测算法(Simple Face Detection Algorithm)

简介: 图像处理之简单脸谱检测算法(Simple Face Detection Algorithm)

图像处理之简单脸谱检测算法(Simple Face Detection Algorithm)


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

1337785219_9119.png

原图如下:

1337785247_2176.png

每步处理以后的效果:

1337789621_8463.png

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

1337786291_3850.png

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

  Image scaledImage = rawImg.getScaledInstance(200, 200, Image.SCALE_FAST); // Java Image API, rawImage is source image
  g2.drawImage(scaledImage, 0, 0, 200, 200, null);

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


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


对比度可以参见这里:http://blog.csdn.net/jia20003/article/details/7385160


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

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色彩空间的五种皮

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

package com.gloomyfish.face.detection;
 
import java.awt.image.BufferedImage;
/**
 * 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 {
 
  @Override
  public BufferedImage filter(BufferedImage src, BufferedImage dest) {
    int width = src.getWidth();
        int height = src.getHeight();
 
        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;
        for(int row=0; row<height; row++) {
          int ta = 0, tr = 0, tg = 0, tb = 0;
          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;
                
                // 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;
                }
                outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;
          }
        }
        setRGB(dest, 0, 0, width, height, outPixels);
        return dest;
  }
}


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

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

http://blog.csdn.net/jia20003/article/details/7483249,里面提到的连通组件算法效率不高,所

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

并标记。源代码如下:

package com.gloomyfish.face.detection;
 
import java.util.Arrays;
import java.util.HashMap;
 
/**
 * fast connected component label algorithm
 * 
 * @date 2012-05-23
 * @author zhigang
 *
 */
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++) {
        index = row * width + col;
        srcrgb = inPixels[index] & 0x000000ff;
        if(srcrgb == bgColor) {
          result = 0; // which means no labeled for this pixel.
        } else {
          // 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 {
              result = (fourNeighborhoodLabels[found] == 0) ? nextlabel : fourNeighborhoodLabels[found];
              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
    for(int row = 0; row < height; row ++) {
      for(int col = 0; col < width; col++) {
        index = row * width + col;
        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);
        } else {
          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) {
      return;
    }
    
    if(col < 0 || col >= dw) {
      return;
    }
    
    int index = row * dw + col;
    data[index] = value;
  }
  
  private int getLabel(int[] data, int row, int col) {
    // handle the edge pixels
    if(row < 0 || row >= dh) {
      return 0;
    }
    
    if(col < 0 || col >= dw) {
      return 0;
    }
    
    int index = row * dw + col;
    return (data[index] & 0x000000ff);
  }
 
  private int getPixel(int[] data, int row, int col) {
    // handle the edge pixels
    if(row < 0 || row >= dh) {
      return bgColor;
    }
    
    if(col < 0 || col >= dw) {
      return bgColor;
    }
    
    int index = row * dw + col;
    return (data[index] & 0x000000ff);
  }
 
  /**
   * 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;
                  }
                }
          }
        }
  }

缺点:

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

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

写本文章的目的:本例为图像处理综合运行的一个简单实例。同时人脸检

测也是个感兴趣的话题。

相关文章
|
4月前
|
监控 安全 算法
137_安全强化:输入过滤与水印 - 实现输出水印的检测算法与LLM安全防护最佳实践
随着大语言模型(LLM)在各行业的广泛应用,安全问题日益凸显。从提示注入攻击到恶意输出生成,从知识产权保护到内容溯源,LLM安全已成为部署和应用过程中不可忽视的关键环节。在2025年的LLM技术生态中,输入过滤和输出水印已成为两大核心安全技术,它们共同构建了LLM服务的安全防护体系。
|
5月前
|
传感器 资源调度 算法
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
本文提出一种多子带相干累积(MSCA)算法,通过引入空带和子带相干处理,解决DDMA-MIMO雷达的多普勒模糊与能量分散问题。该方法在低信噪比下显著提升检测性能,实测验证可有效恢复目标速度,适用于车载雷达高精度感知。
679 4
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
|
4月前
|
开发框架 算法 .NET
基于ADMM无穷范数检测算法的MIMO通信系统信号检测MATLAB仿真,对比ML,MMSE,ZF以及LAMA
简介:本文介绍基于ADMM的MIMO信号检测算法,结合无穷范数优化与交替方向乘子法,降低计算复杂度并提升检测性能。涵盖MATLAB 2024b实现效果图、核心代码及详细注释,并对比ML、MMSE、ZF、OCD_MMSE与LAMA等算法。重点分析LAMA基于消息传递的低复杂度优势,适用于大规模MIMO系统,为通信系统检测提供理论支持与实践方案。(238字)
|
4月前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
456 0
|
4月前
|
数据采集 分布式计算 并行计算
mRMR算法实现特征选择-MATLAB
mRMR算法实现特征选择-MATLAB
309 2
|
5月前
|
传感器 机器学习/深度学习 编解码
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
291 3
|
5月前
|
存储 编解码 算法
【多光谱滤波器阵列设计的最优球体填充】使用MSFA设计方法进行各种重建算法时,图像质量可以提高至多2 dB,并在光谱相似性方面实现了显著提升(Matlab代码实现)
【多光谱滤波器阵列设计的最优球体填充】使用MSFA设计方法进行各种重建算法时,图像质量可以提高至多2 dB,并在光谱相似性方面实现了显著提升(Matlab代码实现)
213 6
|
4月前
|
机器学习/深度学习 算法 机器人
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
235 8
|
4月前
|
机器学习/深度学习 算法 自动驾驶
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
基于导向滤波的暗通道去雾算法在灰度与彩色图像可见度复原中的研究(Matlab代码实现)
264 8
|
4月前
|
机器学习/深度学习 算法 数据可视化
基于MVO多元宇宙优化的DBSCAN聚类算法matlab仿真
本程序基于MATLAB实现MVO优化的DBSCAN聚类算法,通过多元宇宙优化自动搜索最优参数Eps与MinPts,提升聚类精度。对比传统DBSCAN,MVO-DBSCAN有效克服参数依赖问题,适应复杂数据分布,增强鲁棒性,适用于非均匀密度数据集的高效聚类分析。

热门文章

最新文章