生活随笔
收集整理的這篇文章主要介紹了
图像处理之简单脸谱检测算法
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
from: http://blog.csdn.net/jia20003/article/details/7596443
圖像處理之簡(jiǎn)單臉譜檢測(cè)算法(Simple Face Detection Algorithm)
介紹基于皮膚檢測(cè)之后的,尋找最大連通區(qū)域,完成臉譜檢測(cè)的算法。大致的算法步驟如下:
原圖如下:
每步處理以后的效果:
程序運(yùn)行,加載選擇圖像以后的截屏如下:
截屏中顯示圖片,是適當(dāng)放縮以后,代碼如下:
[java] view plaincopy
Image?scaledImage?=?rawImg.getScaledInstance(200,?200,?Image.SCALE_FAST);???g2.drawImage(scaledImage,?0,?0,?200,?200,?null);??
第一步:圖像預(yù)處理,預(yù)處理的目的是為了減少圖像中干擾像素,使得皮膚檢測(cè)步驟可以得
到更好的效果,最常見(jiàn)的手段是調(diào)節(jié)對(duì)比度與亮度,也可以高斯模糊。關(guān)于怎么調(diào)節(jié)亮度與
對(duì)比度可以參見(jiàn)這里:http://blog.csdn.net/jia20003/article/details/7385160
這里調(diào)節(jié)對(duì)比度的算法很簡(jiǎn)單,源代碼如下:
[java] view plaincopy
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;??????????????????????????????????????????????????????tr?=?adjustContrast(tr,?contrast);??????????????????tg?=?adjustContrast(tg,?contrast);??????????????????tb?=?adjustContrast(tb,?contrast);??????????????????????????????????????????????????????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;??????}????}??
注意:第一步不是必須的,如果圖像質(zhì)量已經(jīng)很好,可以直接跳過(guò)。
第二步:皮膚檢測(cè),采用的是基于RGB色彩空間的統(tǒng)計(jì)結(jié)果來(lái)判斷一個(gè)像素是否為skin像
素,如果是皮膚像素,則設(shè)置像素為黑色,否則為白色。給出基于RGB色彩空間的五種皮
膚檢測(cè)統(tǒng)計(jì)方法,最喜歡的一種源代碼如下:
[java] view plaincopy
package?com.gloomyfish.face.detection;????import?java.awt.image.BufferedImage;?????????????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;??????????????????????????????????????????????????????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;??????}??}??
第三步:尋找最大連通區(qū)域
使用連通組件標(biāo)記算法,尋找最大連通區(qū)域,關(guān)于什么是連通組件標(biāo)記算法,可以參見(jiàn)這里
http://blog.csdn.net/jia20003/article/details/7483249,里面提到的連通組件算法效率不高,所
以這里我完成了一個(gè)更具效率的版本,主要思想是對(duì)像素?cái)?shù)據(jù)進(jìn)行八鄰域?qū)ふ疫B通,然后合
并標(biāo)記。源代碼如下:
[java] view plaincopy
package?com.gloomyfish.face.detection;????import?java.util.Arrays;??import?java.util.HashMap;????????????public?class?FastConnectedComponentLabelAlg?{??????private?int?bgColor;??????private?int[]?labels;??????private?int[]?outData;??????private?int?dw;??????private?int?dh;????????????public?FastConnectedComponentLabelAlg()?{??????????bgColor?=?255;???????}????????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;??????????}??????????????????????????????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;???????????????????}?else?{????????????????????????????????????????????fourNeighborhoodPixels[0]?=?getPixel(inPixels,?row-1,?col);???????????????????????fourNeighborhoodPixels[1]?=?getPixel(inPixels,?row,?col-1);???????????????????????fourNeighborhoodPixels[2]?=?getPixel(inPixels,?row+1,?col);???????????????????????fourNeighborhoodPixels[3]?=?getPixel(inPixels,?row,?col+1);???????????????????????????????????????????????????????????????????fourNeighborhoodPixels[4]?=?getPixel(inPixels,?row-1,?col-1);???????????????????????fourNeighborhoodPixels[5]?=?getPixel(inPixels,?row-1,?col+1);???????????????????????fourNeighborhoodPixels[6]?=?getPixel(inPixels,?row+1,?col-1);???????????????????????fourNeighborhoodPixels[7]?=?getPixel(inPixels,?row+1,?col+1);???????????????????????????????????????????????????????????????????fourNeighborhoodLabels[0]?=?getLabel(outData,?row-1,?col);???????????????????????fourNeighborhoodLabels[1]?=?getLabel(outData,?row,?col-1);???????????????????????fourNeighborhoodLabels[2]?=?getLabel(outData,?row+1,?col);???????????????????????fourNeighborhoodLabels[3]?=?getLabel(outData,?row,?col+1);???????????????????????????????????????????????????????????????????fourNeighborhoodLabels[4]?=?getLabel(outData,?row-1,?col-1);???????????????????????fourNeighborhoodLabels[5]?=?getLabel(outData,?row-1,?col+1);???????????????????????fourNeighborhoodLabels[6]?=?getLabel(outData,?row+1,?col-1);???????????????????????fourNeighborhoodLabels[7]?=?getLabel(outData,?row+1,?col+1);?????????????????????????????????????????????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;??????????????????????????????????}??????????????????????????????}??????????????????????????????????????????????????????????????????????????????????????????if(needMerge)?{??????????????????????????????????int?minLabel?=?knownLabels[0];??????????????????????????????????for(int?m=0;?m<knownLabels.length;?m++)?{??????????????????????????????????????if(minLabel?>?knownLabels[m]?&&?knownLabels[m]?!=?0)?{??????????????????????????????????????????minLabel?=?knownLabels[m];??????????????????????????????????????}??????????????????????????????????}??????????????????????????????????????????????????????????????????????????????????????????????????????result?=?(minLabel?==?0)???result?:?minLabel;??????????????????????????????????????????????????????????????????????????????????????????????????????????????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;???????????????}??????????}??????????????????????????????for(int?row?=?0;?row?<?height;?row?++)?{??????????????for(int?col?=?0;?col?<?width;?col++)?{??????????????????index?=?row?*?width?+?col;??????????????????mergeLabels(index);??????????????}??????????}??????????????????????????????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);??????????????????}??????????????}??????????}??????????????????????????????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;??????????????????????????????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;??????????????????????????????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)?{????????????????????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)?{????????????????????if(row?<?0?||?row?>=?dh)?{??????????????return?bgColor;??????????}????????????????????if(col?<?0?||?col?>=?dw)?{??????????????return?bgColor;??????????}????????????????????int?index?=?row?*?dw?+?col;??????????return?(data[index]?&?0x000000ff);??????}?????????????????????????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();??????????}??????}????}??
找到最大連通區(qū)域以后,對(duì)最大連通區(qū)域數(shù)據(jù)進(jìn)行掃描,找出最小點(diǎn),即矩形區(qū)域左上角坐
標(biāo),找出最大點(diǎn),即矩形區(qū)域右下角坐標(biāo)。知道這四個(gè)點(diǎn)坐標(biāo)以后,在原圖上打上紅色矩形
框,標(biāo)記出臉譜位置。尋找四個(gè)點(diǎn)坐標(biāo)的實(shí)現(xiàn)代碼如下:
[java] view plaincopy
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)?{???????????????????if(minY?>?row)?{??????????????????????minY?=?row;??????????????????}????????????????????????????????????if(minX?>?col)?{??????????????????????minX?=?col;??????????????????}????????????????????????????????????if(maxY?<?row)?{??????????????????????maxY?=?row;??????????????????}????????????????????????????????????if(maxX?<?col)?{??????????????????????maxX?=?col;??????????????????}?????????????????}??????????}?????????}??}??
缺點(diǎn):
此算法不支持多臉譜檢測(cè),不支持裸體中的臉譜檢測(cè),但是根據(jù)人臉的
生物學(xué)特征可以進(jìn)一步細(xì)化分析,支持裸體人臉檢測(cè)。
寫(xiě)本文章的目的:本例為圖像處理綜合運(yùn)行的一個(gè)簡(jiǎn)單實(shí)例。同時(shí)人臉檢
測(cè)也是個(gè)感興趣的話題。
總結(jié)
以上是生活随笔為你收集整理的图像处理之简单脸谱检测算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。