生活随笔
收集整理的這篇文章主要介紹了
otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
from:http://blog.csdn.net/onezeros/article/details/6136770
?otsu算法選擇使類間方差最大的灰度值為閾值,具有很好的效果
算法具體描述見(jiàn)otsu論文,或?qū)_雷斯著名的數(shù)字圖像處理那本書
這里給出程序流程:
1、計(jì)算直方圖并歸一化histogram
2、計(jì)算圖像灰度均值avgValue.
3、計(jì)算直方圖的零階w[i]和一級(jí)矩u[i]
4、計(jì)算并找到最大的類間方差(between-class variance)
variance[i]=(avgValue*w[i]-u[i])*(avgValue*w[i]-u[i])/(w[i]*(1-w[i]))
對(duì)應(yīng)此最大方差的灰度值即為要找的閾值
5、用找到的閾值二值化圖像
我在代碼中做了一些優(yōu)化,所以算法描述的某些地方跟程序并不一致
?
otsu代碼,先找閾值,繼而二值化
[cpp]?view plaincopy
?? ?? ?? void?cvThresholdOtsu(IplImage*?src,?IplImage*?dst)?? {?? ????int?height=src->height;?? ????int?width=src->width;?????? ?????? ?????? ????float?histogram[256]={0};?? ????for(int?i=0;i<height;i++)?{?? ????????unsigned?char*?p=(unsigned?char*)src->imageData+src->widthStep*i;?? ????????for(int?j=0;j<width;j++)?{?? ????????????histogram[*p++]++;?? ????????}?? ????}?? ?????? ????int?size=height*width;?? ????for(int?i=0;i<256;i++)?{?? ????????histogram[i]=histogram[i]/size;?? ????}?? ?????? ?????? ????float?avgValue=0;?? ????for(int?i=0;i<256;i++)?{?? ????????avgValue+=i*histogram[i];?? ????}?? ?? ????int?threshold;???? ????float?maxVariance=0;?? ????float?w=0,u=0;?? ????for(int?i=0;i<256;i++)?{?? ????????w+=histogram[i];?? ????????u+=i*histogram[i];?? ?? ????????float?t=avgValue*w-u;?? ????????float?variance=t*t/(w*(1-w));?? ????????if(variance>maxVariance)?{?? ????????????maxVariance=variance;?? ????????????threshold=i;?? ????????}?? ????}?? ?? ????cvThreshold(src,dst,threshold,255,CV_THRESH_BINARY);?? }?? ?
更多情況下我們并不需要對(duì)每一幀都是用otsu尋找閾值,于是可以先找到閾值,然后用找到的閾值處理后面的圖像。下面這個(gè)函數(shù)重載了上面的,返回值就是閾值。只做了一點(diǎn)改變
[cpp]?view plaincopy
?? ?? ?? int?cvThresholdOtsu(IplImage*?src)?? {?? ????int?height=src->height;?? ????int?width=src->width;?????? ?? ?????? ????float?histogram[256]={0};?? ????for(int?i=0;i<height;i++)?{?? ????????unsigned?char*?p=(unsigned?char*)src->imageData+src->widthStep*i;?? ????????for(int?j=0;j<width;j++)?{?? ????????????histogram[*p++]++;?? ????????}?? ????}?? ?????? ????int?size=height*width;?? ????for(int?i=0;i<256;i++)?{?? ????????histogram[i]=histogram[i]/size;?? ????}?? ?? ?????? ????float?avgValue=0;?? ????for(int?i=0;i<256;i++)?{?? ????????avgValue+=i*histogram[i];?? ????}?? ?? ????int?threshold;???? ????float?maxVariance=0;?? ????float?w=0,u=0;?? ????for(int?i=0;i<256;i++)?{?? ????????w+=histogram[i];?? ????????u+=i*histogram[i];?? ?? ????????float?t=avgValue*w-u;?? ????????float?variance=t*t/(w*(1-w));?? ????????if(variance>maxVariance)?{?? ????????????maxVariance=variance;?? ????????????threshold=i;?? ????????}?? ????}?? ?? ????return?threshold;?? }?? ?
我在手的自動(dòng)檢測(cè)中使用這個(gè)方法,效果很好。
下面是使用上述兩個(gè)函數(shù)的簡(jiǎn)單的主程序,可以試運(yùn)行一下,如果處理視頻,要保證第一幀時(shí),手要在圖像中。
?
[cpp]?view plaincopy
#include?<cv.h>?? #include?<cxcore.h>?? #include?<highgui.h>?? #pragma?comment(lib,"cv210d.lib")?? #pragma?comment(lib,"cxcore210d.lib")?? #pragma?comment(lib,"highgui210d.lib")?? ?? #include?<iostream>?? using?namespace?std;?? ?? int?main(int?argc,?char**?argv)?? {????? #ifdef?VIDEO?//video?process?? ????CvCapture*?capture=cvCreateCameraCapture(-1);?? ????if?(!capture){?? ????????cout<<"failed?to?open?camera"<<endl;?? ????????exit(0);?? ????}?? ?? ????int?threshold=-1;?? ????IplImage*?img;???? ????while?(img=cvQueryFrame(capture)){?? ????????cvShowImage("video",img);?? ????????cvCvtColor(img,img,CV_RGB2YCrCb);?? ?? ????????IplImage*?imgCb=cvCreateImage(cvGetSize(img),8,1);?? ????????cvSplit(img,NULL,NULL,imgCb,NULL);?? ????????if?(threshold<0){?? ????????????threshold=cvThresholdOtsu(imgCb);?? ????????}?? ?????????? ????????cvThreshold(imgCb,imgCb,threshold,255,CV_THRESH_BINARY);?? ????????cvErode(imgCb,imgCb);?? ????????cvDilate(imgCb,imgCb);?? ?????????? ????????cvShowImage("object",imgCb);?? ????????cvReleaseImage(&imgCb);?? ?? ????????if?(cvWaitKey(3)==27){?? ????????????break;?? ????????}?? ????}????? ?? ????cvReleaseCapture(&capture);?? ?????? #else?//single?image?process?? ????const?char*?filename=(argc>=2?argv[1]:"cr.jpg");?? ????IplImage*?img=cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE);?? ?? ????cvThresholdOtsu(img,img);?? ????cvShowImage(?"src",?img?);?? ????char?buf[256];?? ????sprintf_s(buf,256,"%s.otsu.jpg",filename);?? ????cvSaveImage(buf,img);?? ?? ????cvErode(img,img);?? ????cvDilate(img,img);?? ????cvShowImage(?"dst",?img?);?? ????sprintf_s(buf,256,"%s.otsu.processed.jpg",filename);?? ????cvSaveImage(buf,img);?? ?? ????cvWaitKey(0);?? #endif?? ?????? ????return?0;?? }?? ?
?
效果圖:
1、膚色cb分量
?
2、otsu自適應(yīng)閾值分割效果
?
3、開運(yùn)算后效果
總結(jié)
以上是生活随笔為你收集整理的otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。