图像处理之简单脸谱检测算法(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天前
|
机器学习/深度学习 监控 算法
面向办公室屏幕监控系统的改进型四叉树屏幕变化检测算法研究
本文提出一种改进型四叉树数据结构模型,用于优化办公室屏幕监控系统。通过动态阈值调节、变化优先级索引及增量更新策略,显著降低计算复杂度并提升实时响应能力。实验表明,该算法在典型企业环境中将屏幕变化检测效率提升40%以上,同时减少资源消耗。其应用场景涵盖安全审计、工作效能分析及远程协作优化等,未来可结合深度学习实现更智能化的功能。
17 0
|
3月前
|
机器学习/深度学习 存储 算法
基于MobileNet深度学习网络的活体人脸识别检测算法matlab仿真
本内容主要介绍一种基于MobileNet深度学习网络的活体人脸识别检测技术及MQAM调制类型识别方法。完整程序运行效果无水印,需使用Matlab2022a版本。核心代码包含详细中文注释与操作视频。理论概述中提到,传统人脸识别易受非活体攻击影响,而MobileNet通过轻量化的深度可分离卷积结构,在保证准确性的同时提升检测效率。活体人脸与非活体在纹理和光照上存在显著差异,MobileNet可有效提取人脸高级特征,为无线通信领域提供先进的调制类型识别方案。
|
3月前
|
机器学习/深度学习 数据采集 算法
基于yolov2和googlenet网络的疲劳驾驶检测算法matlab仿真
本内容展示了基于深度学习的疲劳驾驶检测算法,包括算法运行效果预览(无水印)、Matlab 2022a 软件版本说明、部分核心程序(完整版含中文注释与操作视频)。理论部分详细阐述了疲劳检测原理,通过对比疲劳与正常状态下的特征差异,结合深度学习模型提取驾驶员面部特征变化。具体流程包括数据收集、预处理、模型训练与评估,使用数学公式描述损失函数和推理过程。课题基于 YOLOv2 和 GoogleNet,先用 YOLOv2 定位驾驶员面部区域,再由 GoogleNet 分析特征判断疲劳状态,提供高准确率与鲁棒性的检测方法。
|
4月前
|
机器学习/深度学习 人工智能 运维
[ICDE2024]多正常模式感知的频域异常检测算法MACE
[ICDE2024]多正常模式感知的频域异常检测算法MACE
|
1月前
|
算法 数据安全/隐私保护
基于GA遗传算法的悬索桥静载试验车辆最优布载matlab仿真
本程序基于遗传算法(GA)实现悬索桥静载试验车辆最优布载的MATLAB仿真(2022A版)。目标是自动化确定车辆位置,使加载效率ηq满足0.95≤ηq≤1.05且尽量接近1,同时减少车辆数量与布载时间。核心原理通过优化模型平衡最小车辆使用与ηq接近1的目标,并考虑桥梁载荷、车辆间距等约束条件。测试结果展示布载方案的有效性,适用于悬索桥承载能力评估及性能检测场景。
|
1月前
|
算法 机器人 数据安全/隐私保护
基于双向RRT算法的三维空间最优路线规划matlab仿真
本程序基于双向RRT算法实现三维空间最优路径规划,适用于机器人在复杂环境中的路径寻找问题。通过MATLAB 2022A测试运行,结果展示完整且无水印。算法从起点和终点同时构建两棵随机树,利用随机采样、最近节点查找、扩展等步骤,使两棵树相遇以形成路径,显著提高搜索效率。相比单向RRT,双向RRT在高维或障碍物密集场景中表现更优,为机器人技术提供了有效解决方案。
|
10天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本内容展示了一种基于粒子群优化(PSO)与时间卷积神经网络(TCN)的时间序列预测方法。通过 MATLAB2022a 实现,完整程序运行无水印,核心代码附详细中文注释及操作视频。算法利用 PSO 优化 TCN 的超参数(如卷积核大小、层数等),提升非线性时间序列预测性能。TCN 结构包含因果卷积层与残差连接,结合 LSTM 构建混合模型,经多次迭代选择最优超参数,最终实现更准确可靠的预测效果,适用于金融、气象等领域。
|
7天前
|
算法 数据安全/隐私保护
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
本项目实现了一种基于Logistic Map混沌序列的数字信息加解密算法,使用MATLAB2022A开发并包含GUI操作界面。支持对文字、灰度图像、彩色图像和语音信号进行加密与解密处理。核心程序通过调整Logistic Map的参数生成伪随机密钥序列,确保加密的安全性。混沌系统的不可预测性和对初值的敏感依赖性是该算法的核心优势。示例展示了彩色图像、灰度图像、语音信号及文字信息的加解密效果,运行结果清晰准确,且完整程序输出无水印。
基于Logistic-Map混沌序列的数字信息加解密算法matlab仿真,支持对文字,灰度图,彩色图,语音进行加解密
|
6天前
|
算法
基于PSO粒子群优化的多无人机路径规划matlab仿真,对比WOA优化算法
本程序基于粒子群优化(PSO)算法实现多无人机路径规划,并与鲸鱼优化算法(WOA)进行对比。使用MATLAB2022A运行,通过四个无人机的仿真,评估两种算法在能耗、复杂度、路径规划效果及收敛曲线等指标上的表现。算法原理源于1995年提出的群体智能优化,模拟鸟群觅食行为,在搜索空间中寻找最优解。环境建模采用栅格或几何法,考虑避障、速度限制等因素,将约束条件融入适应度函数。程序包含初始化粒子群、更新速度与位置、计算适应度值、迭代优化等步骤,最终输出最优路径。
|
16天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于PSO粒子群优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于PSO(粒子群优化)改进TCN(时间卷积神经网络)的时间序列预测方法。使用Matlab2022a运行,完整程序无水印,附带核心代码中文注释及操作视频。TCN通过因果卷积层与残差连接处理序列数据,PSO优化其卷积核权重等参数以降低预测误差。算法中,粒子根据个体与全局最优位置更新速度和位置,逐步逼近最佳参数组合,提升预测性能。

热门文章

最新文章