图像处理之简单脸谱检测算法(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;
                  }
                }
          }
        }
  }

缺点:

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

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

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

测也是个感兴趣的话题。

相关文章
|
7月前
|
监控 安全 算法
137_安全强化:输入过滤与水印 - 实现输出水印的检测算法与LLM安全防护最佳实践
随着大语言模型(LLM)在各行业的广泛应用,安全问题日益凸显。从提示注入攻击到恶意输出生成,从知识产权保护到内容溯源,LLM安全已成为部署和应用过程中不可忽视的关键环节。在2025年的LLM技术生态中,输入过滤和输出水印已成为两大核心安全技术,它们共同构建了LLM服务的安全防护体系。
720 148
|
8月前
|
传感器 资源调度 算法
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
本文提出一种多子带相干累积(MSCA)算法,通过引入空带和子带相干处理,解决DDMA-MIMO雷达的多普勒模糊与能量分散问题。该方法在低信噪比下显著提升检测性能,实测验证可有效恢复目标速度,适用于车载雷达高精度感知。
928 4
DDMA-MIMO雷达多子带相干累积目标检测算法——论文阅读
|
7月前
|
开发框架 算法 .NET
基于ADMM无穷范数检测算法的MIMO通信系统信号检测MATLAB仿真,对比ML,MMSE,ZF以及LAMA
简介:本文介绍基于ADMM的MIMO信号检测算法,结合无穷范数优化与交替方向乘子法,降低计算复杂度并提升检测性能。涵盖MATLAB 2024b实现效果图、核心代码及详细注释,并对比ML、MMSE、ZF、OCD_MMSE与LAMA等算法。重点分析LAMA基于消息传递的低复杂度优势,适用于大规模MIMO系统,为通信系统检测提供理论支持与实践方案。(238字)
|
8月前
|
canal 算法 vr&ar
【图像处理】基于电磁学优化算法的多阈值分割算法研究(Matlab代码实现)
【图像处理】基于电磁学优化算法的多阈值分割算法研究(Matlab代码实现)
240 1
|
11月前
|
机器学习/深度学习 运维 监控
实时异常检测实战:Flink+PAI 算法模型服务化架构设计
本文深入探讨了基于 Apache Flink 与阿里云 PAI 构建的实时异常检测系统。内容涵盖技术演进、架构设计、核心模块实现及金融、工业等多领域实战案例,解析流处理、模型服务化、状态管理等关键技术,并提供性能优化与高可用方案,助力企业打造高效智能的实时异常检测平台。
1041 1
|
10月前
|
存储 监控 算法
基于跳表数据结构的企业局域网监控异常连接实时检测 C++ 算法研究
跳表(Skip List)是一种基于概率的数据结构,适用于企业局域网监控中海量连接记录的高效处理。其通过多层索引机制实现快速查找、插入和删除操作,时间复杂度为 $O(\log n)$,优于链表和平衡树。跳表在异常连接识别、黑名单管理和历史记录溯源等场景中表现出色,具备实现简单、支持范围查询等优势,是企业网络监控中动态数据管理的理想选择。
261 0
|
11月前
|
机器学习/深度学习 监控 算法
面向办公室屏幕监控系统的改进型四叉树屏幕变化检测算法研究
本文提出一种改进型四叉树数据结构模型,用于优化办公室屏幕监控系统。通过动态阈值调节、变化优先级索引及增量更新策略,显著降低计算复杂度并提升实时响应能力。实验表明,该算法在典型企业环境中将屏幕变化检测效率提升40%以上,同时减少资源消耗。其应用场景涵盖安全审计、工作效能分析及远程协作优化等,未来可结合深度学习实现更智能化的功能。
200 0
|
机器学习/深度学习 存储 算法
基于MobileNet深度学习网络的活体人脸识别检测算法matlab仿真
本内容主要介绍一种基于MobileNet深度学习网络的活体人脸识别检测技术及MQAM调制类型识别方法。完整程序运行效果无水印,需使用Matlab2022a版本。核心代码包含详细中文注释与操作视频。理论概述中提到,传统人脸识别易受非活体攻击影响,而MobileNet通过轻量化的深度可分离卷积结构,在保证准确性的同时提升检测效率。活体人脸与非活体在纹理和光照上存在显著差异,MobileNet可有效提取人脸高级特征,为无线通信领域提供先进的调制类型识别方案。
|
机器学习/深度学习 数据采集 算法
基于yolov2和googlenet网络的疲劳驾驶检测算法matlab仿真
本内容展示了基于深度学习的疲劳驾驶检测算法,包括算法运行效果预览(无水印)、Matlab 2022a 软件版本说明、部分核心程序(完整版含中文注释与操作视频)。理论部分详细阐述了疲劳检测原理,通过对比疲劳与正常状态下的特征差异,结合深度学习模型提取驾驶员面部特征变化。具体流程包括数据收集、预处理、模型训练与评估,使用数学公式描述损失函数和推理过程。课题基于 YOLOv2 和 GoogleNet,先用 YOLOv2 定位驾驶员面部区域,再由 GoogleNet 分析特征判断疲劳状态,提供高准确率与鲁棒性的检测方法。
|
机器学习/深度学习 人工智能 运维
[ICDE2024]多正常模式感知的频域异常检测算法MACE
[ICDE2024]多正常模式感知的频域异常检测算法MACE
227 0

热门文章

最新文章