www.pudn.com > ImageProcessing.rar.rar > SegApi.cpp
#include "GlobalApi.h" #include "stdafx.h" #include "cdib.h" #include "math.h" #include#include using namespace std; /************************************************************************* * * \函数名称: * RegionSegFixThreshold() * * \输入参数: * CDib * pDib - 指向CDib类的指针,含有原始图象信息 * int nThreshold - 区域分割的阈值 * * \返回值: * 无 * * \说明: * 1(逻辑)表示对应象素为前景区域,0表示背景 * 阈值分割的关键问题在于阈值的选取。阈值的选取一般应该视实际的应用而 * 灵活设定。 * ************************************************************************* */ void RegionSegFixThreshold(CDib * pDib, int nThreshold) { //遍历图象的纵坐标 int y; //遍历图象的横坐标 int x; //图象的长宽大小 CSize sizeImage = pDib->GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); //图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); //图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 局部阈值 int nThd[2][2] ; // 子图象的平均值 int nLocAvg ; // 对左上图像逐点扫描: nLocAvg = 0 ; // y方向 for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; // 初始化 for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 初始化 memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight); // 种子点 int nSeedX, nSeedY; // 设置种子点为图像的中心 nSeedX = nWidth /5 ; nSeedY = nHeight/5 ; // 定义堆栈,存储坐标 int * pnGrowQueX ; int * pnGrowQueY ; // 分配空间 pnGrowQueX = new int [nWidth*nHeight]; pnGrowQueY = new int [nWidth*nHeight]; // 图像数据的指针 unsigned char * pUnchInput =(unsigned char * )pDib->m_lpImage; // 定义堆栈的起点和终点 // 当nStart=nEnd, 表示堆栈中只有一个点 int nStart ; int nEnd ; //初始化 nStart = 0 ; nEnd = 0 ; // 把种子点的坐标压入栈 pnGrowQueX[nEnd] = nSeedX; pnGrowQueY[nEnd] = nSeedY; // 当前正在处理的象素 int nCurrX ; int nCurrY ; // 循环控制变量 int k ; // 图象的横纵坐标,用来对当前象素的4邻域进行遍历 int xx; int yy; while (nStart<=nEnd) { // 当前种子点的坐标 nCurrX = pnGrowQueX[nStart]; nCurrY = pnGrowQueY[nStart]; // 对当前点的4邻域进行遍历 for (k=0; k<4; k++) { // 4邻域象素的坐标 xx = nCurrX+nDx[k]; yy = nCurrY+nDy[k]; // 判断象素(xx,yy) 是否在图像内部 // 判断象素(xx,yy) 是否已经处理过 // pUnRegion[yy*nWidth+xx]==0 表示还没有处理 // 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值 if ( (xx < nWidth) && (xx>=0) && (yy =0) && (pUnRegion[yy*nWidth+xx]==0) && abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX]) GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; //初始化结果数据 for(n=0; n GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; //图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); int nSaveWidth = sizeImageSave.cx; //图像数据的指针 LPBYTE pImageData = pDib->m_lpImage; double fCosTable; double fSinTable; fCosTable=0 ; fSinTable=0 ; double fTmpPxValue; double fRpartValue; double fIpartValue; fTmpPxValue=0; fRpartValue=0; fIpartValue=0; for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 图像在计算机在存储中的实际大小 CSize sizeImageSave = pDib->GetDibSaveDim(); // 图像在内存中每一行象素占用的实际空间 int nSaveWidth = sizeImageSave.cx; // 图像数据的指针 LPBYTE lpImage = pDib->m_lpImage; // 初始化 for(y=0; y GetDimensions(); int nWidth = sizeImage.cx ; int nHeight = sizeImage.cy ; // 指向梯度数据的指针 double * pdGrad; // 按照图像的大小开辟内存空间,存储梯度计算的结果 pdGrad=new double[nHeight*nWidth]; // 调用Roberts算子求梯度 RobertsOperator(pDib, pdGrad); // 定义当前象素梯度值 double dCurrGrad = 0; // 定义最大梯度值 double dMaxGrad; // 设置初值 dMaxGrad = 0; // 最大梯度值对应的象素点坐标 int nPx; int nPy; nPx = 0; nPy = 0; // 求梯度最大值所在的象素点坐标 for(y=0; y 10) { // 设置当前点为边界点 pUnEdgeTrack[nPy*nWidth + nPx] = 255 ; dMaxGrad = 0 ; for(i=0; i<8; i++) { nDetX=nDx[i]; nDetY=nDy[i]; y = nPy + nDetY; x = nPx + nDetX; // 判断是否在图像内部 if(x>=0 && x =0 && y dMaxGrad) && ( pUnEdgeTrack[y*nWidth + x] == 0) ) { dMaxGrad = pdGrad[y*nWidth + x] ; yy = y; xx = x; } } } // 下一个边界点的梯度,横纵坐标 dCurrGrad = dMaxGrad ; nPy = yy; nPx = xx; } //释放内存 delete pdGrad; pdGrad = NULL; } /************************************************************************* * * \函数名称: * MakeGauss() * * \输入参数: * double sigma - 高斯函数的标准差 * double **pdKernel - 指向高斯数据数组的指针 * int *pnWindowSize - 数据的长度 * * \返回值: * 无 * * \说明: * 这个函数可以生成一个一维的高斯函数的数字数据,理论上高斯数据的长度应 * 该是无限长的,但是为了计算的简单和速度,实际的高斯数据只能是有限长的 * pnWindowSize就是数据长度 * ************************************************************************* */ void MakeGauss(double sigma, double **pdKernel, int *pnWindowSize) { // 循环控制变量 int i ; // 数组的中心点 int nCenter; // 数组的某一点到中心点的距离 double dDis ; double PI = 3.14159; // 中间变量 double dValue; double dSum ; dSum = 0 ; // 数组长度,根据概率论的知识,选取[-3*sigma, 3*sigma]以内的数据。 // 这些数据会覆盖绝大部分的滤波系数 *pnWindowSize = 1 + 2 * ceil(3 * sigma); // 中心 nCenter = (*pnWindowSize) / 2; // 分配内存 *pdKernel = new double[*pnWindowSize] ; for(i=0; i< (*pnWindowSize); i++) { dDis = (double)(i - nCenter); dValue = exp(-(1/2)*dDis*dDis/(sigma*sigma)) / (sqrt(2 * PI) * sigma ); (*pdKernel)[i] = dValue ; dSum += dValue; } // 归一化 for(i=0; i<(*pnWindowSize) ; i++) { (*pdKernel)[i] /= dSum; } } /************************************************************************* * * \函数名称: * GaussianSmooth() * * \输入参数: * unsigned char * pUnchImg - 指向图象数据的指针 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * double dSigma - 高斯函数的标准差 * unsigned char * pUnchSmthdImg - 指向经过平滑之后的图象数据 * * \返回值: * 无 * * \说明: * 为了抑止噪声,采用高斯滤波对图象进行滤波,滤波先对x方向进行,然后对 * y方向进行。 * ************************************************************************* */ void GaussianSmooth(unsigned char *pUnchImg, int nWidth, int nHeight, double sigma, unsigned char * pUnchSmthdImg) { // 循环控制变量 int y; int x; int i; // 高斯滤波器的数组长度 int nWindowSize; // 窗口长度的1/2 int nHalfLen; // 一维高斯数据滤波器 double *pdKernel ; // 高斯系数与图象数据的点乘 double dDotMul ; // 高斯滤波系数的总和 double dWeightSum ; // 中间变量 double * pdTmp ; // 分配内存 pdTmp = new double[nWidth*nHeight]; // 产生一维高斯数据滤波器 // MakeGauss(sigma, &dKernel, &nWindowSize); MakeGauss(sigma, &pdKernel, &nWindowSize) ; // MakeGauss返回窗口的长度,利用此变量计算窗口的半长 nHalfLen = nWindowSize / 2; // x方向进行滤波 for(y=0; y = 0 && (i+x) < nWidth ) { dDotMul += (double)pUnchImg[y*nWidth + (i+x)] * pdKernel[nHalfLen+i]; dWeightSum += pdKernel[nHalfLen+i]; } } pdTmp[y*nWidth + x] = dDotMul/dWeightSum ; } } // y方向进行滤波 for(x=0; x = 0 && (i+y) < nHeight ) { dDotMul += (double)pdTmp[(y+i)*nWidth + x] * pdKernel[nHalfLen+i]; dWeightSum += pdKernel[nHalfLen+i]; } } pUnchSmthdImg[y*nWidth + x] = (unsigned char)(int)dDotMul/dWeightSum ; } } // 释放内存 delete []pdKernel; pdKernel = NULL ; delete []pdTmp; pdTmp = NULL; } /************************************************************************* * * \函数名称: * DirGrad() * * \输入参数: * unsigned char *pUnchSmthdImg - 经过高斯滤波后的图象 * int nWidht - 图象宽度 * int nHeight - 图象高度 * int *pnGradX - x方向的方向导数 * int *pnGradY - y方向的方向导数 * \返回值: * 无 * * \说明: * 这个函数计算方向倒数,采用的微分算子是(-1 0 1) 和 (-1 0 1)'(转置) * 计算的时候对边界象素采用了特殊处理 * * ************************************************************************* */ void DirGrad(unsigned char *pUnchSmthdImg, int nWidth, int nHeight, int *pnGradX , int *pnGradY) { // 循环控制变量 int y ; int x ; // 计算x方向的方向导数,在边界出进行了处理,防止要访问的象素出界 for(y=0; y abs(gx)) { // 计算插值的比例 weight = fabs(gx)/fabs(gy); g2 = pnMag[nPos-nWidth] ; g4 = pnMag[nPos+nWidth] ; // 如果x,y两个方向的方向导数的符号相同 // C是当前象素,与g1-g4的位置关系为: // g1 g2 // C // g4 g3 if (gx*gy > 0) { g1 = pnMag[nPos-nWidth-1] ; g3 = pnMag[nPos+nWidth+1] ; } // 如果x,y两个方向的方向导数的符号相反 // C是当前象素,与g1-g4的位置关系为: // g2 g1 // C // g3 g4 else { g1 = pnMag[nPos-nWidth+1] ; g3 = pnMag[nPos+nWidth-1] ; } } // 如果方向导数x分量比y分量大,说明导数的方向更加“趋向”于x分量 // 这个判断语句包含了x分量和y分量相等的情况 else { // 计算插值的比例 weight = fabs(gy)/fabs(gx); g2 = pnMag[nPos+1] ; g4 = pnMag[nPos-1] ; // 如果x,y两个方向的方向导数的符号相同 // C是当前象素,与g1-g4的位置关系为: // g3 // g4 C g2 // g1 if (gx*gy > 0) { g1 = pnMag[nPos+nWidth+1] ; g3 = pnMag[nPos-nWidth-1] ; } // 如果x,y两个方向的方向导数的符号相反 // C是当前象素,与g1-g4的位置关系为: // g1 // g4 C g2 // g3 else { g1 = pnMag[nPos-nWidth+1] ; g3 = pnMag[nPos+nWidth-1] ; } } // 下面利用g1-g4对梯度进行插值 { dTmp1 = weight*g1 + (1-weight)*g2 ; dTmp2 = weight*g3 + (1-weight)*g4 ; // 当前象素的梯度是局部的最大值 // 该点可能是个边界点 if(dTmp>=dTmp1 && dTmp>=dTmp2) { pUnchRst[nPos] = 128 ; } else { // 不可能是边界点 pUnchRst[nPos] = 0 ; } } } //else } // for } } /************************************************************************* * * \函数名称: * TraceEdge() * * \输入参数: * int x - 跟踪起点的x坐标 * int y - 跟踪起点的y坐标 * int nLowThd - 判断一个点是否为边界点的低阈值 * unsigned char *pUnchEdge - 记录边界点的缓冲区 * int *pnMag - 梯度幅度图 * int nWidth - 图象数据宽度 * * \返回值: * 无 * * \说明: * 递归调用 * 从(x,y)坐标出发,进行边界点的跟踪,跟踪只考虑pUnchEdge中没有处理并且 * 可能是边界点的那些象素(=128),象素值为0表明该点不可能是边界点,象素值 * 为255表明该点已经被设置为边界点,不必再考虑 * * ************************************************************************* */ void TraceEdge (int y, int x, int nLowThd, unsigned char *pUnchEdge, int *pnMag, int nWidth) { // 对8邻域象素进行查询 int xNb[8] = {1, 1, 0,-1,-1,-1, 0, 1} ; int yNb[8] = {0, 1, 1, 1,0 ,-1,-1,-1} ; int yy ; int xx ; int k ; for(k=0; k<8; k++) { yy = y + yNb[k] ; xx = x + xNb[k] ; // 如果该象素为可能的边界点,又没有处理过 // 并且梯度大于阈值 if(pUnchEdge[yy*nWidth+xx] == 128 && pnMag[yy*nWidth+xx]>=nLowThd) { // 把该点设置成为边界点 pUnchEdge[yy*nWidth+xx] = 255 ; // 以该点为中心进行跟踪 TraceEdge(yy, xx, nLowThd, pUnchEdge, pnMag, nWidth); } } } /************************************************************************* * * \函数名称: * EstimateThreshold() * * \输入参数: * int *pnMag - 梯度幅度图 * int nWidth - 图象数据宽度 * int nHeight - 图象数据高度 * int *pnThdHigh - 高阈值 * int *pnThdLow - 低阈值 * double dRatioLow - 低阈值和高阈值之间的比例 * double dRatioHigh - 高阈值占图象象素总数的比例 * unsigned char *pUnchEdge - 经过non-maximum处理后的数据 * * \返回值: * 无 * * \说明: * 经过non-maximum处理后的数据pUnchEdge,统计pnMag的直方图,确定阈值。 * 本函数中只是统计pUnchEdge中可能为边界点的那些象素。然后利用直方图, * 根据dRatioHigh设置高阈值,存储到pnThdHigh。利用dRationLow和高阈值, * 设置低阈值,存储到*pnThdLow。dRatioHigh是一种比例:表明梯度小于 * *pnThdHigh的象素数目占象素总数目的比例。dRationLow表明*pnThdHigh * 和*pnThdLow的比例,这个比例在canny算法的原文里,作者给出了一个区间。 * ************************************************************************* */ void EstimateThreshold(int *pnMag, int nWidth, int nHeight, int *pnThdHigh,int *pnThdLow, unsigned char * pUnchEdge, double dRatioHigh, double dRationLow) { // 循环控制变量 int y; int x; int k; // 该数组的大小和梯度值的范围有关,如果采用本程序的算法,那么梯度的范围不会超过pow(2,10) int nHist[1024] ; // 可能的边界数目 int nEdgeNb ; // 最大梯度值 int nMaxMag ; int nHighCount ; nMaxMag = 0 ; // 初始化 for(k=0; k<1024; k++) { nHist[k] = 0; } // 统计直方图,然后利用直方图计算阈值 for(y=0; y = nThdHigh)) { // 设置该点为边界点 pUnchEdge[nPos] = 255; TraceEdge(y, x, nThdLow, pUnchEdge, pnMag, nWidth); } } } // 那些还没有被设置为边界点的象素已经不可能成为边界点 for(y=0; y