from:http://blog.csdn.net/onezeros/article/details/6136770

otsu算法选择使类间方差最大的灰度值为阈值,具有很好的效果
算法具体描述见otsu论文,或冈萨雷斯著名的数字图像处理那本书
这里给出程序流程:
1、计算直方图并归一化histogram
2、计算图像灰度均值avgValue.
3、计算直方图的零阶w[i]和一级矩u[i]
4、计算并找到最大的类间方差(between-class variance)
variance[i]=(avgValue*w[i]-u[i])*(avgValue*w[i]-u[i])/(w[i]*(1-w[i]))
对应此最大方差的灰度值即为要找的阈值
5、用找到的阈值二值化图像

我在代码中做了一些优化,所以算法描述的某些地方跟程序并不一致

otsu代码,先找阈值,继而二值化

[cpp] view plaincopy
  1. // implementation of otsu algorithm
  2. // author: onezeros(@yahoo.cn)
  3. // reference: Rafael C. Gonzalez. Digital Image Processing Using MATLAB
  4. void cvThresholdOtsu(IplImage* src, IplImage* dst)
  5. {
  6. int height=src->height;
  7. int width=src->width;
  8. //histogram
  9. float histogram[256]={0};
  10. for(int i=0;i<height;i++) {
  11. unsigned char* p=(unsigned char*)src->imageData+src->widthStep*i;
  12. for(int j=0;j<width;j++) {
  13. histogram[*p++]++;
  14. }
  15. }
  16. //normalize histogram
  17. int size=height*width;
  18. for(int i=0;i<256;i++) {
  19. histogram[i]=histogram[i]/size;
  20. }
  21. //average pixel value
  22. float avgValue=0;
  23. for(int i=0;i<256;i++) {
  24. avgValue+=i*histogram[i];
  25. }
  26. int threshold;
  27. float maxVariance=0;
  28. float w=0,u=0;
  29. for(int i=0;i<256;i++) {
  30. w+=histogram[i];
  31. u+=i*histogram[i];
  32. float t=avgValue*w-u;
  33. float variance=t*t/(w*(1-w));
  34. if(variance>maxVariance) {
  35. maxVariance=variance;
  36. threshold=i;
  37. }
  38. }
  39. cvThreshold(src,dst,threshold,255,CV_THRESH_BINARY);
  40. }

更多情况下我们并不需要对每一帧都是用otsu寻找阈值,于是可以先找到阈值,然后用找到的阈值处理后面的图像。下面这个函数重载了上面的,返回值就是阈值。只做了一点改变

[cpp] view plaincopy
  1. // implementation of otsu algorithm
  2. // author: onezeros(@yahoo.cn)
  3. // reference: Rafael C. Gonzalez. Digital Image Processing Using MATLAB
  4. int cvThresholdOtsu(IplImage* src)
  5. {
  6. int height=src->height;
  7. int width=src->width;
  8. //histogram
  9. float histogram[256]={0};
  10. for(int i=0;i<height;i++) {
  11. unsigned char* p=(unsigned char*)src->imageData+src->widthStep*i;
  12. for(int j=0;j<width;j++) {
  13. histogram[*p++]++;
  14. }
  15. }
  16. //normalize histogram
  17. int size=height*width;
  18. for(int i=0;i<256;i++) {
  19. histogram[i]=histogram[i]/size;
  20. }
  21. //average pixel value
  22. float avgValue=0;
  23. for(int i=0;i<256;i++) {
  24. avgValue+=i*histogram[i];
  25. }
  26. int threshold;
  27. float maxVariance=0;
  28. float w=0,u=0;
  29. for(int i=0;i<256;i++) {
  30. w+=histogram[i];
  31. u+=i*histogram[i];
  32. float t=avgValue*w-u;
  33. float variance=t*t/(w*(1-w));
  34. if(variance>maxVariance) {
  35. maxVariance=variance;
  36. threshold=i;
  37. }
  38. }
  39. return threshold;
  40. }

我在手的自动检测中使用这个方法,效果很好。

下面是使用上述两个函数的简单的主程序,可以试运行一下,如果处理视频,要保证第一帧时,手要在图像中。

[cpp] view plaincopy
  1. #include <cv.h>
  2. #include <cxcore.h>
  3. #include <highgui.h>
  4. #pragma comment(lib,"cv210d.lib")
  5. #pragma comment(lib,"cxcore210d.lib")
  6. #pragma comment(lib,"highgui210d.lib")
  7. #include <iostream>
  8. using namespace std;
  9. int main(int argc, char** argv)
  10. {
  11. #ifdef VIDEO //video process
  12. CvCapture* capture=cvCreateCameraCapture(-1);
  13. if (!capture){
  14. cout<<"failed to open camera"<<endl;
  15. exit(0);
  16. }
  17. int threshold=-1;
  18. IplImage* img;
  19. while (img=cvQueryFrame(capture)){
  20. cvShowImage("video",img);
  21. cvCvtColor(img,img,CV_RGB2YCrCb);
  22. IplImage* imgCb=cvCreateImage(cvGetSize(img),8,1);
  23. cvSplit(img,NULL,NULL,imgCb,NULL);
  24. if (threshold<0){
  25. threshold=cvThresholdOtsu(imgCb);
  26. }
  27. //cvThresholdOtsu(imgCb,imgCb);
  28. cvThreshold(imgCb,imgCb,threshold,255,CV_THRESH_BINARY);
  29. cvErode(imgCb,imgCb);
  30. cvDilate(imgCb,imgCb);
  31. cvShowImage("object",imgCb);
  32. cvReleaseImage(&imgCb);
  33. if (cvWaitKey(3)==27){//esc
  34. break;
  35. }
  36. }
  37. cvReleaseCapture(&capture);
  38. #else //single image process
  39. const char* filename=(argc>=2?argv[1]:"cr.jpg");
  40. IplImage* img=cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE);
  41. cvThresholdOtsu(img,img);
  42. cvShowImage( "src", img );
  43. char buf[256];
  44. sprintf_s(buf,256,"%s.otsu.jpg",filename);
  45. cvSaveImage(buf,img);
  46. cvErode(img,img);
  47. cvDilate(img,img);
  48. cvShowImage( "dst", img );
  49. sprintf_s(buf,256,"%s.otsu.processed.jpg",filename);
  50. cvSaveImage(buf,img);
  51. cvWaitKey(0);
  52. #endif
  53. return 0;
  54. }

效果图:

1、肤色cb分量

2、otsu自适应阈值分割效果

3、开运算后效果

otsu自适应阈值分割的算法描述和opencv实现,及其在肤色检测中的应用相关推荐

  1. 【千律】OpenCV基础:图像阈值分割 -- 自适应阈值分割 -- 代码实现

    环境:Python3.8 和 OpenCV 内容:自适应阈值分割代码实现 import cv2 as cv import numpy as np import matplotlib.pyplot as ...

  2. 自适应阈值分割—大津法(OTSU算法)C++实现

    转自:https://blog.csdn.net/dcrmg/article/details/52216622 大津法是一种图像灰度自适应的阈值分割算法,是1979年由日本学者大津提出,并由他的名字命 ...

  3. 图像二值化方法及适用场景分析(OTSU Trangle 自适应阈值分割)

    图像二值化 应用场景 二值图像定义 阈值获取的方法 手动阈值法 自动阈值法 灰度均值法 基于直方图均值法 OTSU Triangle 自适应均值阈值分割方法 总结 参考文献 应用场景 二值图像处理与分 ...

  4. 在OpenCV里实现自适应阈值分割

    在图片处理过程中,针对铺前进行二值化等操作的时候,我们希望能够将图片相应区域内所有的信息提供保留.实验室环境下,相应的素材是模板化的,但是将实验室方法应用于现实环境中时,我们会发现光影环境对于效果的影 ...

  5. OpenCV自适应阈值分割函数:adaptiveThreshold()介绍

    OpenCV自适应阈值分割函数:adaptiveThreshold()介绍 [注意] 1. adaptiveThreshold()函数的ThresholdTypes参数只能设置为"CV_TH ...

  6. python+openCV 自适应阈值分割

    当图像各局部亮度不同时,全局阈值就很难将背景和目标分割.因此提出了自适应阈值,即在图像的不同区域采用不同的阈值进行分割.利用函数cv2.adaptiveThreshold()进行分割. cv2.ada ...

  7. 自适应阈值分割(最大类间分割法 + OTSU)

    1.定义 最大类间方差法是由日本学者大津(Nobuyuki Otsu)于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU.它是按图像的灰度特性,将图像分成背景和目标两部分,或者 ...

  8. 局部自适应阈值分割方法

    github地址:https://github.com/radishgiant/ThresholdAndSegment.git Local_Yanowitz 由于光照的影响,图像的灰度可能是不均匀分布 ...

  9. 【opencv-python】大津法(Otsu)阈值分割原理深入分析

    大津法(Otsu)是图像处理领域里面较为重要的阈值分割方法,适用于处理双峰图像.但大多数开发人员并不熟悉其原理,因此有必要对其进行详细说明与分析. opnecv的实例代码链接为: opencv-pyt ...

最新文章

  1. 深度学习作弊,用单个参数 fit 任何数据集,这篇 19 年的论文重新「火」了
  2. oracle数据库link格式,Oracle创设DB Link
  3. 看了这个总结,其实 Matplotlib 可视化,也没那么难!
  4. websocket网络层详解_【技术分享】WebSocket漏洞与防护详解
  5. Hive 基础及安装
  6. 【并行计算-CUDA开发】GPGPU OpenCL/CUDA 高性能编程的10大注意事项
  7. Vs2017添加引用时报错未能正确加载“ReferenceManagerPackage”包。
  8. java五子棋难度_简单五子棋JAVA
  9. 货效,坪效,人效,这三效怎么理解?
  10. 洛阳九县八取名字_洛阳市地图(洛阳市九县六区地图)
  11. 码分复用:为什么可以通过收到的码片序列与站的序列做内积的值判断每个站是否发送数据及数据值
  12. 加州大学4.8万人大罢工!博士竟卖血为生,多校濒临崩溃!
  13. 浙江大学-翁凯 C语言进阶,编程题
  14. 入网模组 |合宙Air 202 GPRS模组接入机智云(硬件开发平台)
  15. CF1548D2 Gregor and the Odd Cows (Hard)
  16. supper和this关键字的区别
  17. 线性代数--1.2 行化简与阶梯形矩阵
  18. 根据cron表达式获取下次执行时间
  19. 下一个7年,保持期待、持续思考,酷雷曼继续向前!
  20. 【ECCV2022】OSFormer: One-Stage Camouflaged Instance Segmentation with Transformers

热门文章

  1. java linux文件夹大小_Linux_Linux文件夹大小查看办法, 1. du -sh查看当前文件 - phpStudy...
  2. 既然安卓是开源的 为何华为要 鸿蒙,安卓靠开源、免费赢得了天下,现在华为鸿蒙也是开源免费的! - 区块网...
  3. leetcode算法题--1~n整数中1出现的次数
  4. leetcode算法题--二叉树的最近公共祖先
  5. oracle怎么小数中多余的零,关于小数中0的处理
  6. access实验报告体会_Access实验报告 - 图文
  7. springboot2 war页面放在那_Spring Boot2 系列教程(三十三)整合 Spring Security
  8. 微服务网关Kong 1.0正式发布!提供100+项功能
  9. Canvas、Paint、Path
  10. 一步一步教你实现简单的自定义错误跟踪