阈值化 OTSU算法 cvAdaptiveThreshold 函数

来源:互联网 发布:阿里云app服务器价格表 编辑:程序博客网 时间:2024/05/19 09:15
/*
 *比较opencv自适应阈值与OTSU自适应阈值的效果
 *OTSU有很好的效果,但是加入光照调节之后效果不好,所以取消
 *cvAdaptiveThreshold 主要功能是边缘提取,也能自适应二值化(但block_size要设置非常大,并且二值化效果依赖于block_size值)
 *cvThreshold 本身能实现OSTU算法, 参考: cvThreshold(src, dst, 0, max_val , CV_THRESH_OTSU) 
 *Date: 2012/3/20 

 *Author: Rocky Chen

 * 参考: http://www.opencv.org.cn/forum/viewtopic.php?t=3355

 */
#include "stdio.h"
#include "stdafx.h"
#include "highgui.h"
#include <cv.h>
#include <cxcore.h>
#include "iostream"
#include "math.h"
#include <ctype.h>


using namespace std;
using namespace cv;
 
void cvOtsu(IplImage *src, int *thresholdValue, int *thresholdValue_deltaT);   //大津发求阈值,deltaT为光照修正后的阈值


int main(int argc, char** argv)  
{  
//窗口,图像文件初始化
const char* file = "D:\\C++ Projects\\OpenCV_project\\test_img\\right14.jpg";
// const char* file = "D:\\C++ Projects\\OpenCV_project\\test_video\\02.jpg";
const char* woriginal = "original image";
const char* wnormal = "normal threshold"; 
const char* wotsu = "otsu";
const char* wotsud = "otsu light revise";
const char* wadaptm = "adapt threshold mean";  //均值方法 自适应阈值
const char* wadaptg = "adapt threshold gaussian"; //高斯方法 自适应阈值


cvNamedWindow(woriginal);
cvNamedWindow(wnormal);
cvNamedWindow(wotsu);
cvNamedWindow(wotsud);
cvNamedWindow(wadaptm);
cvNamedWindow(wadaptg);


IplImage* src = cvLoadImage(file); //源图像为三通道.
assert(src != NULL);
IplImage* dst = cvCreateImage(cvSize(src->width/2, src->height/2), src->depth, src->nChannels);
cvResize(src, dst);
assert( dst != NULL);
IplImage* imgsrc = cvCreateImage(cvGetSize(dst), 8,1);
cvCvtColor(dst, imgsrc, CV_BGR2GRAY);  //xstring文件 Line1070, return (*this) 报错时, 注意检查图像格式是否符合函数要求
assert(imgsrc != NULL);
IplImage* imgnor = cvCloneImage(imgsrc);
assert(imgnor != NULL);
IplImage* imgotsu = cvCloneImage(imgsrc);
assert(imgotsu != NULL);
IplImage* imgotsud = cvCloneImage(imgsrc);
assert(imgotsud != NULL);
IplImage* imgadaptm = cvCreateImage(cvGetSize(imgsrc), imgsrc->depth, imgsrc->nChannels);
assert(imgadaptm != NULL);
IplImage* imgadaptg = cvCloneImage(imgsrc);
assert(imgadaptg != NULL);


int otsu_threshold = 0;
int otsu_threshold_del =0;  //光照调节阈值,效果很不好
double normal_threshold = 150; 
double max_val = 255; //阈值化后的最大值
   cvOtsu(imgsrc, &otsu_threshold, &otsu_threshold_del); //计算出otsu的阈值

//二进制阈值化
cvThreshold(imgsrc, imgnor, normal_threshold, max_val , CV_THRESH_BINARY); //普通阈值化
// cvThreshold(imgsrc, imgnor, 0, max_val , CV_THRESH_OTSU); //调用OTSU算法的参数设置***
cvThreshold(imgsrc, imgotsu, otsu_threshold, max_val , CV_THRESH_BINARY);  //otsu 自适应
cvAdaptiveThreshold(imgsrc, imgadaptm, max_val,0,0,31,5); //自适应均值方法阈值化
cvAdaptiveThreshold(imgsrc, imgadaptg, max_val, 1,0,21,5); //自适应高斯方法阈值化

//对自适应阈值后的图像做闭运算
// cvMorphologyEx(imgotsu,imgotsud,imgadaptg, NULL,CV_MOP_CLOSE);
//    cvErode(imgadaptm,imgotsud);  //腐蚀明亮区域
//    cvDilate(imgadaptm,imgotsud);  //膨胀明亮区域
cout << "test message!\n";  //可以输出到屏幕


cvShowImage(woriginal, imgsrc);
cvShowImage(wnormal, imgnor);
cvShowImage(wotsu, imgotsu);
cvShowImage(wotsud, imgotsud);
cvShowImage(wadaptm, imgadaptm);
cvShowImage(wadaptg, imgadaptg);
cvWaitKey();


cvReleaseImage(&imgsrc);
    cvDestroyAllWindows();  
    
return 0;  
}  


/* OTSU大津法
 * thresholdValue 为使类间方差最大的阈值
 *thresholdValue_deltaT 为 thresholdValue 加上光照修正
 */
void cvOtsu(IplImage *src, int *thresholdValue, int *thresholdValue_deltaT)   
{  
    int deltaT = 0; //光照调节参数
uchar grayflag =1;
IplImage* gray = NULL;
if(src->nChannels != 1) //检查源图像是否为灰度图像
{
gray = cvCreateImage(cvGetSize(src), 8, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);
grayflag = 0;
}
else gray = src;
uchar* ImgData=(uchar*)(gray->imageData);   
int thresholdValue_temp = 1;
    int ihist[256];   //图像直方图,256个点  
   
    int i, imgsize; //循环变量,图像尺寸
    int n, n1, n2;  //n 非零像素个数, n1 前景像素个数, n2 背景像素个数
    double m1, m2, sum, csum, fmax, sb;//m1前景灰度均值,m2背景灰度均值
    //对直方图置零   
    memset(ihist, 0, sizeof(ihist));   
    //生成直方图  
    imgsize = (gray->widthStep)*(gray->height);//图像数据总数 
    for (i=0; i<imgsize;i++)   
    {   
    ihist[((int)(*ImgData))&255]++;//灰度统计 '&255'防止指针溢出  
    ImgData++;//像素遍历
    }   
    // set up everything   
    sum=csum=0.0;   
    n=0;   
    for (i=0; i<255; i++)   
    {   
    sum+=(double)i*(double)ihist[i];  // x*f(x)质量矩   
    n+= ihist[i];   //f(x)质量 像素总数
    }


deltaT = (int)(sum/imgsize); //像素平均灰度
deltaT = deltaT>>1; //与之矫正,delatT = v*n; v=0.5
   
    if (!n)   
    {//图像全黑,输出警告
    fprintf (stderr, "NOT NORMAL thresholdValue=160\n");   
    }   
    // OTSU算法
    fmax=-1.0;   
    n1=0;   
    for (i=0; i<255; i++)   
    {   
        n1+= ihist[i];   
        if (n1==0) {continue;}
        n2=n-n1;   
        if (n2==0) {break;}   
        csum += (double)i *ihist[i];   
        m1=csum/n1;   
        m2=(sum-csum)/n2;   
        sb=(double)n1*(double)n2*(m1-m2)*(m1-m2); //计算类间方差,  公式已简化  
        if (sb>fmax)   
        {   
            fmax=sb;   
            thresholdValue_temp=i;  //找到使类间方差最大的灰度值i   
        }  
    }   
   
*thresholdValue = thresholdValue_temp;
*thresholdValue_deltaT = thresholdValue_temp + deltaT;


if(!grayflag) cvReleaseImage(&gray);
}