Strategi Klasifikasi Citra Penginderaan Jauh

#BelajarGEE 13: Optimalisasi klasifikasi multispektral di Google Earth Engine

Wahyu Ramadhan
12 min readFeb 8, 2024

Halo semuanya,

Sebelumnya saya pernah membuat konten tentang klasifikasi untuk Land Use/Land Cover (LULC) dengan metode klasifikasi multispektral di sini. Seiring berjalannya waktu saya mendapatkan berbagai referensi yang membuat saya menyadari bahwa ternyata ada kekurangan yang perlu dilengkapi. Hal tersebut terkait dengan cara pengambilan training data atau training sample (beda istilah tapi maksudnya sama, selanjutnya akan saya sebut sebagai training sample).

Saya langsung kepada inti pembahasan, setelahnya saya akan sertakan script Google Earth Engine (GEE) beserta keterangannya. Jadi, kenapa cara pengambilan training sample perlu diperhatikan? Ya sudah pasti jawabannya adalah karena itu akan mempengaruhi kualitas sampel dan tingkat akurasi dari model LULC yang menjadi output nantinya. Sayangnya, itu tidak saya point out dalam konten saya sebelumnya, padahal fundamental dan penting.

Sebenarnya apa itu training sample(s)? Dalam klasifikasi citra penginderaan jauh hal tersebut adalah kumpulan data yang digunakan untuk melatih model klasifikasi (training data). Tidak hanya itu, tapi juga sebagai data uji (test data) untuk menilai tingkat akurasi dari output. Nantinya model akan mempelajari fitur dan pola dari data dalam suatu epoch atau jumlah kali seluruh training data yang dilewati oleh algoritma, katakanlah metode yang kita gunakan adalah machine learning [4].

Training sample seharusnya berupa poligon

Tulisan saya sebelumnya menggunakan titik/point untuk mengambil sampel, sebenarnya itu kurang tepat karena hanya mewakili 1 (satu) piksel saja. Padahal bisa jadi piksel-piksel di sekitarnya juga merupakan objek dengan kelas yang sama atau sebaliknya. Maka dari itu sebaiknya kita gunakan poligon yang bisa mengambil spatial extent dan dari objek dengan lebih akurat serta representatif [1] [2].

Single piksel tidak cukup merepresentasikan suatu objek, apalagi kalau citranya memiliki resolusi menengah s/d rendah
Training sample berupa point hanya mewakili piksel tunggal (single pixel)

Justifikasi lain mengapa harus memakai poligon untuk membuat training sample adalah mengurangi efek spatial autocorrelation serta heterogenitas dari data citra. Spatial autocorrelation misalnya piksel yang terletak berdekatan lebih memiliki kemiripan daripada piksel di kejauhan. Sedangkan heterogenitas berarti satu kelas yang sama dapat memiliki ciri spektral (spectral signatures) di lokasi yang berbeda (pada kelas badan air, terdapat laut, sungai, danau dsb) [1] [2] [3].

Training sample dengan poligon yang lebih adaptif dan representatif terhadap bentuk objek[5]

Sebetulnya ada alasan tambahan, tapi menurut saya bahasannya sudah jauh ke ranah bagaimana pekerjaan algoritma machine learning dan/atau deep learning terbantu oleh training sample. Tapi yang jelas tujuannya adalah meningkatkan kualitas dan akurasi output.

Menentukan jumlah dan ukuran training sample

Terdapat formula yang dapat menjadi rujukan dalam pengambilan jumlah training area yakni n+1. Lalu, karena bentuk training sample adalah poligon, maka itu juga harus memenuhi syarat jumlah piksel yang digunakan yakni 100n. Keduanya disini sama-sama menggunakan n= jumlah band yang digunakan [1] [2] [4].

Konten saya ini menggunakan data citra PlanetScope dengan band NIR (b8), Red (b6), Green (b4) dan Blue (b2), jika citra lain yang digunakan maka tinggal disesuaikan saja. Detailnya adalah seperti ini:

  • Konteksnya ini adalah mengambil sampel dari kelas vegetasi
  • n= 4
  • Jumlah minimal lokasi sampel kelas vegetasi n+1= 4+1= 4 lokasi yang diambil secara menyebar.
  • Kemudian masing-masing sampel harus memenuhi jumlah piksel yang jika dijumlahkan harus memenuhi 100n=100(4)=400 piksel.
  • Jika dibagi secara merata untuk masing-masing lokasi sampel kelas vegetasi maka 400 piksel / 4 lokasi = 100 piksel lokasi sampel.

Langkah pemrosesan

1. Load data citra

Kita akan gunakan data citra PlanetScope resolusi spasial 3 meter, data ini diakuisisi pada 27 Oktober 2023. Kunjungi link ini untuk mengunduh datanya.

  • Map.setOptions('SATELLITE'): Mengatur opsi tampilan basemap menjadi mode satelit ketika perintah dijalankan.
  • Map.centerObject(image, 13): Membuat tampilan peta berada di tengah-tengah objek yang ditentukan yakni image dengan tingkat zoom sebesar 13.
  • Map.addLayer(image, { min:0, max: 3000, bands: ['b6', 'b4','b2']}, 'Bali Strait 27 October 23 (true color)')Menambahkan layer citra ‘image’ ke peta dengan parameter sebagai berikut:
    (a) min: Nilai piksel minimum yang akan ditampilkan.
    (b) max: Nilai piksel maksimum yang akan ditampilkan.
    (c) bands: Daftar band yang akan ditampilkan (b6, b4, dan b2).
    (d) Judul layer ditetapkan sebagai Bali Strait 27 October 23 (true color) dan Bali Strait 27 October 23 (false color).
Map.setOptions('SATELLITE')
Map.centerObject(image, 13)

// Show image
Map.addLayer(image,{
min:0,
max: 3000,
bands: ['b6', 'b4','b2']},
'Bali Strait 27 October 23 (true color)')

Map.addLayer(image,{
min:0,
max: 4000,
bands: ['b8', 'b3','b4']},
'Bali Strait 27 October 23 (false color)')
Tampilan citra komposit true color (kiri) dan false color (kanan)

2. Tentukan band yang digunakan

Saluran (band) yang digunakan sebetulnya umum saja, yakni true color atau band red (b6), green (b4) dan blue (b2) ditambah dengan satu band lagi yaitu Near-Infrared (NIR) (b8). Nomor band bisa berbeda tergantung dari produk citra satelit yang digunakan.

  • var bands: Ini adalah variabel yang digunakan untuk menyimpan daftar band yang akan digunakan dalam analisis. Pada kasus ini, terdapat empat band yang ditentukan: 'b8', 'b6', 'b4', 'b2'.
  • 'b8', 'b6', 'b4', 'b2': Merujuk pada nama-nama band spektral dalam citra PlanetScope yang akan digunakan untuk analisis. Misalnya, 'b8' merujuk pada Near Infrared (NIR), 'b6' merujuk pada band Red, 'b4' pada Green, dan 'b2' pada Blue.
// Define bands
var bands = ['b8', 'b6', 'b4', 'b2'];

3. Menyiapkan training sample

Saya akan membuat dua opsi, yakni menggunakan point dengan buffer sehingga sampel tetap merupakan poligon (dan lebih cepat untuk kita yang mager 😁), serta poligon dimana kita tracing objek secara manual. Kalau poligon, maka kita akan membuat training sample sebagai geometri-geometri yang terpisah berdasarkan kelas LULC.

  • ee.FeatureCollection: Ini adalah kumpulan fitur (features) di GEE yang digunakan untuk membuatt training sample.
  • water, vegetation, builtup, bareland: Merupakan titik-titik yang mewakili kelas-kelas yang berbeda dalam klasifikasi LULC.
  • .flatten(): Mengubah feature collection menjadi satu koleksi (semacam merging objek).
  • .map(function(feat) { return feat.buffer(10); }): Membuat buffer dengan radius 10 meter dari titik tengah training sample untuk menjadikan poligon (jika kita menggunakan tipe point untuk membuat training sample).

Opsi lain (jika training sample adalah poligon)

  • Opsi pertama menggunakan flatten() pada ee.FeatureCollection untuk menghasilkan training sample dari semua titik yang ada.
  • Opsi kedua menggunakan .merge(), cara kerjanya mirip dengan .flatten() yakni menggabungkan geometri dari training sample tiap kelas menjadi satu.
// Sample
// If you use point with buffer to create training sample
// var sample = ee.FeatureCollection([
// water, vegetation, builtup, bareland]).flatten().map(function(feat){
// return feat.buffer(10);
// });

// If you use shape or polygon to create roi
var sample = ee.FeatureCollection([water, vegetation, builtup, bareland]).flatten(); //use this
//var sample = water.merge(vegetation).merge(builtup).merge(bareland); //or use this
Pengaturan masing-masing kelas pada tiap feature untuk training sample

4. Menghitung jumlah piksel

Sementara itu, cara untuk menghitung jumlah piksel yang telah diperoleh dalam setiap kelas adalah seperti berikut ini. Namun, jika kita gunakan point yang di buffer untuk mengambil sampel, jumlah piksel akan diasumsikan sama dengan jumlah point yang dibuat tiap kelasnya.

  • reduceRegion: Fungsi ini menghitung statistik untuk setiap wilayah yang ditentukan dalam parameter geometry. Fungsi ini mengagregasi nilai-nilai piksel yang berpotongan dengan geometri yang diberikan (dalam konteks ini seperti point atau poligon) sebuah citra sesuai dengan reducer yang ditentukan.
  • ee.Reducer.count(): Ini adalah reducer yang digunakan untuk menghitung jumlah piksel yang berpotongan dengan geometri yang ditentukan. Reducer ini menghitung jumlah piksel yang berada di dalam setiap training sample.
  • geometry: Setiap parameter geometry menentukan area yang dijadikan training sample. Dalam variabel ini, terdapat geometri terpisah untuk water (air), vegetation (vegetasi) danbuiltup (lahan terbangun), bareland (tanah terbuka). Ini menentukan area di citra di mana perhitungan jumlah piksel akan dilakukan.
  • scale: Parameter ini menentukan skala nominal dalam meter dari proyeksi yang digunakan. Digunakan untuk menentukan resolusi spasial untuk perhitungan. Parameter skala ini memengaruhi area di mana reducer menghitung statistik. Data citra resolusi tinggi tentu membutuhkan resources komputasi yang besar.
  • countWater, countVegetation, countBuiltup, countBareland: Variabel-variabel ini menyimpan hasil dari reduceRegion untuk masing-masing geometri. Semua variabel ini berisi jumlah piksel dalam area yang ditentukan.
  • print: Fungsi print digunakan untuk menampilkan jumlah piksel yang dihitung untuk setiap kelas (water, vegetation, builtup, bareland) untuk tampil di console.

Catatan yang saya bisa sampaikan adalah, meskipun gambar di bawah menunjukkan jumlah piksel dari 8 band, tapi yang akan dimasukkan ke dalam pemrosesan tetap band yang sudah diseleksi.

// Calculate the number of pixels within each class
var countWater = image.reduceRegion({
reducer: ee.Reducer.count(),
geometry: water,
scale: 3 // Adjust the scale based on your image resolution
});

var countVegetation = image.reduceRegion({
reducer: ee.Reducer.count(),
geometry: vegetation,
scale: 3
});

var countBuiltup = image.reduceRegion({
reducer: ee.Reducer.count(),
geometry: builtup,
scale: 3
});

var countBareland = image.reduceRegion({
reducer: ee.Reducer.count(),
geometry: bareland,
scale: 3
});

// Print the number of pixels for each class
print('Number of pixels in water:', countWater);
print('Number of pixels in vegetation:', countVegetation);
print('Number of pixels in builtup:', countBuiltup);
print('Number of pixels in bareland:', countBareland);
Jumlah piksel dari training sample pada setiap kelas

5. Ekstraksi sampel dari citra

  • image.select(bands): Memilih band-band yang telah ditentukan sebelumnya untuk digunakan dalam proses ekstraksi sampel.
  • .sampleRegions({}): Fungsi ini digunakan untuk mengekstrak sampel dari citra pada lokasi-lokasi yang telah diambil melalui training sample.
    (a) collection: sample: Menggunakan sample sebagai daerah untuk mengekstrak sampel, yang telah ditentukan sebelumnya dari training sample.
    (b) properties: ['class']: Menambahkan properti kelas ['class'] ke setiap sampel yang diekstrak. Ini berfungsi untuk menandai kelas yang sesuai dengan setiap sampel.
    (c) scale: 4: Menentukan skala untuk ekstraksi sampel. Ini adalah faktor pengubah yang mempengaruhi jumlah sampel yang diekstrak per area.
  • .randomColumn(): Menambahkan kolom acak ke sampel. Hal ini sering digunakan dalam proses pemisahan data menjadi set pelatihan dan pengujian, dengan cara membagi sampel berdasarkan nilai acak dalam kolom ini.
// Extract sample from image
var extract_lc = image.select(bands).sampleRegions({
collection: sample,
properties: ['class'],
scale: 4
}).randomColumn();
//print(extract_lc);

6. Membagi sampel untuk melatih dan menguji model klasifikasi

  • extract_lc: Merupakan kumpulan sampel yang telah diekstrak sebelumnya dari citra dan training sample.
  • .filter(ee.Filter.lte('random', 0.7)): Menggunakan filter untuk membagi sampel menjadi set pelatihan. Pada kasus ini, sampel dengan nilai acak kurang dari atau sama dengan 0.7 akan masuk ke dalam set pelatihan (train).
  • .filter(ee.Filter.gt('random', 0.3)): Menggunakan filter untuk membagi sampel menjadi set pengujian. Sampel dengan nilai acak (random value) lebih besar dari 0.3 akan masuk ke dalam set pengujian (test).

Pembagian ini dilakukan dengan menggunakan kolom acak yang telah ditambahkan sebelumnya ke dalam sampel. Dalam hal ini, sekitar 70% dari sampel akan digunakan untuk pelatihan model, sementara 30% sisanya akan digunakan untuk menguji kinerja model.

// Split train and test
var train = extract_lc.filter(ee.Filter.lte('random', 0.7));
var test = extract_lc.filter(ee.Filter.gt('random', 0.3));

7. Membuat model klasifikasi

  • ee.Classifier.smileRandomForest(50): Ini adalah fungsi yang membuat model klasifikasi menggunakan algoritma Random Forest dengan 50 pohon keputusan (decision trees). Algoritma ini digunakan untuk melatih model klasifikasi berdasarkan sampel yang diberikan.
  • .train(): Metode ini melatih model klasifikasi dengan menggunakan sampel pelatihan yang telah dibuat sebelumnya.
    (a) features: train: Menggunakan set pelatihan train sebagai fitur-fitur yang akan digunakan untuk melatih model.
    (b) classProperty: 'class': Menentukan properti kelas ['class'] sebagai label atau variabel yang akan diprediksi oleh model.
    (c) inputProperties: bands: Menggunakan daftar band yang telah ditentukan sebelumnya sebagai input properties untuk model.

Setelah melatih model dengan menggunakan sampel pelatihan, print(model.explain()) digunakan untuk mencetak hasil penjelasan dari model. Ini bisa memberikan informasi tentang bagaimana model melakukan klasifikasi berdasarkan input yang diberikan.

// Classification model
var model = ee.Classifier.smileRandomForest(50).train({
features: train,
classProperty: 'class',
inputProperties: bands
});
print(model.explain());
Penjelasan dari klasifikasi model dari 4 kelas dan 4 band

8. Menguji model klasifikasi

  • test.classify(model): Menggunakan model klasifikasi yang telah dilatih sebelumnya untuk mengklasifikasikan set pengujian (test), yang menghasilkan klasifikasi untuk setiap sampel dalam set pengujian.
  • classifiedTest.errorMatrix('class', 'classification'): Metode ini digunakan untuk menghitung matriks kebingungan (confusion matrix) berdasarkan kelas aktual ['class'] dan kelas hasil klasifikasi ('classification'] dari set pengujian yang telah diklasifikasikan.
  • print(): Digunakan untuk mencetak hasil uji akurasi seperti akurasi overall accuracy, indeks kappa, user accuracy, dan producer accuracy.
// Test model
var classifiedTest = test.classify(model);

// Confusion matrix
var cm = classifiedTest.errorMatrix('class', 'classification');
print('Confusion Matrix', cm, 'Overall Accuracy', cm.accuracy(), 'Kappa', cm.kappa());
print('User Accuracy', cm.consumersAccuracy());
print('Producer Accuracy', cm.producersAccuracy());
Hasil uji akurasi dari setiap kelas

9. Parameter visualisasi

  • values: Merupakan nilai-nilai yang mewakili masing-masing kelas yang dihasilkan dari klasifikasi. Misalnya, nilai 1 mewakili kelas air (Water), nilai 2 mewakili kelas vegetasi (Vegetation), dan seterusnya.
  • palette: Merupakan daftar warna yang akan digunakan untuk menampilkan setiap kelas dalam hasil klasifikasi. Misalnya, kelas air (Water) direpresentasikan dengan warna 'blue’, vegetasi (Vegetation) dengan warna 'green' dan seterusnya.
  • names: Merupakan daftar nama kelas yang sesuai dengan nilai-nilai yang telah ditentukan sebelumnya. Setiap nama kelas terkait dengan nilai yang sama dalam list values.
// Visualization Parameter
var values = [1, 2, 3, 4];
var palette = ['blue','green', 'red', 'yellow'];
var names = ['Water', 'Vegetation', 'Builtup', 'Bareland'];

10. Menerapkan model klasifikasi

  • image.classify(model, 'lc_class'): Melakukan klasifikasi menggunakan model yang telah dilatih (model) pada citra yang ingin diklasifikasikan secara keseluruhan (image). Hasil klasifikasi akan ditampung dalam variabel lc_class.
  • .set(): Digunakan untuk menambahkan properti atau metadata ke hasil klasifikasi. Dalam hal ini, dua properti ditambahkan:
    (a) 'lc_class_values': values: Menyimpan nilai-nilai yang mewakili kelas-kelas hasil klasifikasi.
    (b) 'lc_class_palette': palette: Menyimpan palet warna yang akan digunakan untuk visualisasi hasil klasifikasi.
// Apply model
var lc_class = image.classify(model, 'lc_class').set({
'lc_class_values': values,
'lc_class_palette': palette,
});

11. Menampilkan peta sebagai layer

  • Map.addLayer(): Fungsi ini menambahkan citra atau layer ke peta interaktif di GEE.
  • lc_class: Merupakan citra hasil klasifikasi LULC yang ingin ditampilkan.
  • { min: 1, max: 4, palette: palette }: Parameter yang digunakan untuk menampilkan citra.
    (a) min: 1: Nilai minimum pada peta warna yang ditetapkan untuk citra hasil klasifikasi.
    (b) max: 4: Nilai maksimum pada peta warna yang ditetapkan untuk citra hasil klasifikasi.
    (c) palette: palette: Menggunakan palet warna yang telah ditentukan sebelumnya untuk memetakan nilai-nilai kelas (dari 1 hingga 4) ke warna-warna yang sesuai.
  • 'Land Cover Bali Strait October 2023': Label yang digunakan untuk menamai citra hasil klasifikasi yang ditampilkan di peta.
// Display classified image with defined color palette
Map.addLayer(lc_class, {
min: 1,
max: 4,
palette: palette
}, 'Land Cover Bali Strait October 2023');
Hasil klasifikasi citra dengan algoritma Random Forest

12. Menampilkan legenda

  • Map.add(): Fungsi ini digunakan untuk menambahkan elemen ke dalam peta.
  • ui.Panel(): Membuat panel UI, yang berisi elemen-elemen visual yang akan ditampilkan sebagai legenda.
  • names.map(): Menggunakan metode map untuk membuat serangkaian panel yang berisi label dan kotak warna untuk setiap kelas LULC.
  • ui.Label('', { width: '30px', height: '15px', border: '0.5px solid black', backgroundColor: palette[index] }): Membuat kotak kecil dengan warna yang sesuai dari palet palette dan menampilkan warna LULC.
  • ui.Label(name, { height: '15px' }): Menampilkan nama kelas LULC.
  • ui.Panel.Layout.flow(): Mengatur tata letak panel, di sini panel disusun secara horizontal atau vertikal.
  • { position: 'bottom-left' }: Menentukan posisi legenda pada peta, dalam hal ini, posisi legenda ditetapkan di bagian bawah kiri peta.
// Legend
Map.add(
ui.Panel(
names.map(function(name, index){
return ui.Panel([
ui.Label('', { width: '30px', height: '15px', border: '0.5px solid black', backgroundColor: palette[index] }),
ui.Label(name, { height: '15px' })
], ui.Panel.Layout.flow('horizontal'));
}),
ui.Panel.Layout.flow('vertical'),
{ position: 'bottom-left' }
)
);
Legenda peta di bagian kiri bawah

13. Ekspor data ke Google Drive

  • Export.image.toDrive(): Fungsi ini digunakan untuk mengekspor citra atau data dari GEE ke Google Drive.
  • image: lc_class: Menentukan citra yang akan diekspor, dalam hal ini, citra hasil klasifikasi LULC (lc_class).
  • description: 'lc_balistrait_oct23': Deskripsi atau nama untuk proses ekspor ini.
  • maxPixels: 1e13: Batas maksimum jumlah piksel untuk proses ekspor. Nilai ini ditetapkan menjadi 1e13 (10¹³) untuk menghindari pembatasan jumlah piksel maksimum.
  • folder: 'Google Earth Engine': Nama folder dalam Google Drive tempat hasil ekspor akan disimpan.
  • scale: 3: Resolusi spasial dari citra yang akan diekspor, dalam satuan meter per piksel.
  • fileNamePrefix: 'Land Cover Bali Strait Oct 23': Nama file yang akan disimpan di Google Drive.
  • fileFormat: 'GeoTIFF': Format file hasil ekspor, dalam hal ini, GeoTIFF digunakan karena format tersebut mendukung metadata spasial yang kaya dan dapat digunakan dalam banyak perangkat lunak GIS.
// Export
Export.image.toDrive({
image: lc_class,
description: 'lc_balistrait_oct23',
maxPixels: 1e13,
folder: 'Google Earth Engine',
scale: 3,
fileNamePrefix: 'Land Cover Bali Strait Oct 23',
fileFormat: 'GeoTIFF'
});
Tampilan ekspor di Tasks

Link ke full script

Referensi

[1] Jensen, J. R. (2014). Remote Sensing of the environment an earth resource perspective. Pearson Ed.
[2] Jensen, J. R. (2015). Introductory Digital Image Processing a Remote Sensing Perspective. Pearson Education (US).
[3] Karasiak, N., Dejoux, J.-F., Monteil, C., & Sheeren, D. (2021b). Spatial dependence between training and test sets: Another pitfall of Classification Accuracy Assessment in remote sensing. Machine Learning, 111(7), 2715–2740. https://doi.org/10.1007/s10994-021-05972-1
[4] Zhang, H., He, J., Chen, S., Zhan, Y., Bai, Y., & Qin, Y. (2023). Comparing three methods of selecting training samples in supervised classification of Multispectral Remote Sensing Images. Sensors, 23(20), 8530. https://doi.org/10.3390/s23208530
[5] Hu, Y., An, R., Wang, B., Xing, F., & Ju, F. (2020). Shape adaptive neighborhood information-based semi-supervised learning for Hyperspectral Image Classification. Remote Sensing, 12(18), 2976. https://doi.org/10.3390/rs12182976
[6] Google. (2024). Machine learning in Earth engine | Google Earth engine | google for developers. Google. https://developers.google.com/earth-engine/guides/machine-learning
[6] Google. (2024). Supervised classification | google earth engine | google for developers. Google. https://developers.google.com/earth-engine/guides/classification

Connect with me on LinkedIn

--

--

Wahyu Ramadhan

Mapping my way through the GIScience universe. Join me on this journey!