面向对象分割算法是一种将图像中的像素分割成不同的对象的算法。它通过对像素进行聚类,将相似的像素分为同一个对象,从而实现图像分割的目的。常见的面向对象分割算法包括基于区域的分割算法、基于边缘的分割算法和基于能量的分割算法等。
其中,基于区域的分割算法是将图像分成若干个区域,每个区域内的像素具有相似的颜色、纹理或亮度等特征。基于边缘的分割算法则是通过检测图像中的边缘来实现分割,常见的边缘检测算法包括Sobel算子、Canny算子等。基于能量的分割算法则是通过最小化能量函数来实现分割,常见的能量函数包括距离度量、灰度差异度量等。
介绍
遥感影像的面向对象方法是一种基于高分辨率影像的信息提取技术,它主要包括以下几个步骤:
- 影像分割:将影像划分为若干个具有相似光谱、形状、纹理等特征的同质对象,作为分类或提取的基本单元。
- 对象属性计算:根据对象的光谱、形状、纹理、上下文等特征,计算对象的多种属性值,用于后续的分类或提取。
- 对象分类或提取:根据对象的属性值,采用不同的方法对对象进行分类或提取,如监督分类、基于规则的分类、模糊分类等。
- 结果输出:将分类或提取的结果输出为影像或矢量数据,进行后续的分析或应用。
面向对象方法的优点是能够充分利用高分辨率影像的空间、光谱、纹理等信息,提高信息提取的精度和效率。它也能够解决传统基于像元的方法存在的同物异谱、同谱异物等问题。面向对象方法的难点是如何选择合适的分割参数、对象属性和分类规则,以达到最佳的信息提取效果。
eCongnition软件
在eCongnition软件中面向对象分割方法较多,主要有棋盘分割,四叉树分割,反差切割分割,多尺度分割,光谱差异分割,多阈值分割,反差过滤分割及分水岭分割等分割方式。但其中最常用的分割方法一般为棋盘分割,四叉树分割,多阈值分割以及多尺度分割方法。
棋盘分割:该方法将影像划分为大小一致的正方形图像对象,沿着固定大小的网格线进行切割。该方法运行速度快,适用于借用本底矢量或进行边界修正的情况。但该方法也会导致多种地类混淆,难以进行光谱信息提取。
四叉树分割:该方法将影像划分为不同尺寸的正方形图像对象,根据光谱差异进行切割。该方法运行速度快,且可以综合考虑不同波段和颜色因子的情况,适用于某些略微复杂的地块或地类提取。但该方法也会受到地类混淆的限制,且分割边界不够理想。
反差切割分割:该方法根据影像中不同区域的反差值进行切割,可以得到较为精细和自然的图像对象。该方法适用于边界清晰且反差明显的地物提取,如建筑物、道路等。但该方法也会受到噪声和阴影的影响,且需要设置合适的反差阈值。
多尺度分割:该方法根据影像中不同波段的权重、紧致度和平滑度进行分割,可以得到不同尺寸和形状的图像对象。该方法可以充分利用影像的空间、光谱、纹理等信息,适用于复杂地区或细致地类的提取。但该方法运行效率低,且需要设置合适的分割参数。
光谱差异分割:该方法根据影像中不同波段之间的光谱差异进行切割,可以得到较为均匀和一致的图像对象。该方法适用于光谱特征明显且区别较大的地物提取,如植被、水体等。但该方法也会忽略影像中的空间和纹理信息,且需要设置合适的光谱阈值。
多阈值分割:该方法根据波段的反射率值设定阈值,将影像划分为不同的类别。该方法运行速度极快,且基于像素值进行操作,适用于边界清晰的目标地类,如水体、植被等。但该方法也会导致分割结果呈现出锯齿和畸形,且难以处理复杂地类。
反差过滤分割:该方法根据影像中不同区域的反差值进行过滤和平滑处理,然后再进行切割。该方法可以消除噪声和阴影对分割结果的影响,提高分割质量。但该方法也会降低影像中细节信息的保留,且需要设置合适的过滤参数。
分水岭分割:该方法将影像看作一幅灰度图,并将其转化为一幅拓扑表面。然后根据表面上的局部极小值和局部极大值进行切割,类似于水流从高处向低处流动的过程。该方法可以得到较为细致和自然的图像对象,适用于边界复杂且曲率变化大的地物提取。但该方法也会受到噪声和过分割的影响,且需要设置合适的标记参数。
Google earth engine
Google earth engine是一个基于云计算的地理空间数据分析平台,它提供了多种面向对象方法的实现,如:
SNIC:简单非迭代聚类算法,根据影像中不同波段的权重、紧致度和平滑度进行分割,可以得到不同尺寸和形状的图像对象。
KMeans:K均值聚类算法,根据影像中不同波段的光谱距离进行分割,可以得到较为均匀和一致的图像对象。
Weiss:Weiss分割算法,根据影像中不同区域的反差值进行切割,可以得到较为精细和自然的图像对象。
面向对象分类的基本原理就是先分割影像 、再计算特征、最后分类。
目前使用比较广泛的是SNIC方法,下载着重介绍和给出代码。
代码示例
这段代码转载至网络,流传也比较广泛,带来的问题可能使GEE内存的限制导致提取效果存在一定差异。
javascript
//-------------------------------去云 ----------------------------------------------//
function sentinel2toa(img) {
var toa = img.select(['B1','B2','B3','B4','B5','B6','B7','B8','B8A','B9','B10', 'B11','B12'],
['aerosol', 'blue', 'green', 'red', 're1','re2','re3', 'nir','nir2', 'h2o', 'cirrus','swir1', 'swir2'])
.divide(10000)
.addBands(img.select(['QA60']))
.set('solar_azimuth',img.get('MEAN_SOLAR_AZIMUTH_ANGLE'))
.set('solar_zenith',img.get('MEAN_SOLAR_ZENITH_ANGLE'))
return toa;
}
function ESAcloud(toa) {
var qa = toa.select('QA60');
var cloudBitMask = Math.pow(2, 10);
var cirrusBitMask = Math.pow(2, 11);
var clear = qa.bitwiseAnd(cloudBitMask).eq(0).and(
qa.bitwiseAnd(cirrusBitMask).eq(0));
var cloud = clear.eq(0);
return cloud;
}
function shadowMask(toa,cloud){
var azimuth =ee.Number(toa.get('solar_azimuth')).multiply(Math.PI).divide(180.0).add(ee.Number(0.5).multiply(Math.PI));
var zenith =ee.Number(0.5).multiply(Math.PI ).subtract(ee.Number(toa.get('solar_zenith')).multiply(Math.PI).divide(180.0));
var nominalScale = cloud.projection().nominalScale();
var cloudHeights = ee.List.sequence(200,10000,500);
var shadows = cloudHeights.map(function(cloudHeight){
cloudHeight = ee.Number(cloudHeight);
var shadowVector = zenith.tan().multiply(cloudHeight);
var x = azimuth.cos().multiply(shadowVector).divide(nominalScale).round();
var y = azimuth.sin().multiply(shadowVector).divide(nominalScale).round();
return cloud.changeProj(cloud.projection(), cloud.projection().translate(x, y));
});
var potentialShadow = ee.ImageCollection.fromImages(shadows).max();
potentialShadow = potentialShadow.and(cloud.not());
var darkPixels = toa.normalizedDifference(['green', 'swir2']).gt(0.25).rename(['dark_pixels']);
var shadow = potentialShadow.and(darkPixels).rename('shadows');
return shadow;
}
function cloud_and_shadow_mask(img) {
var toa = sentinel2toa(img);
var cloud = ESAcloud(toa);
var shadow = shadowMask(toa,cloud);
var mask = cloud.or(shadow).eq(0);
return toa.updateMask(mask);
}
//-------------------------------Main----------------------------------------------------------------------------------------//
var table = ee.FeatureCollection("users/xzhangsx/sample200");
var bands =['B1','B2', 'B3', 'B4', 'B5', 'B6','B7','B8','B8A','B9'];
var composite = ee.ImageCollection('COPERNICUS/S2')
.filterDate('2020-06-01', '2020-06-15')
.filterBounds(table)
.mean();
var image = ee.Image(composite).divide(255).select(bands);
print(image);
//设置种子
var seeds = ee.Algorithms.Image.Segmentation.seedGrid(36);
//利用 SNIC 进行分割,得到对象。参数的设置根据自己的需求来
var snic = ee.Algorithms.Image.Segmentation.SNIC({
image: image,
size: 100,
compactness: 5,
connectivity: 8,
neighborhoodSize:256,
seeds: seeds
}).select(['B1_mean','B2_mean','B3_mean','B4_mean','B5_mean','B6_mean','B7_mean','B8_mean','B8A_mean','B9_mean', 'clusters'], ['B1','B2','B3','B4','B5','B6','B7','B8','B8A','B9', 'clusters']);
var clusters = snic.select('clusters');//
print("snic",snic);
//计算每个对象的特征
var stdDev = image.addBands(clusters).reduceConnectedComponents(ee.Reducer.stdDev(), 'clusters', 256);//为啥和上面的均值不一样?
var area = ee.Image.pixelArea().addBands(clusters).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256);
var minMax = clusters.reduceNeighborhood(ee.Reducer.minMax(), ee.Kernel.square(1));
var perimeterPixels = minMax.select(0).neq(minMax.select(1)).rename('perimeter');
var perimeter = perimeterPixels.addBands(clusters).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256);
var sizes = ee.Image.pixelLonLat().addBands(clusters).reduceConnectedComponents(ee.Reducer.minMax(), 'clusters', 256);
var width = sizes.select('longitude_max').subtract(sizes.select('longitude_min')).rename('width');
var height = sizes.select('latitude_max').subtract(sizes.select('latitude_min')).rename('height');
//分类器参数设置,选择分类依据:包括了area,width等
var objectPropertiesImage = ee.Image.cat([
snic.select(bands),
//stdDev,
// area,
//perimeter,
//width,
//height
]).float();
print(objectPropertiesImage);
//var img2 = ee.Image(objectPropertiesImage).divide(255);
//选择训练样本
var training = objectPropertiesImage.sampleRegions({
collection: table,
properties: ['value'],
scale: 30,
tileScale:8
});
print("training",training);
/*var training = objectPropertiesImage.addBands(table.select('value'))
.updateMask(seeds)
.sample(table, 5);
print("training2",training2);*/
//分类
var classifier = ee.Classifier.smileCart().train(training, 'value');
print(classifier);
var result=objectPropertiesImage.classify(classifier,'Classified objects');
//Export.image.toCloudStorage(result,"202012172236");
//var resultshp = result.updateMask(seeds).sample(table, 5);
//Export.table.toCloudStorage(resultshp,"202012172120");
print(result);
Map.centerObject(table);
Map.addLayer(result.randomVisualizer(), {}, 'Classified objects2');
// 计算混淆矩阵
var test=training.classify(classifier,'classification');
var confusionMatrix = test.errorMatrix('value', 'classification');
print('Confusion Matrix', confusionMatrix);
简单非迭代聚类(SNIC)超像素分割
在本文中,我们主要介绍一种常用的面向对象分割算法------简单非迭代聚类(SNIC)算法。该算法是一种基于超像素(superpixel)概念的分割方法。超像素是指具有相似颜色和空间位置的像素组成的小区域。超像素可以有效地减少图像中需要处理的数据量,并保留图像中重要的边缘和结构信息。
SNIC算法是一种基于K均值聚类(K-means clustering)思想的超像素分割方法。它从规则网格上创建一些种子点作为初始聚类中心,然后根据每个像素与聚类中心之间在颜色和空间上的距离来将其分配到最近的聚类中。距离计算公式如下:
其中,d(x,y)是像素x和聚类中心y之间的距离,dc(x,y)是它们在CIELAB颜色空间上的欧氏距离,ds(x,y)是它们在图像空间上的欧氏距离,α是一个权重参数,用于控制颜色和空间的相对重要性。一般来说,α的值越大,分割结果越紧致,越小则越平滑。
SNIC算法的执行流程如下所示:
-
首先,将图像划分为一个规则网格,每个网格单元的大小为s×s像素。然后,在每个网格单元的中心位置创建一个种子点作为初始聚类中心,并将其周围的s×s像素作为初始聚类成员。
-
其次,对于每个种子点,计算其与其邻域内的所有像素之间的距离,并将距离最小的像素添加到相应的聚类中。邻域的大小为m×m像素,一般取m=2s+1。同时,更新聚类中心的颜色和空间坐标为聚类成员的平均值。
-
然后,重复上一步骤,直到所有像素都被分配到最近的聚类中,或者达到最大迭代次数为止。最后,输出每个聚类的颜色、空间和形状等属性,以及每个像素所属的聚类标签。
下面是一个简单的例子来展示SNIC算法的输入和输出:
- 输入是一幅高分辨率遥感影像,大小为512 × 512像素,包含多种地物类型。
- 输出是一个包含超像素属性和标签的图像对象集合。我们可以看到,超像素具有较好的边缘保持性和形状适应性,能够有效地提取出不同地物类型。
SNIC算法中需要设置的参数主要有四个:
- 种子距离s:决定了初始网格单元和邻域大小的参数。一般来说,s越大,分割出来的超像素越少,越小则越多。根据影像的分辨率和目标地物类型的大小来选择合适的s值。
- 分割紧密度α:决定了颜色和空间距离的相对重要性的参数。一般来说,α越大,分割结果越紧致,越小则越平滑。根据影像中地物类型的光谱差异和空间分布来选择合适的α值。
- 连通性c:决定了超像素是否允许出现孔洞或裂缝的参数。一般来说,c=4表示只考虑上下左右四个方向的连通性,c=8表示考虑八个方向的连通性。根据影像中地物类型的形状复杂度和连通性要求来选择合适的c值。
- 邻域大小m:决定了每次迭代时考虑的邻域范围的参数。一般来说,m=2s+1是一个比较合理的选择,可以保证每个种子点能够覆盖到其周围所有可能属于同一聚类的像素。
SNIC算法的执行过程和结果如下:
- 首先,我们需要设置好SNIC算法的参数,如种子距离s,分割紧密度α,连通性c和邻域大小m。1这些参数会影响分割结果的数量、质量和形状。我们可以根据影像的特点和目标来选择合适的参数值,也可以通过试验和评估来找到最佳的参数组合。
- 其次,我们需要导入一幅高分辨率遥感影像,作为SNIC算法的输入。我们可以使用Google earth engine平台提供的各种遥感数据源,如Landsat、Sentinel、MODIS等,也可以上传自己的遥感影像数据。我们可以根据需要对影像进行预处理,如裁剪、投影、波段选择等。
- 然后,我们需要调用Google earth engine平台提供的SNIC算法函数,对影像进行分割。该函数会返回一个包含超像素属性和标签的图像对象集合。我们可以对该集合进行后处理,如去除噪声、合并小对象、提取边界等。
- 最后,我们可以将分割结果输出为影像或矢量数据,进行后续的分析或应用。23我们可以使用Google earth engine平台提供的各种可视化和统计工具,对分割结果进行展示和评估。我们也可以将分割结果导出到本地或云端,进行其他操作或使用。
以下是一个使用Google earth engine平台实现SNIC算法的代码示例:
javascript
// Import a high-resolution Landsat 8 image
var image = ee.Image('LANDSAT/LC08/C01/T1_SR/LC08_044034_20140318');
// Define the SNIC parameters
var size = 32; // the seed distance in pixels
var compactness = 5; // the segmentation compactness factor
var connectivity = 8; // the connectivity mode (4 or 8)
var neighborhoodSize = 256; // the neighborhood size in pixels
// Apply the SNIC algorithm to the image
var snic = ee.Algorithms.Image.Segmentation.SNIC({
image: image,
size: size,
compactness: compactness,
connectivity: connectivity,
neighborhoodSize: neighborhoodSize
});
// Display the original image and the segmentation result
Map.centerObject(image, 10);
Map.addLayer(image, {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000}, 'Original Image');
Map.addLayer(snic.randomVisualizer(), {}, 'Segmentation Result');
下面的代码同样也转载至网络,他可能会使你从过程上了解面向对象的具体过程。
将下面的代码复制到新的脚本中:
javascript
// 1.1 Unsupervised k-Means classification
// This function does unsupervised clustering classification
// input = any image. All bands will be used for clustering.
// numberOfUnsupervisedClusters = tunable parameter for how
// many clusters to create.
var afn_Kmeans = function(input, numberOfUnsupervisedClusters,
defaultStudyArea, nativeScaleOfImage) {
// Make a new sample set on the input. Here the sample set is
// randomly selected spatially.
var training = input.sample({
region: defaultStudyArea,
scale: nativeScaleOfImage,
numPixels: 1000
});
var cluster = ee.Clusterer.wekaKMeans(
numberOfUnsupervisedClusters)
.train(training);
// Now apply that clusterer to the raw image that was also passed in.
var toexport = input.cluster(cluster);
// The first item is the unsupervised classification. Name the band.
var clusterUnsup = toexport.select(0).rename(
'unsupervisedClass');
return (clusterUnsup);
};
我们还需要一个函数来将频带值归一化为从0到1的公共刻度。当我们创建对象时,这将是最有用的。此外,我们还需要一个函数将平均值添加到波段名称中。将以下函数粘贴到代码中。注意,代码编号故意从1.2跳到1.4;稍后我们将添加第1.3节:
javascript
// 1.2 Simple normalization by maxes function.
var afn_normalize_by_maxes = function(img, bandMaxes) {
return img.divide(bandMaxes);
};
// 1.4 Simple add mean to Band Name function
var afn_addMeanToBandName = (function(i) {
return i + '_mean';
});
我们将创建一个小节,定义您可以调整的变量。一个重要的可调参数是集群器使用的集群数量。将以下代码添加到函数定义下面:
javascript
// 2. Parameters to function calls
// 2.1. Unsupervised KMeans Classification Parameters
var numberOfUnsupervisedClusters = 4;
该脚本将允许您放大到指定的区域以更好地查看,并且已经存在于代码存储库检查点中。添加下面的代码:
javascript
// 2.2. Visualization and Saving parameters
// For different images, you might want to change the min and max
// values to stretch. Useful for images 2 and 3, the normalized images.
var centerObjectYN = true;
现在,有了这些函数、参数和标志之后,让我们定义一个新图像,并设置有助于分析图像的特定图像值。我们将把它放在代码的一个新部分中,其中包含来自多个传感器的图像的"if"语句。我们像这样设置代码,因为我们将在接下来的部分中使用来自不同传感器的几张图像,因此它们是预加载的,所以你所要做的就是改变参数"whohimage"。在这张特别的Sentinel-2图像中,专注于区分美国华盛顿普吉特海湾的森林和非森林地区。脚本将自动缩放到感兴趣的区域:
javascript
//
// 3. Statements
//
// 3.1 Selecting Image to Classify
var whichImage = 1; // will be used to select among images
if (whichImage == 1) {
// Image 1.
// Puget Sound, WA: Forest Harvest
// (April 21, 2016)
// Harvested Parcels
// Clear Parcel Boundaries
// Sentinel 2, 10m
var whichCollection = 'COPERNICUS/S2';
var ImageToUseID = '20160421T191704_20160421T212107_T10TDT';
var originalImage = ee.Image(whichCollection + '/' +
ImageToUseID);
print(ImageToUseID, originalImage);
var nativeScaleOfImage = 10;
var threeBandsToDraw = ['B4', 'B3', 'B2'];
var bandsToUse = ['B4', 'B3', 'B2'];
var bandMaxes = [1e4, 1e4, 1e4];
var drawMin = 0;
var drawMax = 0.3;
var defaultStudyArea = ee.Geometry.Polygon(
[
[
[-123.13105468749993, 47.612974066532004],
[-123.13105468749993, 47.56214700543596],
[-123.00179367065422, 47.56214700543596],
[-123.00179367065422, 47.612974066532004]
]
]);
var zoomArea = ee.Geometry.Polygon(
[
[
[-123.13105468749993, 47.612974066532004],
[-123.13105468749993, 47.56214700543596],
[-123.00179367065422, 47.56214700543596],
[-123.00179367065422, 47.612974066532004]
]
], null, false);
}
Map.addLayer(originalImage.select(threeBandsToDraw), {
min: 0,
max: 2000
}, '3.1 ' + ImageToUseID, true, 1);
现在,让我们将图像剪辑到我们感兴趣的研究区域,然后提取用于分类过程的波段:
javascript
// 4. Image Preprocessing
var clippedImageSelectedBands = originalImage.clip(defaultStudyArea)
.select(bandsToUse);
var ImageToUse = afn_normalize_by_maxes(clippedImageSelectedBands,
bandMaxes);
Map.addLayer(ImageToUse.select(threeBandsToDraw), {
min: 0.028,
max: 0.12
},
'4.3 Pre-normalized image', true, 0);
现在,让我们查看使用k-means分类器生成的逐像素无监督分类。请注意,正如前面所做的,我们跳过了代码编号的一部分(从第4节移动到第6节),我们将在稍后进一步开发脚本时填充该部分:
javascript
// 6. Execute Classifications
// 6.1 Per Pixel Unsupervised Classification for Comparison
var PerPixelUnsupervised = afn_Kmeans(ImageToUse,
numberOfUnsupervisedClusters, defaultStudyArea,
nativeScaleOfImage);
Map.addLayer(PerPixelUnsupervised.select('unsupervisedClass')
.randomVisualizer(), {}, '6.1 Per-Pixel Unsupervised', true, 0
);
print('6.1b Per-Pixel Unsupervised Results:', PerPixelUnsupervised);
然后插入此代码,以便在需要时可以缩放:
javascript
// 7. Zoom if requested
if (centerObjectYN === true) {
Map.centerObject(zoomArea, 14);
}
运行脚本。它将以真彩色视图绘制研究区域,在那里您可以检查景观的复杂性,就像2016年拍摄图像时您所看到的那样。
S2A原始影像
SNIC分割结果
在绘制真彩色图像时,Earth Engine还执行k-means分类,并将其添加到图层集中。打开6.1每像素无监督层的可见性,它显示了使用随机选择的颜色的每像素四类分类结果。结果应该如图所示。
您在基于像素的分类中注意到的噪声现在将使用基于对象的图像分析的两步方法进行改进。首先,您将使用SNIC算法分割图像,然后使用k-means无监督分类器对其进行分类。返回到脚本,我们将在其中添加一个新函数。请注意,代码的部分是编号的,找到代码部分1.2并将下面的函数添加到它下面:
javascript
// 1.3 Seed Creation and SNIC segmentation Function
var afn_SNIC = function(imageOriginal, SuperPixelSize, Compactness,
Connectivity, NeighborhoodSize, SeedShape) {
var theSeeds = ee.Algorithms.Image.Segmentation.seedGrid(
SuperPixelSize, SeedShape);
var snic2 = ee.Algorithms.Image.Segmentation.SNIC({
image: imageOriginal,
size: SuperPixelSize,
compactness: Compactness,
connectivity: Connectivity,
neighborhoodSize: NeighborhoodSize,
seeds: theSeeds
});
var theStack = snic2.addBands(theSeeds);
return (theStack);
};
如您所见,该函数组装了运行SNIC所需的参数(Achanta和Süsstrunk 2017, Crowley等人,2019),该函数在图像中描绘对象。对SNIC的调用需要几个参数,我们将探讨这些参数。在代码节2.2下面添加以下代码:
javascript
// 2.3 Object-growing parameters to change
// Adjustable Superpixel Seed and SNIC segmentation Parameters:
// The superpixel seed location spacing, in pixels.
var SNIC_SuperPixelSize = 16;
// Larger values cause clusters to be more compact (square/hexagonal).
// Setting this to 0 disables spatial distance weighting.
var SNIC_Compactness = 0;
// Connectivity. Either 4 or 8.
var SNIC_Connectivity = 4;
// Either 'square' or 'hex'.
var SNIC_SeedShape = 'square';
// 2.4 Parameters that can stay unchanged
// Tile neighborhood size (to avoid tile boundary artifacts). Defaults to 2 * size.
var SNIC_NeighborhoodSize = 2 * SNIC_SuperPixelSize;
现在添加一个对SNIC函数的调用。您将注意到,它接受代码第2节中指定的参数,并将它们发送给SNIC算法。将下面的代码放入脚本中,作为代码的第5节,位于第4节和第6节之间:
javascript
// 5. SNIC Clustering
// This function returns a multi-banded image that has had SNIC
// applied to it. It automatically determine the new names
// of the bands that will be returned from the segmentation.
print('5.1 Execute SNIC');
var SNIC_MultiBandedResults = afn_SNIC(
ImageToUse,
SNIC_SuperPixelSize,
SNIC_Compactness,
SNIC_Connectivity,
SNIC_NeighborhoodSize,
SNIC_SeedShape
);
var SNIC_MultiBandedResults = SNIC_MultiBandedResults
.reproject('EPSG:3857', null, nativeScaleOfImage);
print('5.2 SNIC Multi-Banded Results', SNIC_MultiBandedResults);
Map.addLayer(SNIC_MultiBandedResults.select('clusters')
.randomVisualizer(), {}, '5.3 SNIC Segment Clusters', true, 1);
var theSeeds = SNIC_MultiBandedResults.select('seeds');
Map.addLayer(theSeeds, {
palette: 'red'
}, '5.4 Seed points of clusters', true, 1);
var bandMeansToDraw = threeBandsToDraw.map(afn_addMeanToBandName);
print('5.5 band means to draw', bandMeansToDraw);
var clusterMeans = SNIC_MultiBandedResults.select(bandMeansToDraw);
print('5.6 Cluster Means by Band', clusterMeans);
Map.addLayer(clusterMeans, {
min: drawMin,
max: drawMax
}, '5.7 Image repainted by segments', true, 0);
现在运行脚本。它将绘制几个图层,下图所示的图层在最上面。
这显示了SNIC在发送给它的图像上的工作------在这种情况下,在波段8、11和12的合成上。如果你仔细观察彩色图层,你可以看到红色的小"种子"像素。为了启动这个过程,需要创建这些种子,并按照传递给函数的参数所给出的间隔形成正方形或六角形的"超级像素"。然后,这些块的边缘被推拉,并在输入图像的边缘处停止。作为算法的一部分,一些超级像素被合并成更大的块,这就是为什么你会发现一些形状包含两个或更多的种子像素。通过改变第5层的透明度来探索结构,以判断对于给定的参数值集图像分割的表现。你也可以比较5.7层和3.1层。5.7层是3.1层的重新解释,其中5.3层中给定形状中的每个像素都被分配了形状内像素的平均值。当以一种对给定项目目标有用的方式进行参数化时,图像中同质的部分将获得相同的颜色,而高异质性的区域将获得多种颜色。
现在你可以花些时间研究控制代码参数的效果来回答下面的问题。
问题1:改变参数SNIC_SuperPixelSize对SNIC集群有什么影响?
问题2:改变参数SNIC_Compactness会有什么影响?
问题3:改变参数SNIC_Connectivity和SNIC_SeedShape会有什么影响?
本教程中使用的k-means分类器没有意识到我们通常更喜欢将相邻像素分组到同一类中------它没有物理空间的感觉。这就是为什么你会在无监督分类中看到噪音。但是,因为我们重新着色了SNIC集群中的像素以共享完全相同的频带值,k-means将每个集群中的所有像素分组为具有相同的类。在最好的情况下,这允许我们从基于像素增强我们的分类,以显示景观中干净和明确的对象。在本节中,我们将对这些对象进行分类,探索在此图像中寻找对象的优势和局限性。返回SNIC设置的第一个值,即:
javascript
// The superpixel seed location spacing, in pixels.
var SNIC_SuperPixelSize = 16;
// Larger values cause clusters to be more compact (square/hexagonal).
// Setting this to 0 disables spatial distance weighting.
var SNIC_Compactness = 0;
// Connectivity. Either 4 or 8.
var SNIC_Connectivity = 4;
// Either 'square' or 'hex'.
var SNIC_SeedShape = 'square';
作为代码6.2节,添加这段代码,它将调用SNIC函数并绘制结果:
javascript
// 6.2 SNIC Unsupervised Classification for Comparison
var bandMeansNames = bandsToUse.map(afn_addMeanToBandName);
print('6.2 band mean names returned by segmentation', bandMeansNames);
var meanSegments = SNIC_MultiBandedResults.select(bandMeansNames);
var SegmentUnsupervised = afn_Kmeans(meanSegments,
numberOfUnsupervisedClusters, defaultStudyArea,
nativeScaleOfImage);
Map.addLayer(SegmentUnsupervised.randomVisualizer(), {},
'6.3 SNIC Clusters Unsupervised', true, 0);
print('6.3b Per-Segment Unsupervised Results:', SegmentUnsupervised);
当您运行脚本时,新函数将在5.7层中对图像进行分类,这是根据第5层中显示的片段对原始图像进行重新着色。比较超级像素的分类(6.3)和逐像素值的无监督分类(6.1)。您应该能够更改这两个层的透明度,以便直接比较它们。
SNIC分割算法有效解决了大区域计算数量的难题,关于其中最优参数的调节要根据具体数据的使用情况来看,如果研究区域太大,可以将研究区分为几个小瓦片进行同样也可以完成分割。