图像处理之简单脸谱检测算法(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天前
|
算法 JavaScript 前端开发
在JavaScript中实现基本的碰撞检测算法,我们通常会用到矩形碰撞检测,也就是AABB(Axis-Aligned Bounding Box)碰撞检测
【6月更文挑战第16天】JavaScript中的基本碰撞检测涉及AABB(轴对齐边界框)方法,常用于2D游戏。`Rectangle`类定义了矩形的属性,并包含一个`collidesWith`方法,通过比较边界来检测碰撞。若两矩形无重叠部分,四个条件(关于边界相对位置)均需满足。此基础算法适用于简单场景,复杂情况可能需采用更高级的检测技术或物理引擎库。
43 6
|
2天前
|
机器学习/深度学习 算法 语音技术
基于语音信号MFCC特征提取和GRNN神经网络的人员身份检测算法matlab仿真
**语音识别算法概览** MATLAB2022a中实现,结合MFCC与GRNN技术进行说话人身份检测。MFCC利用人耳感知特性提取语音频谱特征,GRNN作为非线性映射工具,擅长序列学习,确保高效识别。预加重、分帧、加窗、FFT、滤波器组、IDCT构成MFCC步骤,GRNN以其快速学习与鲁棒性处理不稳定数据。适用于多种领域。
|
3天前
|
机器学习/深度学习 算法 计算机视觉
基于ADAS的车道线检测算法matlab仿真
**摘要:** 基于ADAS的车道线检测算法利用Hough变换和边缘检测在视频中识别车道线,判断车道弯曲情况,提供行驶方向信息,并高亮显示。在MATLAB2022a中实现,系统包括图像预处理(灰度化、滤波、边缘检测)、车道线特征提取(霍夫变换、曲线拟合)和车道线跟踪,确保在实时场景中的准确性和稳定性。预处理通过灰度转换减少光照影响,滤波去除噪声,Canny算法检测边缘。霍夫变换用于直线检测,曲线拟合适应弯道,跟踪则增强连续帧的车道线检测。
|
3天前
|
机器学习/深度学习 算法 数据可视化
m基于PSO-LSTM粒子群优化长短记忆网络的电力负荷数据预测算法matlab仿真
在MATLAB 2022a中,应用PSO优化的LSTM模型提升了电力负荷预测效果。优化前预测波动大,优化后预测更稳定。PSO借鉴群体智能,寻找LSTM超参数(如学习率、隐藏层大小)的最优组合,以最小化误差。LSTM通过门控机制处理序列数据。代码显示了模型训练、预测及误差可视化过程。经过优化,模型性能得到改善。
18 6
|
1天前
|
缓存 算法
基于机会网络编码(COPE)的卫星网络路由算法matlab仿真
**摘要:** 该程序实现了一个基于机会网络编码(COPE)的卫星网络路由算法,旨在提升无线网络的传输效率和吞吐量。在MATLAB2022a中测试,结果显示了不同数据流个数下的网络吞吐量。算法通过Dijkstra函数寻找路径,计算编码机会(Nab和Nx),并根据编码机会减少传输次数。当有编码机会时,中间节点执行编码和解码操作,优化传输路径。结果以图表形式展示,显示数据流与吞吐量的关系,并保存为`R0.mat`。COPE算法预测和利用编码机会,适应卫星网络的动态特性,提高数据传输的可靠性和效率。
|
3天前
|
算法 调度
基于变异混合蛙跳算法的车间调度最优化matlab仿真,可以任意调整工件数和机器数,输出甘特图
**摘要:** 实现变异混合蛙跳算法的MATLAB2022a版车间调度优化程序,支持动态调整工件和机器数,输出甘特图。核心算法结合SFLA与变异策略,解决Job-Shop Scheduling Problem,最小化总完成时间。SFLA模拟蛙群行为,分组进行局部搜索和全局信息交换。变异策略增强全局探索,避免局部最优。程序初始化随机解,按规则更新,经多次迭代和信息交换后终止。
|
14天前
|
算法
基于GA-PSO遗传粒子群混合优化算法的VRPTW问题求解matlab仿真
摘要: 本文介绍了考虑时间窗的车辆路径问题(VRPTW),在MATLAB2022a中进行测试。VRPTW涉及车辆从配送中心出发,服务客户并返回,需在指定时间窗内完成且满足车辆容量限制,目标是最小化总行驶成本。文章探讨了遗传算法(GA)和粒子群优化(PSO)的基本原理及其在VRPTW中的应用,包括编码、适应度函数、选择、交叉、变异等步骤。同时,提出了动态惯性权重、精英策略、邻域搜索、多种群和启发式信息等优化策略,以应对时间窗限制并提升算法性能。
|
8天前
|
算法 JavaScript 决策智能
基于禁忌搜索算法的TSP路径规划matlab仿真
**摘要:** 使用禁忌搜索算法解决旅行商问题(TSP),在MATLAB2022a中实现路径规划,显示优化曲线与路线图。TSP寻找最短城市访问路径,算法通过避免局部最优,利用禁忌列表不断调整顺序。关键步骤包括初始路径选择、邻域搜索、解评估、选择及禁忌列表更新。过程示意图展示搜索效果。
|
8天前
|
机器学习/深度学习 算法
基于BP神经网络和小波变换特征提取的烟草香型分类算法matlab仿真,分为浓香型,清香型和中间香型
```markdown 探索烟草香型分类:使用Matlab2022a中的BP神经网络结合小波变换。小波分析揭示香气成分的局部特征,降低维度,PCA等用于特征选择。BP网络随后处理这些特征,以区分浓香、清香和中间香型。 ```
|
11天前
|
算法 调度 决策智能
基于自适应遗传算法的车间调度matlab仿真,可以任意调整工件数和机器数,输出甘特图
这是一个使用MATLAB2022a实现的自适应遗传算法解决车间调度问题的程序,能调整工件数和机器数,输出甘特图和适应度收敛曲线。程序通过编码初始化、适应度函数、遗传操作(选择、交叉、变异)及自适应机制进行优化,目标如最小化完工时间。算法在迭代过程中动态调整参数,以提升搜索效率和全局优化。