图像分割——meanshift算法(C++&GDAL库)

  • 一、meanshift分割原理
  • 二、分割技术流程
  • 三、代码实例
    • 3.1 C++&GDAL库 实现
    • 3.2 分割结果
    • 3.3 结果分析

一、meanshift分割原理

Mean-Shift是一种非参数化的多模型分割方法,它的基本计算模块采用的是传统的模式识别程序,即通过分析图像的特征空间和聚类的方法来达到分割的目的。它是通过直接估计特征空间概率密度函数的局部极大值来获得未知类别的密度模式,并确定这个模式的位置,然后使之聚类到和这个模式有关的类别当中。

设S是n维空间X中的一个有限集合,K表示X空间中λ球体的一个特征函数,则其表达式为:


其中,x∈X,那么在向量x点处的样本均值为:


Fukunaga和Hostetle等人在其自己的论文中把m(x)-x的差叫做Mean-Shift。Mean-Shift算法实际上就是数据点到样本均值的重复移动,而且在算法的每一次迭代过程中,对于所有的s∈S,s←m (s)都是同时的。同时,模糊聚类算法还包括最大墒聚类算法以及常用的k均值聚类算法,它们都是Mean-Shift算法的一个有限的特例。Mean-Shift算法作为一种聚类分析方法,由于其密度估计器的梯度是递增的,而其收敛点即为密度梯度的局部极大值点,这个局部极大值即对应特征空间中的一个模式。

Mean-Shift算法对于概率密度函数的估计通常采用Parzen窗函数法,即核密度估计器。在d维空间Rd中,给定n个数据点xi,i=1,2…n,点x的多变量核密度估计器的计算式如式(3)所示。这个估计量可以由核K(x)和一个对称正定的d×d宽度的矩阵H来表示。


一般情况下,具有d个变量的核K(x)是一个满足以下条件的边界函数:


其中,ck是一个常量。从图像分割的目的出发,多变量核K (x)采用的是放射状对称核Ks(x)=ak,dK1(‖x‖),其中K1(z)是一个对称的单变量核,且K (x)满足下式:


其中,ck,d是可使K (x)等于1的归一化常量。带宽矩阵H一般选择对角阵,H=diag[h12,…,h2d]或与单位矩阵H=h2I成比例。H=h2I情况下的一个明显优点是只需带宽参数h>0。然而,从式(4)可以看出,首先应确定用于特征空间的欧几里德矩阵的有效性。若使用一个宽度参数h,则式(3)就会变成如下典型的表示式:

将(6)式代入上式,就可以得到一个通用的、用核符号表示的核密度估计式:

对有基本密度函数f(x)的一个特征空间,Mean-Shift算法分析的第一步是找到这个密度模式,然后对这个模式进行相关聚类。此模式应该在梯度▽f(x)=0的零点当中,而Mean-Shift程序是不用估计密度,而直接对密度的梯度进行估计,就能定位这些零点。

二、分割技术流程

对于Mean-Shift算法的应用与分割,首先,可设xi和zi(i=1,2,…,n)分别为n维空间内的输人和联合的空值域内的滤波图像的像素,Li为分割后的图像中的第i个像素。那么,其操作可分为以下步骤:

  1. 运行均值平移滤波程序对图像进行滤波,并存储所有d维空间内在zi处的收敛点zi=yi,c。
  2. 在联合域中对所有的zi进行分组以描述类,这些类{Cp}p=1…m在空域内较hs较近,在值域内较hr较近。
  3. 对于每一个i=1,…,n,并记为: Li={p|zi∈Cp|} (4)消除在空间区域内少于M个像素的区域。
    输入:遥感图像,输出:分割后的斑块

三、代码实例

3.1 C++&GDAL库 实现

#include "gdal_priv.h"
#include "cpl_conv.h"
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include<cmath>
using namespace std;
using namespace cv;struct aboutmat{Mat mat;int width;int height;
};//读取数据并进行初始化:将其保存到3通道的数组中
aboutmat Initialization(const char *path)
{//使之支持中文路径CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");//注册栅格驱动GDALAllRegister();//打开要更新的数据,第二个参数使用介个GDALDataset *poDS = (GDALDataset*)GDALOpen(path, GA_Update);//获取图像大小int iWidth = poDS->GetRasterXSize();int iHeight = poDS->GetRasterYSize();//获取波段个数int nBands = poDS->GetRasterCount();//创建数组//Mat data_XY(iWidth, iHeight, CV_8U, Scalar::all(0));    //坐标空间Mat data_RGB(iHeight,iWidth, CV_8UC3, Scalar::all(0));        //色彩空间//only三波段,多了不行//进行波段循环,将矩阵形式表示的图像数据以行向量形式表示,如92*112的图像,表示为1*10304,for (int r = 1; r <= nBands; ++r){GDALRasterBand *xBand = poDS->GetRasterBand(r);unsigned char *pBuf = new unsigned char[iWidth * iHeight]; //动态指配数组xBand->RasterIO(GF_Read, 0, 0, iWidth, iHeight, pBuf, iWidth, iHeight, GDT_Byte, 0, 0);for (int i = 0; i < iHeight; i++){for (int j = 0; j < iWidth; j++){data_RGB.at<Vec3b>(i, j)[r-1] = pBuf[i*iWidth+j];}}delete[]xBand;delete pBuf;}cout << data_RGB.at<Vec3b>(10,22)[2] << endl;aboutmat data;data.mat = data_RGB;data.width = iWidth;data.height = iHeight;return data;
}void SingleMeanShift(int h,int w,int coreYCoordinate, int coreXCoordinate, int spacialH, int colorRadiusSpace,Mat newDataXY, Mat& corePointXY, Mat& dataColorSpatial, int coreIteration, int &corePointNum)
{//int meanNeiborR = 0;float xGaussNumerator =  0;    //x轴高斯核分子float xGaussDenominator = 0;  //高斯核分母float yGaussNumerator =  0; //y轴高斯核分子float yGaussDenominator = 0;  //高斯核分母int coreVector[2] = { 0, 0 };   //步长int coreNeiborNum = 0; //记录有几个符合要求的邻域点for (int i = coreYCoordinate - spacialH; i <= coreYCoordinate + spacialH; ++i){if (i >= newDataXY.rows || i <= 0) continue;for (int j = coreXCoordinate - spacialH; j <= coreXCoordinate + spacialH; ++j)    //空间筛选{if (j <= 0 || j >= newDataXY.cols) continue;int rDistance = pow((dataColorSpatial.at<Vec3b>(i, j)[0] - dataColorSpatial.at<Vec3b>(h, w)[0]), 2);int gDistance = pow((dataColorSpatial.at<Vec3b>(i, j)[1] - dataColorSpatial.at<Vec3b>(h, w)[1]), 2);int bDistance = pow((dataColorSpatial.at<Vec3b>(i, j)[2] - dataColorSpatial.at<Vec3b>(h, w)[2]), 2);int colorDistance = rDistance + gDistance + bDistance;if (colorDistance <= colorRadiusSpace)   //如果色彩差异在指定范围内(颜色筛选){//这里用高斯核函数计算重心xGaussNumerator = xGaussNumerator + exp(-0.5*pow(float(j - coreXCoordinate) / float(spacialH), 2))*j;xGaussDenominator = xGaussDenominator + exp(-0.5*pow(float(j - coreXCoordinate) / float(spacialH), 2));yGaussNumerator = yGaussNumerator + exp(-0.5*pow(float(i - coreYCoordinate) / float(spacialH), 2))*i;yGaussDenominator = yGaussDenominator + exp(-0.5*pow(float(i - coreYCoordinate) / float(spacialH), 2));coreNeiborNum += 1;}}}int newCoreYCoordinate;int newCoreXCoordinate;if (xGaussDenominator != 0 && yGaussDenominator != 0){//cout << "符合要求的邻域点coreNeiborNum= " << coreNeiborNum << endl;coreVector[1] = xGaussNumerator / xGaussDenominator - coreXCoordinate;    //向量coreVector[0] = yGaussNumerator / yGaussDenominator - coreYCoordinate;newCoreYCoordinate = yGaussNumerator / yGaussDenominator;   //新坐标newCoreXCoordinate = xGaussNumerator / xGaussDenominator;//newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[0] = corePointNum;coreIteration++;}else{coreVector[1] = 0;coreVector[0] = 0;newCoreYCoordinate = coreYCoordinate;   //新坐标newCoreXCoordinate = coreXCoordinate;}    //防止溢出if (newDataXY.at <Vec2i>(newCoreYCoordinate, newCoreXCoordinate)[0] >= 0)   //如果步进的位置已经有模点{newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[0] = newDataXY.at <Vec2i>(newCoreYCoordinate, newCoreXCoordinate)[0];//行:坐标赋值newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[1] = newDataXY.at <Vec2i>(newCoreYCoordinate, newCoreXCoordinate)[1];    //列}else if ((abs(coreVector[0]) < 1 && abs(coreVector[1]) < 1)) //|| (coreIteration >= 100)) //如果找到了重心或者循环次数达到100{corePointXY.at<int>(0, corePointNum) = coreYCoordinate;corePointXY.at<int>(1, corePointNum) = coreXCoordinate;newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[0] = coreYCoordinate;    //行:坐标赋值newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[1] = coreXCoordinate;  //列corePointNum++;    //模点数目加一}else{SingleMeanShift(h,w,newCoreYCoordinate, newCoreXCoordinate,spacialH, colorRadiusSpace, newDataXY, corePointXY, dataColorSpatial, coreIteration, corePointNum);newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[0] = newDataXY.at <Vec2i>(newCoreYCoordinate, newCoreXCoordinate)[0];//行:坐标赋值newDataXY.at <Vec2i>(coreYCoordinate, coreXCoordinate)[1] = newDataXY.at <Vec2i>(newCoreYCoordinate, newCoreXCoordinate)[1];   //列}/*delete xGaussNumerator;delete yGaussNumerator;delete xGaussDenominator;delete yGaussDenominator;delete []coreVector;*/
}Mat MeanShift(aboutmat data, int spacialH, int colorR, Mat &corePointXY)
{Mat dataColorSpatial = data.mat;int iWidth = data.width;int iHeight = data.height;int colorRadiusSpace = pow(colorR, 2) + pow(colorR, 2) + pow(colorR, 2);Mat newDataXY(iHeight, iWidth, CV_32SC2, Scalar::all(-1)); //存放新的点坐标,以模点的顺序代替int corePointNum = 0; //共有几个模点for (int h = 0; h < iHeight; h++){for (int w = 0; w < iWidth; w++){//对每个数据点进行循环计算int coreIteration = 0;    //一个点移动了几步if (newDataXY.at<Vec2i>(h, w)[0] >= 0)continue;//执行单个的meanshift操作SingleMeanShift(h, w, h, w,spacialH, colorRadiusSpace, newDataXY, corePointXY, dataColorSpatial, coreIteration, corePointNum);}}dataColorSpatial.release();    //用来释放Mat空间return newDataXY;
}//opencv的分层迭代法,速度快,处理难
//系统采样/随机采样选取一定间隔的初始点
//进行meanshift迭代,选出最后的模点位置
Mat randomPointmeanShift(aboutmat data,int spacialH,int colorR)
{int coreInterval = 500;   //踩点间隔Mat dataColor = data.mat;int iWidth = data.width;int iHeight = data.height;int pointNum = int(iWidth / coreInterval) * int(iHeight / coreInterval);//系统采样 / 随机采样选取一定间隔的初始点Mat soursePoint(iHeight, iWidth, CV_8U, Scalar::all(-1));Mat point_XY(2, pointNum, CV_32S, Scalar::all(0));int n = 0;for (int i = coreInterval-1; i < iHeight; i += coreInterval)    //必须是最大的数字,否则容易溢出{for (int j = coreInterval-1; j < iWidth; j += coreInterval){soursePoint.at<uchar>(i,j) = 1;point_XY.at<int>(0, n) = i; //存储初始点的xy坐标point_XY.at<int>(1, n) = j;n++;}}/*用于验证坐标正不正确cout << "point_XY.at<int>(0, n)" << point_XY.at<int>(0, 4000) << endl;cout << "point_XY.at<int>(0, n)" << point_XY.at<int>(1, 4000) << endl;*/int colorRadiusSpace = pow(colorR, 2)+ pow(colorR, 2)+ pow(colorR, 2);//开始meanshift迭代for (int p = 0; p < pointNum; p++)    //每个初始点循环{int coreIteration = 0;do{int coreVector[2] = { 0, 0 };int coreNeiborNum = 0;for (int i = point_XY.at<int>(0, p) - spacialH; i <= point_XY.at<int>(0, p) + spacialH; ++i){if (i < iHeight && i >= 0){for (int j = point_XY.at<int>(1, p) - spacialH; j <= point_XY.at<int>(1, p) + spacialH; ++j){if (j >= 0 && j < iWidth){//求出核与周围点的色彩差异int rDistance = pow((dataColor.at<Vec3b>(i, j)[0] - dataColor.at<Vec3b>(point_XY.at<int>(0, p), point_XY.at<int>(1, p))[0]), 2);int gDistance = pow((dataColor.at<Vec3b>(i, j)[0] - dataColor.at<Vec3b>(point_XY.at<int>(0, p), point_XY.at<int>(1, p))[1]), 2);int bDistance = pow((dataColor.at<Vec3b>(i, j)[0] - dataColor.at<Vec3b>(point_XY.at<int>(0, p), point_XY.at<int>(1, p))[2]), 2);int colorDistance = rDistance + gDistance + bDistance;if (colorDistance <= colorRadiusSpace)  //如果色彩差异在指定范围内{//这里用高斯核计算中心coreVector[0] = coreVector[0] + i - point_XY.at<int>(0, p);coreVector[1] = coreVector[1] + j - point_XY.at<int>(1, p);coreNeiborNum += 1;} }}}if (coreNeiborNum != 0){//求meanshift向量并移动模点coreVector[0] = coreVector[0] / coreNeiborNum;coreVector[1] = coreVector[1] / coreNeiborNum;point_XY.at<int>(0, p) = point_XY.at<int>(0, p) + coreVector[0];if (point_XY.at<int>(0, p) < 0)point_XY.at<int>(0, p) = 0;if (point_XY.at<int>(0, p) > iHeight) point_XY.at<int>(0, p) = iHeight;point_XY.at<int>(1, p) = point_XY.at<int>(1, p) + coreVector[1];if (point_XY.at<int>(1, p) < 0) point_XY.at<int>(1, p) = 0;if (point_XY.at<int>(1, p) > iWidth) point_XY.at<int>(1, p) = iWidth;coreIteration++;if (coreVector[0] <= 5 && coreVector[1] <= 5){break;}}else{break;}} while (coreIteration <= 10);//cout << "p" << p << endl;std::cout << p << " 模点移动次数为" << coreIteration << endl;}//cout << "点坐标" << point_XY << endl;return point_XY;
}//筛选合格点,将颜色相近的点合并
Mat MergeCorePoint(Mat data, Mat sCorePoint_XY, Mat newDataXY, int colorDisparity)
{int colorDisparitySpace = pow(colorDisparity, 2) + pow(colorDisparity, 2) + pow(colorDisparity, 2);int mergeTimes = 0;for (int p1 = 0; p1 < sCorePoint_XY.cols; p1++){if (sCorePoint_XY.at<int>(0, p1) != 0){for (int p2 = 0; p2 < sCorePoint_XY.cols; p2++)    //p2=p1+1{if ((sCorePoint_XY.at<int>(0, p2) != 0) && (p1 != p2)){//三个总和差距小合并              /*int rPoColorDisparity = pow(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[0]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[0], 2);int gPoColorDisparity = pow(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[1]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[1], 2);int bPoColorDisparity = pow(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[2]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[2], 2);int pointColorDisparity = rPoColorDisparity + gPoColorDisparity + bPoColorDisparity;if (pointColorDisparity < colorDisparitySpace){newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[0] =newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[0];newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[1] =newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[1];sCorePoint_XY.at<int>(0, p2) = 0;sCorePoint_XY.at<int>(1, p2) = 0;}*///三个波段相差小就合并                int rPoDisparity = abs(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[0]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[0]);int gPoDisparity = abs(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[1]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[1]);int bPoDisparity = abs(data.at<Vec3b>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[2]- data.at<Vec3b>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[2]);if ((rPoDisparity < colorDisparity) && (gPoDisparity < colorDisparity) && (bPoDisparity < colorDisparity)){newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[0] =newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[0];newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p2), sCorePoint_XY.at<int>(1, p2))[1] =newDataXY.at<Vec2i>(sCorePoint_XY.at<int>(0, p1), sCorePoint_XY.at<int>(1, p1))[1];sCorePoint_XY.at<int>(0, p2) = 0;sCorePoint_XY.at<int>(1, p2) = 0;}}}}}//将存在的剩下的模点导出int newCorePointNum = 0;//Mat newCorePoint_XY(2, newCorePointNum, CV_32S, Scalar::all(0));for (int n = 0;n < sCorePoint_XY.cols; n++){if (sCorePoint_XY.at<int>(0, n) > 0){newCorePointNum++;}}Mat newCorePoint_XY(2, newCorePointNum, CV_32S, Scalar::all(0));int n1 = 0;for (int n = 0; n < sCorePoint_XY.cols; n++){if (sCorePoint_XY.at<int>(0, n) != 0){newCorePoint_XY.at<int>(0, n1) = sCorePoint_XY.at<int>(0, n);newCorePoint_XY.at<int>(1, n1) = sCorePoint_XY.at<int>(1, n);n1++;}}//输出所对应的颜色信息(用于验证)for (int i = 0; i < newCorePointNum; i++){for (int c = 0; c < data.channels(); c++){cout <<"第"<<i+1<<"个模点颜色为 "<< int(data.at<Vec3b>(newCorePoint_XY.at<int>(0, i), newCorePoint_XY.at<int>(1, i))[c])<<"  ";}cout << endl;}Mat newDataColorSpa(data.rows, data.cols, CV_8UC3, Scalar::all(0));   //存放新的图像(分割后)for (int r = 0; r < newDataXY.rows; r++){for (int c = 0; c < newDataXY.cols; c++){if ((newDataXY.at<Vec2i>(newDataXY.at<Vec2i>(r, c)[0], newDataXY.at<Vec2i>(r, c)[1])[0] != newDataXY.at<Vec2i>(r, c)[0]) &&(newDataXY.at<Vec2i>(newDataXY.at<Vec2i>(r, c)[0], newDataXY.at<Vec2i>(r, c)[1])[1] != newDataXY.at<Vec2i>(r, c)[1])){newDataXY.at<Vec2i>(r, c)[0] = newDataXY.at<Vec2i>(newDataXY.at<Vec2i>(r, c)[0], newDataXY.at<Vec2i>(r, c)[1])[0];newDataXY.at<Vec2i>(r, c)[1] = newDataXY.at<Vec2i>(newDataXY.at<Vec2i>(r, c)[0], newDataXY.at<Vec2i>(r, c)[1])[1];}for (int ch = 0; ch < data.channels(); ch++){newDataColorSpa.at<Vec3b>(r, c)[ch] = data.at<Vec3b>(newDataXY.at<Vec2i>(r, c)[0], newDataXY.at<Vec2i>(r, c)[1])[ch];//cout << newDataColorSpa.at<Vec3b>(r, c)[ch]<<endl;}}}return  newDataColorSpa;std::system("pause");
}//将其根据位置赋值并输出
void SaveImage(const char* pszRasterFile, Mat newData, const char*path)
{//int bytesPerLine = (newData.cols * 24 ) / 8;CPLSetConfigOption("GDAL_FILNAME_IS_UTF8", "NO");unsigned char *pBuf = new unsigned char[newData.cols*newData.rows*newData.channels()];for (int r = 1; r <= newData.channels(); ++r){for (int i = 0; i < newData.rows; i++){for (int j = 0; j < newData.cols; j++){pBuf[(r - 1)*newData.rows*newData.cols+i*newData.cols + j] = newData.at<Vec3b>(i, j)[r - 1];}}}GDALAllRegister();//注册数据集  GDALDriver *poDriver;GDALDataset *BiomassDataset;poDriver = GetGDALDriverManager()->GetDriverByName("Gtiff");//const char *output_file = "D:\xxxx";BiomassDataset = poDriver->Create(pszRasterFile, newData.cols, newData.rows, 3, GDT_Byte, NULL);int panBandMap[3] = { 1, 2, 3 };BiomassDataset->RasterIO(GF_Write, 0, 0, newData.cols, newData.rows, pBuf, newData.cols, newData.rows, GDT_Byte, 3, panBandMap, 0,0,0);GDALClose(BiomassDataset);BiomassDataset = NULL;delete [] pBuf;pBuf = NULL;
}
int main()
{const char* path = "G:/ER03634.tif";    const char* pszRasterFile = "G:/result/Mean8_8.tif";aboutmat sourseData = Initialization(path);int spaceRadius = 8;    //空间搜索半径int colorRadius = 2;   //色彩空间半径int colorDisparity = 4;Mat corePointXY(2, sourseData.width*sourseData.height / 2, CV_32S, Scalar::all(0)); //存放模点坐标Mat newDataXY = MeanShift(sourseData, spaceRadius, colorRadius, corePointXY);//Mat soursePointXY = randomPointmeanShift(sourseData, spaceRadius, colorRadius);Mat newData = MergeCorePoint(sourseData.mat, corePointXY, newDataXY, colorDisparity);cout << newDataXY.col(2) << endl;SaveImage(pszRasterFile, newData, path);return 0;
}

3.2 分割结果

首先用openCV库的库函数进行分割,作为对照结果如下(左:原图,右:库函数分割结果)


上述代码处理结果:

从左到右依次为:(8-8-4,8-8-16,16-4-4)数据说明:第一个参数表示空间半径(像素),第二个参数表示色彩半径(像素),第三个参数表示类合并最小半径,若无说明,合并方法为三个总和差距小合并。

不同空间半径分割结果(左侧8,右侧16)

不同合并方法分割结果


注:都是8-8-8的窗口大小,左:合并方法为三个总和差距小合并;右:合并方法为一个波段相差小就合并。

3.3 结果分析

由分割总体结果显示来看,分割效果较差,代码优化不好导致分割时间较长。 代码相比openCV库Mean—Shift分割函数有一些改进:

  • 1.其库函数的寻找模点(密度最大的点)方法为随机选取点进行寻找,改进对所有像元遍历寻找,更符合Mean-Shift原理,但耗费时间更长。
  • 2.核密度计算由均匀核函数改进为高斯核函数,即越靠近中心点,对中心点的密度贡献越大,更符合实际情况。
  • 3.平滑时,库函数对寻找到的模点进行洪水蔓延算法平滑,改进为在寻找模点的同时进行图像平滑,避免了二次遍历,也更符合MeanShift原理。
  • 4.在合并类时采取了两种方法,可以任意选择。 由不同分类参数结果可以得出结论:在一定范围内,空间半径越大,分割效果越差,运行时间越长;色彩半径同样;合并方法“一个波段相差小就合并”比“三个总和差距小合并”更好。

图像分割——meanshift算法(C++GDAL库)相关推荐

  1. 基于Mean-shift算法跟踪对象

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 跟踪对象是计算机视觉领域的重要应用.这在监控系统.国防.自动驾驶汽 ...

  2. GDAL库简介以及在Windows下编译过程

    GDAL(Geospatial Data Abstraction Library,地理空间数据抽象库)是一个在X/MIT许可协议下的开源栅格空间数据转换库.官网http://www.gdal.org/ ...

  3. OpenCV中MeanShift算法视频移动对象分析

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 MeanShift算法 Mean Shift是一种聚类算法,在数据 ...

  4. 基于边缘的图像分割——分水岭算法(watershed)算法分析(附opencv源码分析)

    最近需要做一个图像分割的程序,查了opencv的源代码,发现opencv里实现的图像分割一共有两个方法,watershed和mean-shift算法.这两个算法的具体实现都在segmentation. ...

  5. GDAL库进度信息编写示例

    GDAL进度信息编写 GDAL库中的算法以及读写数据的时候一般都会提供两个与进度信息相关的参数,下面分别进行描述: GDALProgressFunc pfnProgress void * pProgr ...

  6. GDAL库调试(包括跨语言调试)

    很多时候都需要调试GDAL库,尤其是像学习GDAL库中的某些算法是如何实现的时候,调试就必不可少了. 首先说明用C++的调试.以VS2008为例进行说明. 编译DEBUG版本的GDAL库,这个可以参考 ...

  7. meanshift算法

    meanshift主要用来做目标跟踪和图像分割. 转载自:http://www.cnblogs.com/liqizhou/archive/2012/05/12/2497220.html 记得刚读研究生 ...

  8. Qt多线程调用gdal库接口

    作者:朱金灿 来源:clever101的专栏 为什么大多数人学不会人工智能编程?>>> 效果图和程序说明   效果图如下:   这个程序是Qt的GUI程序,用于给指定的图像文件创建金 ...

  9. 基于python的mean-shift算法

    一.Mean Shift算法概述 Mean Shift算法又称均值漂移算法,Mean Shift的概念最早是由Fukunage在1975年提出的,在后来又由Yzong Cheng对其进行扩充,主要提出 ...

  10. openCV中meanshift算法查找目标

    一.简介 图像直方图的反向投影是一个概率分布图,表示一个指定图像片段出现在特定位置的概率.当我们已知图像中某个物体的大体位置时,可以通过概率分布图找到物体在另一张图像中的准确位置.我们可以设定一个初始 ...

最新文章

  1. Python如何实现穷举搜索?
  2. Python3 与 C# 面向对象之~封装
  3. 从VMware ESX Server 4升级到ESXi 5
  4. python脚本实例手机端-用Python实现自动化操作Android手机
  5. 脉冲宽度调制pdm_NHWYM脉冲硬质氧化电源-高压脉冲电源-双极性脉冲电源品牌-济南能华...
  6. zabbix 2.2 监控mysql_Zabbix-2.2.2监控MySQL的复制-阿里云开发者社区
  7. magento 报错及解决方法
  8. Jsoup处理URLs
  9. kafka配置文件server.properties
  10. Tensor的填充与复制
  11. [转]解决Sublime Text 2中文显示乱码问题
  12. 【机器人算法】机器人运动学参数辨识/DH参数校准/DH参数辨识
  13. 建图时,计算激光点在map坐标系下的坐标
  14. Python3 async def和@asyncio.coroutine的区别
  15. 不是 SELECTed 表达式
  16. ubuntu安装使用redis并设置开机启动
  17. DBF文件的初步了解(一)
  18. java计算机毕业设计html5健身房信息管理系统MyBatis+系统+LW文档+源码+调试部署
  19. python基础部分
  20. 使用echarts生成漂亮的3D地图

热门文章

  1. 《数据结构》线性表——链式存储结构
  2. 大气数据计算机英语,大气数据计算机(ADC)
  3. 2022年最新区块链电商赋能企业的解决方案
  4. Win10禁用UAC(用户账户控制)
  5. 嵌入式软件分层及目录结构设计规范
  6. xshell卸载注册表删除问题
  7. Codeforces #555 (Div. 3)--C2 Increasing Subsequence (hard version)--投石问路+deque/双指针
  8. eyoucms留言模版验证码实现
  9. C语言递归函数 计算学生年龄
  10. 飞秒激光制备量子计算机,飞秒激光直写光量子逻辑门.PDF