opencv-最小二乘法拟合光斑最大轮廓

预处理中,使用了OTSU阈值分割,opencv中cvThreshold函数可以一步实现,但发现效果不理想,改用了先前自己实现的的办法。轮廓只保留面积最大的,然后找出圆心并在图上标注。

代码:

#include "cv.h"
#include "highgui.h"
#include <iostream>
using namespace std;
int otsu(IplImage* src)  
{
    CvScalar cs = cvSum(src);  
    
    int hist_size = 256;    //直方图尺寸    
    int hist_height = 256;    
    float range[] = {0,255};  //灰度级的范围    
    float* ranges[]={range};    
    //创建一维直方图,统计图像在[0 255]像素的均匀分布    
    CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);    
    //计算灰度图像的一维直方图    
    cvCalcHist(&src,gray_hist,0,0);    
    //归一化直方图    
    cvNormalizeHist(gray_hist,1.0);    
      
    int Width = src->width;  
    int Height = src->height;  
    double U_t = cs.val[0]/Width*Height;                  //图像的平均灰度  
    int threshold = 0;  
    double delta = 0;  
    for(int k=0; k<256; k++)  
    {  
        double u0 = 0;  
        double u1 = 0;  
        double w0 = 0;  
        double w1 = 0;  
        double delta_tmp = 0;  
        for(int i=0; i<=k; i++)  
        {  
            u0 += cvQueryHistValue_1D(gray_hist,i)*i;   //灰度小于阈值k的像素的平均灰度值  
            w0 += cvQueryHistValue_1D(gray_hist,i);     //灰度小于阈值k的像素的概率  
        }  
        for(int j=k+1; j<256; j++)  
        {  
            u1 += cvQueryHistValue_1D(gray_hist,j)*j;   //灰度大于阈值k的像素的平均灰度值  
            w1 += cvQueryHistValue_1D(gray_hist,j);     //灰度小于阈值k的像素的概率  
        }  
        delta_tmp = w0*pow(u0-U_t,2) + w1*pow(u1-U_t,2);    //类间方差  
        if(delta_tmp > delta)  
        {  
            delta = delta_tmp;  
            threshold = k;  
        }  
    }  
    return threshold;  
}  
CvPoint process_image(IplImage* srcImage)
{
    CvMemStorage* stor;
    CvSeq* cont;
    CvPoint center;
    CvBox2D32f* box;
    CvPoint* PointArray;
    CvPoint2D32f* PointArray2D32f;

    IplImage* copy1 = cvCloneImage(srcImage);
    
    // 创建动态结构序列
    stor = cvCreateMemStorage(0);
    cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);
    
    // 二值话图像.
    cvThreshold( srcImage, copy1, otsu(srcImage), 255, CV_THRESH_BINARY );
    
    // 寻找所有轮廓.
    cvFindContours( copy1, stor, &cont, sizeof(CvContour), 
                    CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
    
    // Clear images. IPL use.
    cvZero(copy1);
    double maxarea = 0;
	CvSeq* pMax;

	//寻找面积最大轮廓
	for(;cont;cont=cont->h_next)
	{
		double tmparea=fabs(cvContourArea(cont));
		if(tmparea > maxarea)
		{
			pMax = cont;
			maxarea = tmparea;
		}
	}

    int i;   // Indicator of cycle
    int count = pMax->total;
    CvSize size;
        
    // Number point must be more than or equal to 6 (for cvFitEllipse_32f).        
    if( count >= 6 )
    {
        // Alloc memory for contour point set.    
        PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
        PointArray2D32f= (CvPoint2D32f*)malloc( count*sizeof(CvPoint2D32f) );
        
        // Alloc memory for ellipse data.
        box = (CvBox2D32f*)malloc(sizeof(CvBox2D32f));
        
        // Get contour point set.
        cvCvtSeqToArray(pMax, PointArray, CV_WHOLE_SEQ);
        
        // Convert CvPoint set to CvBox2D32f set.
        for(i=0; i<count; i++)
        {
            PointArray2D32f[i].x = (float)PointArray[i].x;
            PointArray2D32f[i].y = (float)PointArray[i].y;
        }

        //拟合当前轮廓.
        cvFitEllipse(PointArray2D32f, count, box);
        
        // 绘制当前轮廓.
        cvDrawContours(srcImage,pMax,CV_RGB(255,255,255),
        CV_RGB(255,255,255),0,1,8,cvPoint(0,0));
        
        // Convert ellipse data from float to integer representation.
        center.x = cvRound(box->center.x);
        center.y = cvRound(box->center.y);
        size.width = cvRound(box->size.width*0.5);
        size.height = cvRound(box->size.height*0.5);
        box->angle = -box->angle;
		// Draw ellipse.
        cvEllipse(srcImage, center, size, box->angle, 0, 360, CV_RGB(0,0,255), 1, CV_AA, 0);
	}
			
            
    // Free memory.          
    free(PointArray);
    free(PointArray2D32f);
    free(box);
   
    cvReleaseImage(&copy1);
    return center;
}
int main()
{
	CvPoint pt1,pt2,center;
	IplImage* src = cvLoadImage("E:\\激光\\228.bmp", 1);
	IplImage* srcCopy = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);


    /* 转换成单通道 */
    cvCvtColor(src, srcCopy, CV_RGB2GRAY);

	IplImage* thresholdImage = cvCreateImage(cvGetSize(srcCopy),srcCopy->depth,srcCopy->nChannels);  
    cvThreshold(srcCopy,thresholdImage,otsu(srcCopy),255,CV_THRESH_TOZERO); 
	//cvThreshold(srcCopy, thresholdImage, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    // Create windows.
	cvNamedWindow("Threshold", 1);
    cvNamedWindow("Source", 1);
    cvNamedWindow("Result", 1);
    // Show the image.
    cvShowImage("Source", srcCopy);
	cvShowImage("Threshold", thresholdImage);
    //最小二乘法椭圆拟合
    center = process_image(thresholdImage);

	cout<<center.x<<endl;
	cout<<center.y<<endl;

	//画彩色点需要转化为RGB
	IplImage* thresholdImageCopy = cvCreateImage(cvGetSize(thresholdImage), IPL_DEPTH_8U, 3);
	cvCvtColor(thresholdImage, thresholdImageCopy, CV_GRAY2BGR);

	pt1.x=center.x-1;pt1.y=center.y; 
	pt2.x=center.x+1;pt2.y=center.y; 
	cvLine(thresholdImageCopy, pt1, pt2, CV_RGB(255,0,0), 2, CV_AA, 0 ); 
	pt1.x=center.x;pt1.y=center.y-1; 
	pt2.x=center.x;pt2.y=center.y+1; 
	cvLine(thresholdImageCopy, pt1, pt2, CV_RGB(255,0,0), 2, CV_AA, 0 );

	cvShowImage("Result", thresholdImageCopy);
    // Wait for a key stroke; the same function arranges events processing                
    cvWaitKey(0);
	cvReleaseImage(&src);
    cvReleaseImage(&thresholdImage);
	cvReleaseImage(&thresholdImageCopy);
	cvReleaseImage(&srcCopy);
    cvDestroyWindow("Source");
    cvDestroyWindow("Result");
	cvDestroyWindow("Threshold");
    return 0;
}


你可能感兴趣的:(opencv-最小二乘法拟合光斑最大轮廓)