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

缺点:

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

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

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

测也是个感兴趣的话题。

目录
打赏
0
0
0
0
81
分享
相关文章
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
本研究基于MATLAB 2022a,使用GRU网络对QAM调制信号进行检测。QAM是一种高效调制技术,广泛应用于现代通信系统。传统方法在复杂环境下性能下降,而GRU通过门控机制有效提取时间序列特征,实现16QAM、32QAM、64QAM、128QAM的准确检测。仿真结果显示,GRU在低SNR下表现优异,且训练速度快,参数少。核心程序包括模型预测、误检率和漏检率计算,并绘制准确率图。
111 65
基于GRU网络的MQAM调制信号检测算法matlab仿真,对比LSTM
基于MobileNet深度学习网络的活体人脸识别检测算法matlab仿真
本内容主要介绍一种基于MobileNet深度学习网络的活体人脸识别检测技术及MQAM调制类型识别方法。完整程序运行效果无水印,需使用Matlab2022a版本。核心代码包含详细中文注释与操作视频。理论概述中提到,传统人脸识别易受非活体攻击影响,而MobileNet通过轻量化的深度可分离卷积结构,在保证准确性的同时提升检测效率。活体人脸与非活体在纹理和光照上存在显著差异,MobileNet可有效提取人脸高级特征,为无线通信领域提供先进的调制类型识别方案。
基于MSER和HOG特征提取的SVM交通标志检测和识别算法matlab仿真
### 算法简介 1. **算法运行效果图预览**:展示算法效果,完整程序运行后无水印。 2. **算法运行软件版本**:Matlab 2017b。 3. **部分核心程序**:完整版代码包含中文注释及操作步骤视频。 4. **算法理论概述**: - **MSER**:用于检测显著区域,提取图像中稳定区域,适用于光照变化下的交通标志检测。 - **HOG特征提取**:通过计算图像小区域的梯度直方图捕捉局部纹理信息,用于物体检测。 - **SVM**:寻找最大化间隔的超平面以分类样本。 整个算法流程图见下图。
基于深度学习的路面裂缝检测算法matlab仿真
本项目基于YOLOv2算法实现高效的路面裂缝检测,使用Matlab 2022a开发。完整程序运行效果无水印,核心代码配有详细中文注释及操作视频。通过深度学习技术,将目标检测转化为回归问题,直接预测裂缝位置和类别,大幅提升检测效率与准确性。适用于实时检测任务,确保道路安全维护。 简介涵盖了算法理论、数据集准备、网络训练及检测过程,采用Darknet-19卷积神经网络结构,结合随机梯度下降算法进行训练。
基于yolov2和googlenet网络的疲劳驾驶检测算法matlab仿真
本内容展示了基于深度学习的疲劳驾驶检测算法,包括算法运行效果预览(无水印)、Matlab 2022a 软件版本说明、部分核心程序(完整版含中文注释与操作视频)。理论部分详细阐述了疲劳检测原理,通过对比疲劳与正常状态下的特征差异,结合深度学习模型提取驾驶员面部特征变化。具体流程包括数据收集、预处理、模型训练与评估,使用数学公式描述损失函数和推理过程。课题基于 YOLOv2 和 GoogleNet,先用 YOLOv2 定位驾驶员面部区域,再由 GoogleNet 分析特征判断疲劳状态,提供高准确率与鲁棒性的检测方法。
基于反光衣和检测算法的应用探索
本文探讨了利用机器学习和计算机视觉技术进行反光衣检测的方法,涵盖图像预处理、目标检测与分类、特征提取等关键技术。通过YOLOv5等模型的训练与优化,展示了实现高效反光衣识别的完整流程,旨在提升智能检测系统的性能,应用于交通安全、工地监控等领域。
基于GA-PSO-SVM算法的混沌背景下微弱信号检测matlab仿真
本项目基于MATLAB 2022a,展示了SVM、PSO、GA-PSO-SVM在混沌背景下微弱信号检测中的性能对比。核心程序包含详细中文注释和操作步骤视频。GA-PSO-SVM算法通过遗传算法和粒子群优化算法优化SVM参数,提高信号检测的准确性和鲁棒性,尤其适用于低信噪比环境。
|
5月前
|
TDengine 检测数据最佳压缩算法工具,助你一键找出最优压缩方案
在使用 TDengine 存储时序数据时,压缩数据以节省磁盘空间是至关重要的。TDengine 支持用户根据自身数据特性灵活指定压缩算法,从而实现更高效的存储。然而,如何选择最合适的压缩算法,才能最大限度地降低存储开销?为了解决这一问题,我们特别推出了一个实用工具,帮助用户快速判断并选择最适合其数据特征的压缩算法。
135 0
圆形检测算法-基于颜色和形状(opencv)
该代码实现了一个圆检测算法,用于识别视频中的红色、白色和蓝色圆形。通过将图像从RGB转换为HSV颜色空间,并设置对应颜色的阈值范围,提取出目标颜色的区域。接着对这些区域进行轮廓提取和面积筛选,使用霍夫圆变换检测圆形,并在原图上绘制检测结果。
180 0
下一篇
oss创建bucket