Files
RS4OSINT/docs/B2_Interpreting_Images.html
Ollie Ballinger 91bc4d16a9 logo path
2023-04-17 13:36:03 +01:00

1723 lines
165 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.3.326">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Remote Sensing for OSINT - Interpreting Images</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="site_libs/quarto-nav/quarto-nav.js"></script>
<script src="site_libs/quarto-nav/headroom.min.js"></script>
<script src="site_libs/clipboard/clipboard.min.js"></script>
<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="site_libs/quarto-search/fuse.min.js"></script>
<script src="site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="./">
<link href="./B3_Image_Series.html" rel="next">
<link href="./B1_Getting_Started.html" rel="prev">
<link href="./favicon.ico" rel="icon">
<script src="site_libs/quarto-html/quarto.js"></script>
<script src="site_libs/quarto-html/popper.min.js"></script>
<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="site_libs/quarto-html/anchor.min.js"></script>
<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="site_libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-text-highlighting-styles">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-dark.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-text-highlighting-styles">
<script src="site_libs/bootstrap/bootstrap.min.js"></script>
<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="site_libs/bootstrap/bootstrap.min.css" rel="stylesheet" class="quarto-color-scheme" id="quarto-bootstrap" data-mode="light">
<link href="site_libs/bootstrap/bootstrap-dark.min.css" rel="stylesheet" class="quarto-color-scheme quarto-color-alternate" id="quarto-bootstrap" data-mode="dark">
<script id="quarto-search-options" type="application/json">{
"location": "sidebar",
"copy-button": false,
"collapse-after": 3,
"panel-placement": "start",
"type": "textbox",
"limit": 20,
"language": {
"search-no-results-text": "No results",
"search-matching-documents-text": "matching documents",
"search-copy-link-title": "Copy link to search",
"search-hide-matches-text": "Hide additional matches",
"search-more-match-text": "more match in this document",
"search-more-matches-text": "more matches in this document",
"search-clear-button-title": "Clear",
"search-detached-cancel-button-title": "Cancel",
"search-submit-button-title": "Submit"
}
}</script>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-RK9ZLZQ6GL"></script>
<script type="text/javascript">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-RK9ZLZQ6GL', { 'anonymize_ip': true});
</script>
</head>
<body class="nav-sidebar floating">
<div id="quarto-search-results"></div>
<header id="quarto-header" class="headroom fixed-top">
<nav class="quarto-secondary-nav">
<div class="container-fluid d-flex">
<button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
<i class="bi bi-layout-text-sidebar-reverse"></i>
</button>
<nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./B1_Getting_Started.html">B. Google Earth Engine</a></li><li class="breadcrumb-item"><a href="./B2_Interpreting_Images.html"><span class="chapter-number">5</span>&nbsp; <span class="chapter-title">Interpreting Images</span></a></li></ol></nav>
<a class="flex-grow-1" role="button" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
</a>
<button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();">
<i class="bi bi-search"></i>
</button>
</div>
</nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar -->
<nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal sidebar-navigation floating overflow-auto">
<div class="pt-lg-2 mt-2 text-left sidebar-header sidebar-header-stacked">
<a href="./index.html" class="sidebar-logo-link">
<img src="./images/logo_white.png" alt="" class="sidebar-logo py-0 d-lg-inline d-none">
</a>
<div class="sidebar-title mb-0 py-0">
<a href="./">Remote Sensing for OSINT</a>
<div class="sidebar-tools-main tools-wide">
<a href="https://github.com/oballinger/RS4OSINT/" title="Source Code" class="quarto-navigation-tool px-1" aria-label="Source Code"><i class="bi bi-github"></i></a>
<div class="dropdown">
<a href="" title="Download" id="quarto-navigation-tool-dropdown-0" class="quarto-navigation-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Download"><i class="bi bi-download"></i></a>
<ul class="dropdown-menu" aria-labelledby="quarto-navigation-tool-dropdown-0">
<li>
<a class="dropdown-item sidebar-tools-main-item" href="./Remote-Sensing-
-for-OSINT.pdf">
<i class="bi bi-bi-file-pdf pe-1"></i>
Download PDF
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="./Remote-Sensing-
-for-OSINT.epub">
<i class="bi bi-bi-journal pe-1"></i>
Download ePub
</a>
</li>
</ul>
</div>
<div class="dropdown">
<a href="" title="Share" id="quarto-navigation-tool-dropdown-1" class="quarto-navigation-tool dropdown-toggle px-1" data-bs-toggle="dropdown" aria-expanded="false" aria-label="Share"><i class="bi bi-share"></i></a>
<ul class="dropdown-menu" aria-labelledby="quarto-navigation-tool-dropdown-1">
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://twitter.com/intent/tweet?url=|url|">
<i class="bi bi-bi-twitter pe-1"></i>
Twitter
</a>
</li>
<li>
<a class="dropdown-item sidebar-tools-main-item" href="https://www.facebook.com/sharer/sharer.php?u=|url|">
<i class="bi bi-bi-facebook pe-1"></i>
Facebook
</a>
</li>
</ul>
</div>
<a href="" class="quarto-color-scheme-toggle quarto-navigation-tool px-1" onclick="window.quartoToggleColorScheme(); return false;" title="Toggle dark mode"><i class="bi"></i></a>
</div>
</div>
</div>
<div class="mt-2 flex-shrink-0 align-items-center">
<div class="sidebar-search">
<div id="quarto-search" class="" title="Search"></div>
</div>
</div>
<div class="sidebar-menu-container">
<ul class="list-unstyled mt-1">
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false">
<span class="menu-text">A. Introduction</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-1" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-1" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./index.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Overview</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./A2_Remote_Sensing.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Remote Sensing</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./A3_Data_Acquisition.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Data Acquisition</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="true">
<span class="menu-text">B. Google Earth Engine</span></a>
<a class="sidebar-item-toggle text-start" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-2" aria-expanded="true" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-2" class="collapse list-unstyled sidebar-section depth1 show">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B1_Getting_Started.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Getting Started</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B2_Interpreting_Images.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Interpreting Images</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B3_Image_Series.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Image Series</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./B4_Vectors_Tables.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Vectors and Tables</span></a>
</div>
</li>
</ul>
</li>
<li class="sidebar-item sidebar-item-section">
<div class="sidebar-item-container">
<a class="sidebar-item-text sidebar-link text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="false">
<span class="menu-text">C. Case Studies</span></a>
<a class="sidebar-item-toggle text-start collapsed" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar-section-3" aria-expanded="false" aria-label="Toggle section">
<i class="bi bi-chevron-right ms-2"></i>
</a>
</div>
<ul id="quarto-sidebar-section-3" class="collapse list-unstyled sidebar-section depth1 ">
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C1_Lights.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">War at Night</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C2_Refineries.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Refinery Identification</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C3_Blast.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Blast Damage Assessment</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C4_Ships.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Ship Detection</span></a>
</div>
</li>
<li class="sidebar-item">
<div class="sidebar-item-container">
<a href="./C5_Object_Detection.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">Object Detection</span></a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</nav>
<div id="quarto-sidebar-glass" data-bs-toggle="collapse" data-bs-target="#quarto-sidebar,#quarto-sidebar-glass"></div>
<!-- margin-sidebar -->
<div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
<nav id="TOC" role="doc-toc" class="toc-active">
<h2 id="toc-title">Table of contents</h2>
<ul>
<li><a href="#image-manipulation-bands-arithmetic-thresholds-and-masks" id="toc-image-manipulation-bands-arithmetic-thresholds-and-masks" class="nav-link active" data-scroll-target="#image-manipulation-bands-arithmetic-thresholds-and-masks">Image Manipulation: Bands, Arithmetic, Thresholds, and Masks</a>
<ul class="collapse">
<li><a href="#band-arithmetic-in-earth-engine" id="toc-band-arithmetic-in-earth-engine" class="nav-link" data-scroll-target="#band-arithmetic-in-earth-engine">Band Arithmetic in Earth Engine</a></li>
<li><a href="#thresholding-masking-and-remapping-images" id="toc-thresholding-masking-and-remapping-images" class="nav-link" data-scroll-target="#thresholding-masking-and-remapping-images">Thresholding, Masking, and Remapping Images</a></li>
<li><a href="#conclusion" id="toc-conclusion" class="nav-link" data-scroll-target="#conclusion">Conclusion</a></li>
<li><a href="#references" id="toc-references" class="nav-link" data-scroll-target="#references">References</a></li>
</ul></li>
<li><a href="#interpreting-an-image-classification" id="toc-interpreting-an-image-classification" class="nav-link" data-scroll-target="#interpreting-an-image-classification">Interpreting an Image: Classification</a>
<ul class="collapse">
<li><a href="#supervised-classification" id="toc-supervised-classification" class="nav-link" data-scroll-target="#supervised-classification">Supervised Classification</a></li>
<li><a href="#unsupervised-classification" id="toc-unsupervised-classification" class="nav-link" data-scroll-target="#unsupervised-classification">Unsupervised Classification</a></li>
<li><a href="#conclusion-1" id="toc-conclusion-1" class="nav-link" data-scroll-target="#conclusion-1">Conclusion</a></li>
<li><a href="#references-1" id="toc-references-1" class="nav-link" data-scroll-target="#references-1">References</a></li>
</ul></li>
<li><a href="#accuracy-assessment-quantifying-classification-quality" id="toc-accuracy-assessment-quantifying-classification-quality" class="nav-link" data-scroll-target="#accuracy-assessment-quantifying-classification-quality">Accuracy Assessment: Quantifying Classification Quality</a>
<ul class="collapse">
<li><a href="#quantifying-classification-accuracy-through-a-confusion-matrix" id="toc-quantifying-classification-accuracy-through-a-confusion-matrix" class="nav-link" data-scroll-target="#quantifying-classification-accuracy-through-a-confusion-matrix">Quantifying Classification Accuracy Through a Confusion Matrix</a></li>
<li><a href="#hyperparameter-tuning" id="toc-hyperparameter-tuning" class="nav-link" data-scroll-target="#hyperparameter-tuning">Hyperparameter tuning</a></li>
<li><a href="#conclusion-2" id="toc-conclusion-2" class="nav-link" data-scroll-target="#conclusion-2">Conclusion</a></li>
</ul></li>
</ul>
<div class="toc-actions"><div><i class="bi bi-github"></i></div><div class="action-links"><p><a href="https://github.com/oballinger/RS4OSINT/edit/main/B2_Interpreting_Images.qmd" class="toc-action">Edit this page</a></p></div></div></nav>
</div>
<!-- main -->
<main class="content" id="quarto-document-content">
<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title"><span class="chapter-title">Interpreting Images</span></h1>
</div>
<div class="quarto-title-meta">
</div>
</header>
<p>Now that you know how images are viewed and what kinds of images exist in Earth Engine, how do we manipulate them? To gain the skills of interpreting images, youll work with bands, combining values to form indices and masking unwanted pixels. Then, youll learn some of the techniques available in Earth Engine for classifying images and interpreting the results.</p>
<section id="image-manipulation-bands-arithmetic-thresholds-and-masks" class="level2">
<h2 class="anchored" data-anchor-id="image-manipulation-bands-arithmetic-thresholds-and-masks">Image Manipulation: Bands, Arithmetic, Thresholds, and Masks</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author">Author</h4>
<p>Karen Dyson, Andréa Puzzi Nicolau, David Saah, and Nicholas Clinton</p>
</section>
<section id="overview" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview">Overview</h4>
<p>Once images have been identified in Earth Engine, they can be viewed in a wide array of band combinations for targeted purposes. For users who are already versed in remote sensing concepts, this chapter shows how to do familiar tasks on this platform; for those who are entirely new to such concepts, it introduces the idea of band combinations.</p>
</section>
<section id="learning-outcomes" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes">Learning Outcomes</h4>
<ul>
<li>Understanding what spectral indices are and why they are useful.</li>
<li>Being introduced to a range of example spectral indices used for a variety of purposes.</li>
</ul>
</section>
<section id="assumes-you-know-how-to" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to">Assumes you know how to:</h4>
<ul>
<li>Import images and image collections, filter, and visualize (Part F1).</li>
</ul>
</section>
</div>
</div>
<section id="introduction" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction">Introduction</h3>
<p>Spectral indices are based on the fact that different objects and land covers on the Earths surface reflect different amounts of light from the Sun at different wavelengths. In the visible part of the spectrum, for example, a healthy green plant reflects a large amount of green light while absorbing blue and red light — which is why it appears green to our eyes. Light also arrives from the Sun at wavelengths outside what the human eye can see, and there are large differences in reflectances between living and nonliving land covers, and between different types of vegetation, both in the visible and outside the visible wavelengths. We visualized this earlier, in Chaps. F1.1 and F1.3 when we mapped color-infrared images (Fig. F2.0.1).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image39.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.1 Mapped color-IR images from multiple satellite sensors that we mapped in Chap. F1.3. The near infrared spectrum is mapped as red, showing where there are high amounts of healthy vegetation.</figcaption><p></p>
</figure>
</div>
<p>If we graph the amount of light (reflectance) at different wavelengths that an object or land cover reflects, we can visualize this more easily (Fig. F2.0.2). For example, look at the reflectance curves for soil and water in the graph below. Soil and water both have relatively low reflectance at wavelengths around 300 nm (ultraviolet and violet light). Conversely, at wavelengths above 700 nm (red and infrared light) soil has relatively high reflectance, while water has very low reflectance. Vegetation, meanwhile, generally reflects large amounts of near infrared light, relative to other land covers.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image32.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.2 A graph of the amount of reflectance for different objects on the Earths surface at different wavelengths in the visible and infrared portions of the electromagnetic spectrum. 1 micrometer (µm) = 1,000 nanometers (nm).</figcaption><p></p>
</figure>
</div>
<p>Spectral indices use math to express how objects reflect light across multiple portions of the spectrum as a single number. Indices combine multiple bands, often with simple operations of subtraction and division, to create a single value across an image that is intended to help to distinguish particular land uses or land covers of interest. Using Fig. F2.0.2, you can imagine which wavelengths might be the most informative for distinguishing among a variety of land covers. We will explore a variety of calculations made from combinations of bands in the following sections.</p>
<p>Indices derived from satellite imagery are used as the basis of many remote-sensing analyses. Indices have been used in thousands of applications, from detecting anthropogenic deforestation to examining crop health. For example, the growth of economically important crops such as wheat and cotton can be monitored throughout the growing season: Bare soil reflects more red wavelengths, whereas growing crops reflect more of the near-infrared (NIR) wavelengths. Thus, calculating a ratio of these two bands can help monitor how well crops are growing (Jackson and Huete 1991).</p>
</section>
<section id="band-arithmetic-in-earth-engine" class="level3">
<h3 class="anchored" data-anchor-id="band-arithmetic-in-earth-engine">Band Arithmetic in Earth Engine</h3>
<p>If you have not already done so, be sure to add the books code repository to the Code Editor by entering <a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829783542&amp;usg=AOvVaw2f8xfEZP6c0zP_Ke8jL26U"></a><a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829783919&amp;usg=AOvVaw2i09J44MzpMZkjV_JLEnNR">https://code.earthengine.google.com/?accept_repo=projects/gee-edu/book</a> into your browser. The books scripts will then be available in the script manager panel. If you have trouble finding the repo, you can visit <a href="https://www.google.com/url?q=https://docs.google.com/presentation/d/1Kt6wGNoesYm__Cu3k3bnlbbyPN6m9SF4hQHK-pIDHfc/edit%23slide%3Did.g18a7b4b055d_0_624&amp;sa=D&amp;source=editors&amp;ust=1671458829784270&amp;usg=AOvVaw1Kr82KG60ZeFLYC8cOZ67A">this link</a> for help.</p>
<p>Many indices can be calculated using band arithmetic in Earth Engine. Band arithmetic is the process of adding, subtracting, multiplying, or dividing two or more bands from an image. Here well first do this manually, and then show you some more efficient ways to perform band arithmetic in Earth Engine.</p>
<section id="arithmetic-calculation-of-ndvi" class="level4">
<h4 class="anchored" data-anchor-id="arithmetic-calculation-of-ndvi">Arithmetic Calculation of NDVI</h4>
<p>The red and near-infrared bands provide a lot of information about vegetation due to vegetations high reflectance in these wavelengths. Take a look at Fig. F2.0.2 and note, in particular, that vegetation curves (graphed in green) have relatively high reflectance in the NIR range (approximately 750900 nm). Also note that vegetation has low reflectance in the red range (approximately 630690 nm), where sunlight is absorbed by chlorophyll. This suggests that if the red and near-infrared bands could be combined, they would provide substantial information about vegetation.</p>
<p>Soon after the launch of Landsat 1 in 1972, analysts worked to devise a robust single value that would convey the health of vegetation along a scale of 1 to 1. This yielded the NDVI, using the formula:</p>
<p><img src="images/F2/image1.png" class="img-fluid"> (F2.0.1)</p>
<p>where NIR and red refer to the brightness of each of those two bands. As seen in Chaps. F1.1 and F1.2, this brightness might be conveyed in units of reflectance, radiance, or digital number (DN); the NDVI is intended to give nearly equivalent values across platforms that use these wavelengths. The general form of this equation is called a “normalized difference”—the numerator is the “difference” and the denominator “normalizes” the value. Outputs for NDVI vary between 1 and 1. High amounts of green vegetation have values around 0.80.9. Absence of green leaves gives values near 0, and water gives values near 1.</p>
<p>To compute the NDVI, we will introduce Earth Engines implementation of band arithmetic. Cloud-based band arithmetic is one of the most powerful aspects of Earth Engine, because the platforms computers are optimized for this type of heavy processing. Arithmetic on bands can be done even at planetary scale very quickly—an idea that was out of reach before the advent of cloud-based remote sensing. Earth Engine automatically partitions calculations across a large number of computers as needed, and assembles the answer for display.</p>
<p>As an example, lets examine an image of San Francisco (Fig. F2.0.3).</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">///// </span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">// Band Arithmetic </span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">///// </span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate NDVI using Sentinel 2 </span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co">// Import and filter imagery by location and date. </span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfoPoint <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>(<span class="op">-</span><span class="fl">122.3774</span><span class="op">,</span> <span class="fl">37.6194</span>)<span class="op">;</span> </span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> sfoImage <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'COPERNICUS/S2'</span>) </span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(sfoPoint) </span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(<span class="st">'2020-02-01'</span><span class="op">,</span> <span class="st">'2020-04-01'</span>) </span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>()<span class="op">;</span> </span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Display the image as a false color composite. </span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(sfoImage<span class="op">,</span> <span class="dv">11</span>)<span class="op">;</span> </span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(sfoImage<span class="op">,</span> { </span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> [<span class="st">'B8'</span><span class="op">,</span> <span class="st">'B4'</span><span class="op">,</span> <span class="st">'B3'</span>]<span class="op">,</span> </span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">2000</span>}<span class="op">,</span> <span class="st">'False color'</span>)<span class="op">;</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image46.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.3 False color Sentinel-2 imagery of San Francisco and surroundings</figcaption><p></p>
</figure>
</div>
<p>The simplest mathematical operations in Earth Engine are the add, subtract, multiply, and divide methods. Lets select the near-infrared and red bands and use these operations to calculate NDVI for our image.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Extract the near infrared and red bands. </span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> nir <span class="op">=</span> sfoImage<span class="op">.</span><span class="fu">select</span>(<span class="st">'B8'</span>)<span class="op">;</span> </span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> red <span class="op">=</span> sfoImage<span class="op">.</span><span class="fu">select</span>(<span class="st">'B4'</span>)<span class="op">;</span> </span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Calculate the numerator and the denominator using subtraction and addition respectively. </span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> numerator <span class="op">=</span> nir<span class="op">.</span><span class="fu">subtract</span>(red)<span class="op">;</span> </span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> denominator <span class="op">=</span> nir<span class="op">.</span><span class="fu">add</span>(red)<span class="op">;</span> </span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="co">// Now calculate NDVI. </span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndvi <span class="op">=</span> numerator<span class="op">.</span><span class="fu">divide</span>(denominator)<span class="op">;</span> </span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the layer to our map with a palette. </span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> vegPalette <span class="op">=</span> [<span class="st">'red'</span><span class="op">,</span> <span class="st">'white'</span><span class="op">,</span> <span class="st">'green'</span>]<span class="op">;</span> </span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(ndvi<span class="op">,</span> { </span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="dv">1</span><span class="op">,</span> </span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> vegPalette </span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'NDVI Manual'</span>)<span class="op">;</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Examine the resulting index, using the Inspector to pick out pixel values in areas of vegetation and non-vegetation if desired.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image50.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.4 NDVI calculated using Sentinel-2. Remember that outputs for NDVI vary between 1 and 1. High amounts of green vegetation have values around 0.80.9. Absence of green leaves gives values near 0, and water gives values near 1.</figcaption><p></p>
</figure>
</div>
<p>Using these simple arithmetic tools, you can build almost any index, or develop and visualize your own. Earth Engine allows you to quickly and easily calculate and display the index across a large area.</p>
</section>
<section id="single-operation-computation-of-normalized-difference-for-ndvi" class="level4">
<h4 class="anchored" data-anchor-id="single-operation-computation-of-normalized-difference-for-ndvi">Single-Operation Computation of Normalized Difference for NDVI</h4>
<p>Normalized differences like NDVI are so common in remote sensing that Earth Engine provides the ability to do that particular sequence of subtraction, addition, and division in a single step, using the normalizedDifference method. This method takes an input image, along with bands you specify, and creates a normalized difference of those two bands. The NDVI computation previously created with band arithmetic can be replaced with one line of code:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Now use the built-in normalizedDifference function to achieve the same outcome. </span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndviND <span class="op">=</span> sfoImage<span class="op">.</span><span class="fu">normalizedDifference</span>([<span class="st">'B8'</span><span class="op">,</span> <span class="st">'B4'</span>])<span class="op">;</span> </span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(ndviND<span class="op">,</span> { </span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="dv">1</span><span class="op">,</span> </span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> vegPalette </span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'NDVI normalizedDiff'</span>)<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note that the order in which you provide the two bands to normalizedDifference is important. We use B8, the near-infrared band, as the first parameter, and the red band B4 as the second. If your two computations of NDVI do not look identical when drawn to the screen, check to make sure that the order you have for the NIR and red bands is correct.</p>
</section>
<section id="using-normalized-difference-for-ndwi" class="level4">
<h4 class="anchored" data-anchor-id="using-normalized-difference-for-ndwi">Using Normalized Difference for NDWI</h4>
<p>As mentioned, the normalized difference approach is used for many different indices. Lets apply the same normalizedDifference method to another index.</p>
<p>The Normalized Difference Water Index (NDWI) was developed by Gao (1996) as an index of vegetation water content. The index is sensitive to changes in the liquid content of vegetation canopies. This means that the index can be used, for example, to detect vegetation experiencing drought conditions or differentiate crop irrigation levels. In dry areas, crops that are irrigated can be differentiated from natural vegetation. It is also sometimes called the Normalized Difference Moisture Index (NDMI). NDWI is formulated as follows:</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image2.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">(F2.0.2)</figcaption><p></p>
</figure>
</div>
<p>where NIR is near-infrared, centered near 860 nm (0.86 μm), and SWIR is short-wave infrared, centered near 1,240 nm (1.24 μm).</p>
<p>Compute and display NDWI in Earth Engine using the normalizedDifference method. Remember that for Sentinel-2, B8 is the NIR band and B11 is the SWIR band (refer to Chaps. F1.1 and F1.3 to find information about imagery bands).</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Use normalizedDifference to calculate NDWI </span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> ndwi <span class="op">=</span> sfoImage<span class="op">.</span><span class="fu">normalizedDifference</span>([<span class="st">'B8'</span><span class="op">,</span> <span class="st">'B11'</span>])<span class="op">;</span> </span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> waterPalette <span class="op">=</span> [<span class="st">'white'</span><span class="op">,</span> <span class="st">'blue'</span>]<span class="op">;</span> </span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(ndwi<span class="op">,</span> { </span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="fl">0.5</span><span class="op">,</span> </span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> waterPalette </span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>}<span class="op">,</span> <span class="st">'NDWI'</span>)<span class="op">;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Examine the areas of the map that NDVI identified as having a lot of vegetation. Notice which are more blue. This is vegetation that has higher water content.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image40.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.5 NDWI displayed for Sentinel-2 over San Francisco</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F20a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="thresholding-masking-and-remapping-images" class="level3">
<h3 class="anchored" data-anchor-id="thresholding-masking-and-remapping-images">Thresholding, Masking, and Remapping Images</h3>
<p>The previous section in this chapter discussed how to use band arithmetic to manipulate images. Those methods created new continuous values by combining bands within an image. This section uses logical operators to categorize band or index values to create a categorized image.</p>
<section id="implementing-a-threshold" class="level4">
<h4 class="anchored" data-anchor-id="implementing-a-threshold">Implementing a Threshold</h4>
<p>Implementing a threshold uses a number (the threshold value) and logical operators to help us partition the variability of images into categories. For example, recall our map of NDVI. High amounts of vegetation have NDVI values near 1 and non-vegetated areas are near 0. If we want to see what areas of the map have vegetation, we can use a threshold to generalize the NDVI value in each pixel as being either “no vegetation” or “vegetation”. That is a substantial simplification, to be sure, but can help us to better comprehend the rich variation on the Earths surface. This type of categorization may be useful if, for example, we want to look at the proportion of a city that is vegetated. Lets create a Sentinel-2 map of NDVI near Seattle, Washington, USA. Enter the code below in a new script.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Create an NDVI image using Sentinel 2. </span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaPoint <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>(<span class="op">-</span><span class="fl">122.2040</span><span class="op">,</span> <span class="fl">47.6221</span>)<span class="op">;</span> </span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaImage <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'COPERNICUS/S2'</span>) </span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(seaPoint) </span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(<span class="st">'2020-08-15'</span><span class="op">,</span> <span class="st">'2020-10-01'</span>) </span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>()<span class="op">;</span> </span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaNDVI <span class="op">=</span> seaImage<span class="op">.</span><span class="fu">normalizedDifference</span>([<span class="st">'B8'</span><span class="op">,</span> <span class="st">'B4'</span>])<span class="op">;</span> </span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co">// And map it. </span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(seaPoint<span class="op">,</span> <span class="dv">10</span>)<span class="op">;</span> </span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> vegPalette <span class="op">=</span> [<span class="st">'red'</span><span class="op">,</span> <span class="st">'white'</span><span class="op">,</span> <span class="st">'green'</span>]<span class="op">;</span> </span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(seaNDVI<span class="op">,</span> </span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="op">-</span><span class="dv">1</span><span class="op">,</span> </span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> vegPalette </span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'NDVI Seattle'</span>)<span class="op">;</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image30.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.6 NDVI image of Sentinel-2 imagery over Seattle, Washington, USA</figcaption><p></p>
</figure>
</div>
<p>Inspect the image. We can see that vegetated areas are darker green while non-vegetated locations are white and water is pink. If we use the Inspector to query our image, we can see that parks and other forested areas have an NDVI over about 0.5. Thus, it would make sense to define areas with NDVI values greater than 0.5 as forested, and those below that threshold as not forested.</p>
<p>Now lets define that value as a threshold and use it to threshold our vegetated areas.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Implement a threshold. </span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaVeg <span class="op">=</span> seaNDVI<span class="op">.</span><span class="fu">gt</span>(<span class="fl">0.5</span>)<span class="op">;</span> </span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Map the threshold. </span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(seaVeg<span class="op">,</span> </span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'white'</span><span class="op">,</span> <span class="st">'green'</span>] </span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'Non-forest vs. Forest'</span>)<span class="op">;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>The gt method is from the family of Boolean operators — that is, gt is a function that performs a test in each pixel and returns the value 1 if the test evaluates to true, and 0 otherwise. Here, for every pixel in the image, it tests whether the NDVI value is greater than 0.5. When this condition is met, the layer seaVeg gets the value 1. When the condition is false, it receives the value 0.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image47.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.7 Thresholded forest and non-forest image based on NDVI for Seattle, Washington, USA</figcaption><p></p>
</figure>
</div>
<p>Use the Inspector tool to explore this new layer. If you click on a green location, that NDVI should be greater than 0.5. If you click on a white pixel, the NDVI value should be equal to or less than 0.5.</p>
<p>Other operators in this Boolean family include less than (lt), less than or equal to (lte), equal to (eq), not equal to (neq), and greater than or equal to (gte) and more.</p>
</section>
<section id="building-complex-categorizations-with-.where" class="level4">
<h4 class="anchored" data-anchor-id="building-complex-categorizations-with-.where">Building Complex Categorizations with .where</h4>
<p>A binary map classifying NDVI is very useful. However, there are situations where you may want to split your image into more than two bins. Earth Engine provides a tool, the where method, that conditionally evaluates to true or false within each pixel depending on the outcome of a test. This is analogous to an if statement seen commonly in other languages. However, to perform this logic when programming for Earth Engine, we avoid using the JavaScript if statement. Importantly, JavaScript if commands are not calculated on Googles servers, and can create serious problems when running your code — in effect, the servers try to ship all of the information to be executed to your own computers browser, which is very underequipped for such enormous tasks. Instead, we use the where clause for conditional logic.</p>
<p>Suppose instead of just splitting the forested areas from the non-forested areas in our NDVI, we want to split the image into likely water, non-forested and forested areas. We can use where and thresholds of -0.1 and 0.5. We will start by creating an image using ee.Image. We then clip the new image so that it covers the same area as our seaNDVI layer.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Implement .where. </span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a starting image with all values = 1. </span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaWhere <span class="op">=</span> ee<span class="op">.</span><span class="fu">Image</span>(<span class="dv">1</span>) <span class="co">// Use clip to constrain the size of the new image. .clip(seaNDVI.geometry()); </span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Make all NDVI values less than -0.1 equal 0. </span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>seaWhere <span class="op">=</span> seaWhere<span class="op">.</span><span class="fu">where</span>(seaNDVI<span class="op">.</span><span class="fu">lte</span>(<span class="op">-</span><span class="fl">0.1</span>)<span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span> </span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Make all NDVI values greater than 0.5 equal 2. </span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>seaWhere <span class="op">=</span> seaWhere<span class="op">.</span><span class="fu">where</span>(seaNDVI<span class="op">.</span><span class="fu">gte</span>(<span class="fl">0.5</span>)<span class="op">,</span> <span class="dv">2</span>)<span class="op">;</span> </span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Map our layer that has been divided into three classes. </span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(seaWhere<span class="op">,</span> </span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">2</span><span class="op">,</span> </span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'blue'</span><span class="op">,</span> <span class="st">'white'</span><span class="op">,</span> <span class="st">'green'</span>] </span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'Water, Non-forest, Forest'</span>)<span class="op">;</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>There are a few interesting things to note about this code that you may not have seen before. First, were not defining a new variable for each where call. As a result, we can perform many where calls without creating a new variable each time and needing to keep track of them. Second, when we created the starting image, we set the value to 1. This means that we could easily set the bottom and top values with one where clause each. Finally, while we did not do it here, we can combine multiple where clauses using and and or. For example, we could identify pixels with an intermediate level of NDVI using seaNDVI.gte(-0.1).and(seaNDVI.lt(0.5)).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image37.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.8 Thresholded water, forest, and non-forest image based on NDVI for Seattle, Washington, USA.</figcaption><p></p>
</figure>
</div>
</section>
<section id="masking-specific-values-in-an-image" class="level4">
<h4 class="anchored" data-anchor-id="masking-specific-values-in-an-image">Masking Specific Values in an Image</h4>
<p>Masking an image is a technique that removes specific areas of an image — those covered by the mask — from being displayed or analyzed. Earth Engine allows you to both view the current mask and update the mask.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Implement masking. </span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="co">// View the seaVeg layer's current mask. </span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(seaPoint<span class="op">,</span> <span class="dv">9</span>)<span class="op">;</span> </span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(seaVeg<span class="op">.</span><span class="fu">mask</span>()<span class="op">,</span> {}<span class="op">,</span> <span class="st">'seaVeg Mask'</span>)<span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image23.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.9 The existing mask for the seaVeg layer we created previously</figcaption><p></p>
</figure>
</div>
<p>You can use the Inspector to see that the black area is masked and the white area has a constant value of 1. This means that data values are mapped and available for analysis within the white area only.</p>
<p>Now suppose we only want to display and conduct analyses in the forested areas. Lets mask out the non-forested areas from our image. First, we create a binary mask using the equals (eq) method.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Create a binary mask of non-forest. </span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> vegMask <span class="op">=</span> seaVeg<span class="op">.</span><span class="fu">eq</span>(<span class="dv">1</span>)<span class="op">;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>In making a mask, you set the values you want to see and analyze to be a number greater than 0. The idea is to set unwanted values to get the value of 0. Pixels that had 0 values become masked out (in practice, they do not appear on the screen at all) once we use the updateMask method to add these values to the existing mask.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Update the seaVeg mask with the non-forest mask. </span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> maskedVeg <span class="op">=</span> seaVeg<span class="op">.</span><span class="fu">updateMask</span>(vegMask)<span class="op">;</span> </span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Map the updated Veg layer </span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(maskedVeg<span class="op">,</span> </span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">1</span><span class="op">,</span> </span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'green'</span>] </span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'Masked Forest Layer'</span>)<span class="op">;</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Turn off all of the other layers. You can see how the maskedVeg layer now has masked out all non-forested areas.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image26.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.10 An updated mask now displays only the forested areas. Non-forested areas are masked out and transparent.</figcaption><p></p>
</figure>
</div>
<p>Map the updated mask for the layer and you can see why this is.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Map the updated mask </span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(maskedVeg<span class="op">.</span><span class="fu">mask</span>()<span class="op">,</span> {}<span class="op">,</span> <span class="st">'maskedVeg Mask'</span>)<span class="op">;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image33.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.11 The updated mask. Areas of non-forest are now masked out as well (black areas of the image).</figcaption><p></p>
</figure>
</div>
</section>
<section id="remapping-values-in-an-image" class="level4">
<h4 class="anchored" data-anchor-id="remapping-values-in-an-image">Remapping Values in an Image</h4>
<p>Remapping takes specific values in an image and assigns them a different value. This is particularly useful for categorical datasets, including those you read about in Chap. F1.2 and those we have created earlier in this chapter.</p>
<p>Lets use the remap method to change the values for our seaWhere layer. Note that since were changing the middle value to be the largest, well need to adjust our palette as well.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Implement remapping. </span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="co">// Remap the values from the seaWhere layer. </span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> seaRemap <span class="op">=</span> seaWhere<span class="op">.</span><span class="fu">remap</span>([<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">2</span>]<span class="op">,</span> <span class="co">// Existing values. [9, 11, 10]); // Remapped values. </span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(seaRemap<span class="op">,</span> </span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a> { </span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">9</span><span class="op">,</span> </span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">11</span><span class="op">,</span> </span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'blue'</span><span class="op">,</span> <span class="st">'green'</span><span class="op">,</span> <span class="st">'white'</span>] </span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span> <span class="st">'Remapped Values'</span>)<span class="op">;</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Use the inspector to compare values between our original seaWhere (displayed as Water, Non-Forest, Forest) and the seaRemap, marked as “Remapped Values.” Click on a forested area and you should see that the Remapped Values should be 10, instead of 2 (Fig. F2.0.12).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image28.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.0.12 For forested areas, the remapped layer has a value of 10, compared with the original layer, which has a value of 2. You may have more layers in your Inspector.</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F20b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
</section>
<section id="conclusion" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion">Conclusion</h3>
<p>In this chapter, you learned how to select multiple bands from an image and calculate indices. You also learned about thresholding values in an image, slicing them into multiple categories using thresholds. It is also possible to work with one set of class numbers and remap them quickly to another set. Using these techniques, you have some of the basic tools of image manipulation. In subsequent chapters you will encounter more complex and specialized image manipulation techniques, including pixel-based image transformations (Chap. F3.1), neighborhood-based image transformations (Chap. F3.2), and object-based image analysis (Chap. F3.3).</p>
</section>
<section id="references" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="references">References</h3>
<p>Baig MHA, Zhang L, Shuai T, Tong Q (2014) Derivation of a tasselled cap transformation based on Landsat 8 at-satellite reflectance. Remote Sens Lett 5:423431. https://doi.org/10.1080/2150704X.2014.915434</p>
<p>Crist EP (1985) A TM tasseled cap equivalent transformation for reflectance factor data. Remote Sens Environ 17:301306. https://doi.org/10.1016/0034-4257(85)90102-6</p>
<p>Drury SA (1987) Image interpretation in geology. Geocarto Int 2:48. https://doi.org/10.1080/10106048709354098</p>
<p>Gao BC (1996) NDWI - A normalized difference water index for remote sensing of vegetation liquid water from space. Remote Sens Environ 58:257266. https://doi.org/10.1016/S0034-4257(96)00067-3</p>
<p>Huang C, Wylie B, Yang L, et al (2002) Derivation of a tasselled cap transformation based on Landsat 7 at-satellite reflectance. Int J Remote Sens 23:17411748. https://doi.org/10.1080/01431160110106113</p>
<p>Jackson RD, Huete AR (1991) Interpreting vegetation indices. Prev Vet Med 11:185200. https://doi.org/10.1016/S0167-5877(05)80004-2</p>
<p>Martín MP (1998) Cartografía e inventario de incendios forestales en la Península Ibérica a partir de imágenes NOAA-AVHRR. Universidad de Alcalá</p>
<p>McFeeters SK (1996) The use of the Normalized Difference Water Index (NDWI) in the delineation of open water features. Int J Remote Sens 17:14251432. https://doi.org/10.1080/01431169608948714</p>
<p>Nath B, Niu Z, Mitra AK (2019) Observation of short-term variations in the clay minerals ratio after the 2015 Chile great earthquake (8.3 Mw) using Landsat 8 OLI data. J Earth Syst Sci 128:121. https://doi.org/10.1007/s12040-019-1129-2</p>
<p>Schultz M, Clevers JGPW, Carter S, et al (2016) Performance of vegetation indices from Landsat time series in deforestation monitoring. Int J Appl Earth Obs Geoinf 52:318327. https://doi.org/10.1016/j.jag.2016.06.020</p>
<p>Segal D (1982) Theoretical basis for differentiation of ferric-iron bearing minerals, using Landsat MSS data. In: Proceedings of Symposium for Remote Sensing of Environment, 2nd Thematic Conference on Remote Sensing for Exploratory Geology, Fort Worth, TX. pp 949951</p>
<p>Souza Jr CM, Roberts DA, Cochrane MA (2005) Combining spectral and spatial information to map canopy damage from selective logging and forest fires. Remote Sens Environ 98:329343. https://doi.org/10.1016/j.rse.2005.07.013</p>
<p>Souza Jr CM, Siqueira JV, Sales MH, et al (2013) Ten-year Landsat classification of deforestation and forest degradation in the Brazilian Amazon. Remote Sens 5:54935513. https://doi.org/10.3390/rs5115493</p>
</section>
</section>
<section id="interpreting-an-image-classification" class="level2">
<h2 class="anchored" data-anchor-id="interpreting-an-image-classification">Interpreting an Image: Classification</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author-1">Author</h4>
<p>Andréa Puzzi Nicolau, Karen Dyson, David Saah, Nicholas Clinton</p>
</section>
<section id="overview-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview-1">Overview</h4>
<p>Image classification is a fundamental goal of remote sensing. It takes the user from viewing an image to labeling its contents. This chapter introduces readers to the concept of classification and walks users through the many options for image classification in Earth Engine. You will explore the processes of training data collection, classifier selection, classifier training and image classification.</p>
</section>
<section id="learning-outcomes-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes-1">Learning Outcomes</h4>
<ul>
<li>Running a classification in Earth Engine.</li>
<li>Understanding the difference between supervised and unsupervised classification.</li>
<li>Learning how to use Earth Engine geometry drawing tools.</li>
<li>Learning how to collect sample data in Earth Engine.</li>
<li>Learning the basics of the hexadecimal numbering system.</li>
</ul>
</section>
<section id="assumes-you-know-how-to-1" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to-1">Assumes you know how to:</h4>
<ul>
<li>Import images and image collections, filter and visualize (Part F1).</li>
<li>Understand bands and how to select them (Chap. F1.2, Chap. F2.0).</li>
</ul>
</section>
</div>
</div>
<section id="introduction-1" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction-1">Introduction</h3>
<p>Classification is addressed in a broad range of fields, including mathematics, statistics, data mining, machine learning and more. For a deeper treatment of classification, interested readers may see some of the following suggestions: Witten et al.&nbsp;(2011), Hastie et al.&nbsp;(2009), Goodfellow et al.&nbsp;(2016), Gareth et al.&nbsp;(2013), Géron (2019), Müller et al.&nbsp;(2016), or Witten et al.&nbsp;(2005). Unlike regression, which predicts continuous variables, classification predicts categorical, or discrete, variables — those with a finite number of categories (e.g., age range).</p>
<p>In remote sensing, image classification is an attempt to categorize all pixels in an image into a finite number of labeled land cover and/or land use classes. The resulting classified image is a simplified thematic map derived from the original image (Fig. F2.1.1). Land cover and land use information is essential for many environmental and socioeconomic applications, including natural resource management, urban planning, biodiversity conservation, agricultural monitoring and carbon accounting.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image48.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.1 Image classification concept</figcaption><p></p>
</figure>
</div>
<p>Image classification techniques for generating land cover and land use information have been in use since the 1980s (Li et al.&nbsp;2014). Here, we will cover the concepts of pixel-based supervised and unsupervised classifications, testing out different classifiers. Chapter F3.3 covers the concept and application of object-based classification.</p>
<p>It is important to define land use and land cover. Land cover relates to the physical characteristics of the surface: simply put, it documents whether an area of the Earths surface is covered by forests, water, impervious surfaces, etc. Land use refers to how this land is being used by people. For example, herbaceous vegetation is considered a land cover but can indicate different land uses: the grass in a pasture is an agricultural land use, whereas the grass in an urban area can be classified as a park.</p>
</section>
<section id="supervised-classification" class="level3">
<h3 class="anchored" data-anchor-id="supervised-classification">Supervised Classification</h3>
<p>If you have not already done so, be sure to add the books code repository to the Code Editor by entering <a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829866098&amp;usg=AOvVaw16x5swm9HlorS5Mbw7E42X"></a><a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829866485&amp;usg=AOvVaw0-N-JCWWgnM493BKa7Ichm">https://code.earthengine.google.com/?accept_repo=projects/gee-edu/book</a> into your browser. The books scripts will then be available in the script manager panel. If you have trouble finding the repo, you can visit <a href="https://www.google.com/url?q=https://docs.google.com/presentation/d/1Kt6wGNoesYm__Cu3k3bnlbbyPN6m9SF4hQHK-pIDHfc/edit%23slide%3Did.g18a7b4b055d_0_624&amp;sa=D&amp;source=editors&amp;ust=1671458829866823&amp;usg=AOvVaw0ytMyRvutssBcVr2GdcBHA">this link</a> for help.</p>
<p>Supervised classification uses a training dataset with known labels and representing the spectral characteristics of each land cover class of interest to “supervise” the classification. The overall approach of a supervised classification in Earth Engine is summarized as follows:</p>
<ol type="1">
<li>Get a scene.</li>
<li>Collect training data.</li>
<li>Select and train a classifier using the training data.</li>
<li>Classify the image using the selected classifier.</li>
</ol>
<p>We will begin by creating training data manually, based on a clear Landsat image (Fig. F2.1.2). Copy the code block below to define your Landsat 8 scene variable and add it to the map. We will use a point in Milan, Italy, as the center of the area for our image classification.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Create an Earth Engine Point object over Milan. </span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> pt <span class="op">=</span> ee<span class="op">.</span><span class="at">Geometry</span><span class="op">.</span><span class="fu">Point</span>([<span class="fl">9.453</span><span class="op">,</span> <span class="fl">45.424</span>])<span class="op">;</span> </span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Filter the Landsat 8 collection and select the least cloudy image. </span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> landsat <span class="op">=</span> ee<span class="op">.</span><span class="fu">ImageCollection</span>(<span class="st">'LANDSAT/LC08/C02/T1_L2'</span>) </span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterBounds</span>(pt) </span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filterDate</span>(<span class="st">'2019-01-01'</span><span class="op">,</span> <span class="st">'2020-01-01'</span>) </span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sort</span>(<span class="st">'CLOUD_COVER'</span>) </span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">first</span>()<span class="op">;</span> </span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Center the map on that image. </span></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">centerObject</span>(landsat<span class="op">,</span> <span class="dv">8</span>)<span class="op">;</span> </span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Add Landsat image to the map. </span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> visParams <span class="op">=</span> { </span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">bands</span><span class="op">:</span> [<span class="st">'SR_B4'</span><span class="op">,</span> <span class="st">'SR_B3'</span><span class="op">,</span> <span class="st">'SR_B2'</span>]<span class="op">,</span> </span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">7000</span><span class="op">,</span> </span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">12000</span> </span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(landsat<span class="op">,</span> visParams<span class="op">,</span> <span class="st">'Landsat 8 image'</span>)<span class="op">;</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image44.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.2 Landsat image</figcaption><p></p>
</figure>
</div>
<p>Using the Geometry Tools, we will create points on the Landsat image that represent land cover classes of interest to use as our training data. Well need to do two things: (1) identify where each land cover occurs on the ground, and (2) label the points with the proper class number. For this exercise, we will use the classes and codes shown below:</p>
<ul>
<li>Forest: 0</li>
<li>Developed: 1</li>
<li>Water: 2</li>
<li>Herbaceous: 3</li>
</ul>
<p>In the Geometry Tools, click on the marker option (Fig. F2.1.3). This will create a point geometry which will show up as an import named “geometry”. Click on the gear icon to configure this import.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image22.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.3 Creating a new layer in the Geometry Imports</figcaption><p></p>
</figure>
</div>
<p>We will start by collecting forest points, so name the import forest. Import it as a FeatureCollection, and then click + Property. Name the new property “class” and give it a value of 0 (Fig. F2.1.4). We can also choose a color to represent this class. For a forest class, it is natural to choose a green color. You can choose the color you prefer by clicking on it, or, for more control, you can use a hexadecimal value.</p>
<p>Hexadecimal values are used throughout the digital world to represent specific colors across computers and operating systems. They are specified by six values arranged in three pairs, with one pair each for the red, green and blue brightness values. If youre unfamiliar with hexadecimal values, imagine for a moment that colors were specified in pairs of base 10 numbers instead of pairs of base 16. In that case, a bright pure red value would be “990000”; a bright pure green value would be “009900”; and a bright pure blue value would be “000099”. A value like “501263” would be a mixture of the three colors, not especially bright, having roughly equal amounts of blue and red, and much less green: a color that would be a shade of purple. To create numbers in the hexadecimal system, which might feel entirely natural if humans had evolved to have 16 fingers, sixteen “digits” are needed: a base 16 counter goes 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, then 10, 11, and so on. Given that counting framework, the number “FF” is like “99” in base 10: the largest two-digit number. The hexadecimal color used for coloring the letters of the word FeatureCollection in this book, a color with roughly equal amounts of blue and red and much less green, is “7F1FA2”</p>
<p>Returning to the coloring of the forest points, the hexadecimal value “589400” is a little bit of red, about twice as much green and no blue: the deep green seen in Figure F2.1.4. Enter that value, with or without the “#” in front, and click OK after finishing the configuration.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image36.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.4 Edit geometry layer properties</figcaption><p></p>
</figure>
</div>
<p>Now, in the Geometry Imports, we will see that the import has been renamed forest. Click on it to activate the drawing mode (Fig. F2.1.5) in order to start collecting forest points.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image29.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.5 Activate forest layer to start collection</figcaption><p></p>
</figure>
</div>
<p>Now, start collecting points over forested areas (Fig. F2.1.6). Zoom in and out as needed. You can use the satellite basemap to assist you, but the basis of your collection should be the Landsat image. Remember that the more points you collect, the more the classifier will learn from the information you provide. For now, lets set a goal to collect 25 points per class. Click Exit next to Point drawing (Fig. F2.1.5) when finished.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image38.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.6 Forest points</figcaption><p></p>
</figure>
</div>
<p>Repeat the same process for the other classes by creating new layers (Fig. F2.1.7). Dont forget to import using the FeatureCollection option as mentioned above. For the developed class, collect points over urban areas. For the water class, collect points over the Ligurian Sea, and also look for other bodies of water, like rivers. For the herbaceous class, collect points over agricultural fields. Remember to set the “class” property for each class to its corresponding code (see Table 2.1.1) and click Exit once you finalize collecting points for each class as mentioned above. We will be using the following hexadecimal colors for the other classes: #FF0000 for developed, #1A11FF for water, and #D0741E for herbaceous.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image41.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.7 New layer option in Geometry Imports</figcaption><p></p>
</figure>
</div>
<p>You should now have four FeatureCollection imports named forest, developed, water, and herbaceous (Fig. F2.1.8).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image42.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.8 Example of training points</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F21a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
<p>If you wish to have the exact same results demonstrated in this chapter from now on, continue beginning with this Code Checkpoint. If you use the points collected yourself, the results may vary from this point forward.</p>
<p>The next step is to combine all the training feature collections into one. Copy and paste the code below to combine them into one FeatureCollection called trainingFeatures. Here, we use the flatten method to avoid having a collection of feature collections — we want individual features within our FeatureCollection.</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Combine training feature collections. </span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> trainingFeatures <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>([ </span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> forest<span class="op">,</span> developed<span class="op">,</span> water<span class="op">,</span> herbaceous </span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>])<span class="op">.</span><span class="fu">flatten</span>()<span class="op">;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note: Alternatively, you could use an existing set of reference data. For example, the European Space Agency (ESA) WorldCover dataset is a global map of land use and land cover derived from ESAs Sentinel-2 imagery at 10 m resolution. With existing datasets, we can randomly place points on pixels classified as the classes of interest (if you are curious, you can explore the Earth Engine documentation to learn about the ee.Image.stratifiedSample and the ee.FeatureCollection.randomPoints methods). The drawback is that these global datasets will not always contain the specific classes of interest for your region, or may not be entirely accurate at the local scale. Another option is to use samples that were collected in the field (e.g., GPS points). In Chap. F5.0, you will see how to upload your own data as Earth Engine assets.</p>
<p>In the combined FeatureCollection, each Feature point should have a property called “class”. The class values are consecutive integers from 0 to 3 (you could verify that this is true by printing trainingFeatures and checking the properties of the features).</p>
<p>Now that we have our training points, copy and paste the code below to extract the band information for each class at each point location. First, we define the prediction bands to extract different spectral and thermal information from different bands for each class. Then, we use the sampleRegions method to sample the information from the Landsat image at each point location. This method requires information about the FeatureCollection (our reference points), the property to extract (“class”), and the pixel scale (in meters).</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Define prediction bands. </span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> predictionBands <span class="op">=</span> [ <span class="st">'SR_B1'</span><span class="op">,</span> <span class="st">'SR_B2'</span><span class="op">,</span> <span class="st">'SR_B3'</span><span class="op">,</span> <span class="st">'SR_B4'</span><span class="op">,</span> <span class="st">'SR_B5'</span><span class="op">,</span> <span class="st">'SR_B6'</span><span class="op">,</span> <span class="st">'SR_B7'</span><span class="op">,</span> <span class="st">'ST_B10'</span> </span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span> </span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="co">// Sample training points. </span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> classifierTraining <span class="op">=</span> landsat<span class="op">.</span><span class="fu">select</span>(predictionBands) </span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sampleRegions</span>({ </span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">collection</span><span class="op">:</span> trainingFeatures<span class="op">,</span> </span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">properties</span><span class="op">:</span> [<span class="st">'class'</span>]<span class="op">,</span> </span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">30</span> })<span class="op">;</span></span>
<span id="cb15-11"><a href="#cb15-11" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>You can check whether the classifierTraining object extracted the properties of interest by printing it and expanding the first feature. You should see the band and class information (Fig. F2.1.9).</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image20.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.9 Example of extracted band information for one point of class 0 (forest)</figcaption><p></p>
</figure>
</div>
<p>Now we can choose a classifier. The choice of classifier is not always obvious, and there are many options from which to pick — you can quickly expand the ee.Classifier object under Docs to get an idea of how many options we have for image classification. Therefore, we will be testing different classifiers and comparing their results. We will start with a Classification and Regression Tree (CART) classifier, a well-known classification algorithm (Fig. F2.1.10) that has been around for decades.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image25.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.10 Example of a decision tree for satellite image classification. Values and classes are hypothetical.</figcaption><p></p>
</figure>
</div>
<p>Copy and paste the code below to instantiate a CART classifier (ee.Classifier.smileCart) and train it.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co">//////////////// CART Classifier /////////////////// </span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Train a CART Classifier. </span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> classifier <span class="op">=</span> ee<span class="op">.</span><span class="at">Classifier</span><span class="op">.</span><span class="fu">smileCart</span>()<span class="op">.</span><span class="fu">train</span>({ </span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> classifierTraining<span class="op">,</span> </span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">classProperty</span><span class="op">:</span> <span class="st">'class'</span><span class="op">,</span> </span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">inputProperties</span><span class="op">:</span> predictionBands </span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Essentially, the classifier contains the mathematical rules that link labels to spectral information. If you print the variable classifier and expand its properties, you can confirm the basic characteristics of the object (bands, properties, and classifier being used). If you print classifier.explain, you can find a property called “tree” that contains the decision rules.</p>
<p>After training the classifier, copy and paste the code below to classify the Landsat image and add it to the Map.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Classify the Landsat image. </span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> classified <span class="op">=</span> landsat<span class="op">.</span><span class="fu">select</span>(predictionBands)<span class="op">.</span><span class="fu">classify</span>(classifier)<span class="op">;</span> </span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Define classification image visualization parameters. </span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> classificationVis <span class="op">=</span> { </span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">min</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">max</span><span class="op">:</span> <span class="dv">3</span><span class="op">,</span> </span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">palette</span><span class="op">:</span> [<span class="st">'589400'</span><span class="op">,</span> <span class="st">'ff0000'</span><span class="op">,</span> <span class="st">'1a11ff'</span><span class="op">,</span> <span class="st">'d0741e'</span>] </span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>}<span class="op">;</span> </span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a><span class="co">// Add the classified image to the map. </span></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(classified<span class="op">,</span> classificationVis<span class="op">,</span> <span class="st">'CART classified'</span>)<span class="op">;</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note that, in the visualization parameters, we define a palette parameter which in this case represents colors for each pixel value (03, our class codes). We use the same hexadecimal colors used when creating our training points for each class. This way, we can associate a color with a class when visualizing the classified image in the Map.</p>
<p>Inspect the result: Activate the Landsat composite layer and the satellite basemap to overlay with the classified images (Fig. F2.1.11). Change the layers transparency to inspect some areas. What do you notice? The result might not look very satisfactory in some areas (e.g., confusion between developed and herbaceous classes). Why do you think this is happening? There are a few options to handle misclassification errors:</p>
<ul>
<li>Collect more training data We can try incorporating more points to have a more representative sample of the classes.</li>
<li>Tune the model. Classifiers typically have “hyperparameters,” which are set to default values. In the case of classification trees, there are ways to tune the number of leaves in the tree, for example. Tuning models is addressed in Chap. F2.2.</li>
<li>Try other classifiers. If a classifiers results are unsatisfying, we can try some of the other classifiers in Earth Engine to see if the result is better or different.</li>
<li>Expand the collection location. It is good practice to collect points across the entire image and not just focus on one location. Also, look for pixels of the same class that show variability (e.g., for the developed class, building rooftops look different than house rooftops; for the herbaceous class, crop fields show distinctive seasonality/phenology).</li>
<li>Add more predictors. We can try adding spectral indices to the input variables; this way, we are feeding the classifier new, unique information about each class. For example, there is a good chance that a vegetation index specialized for detecting vegetation health (e.g., NDVI) would improve the developed versus herbaceous classification.</li>
</ul>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image21.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.11 CART classification</figcaption><p></p>
</figure>
</div>
<p>For now, we will try another supervised learning classifier that is widely used: Random Forests (RF). The RF algorithm (Breiman 2001, Pal 2005) builds on the concept of decision trees, but adds strategies to make them more powerful. It is called a “forest” because it operates by constructing a multitude of decision trees. As mentioned previously, a decision tree creates the rules which are used to make decisions. A Random Forest will randomly choose features and make observations, build a forest of decision trees and then use the full set of trees to estimate the class. It is a great choice when you do not have a lot of insight about the training data.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image27.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.12 General concept of Random Forests</figcaption><p></p>
</figure>
</div>
<p>Copy and paste the code below to train the RF classifier (ee.Classifier.smileRandomForest) and apply the classifier to the image. The RF algorithm requires, as its argument, the number of trees to build. We will use 50 trees.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="co">/////////////// Random Forest Classifier ///////////////////// </span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Train RF classifier. </span></span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> RFclassifier <span class="op">=</span> ee<span class="op">.</span><span class="at">Classifier</span><span class="op">.</span><span class="fu">smileRandomForest</span>(<span class="dv">50</span>)<span class="op">.</span><span class="fu">train</span>({ </span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> classifierTraining<span class="op">,</span> </span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">classProperty</span><span class="op">:</span> <span class="st">'class'</span><span class="op">,</span> </span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">inputProperties</span><span class="op">:</span> predictionBands </span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a><span class="co">// Classify Landsat image. </span></span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> RFclassified <span class="op">=</span> landsat<span class="op">.</span><span class="fu">select</span>(predictionBands)<span class="op">.</span><span class="fu">classify</span>( </span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a> RFclassifier)<span class="op">;</span> </span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Add classified image to the map. </span></span>
<span id="cb18-15"><a href="#cb18-15" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(RFclassified<span class="op">,</span> classificationVis<span class="op">,</span> <span class="st">'RF classified'</span>)<span class="op">;</span></span>
<span id="cb18-16"><a href="#cb18-16" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note that in the ee.Classifier.smileRandomForest documentation (Docs tab), there is a seed (random number) parameter. Setting a seed allows you to exactly replicate your model each time you run it. Any number is acceptable as a seed.</p>
<p>Inspect the result (Fig. F2.1.13). How does this classified image differ from the CART one? Is the classifications better or worse? Zoom in and out and change the transparency of layers as needed. In Chap. F2.2, you will see more systematic ways to assess what is better or worse, based on accuracy metrics.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image34.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.13 Random Forest classified image</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F21b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="unsupervised-classification" class="level3">
<h3 class="anchored" data-anchor-id="unsupervised-classification">Unsupervised Classification</h3>
<p>In an unsupervised classification, we have the opposite process of supervised classification. Spectral classes are grouped first and then categorized into clusters. Therefore, in Earth Engine, these classifiers are ee.Clusterer objects. They are “self-taught” algorithms that do not use a set of labeled training data (i.e., they are “unsupervised”). You can think of it as performing a task that you have not experienced before, starting by gathering as much information as possible. For example, imagine learning a new language without knowing the basic grammar, learning only by watching a TV series in that language, listening to examples and finding patterns.</p>
<p>Similar to the supervised classification, unsupervised classification in Earth Engine has this workflow:</p>
<ol type="1">
<li>Assemble features with numeric properties in which to find clusters (training data).</li>
<li>Select and instantiate a clusterer.</li>
<li>Train the clusterer with the training data.</li>
<li>Apply the clusterer to the scene (classification).</li>
<li>Label the clusters.</li>
</ol>
<p>In order to generate training data, we will use the sample method, which randomly takes samples from a region (unlike sampleRegions, which takes samples from predefined locations). We will use the images footprint as the region by calling the geometry method. Additionally, we will define the number of pixels (numPixels) to sample — in this case, 1000 pixels — and define a tileScale of 8 to avoid computation errors due to the size of the region. Copy and paste the code below to sample 1000 pixels from the Landsat image. You should add to the same script as before to compare supervised versus unsupervised classification results at the end.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="co">//////////////// Unsupervised classification //////////////// </span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="co">// Make the training dataset. </span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> training <span class="op">=</span> landsat<span class="op">.</span><span class="fu">sample</span>({ </span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">region</span><span class="op">:</span> landsat<span class="op">.</span><span class="fu">geometry</span>()<span class="op">,</span> </span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">scale</span><span class="op">:</span> <span class="dv">30</span><span class="op">,</span> </span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">numPixels</span><span class="op">:</span> <span class="dv">1000</span><span class="op">,</span> </span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">tileScale</span><span class="op">:</span> <span class="dv">8</span> </span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now we can instantiate a clusterer and train it. As with the supervised algorithms, there are many unsupervised algorithms to choose from. We will use the k-means clustering algorithm, which is a commonly used approach in remote sensing. This algorithm identifies groups of pixels near each other in the spectral space (image x bands) by using an iterative regrouping strategy. We define a number of clusters, k, and then the method randomly distributes that number of seed points into the spectral space. A large sample of pixels is then grouped into its closest seed, and the mean spectral value of this group is calculated. That mean value is akin to a center of mass of the points, and is known as the centroid. Each iteration recalculates the class means and reclassifies pixels with respect to the new means. This process is repeated until the centroids remain relatively stable and only a few pixels change from class to class on subsequent iterations.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image35.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.14 K-means visual concept</figcaption><p></p>
</figure>
</div>
<p>Copy and paste the code below to request four clusters, the same number as for the supervised classification, in order to directly compare them.</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Instantiate the clusterer and train it. </span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> clusterer <span class="op">=</span> ee<span class="op">.</span><span class="at">Clusterer</span><span class="op">.</span><span class="fu">wekaKMeans</span>(<span class="dv">4</span>)<span class="op">.</span><span class="fu">train</span>(training)<span class="op">;</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now copy and paste the code below to apply the clusterer to the image and add the resulting classification to the Map (Fig. F2.1.15). Note that we are using a method called randomVisualizer to assign colors for the visualization. We are not associating the unsupervised classes with the color palette we defined earlier in the supervised classification. Instead, we are assigning random colors to the classes, since we do not yet know which of the unsupervised classes best corresponds to each of the named classes (e.g., forest , herbaceous). Note that the colors in Fig. F1.2.15 might not be the same as you see on your Map, since they are assigned randomly.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Cluster the input using the trained clusterer. </span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> Kclassified <span class="op">=</span> landsat<span class="op">.</span><span class="fu">cluster</span>(clusterer)<span class="op">;</span> </span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Display the clusters with random colors. </span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="bu">Map</span><span class="op">.</span><span class="fu">addLayer</span>(Kclassified<span class="op">.</span><span class="fu">randomVisualizer</span>()<span class="op">,</span> {}<span class="op">,</span> <span class="st">'K-means classified - random colors'</span>)<span class="op">;</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image31.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.1.15 K-means classification</figcaption><p></p>
</figure>
</div>
<p>Inspect the results. How does this classification compare to the previous ones? If preferred, use the Inspector to check which classes were assigned to each pixel value (“cluster” band) and change the last line of your code to apply the same palette used for the supervised classification results (see Code Checkpoint below for an example).</p>
<p>Another key point of classification is the accuracy assessment of the results. This will be covered in Chap. F2.2.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F21c. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="conclusion-1" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion-1">Conclusion</h3>
<p>Classification algorithms are key for many different applications because they allow you to predict categorical variables. You should now understand the difference between supervised and unsupervised classification and have the basic knowledge on how to handle misclassifications. By being able to map the landscape for land use and land cover, we will also be able to monitor how it changes (Part F4).</p>
</section>
<section id="references-1" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="references-1">References</h3>
<p>Breiman L (2001) Random forests. Mach Learn 45:532. https://doi.org/10.1023/A:1010933404324</p>
<p>Gareth J, Witten D, Hastie T, Tibshirani R (2013) An Introduction to Statistical Learning. Springer</p>
<p>Géron A (2019) Hands-on Machine Learning with Scikit-Learn, Keras and TensorFlow: Concepts, Tools, and Techniques to Build Intelligent Systems. OReilly Media, Inc.</p>
<p>Goodfellow I, Bengio Y, Courville A (2016) Deep Learning. MIT Press</p>
<p>Hastie T, Tibshirani R, Friedman JH (2009) The Elements of Statistical Learning: Data Mining, Inference, and Prediction. Springer</p>
<p>Li M, Zang S, Zhang B, et al (2014) A review of remote sensing image classification techniques: The role of spatio-contextual information. Eur J Remote Sens 47:389411. https://doi.org/10.5721/EuJRS20144723</p>
<p>Müller AC, Guido S (2016) Introduction to Machine Learning with Python: A Guide for Data Scientists. OReilly Media, Inc.</p>
<p>Pal M (2005) Random forest classifier for remote sensing classification. Int J Remote Sens 26:217222. https://doi.org/10.1080/01431160412331269698</p>
<p>Witten IH, Frank E, Hall MA, et al (2005) Practical machine learning tools and techniques. In: Data Mining. pp 4</p>
</section>
</section>
<section id="accuracy-assessment-quantifying-classification-quality" class="level2">
<h2 class="anchored" data-anchor-id="accuracy-assessment-quantifying-classification-quality">Accuracy Assessment: Quantifying Classification Quality</h2>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Chapter Information
</div>
</div>
<div class="callout-body-container callout-body">
<section id="author-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="author-2">Author</h4>
<p>Andréa Puzzi Nicolau, Karen Dyson, David Saah, Nicholas Clinton</p>
</section>
<section id="overview-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="overview-2">Overview</h4>
<p>This chapter will enable you to assess the accuracy of an image classification. You will learn about different metrics and ways to quantify classification quality in Earth Engine. Upon completion, you should be able to evaluate whether your classification needs improvement and know how to proceed when it does.</p>
</section>
<section id="learning-outcomes-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="learning-outcomes-2">Learning Outcomes</h4>
<ul>
<li>Learning how to perform accuracy assessment in Earth Engine.</li>
<li>Understanding how to generate and read a confusion matrix.</li>
<li>Understanding overall accuracy and the kappa coefficient.</li>
<li>Understanding the difference between users and producers accuracy, and the difference between omission and commission errors.</li>
</ul>
</section>
<section id="assumes-you-know-how-to-2" class="level4 unlisted unnumbered">
<h4 class="unlisted unnumbered anchored" data-anchor-id="assumes-you-know-how-to-2">Assumes you know how to:</h4>
<ul>
<li>Create a graph using ui.Chart (Chap. F1.3).</li>
<li>Perform a supervised Random Forest image classification (Chap. F2.1).</li>
</ul>
</section>
</div>
</div>
<section id="introduction-2" class="level3 unlisted unnumbered">
<h3 class="unlisted unnumbered anchored" data-anchor-id="introduction-2">Introduction</h3>
<p>Any map or remotely sensed product is a generalization or model that will have inherent errors. Products derived from remotely sensed data used for scientific purposes and policymaking require a quantitative measure of accuracy to strengthen the confidence in the information generated (Foody 2002, Strahler et al.&nbsp;2006, Olofsson et al.&nbsp;2014). Accuracy assessment is a crucial part of any classification project, as it measures the degree to which the classification agrees with another data source that is considered to be accurate, ground-truth data (i.e., “reality”).</p>
<p>The history of accuracy assessment reveals increasing detail and rigor in the analysis, moving from a basic visual appraisal of the derived map (Congalton 1994, Foody 2002) to the definition of best practices for sampling and response designs and the calculation of accuracy metrics (Foody 2002, Stehman 2013, Olofsson et al.&nbsp;2014, Stehman and Foody 2019). The confusion matrix (also called the “error matrix”) (Stehman 1997) summarizes key accuracy metrics used to assess products derived from remotely sensed data.</p>
<p>In Chap. F2.1, we asked whether the classification results were satisfactory. In remote sensing, the quantification of the answer to that question is called accuracy assessment. In the classification context, accuracy measurements are often derived from a confusion matrix.</p>
<p>In a thorough accuracy assessment, we think carefully about the sampling design, the response design, and the analysis (Olofsson et al.&nbsp;2014). Fundamental protocols are taken into account to produce scientifically rigorous and transparent estimates of accuracy and area, which requires robust planning and time. In a standard setting, we would calculate the number of samples needed for measuring accuracy (sampling design). Here, we will focus mainly on the last step, analysis, by examining the confusion matrix and learning how to calculate the accuracy metrics. This will be done by partitioning the existing data into training and testing sets.</p>
</section>
<section id="quantifying-classification-accuracy-through-a-confusion-matrix" class="level3">
<h3 class="anchored" data-anchor-id="quantifying-classification-accuracy-through-a-confusion-matrix">Quantifying Classification Accuracy Through a Confusion Matrix</h3>
<p>If you have not already done so, be sure to add the books code repository to the Code Editor by entering <a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829937499&amp;usg=AOvVaw3qqOwSX_A-Pllh6X3X31q4"></a><a href="https://www.google.com/url?q=https://code.earthengine.google.com/?accept_repo%3Dprojects/gee-edu/book&amp;sa=D&amp;source=editors&amp;ust=1671458829937976&amp;usg=AOvVaw0WioXIhzue8-WoaX4UtabH">https://code.earthengine.google.com/?accept_repo=projects/gee-edu/book</a> into your browser. The books scripts will then be available in the script manager panel. If you have trouble finding the repo, you can visit <a href="https://www.google.com/url?q=https://docs.google.com/presentation/d/1Kt6wGNoesYm__Cu3k3bnlbbyPN6m9SF4hQHK-pIDHfc/edit%23slide%3Did.g18a7b4b055d_0_624&amp;sa=D&amp;source=editors&amp;ust=1671458829938470&amp;usg=AOvVaw2CH8V3-_qV99EcgMxUAaSO">this link</a> for help.</p>
<p>To illustrate some of the basic ideas about classification accuracy, we will revisit the data and location of part of Chap. F2.1, where we tested different classifiers and classified a Landsat image of the area around Milan, Italy. We will name this dataset data. This variable is a FeatureCollection with features containing the “class” values and spectral information of four land cover / land use classes: forest, developed, water, and herbaceous (see Fig. F2.1.8 and Fig. F2.1.9 for a refresher). We will also define a variable, predictionBands, which is a list of bands that will be used for prediction (classification)—the spectral information in the data variable.</p>
<p>Class Values:</p>
<ul>
<li>Forest: 0</li>
<li>Developed: 1</li>
<li>Water: 2</li>
<li>Herbaceous: 3</li>
</ul>
<p>The first step is to partition the set of known values into training and testing sets in order to have something for the classifier to predict over that it has not been shown before (the testing set), mimicking unseen data that the model might see in the future. We add a column of random numbers to our FeatureCollection using the randomColumn method. Then, we filter the features into about 80% for training and 20% for testing using ee.Filter. Copy and paste the code below to partition the data and filter features based on the random number.</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Import the reference dataset. </span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> data <span class="op">=</span> ee<span class="op">.</span><span class="fu">FeatureCollection</span>( <span class="st">'projects/gee-book/assets/F2-2/milan_data'</span>)<span class="op">;</span> </span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Define the prediction bands. </span></span>
<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> predictionBands <span class="op">=</span> [ <span class="st">'SR_B1'</span><span class="op">,</span> <span class="st">'SR_B2'</span><span class="op">,</span> <span class="st">'SR_B3'</span><span class="op">,</span> <span class="st">'SR_B4'</span><span class="op">,</span> <span class="st">'SR_B5'</span><span class="op">,</span> <span class="st">'SR_B6'</span><span class="op">,</span> <span class="st">'SR_B7'</span><span class="op">,</span> <span class="st">'ST_B10'</span><span class="op">,</span> <span class="st">'ndvi'</span><span class="op">,</span> <span class="st">'ndwi'</span> </span>
<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span> </span>
<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a><span class="co">// Split the dataset into training and testing sets. </span></span>
<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> trainingTesting <span class="op">=</span> data<span class="op">.</span><span class="fu">randomColumn</span>()<span class="op">;</span> </span>
<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> trainingSet <span class="op">=</span> trainingTesting </span>
<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">lessThan</span>(<span class="st">'random'</span><span class="op">,</span> <span class="fl">0.8</span>))<span class="op">;</span> </span>
<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> testingSet <span class="op">=</span> trainingTesting </span>
<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">filter</span>(ee<span class="op">.</span><span class="at">Filter</span><span class="op">.</span><span class="fu">greaterThanOrEquals</span>(<span class="st">'random'</span><span class="op">,</span> <span class="fl">0.8</span>))<span class="op">;</span></span>
<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Note that randomColumn creates pseudorandom numbers in a deterministic way. This makes it possible to generate a reproducible pseudorandom sequence by defining the seed parameter (Earth Engine uses a seed of 0 by default). In other words, given a starting value (i.e., the seed), randomColumn will always provide the same sequence of pseudorandom numbers.</p>
<p>Copy and paste the code below to train a Random Forest classifier with 50 decision trees using the trainingSet.</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Train the Random Forest Classifier with the trainingSet. </span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> RFclassifier <span class="op">=</span> ee<span class="op">.</span><span class="at">Classifier</span><span class="op">.</span><span class="fu">smileRandomForest</span>(<span class="dv">50</span>)<span class="op">.</span><span class="fu">train</span>({ </span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> trainingSet<span class="op">,</span> </span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">classProperty</span><span class="op">:</span> <span class="st">'class'</span><span class="op">,</span> </span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">inputProperties</span><span class="op">:</span> predictionBands </span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Now, lets discuss what a confusion matrix is. A confusion matrix describes the quality of a classification by comparing the predicted values to the actual values. A simple example is a confusion matrix for a binary classification into the classes “positive” and “negative,” as shown in Table F2.2.1.</p>
<p>Table F2.2.1 Confusion matrix for a binary classification where the classes are “positive” and “negative”</p>
<table class="table">
<colgroup>
<col style="width: 25%">
<col style="width: 14%">
<col style="width: 30%">
<col style="width: 30%">
</colgroup>
<thead>
<tr class="header">
<th></th>
<th></th>
<th style="text-align: center;">Actual values</th>
<th style="text-align: center;"></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td></td>
<td></td>
<td style="text-align: center;">Positive</td>
<td style="text-align: center;">Negative</td>
</tr>
<tr class="even">
<td>Predicted values</td>
<td>Positive</td>
<td style="text-align: center;">TP (true positive)</td>
<td style="text-align: center;">FP (false positive)</td>
</tr>
<tr class="odd">
<td></td>
<td>Negative</td>
<td style="text-align: center;">FN (false negative)</td>
<td style="text-align: center;">TN (true negative)</td>
</tr>
</tbody>
</table>
<p>In Table F2.2.1, the columns represent the actual values (the truth), while the rows represent the predictions (the classification). “True positive” (TP) and “true negative” (TN) mean that the classification of a pixel matches the truth (e.g., a water pixel correctly classified as water). “False positive” (FP) and “false negative” (FN) mean that the classification of a pixel does not match the truth (e.g., a non-water pixel incorrectly classified as water).</p>
<ul>
<li>TP: classified as positive and the actual class is positive</li>
<li>FP: classified as positive and the actual class is negative</li>
<li>FN: classified as negative and the actual class is positive</li>
<li>TN: classified as negative and the actual class is negative</li>
</ul>
<p>We can extract some statistical information from a confusion matrix.. Lets look at an example to make this clearer. Table F2.2.2 is a confusion matrix for a sample of 1,000 pixels for a classifier that identifies whether a pixel is forest (positive) or non-forest (negative), a binary classification.</p>
<p>Table F2.2.2 Confusion matrix for a binary classification where the classes are “positive” (forest) and “negative” (non-forest)</p>
<table class="table">
<thead>
<tr class="header">
<th></th>
<th></th>
<th style="text-align: center;">Actual values</th>
<th style="text-align: center;"></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td></td>
<td></td>
<td style="text-align: center;">Positive</td>
<td style="text-align: center;">Negative</td>
</tr>
<tr class="even">
<td>Predicted values</td>
<td>Positive</td>
<td style="text-align: center;">307</td>
<td style="text-align: center;">18</td>
</tr>
<tr class="odd">
<td></td>
<td>Negative</td>
<td style="text-align: center;">14</td>
<td style="text-align: center;">661</td>
</tr>
</tbody>
</table>
<p>In this case, the classifier correctly identified 307 forest pixels, wrongly classified 18 non-forest pixels as forest, correctly identified 661 non-forest pixels, and wrongly classified 14 forest pixels as non-forest. Therefore, the classifier was correct 968 times and wrong 32 times. Lets calculate the main accuracy metrics for this example.</p>
<p>The overall accuracy tells us what proportion of the reference data was classified correctly, and is calculated as the total number of correctly identified pixels divided by the total number of pixels in the sample.</p>
<p><img src="images/F2/image6.png" class="img-fluid"></p>
<p>In this case, the overall accuracy is 96.8%, calculated using (<img src="images/F2/image7.png" class="img-fluid">.</p>
<p>Two other important accuracy metrics are the producers accuracy and the users accuracy, also referred to as the “recall” and the “precision,” respectively. Importantly, these metrics quantify aspects of per-class accuracy.</p>
<p>The producers accuracy is the accuracy of the map from the point of view of the map maker (the “producer”), and is calculated as the number of correctly identified pixels of a given class divided by the total number of pixels actually in that class. The producers accuracy for a given class tells us the proportion of the pixels in that class that were classified correctly.</p>
<p><img src="images/F2/image8.png" class="img-fluid"></p>
<p><img src="images/F2/image9.png" class="img-fluid"></p>
<p>In this case, the producers accuracy for the forest class is 95.6%, calculated using <img src="images/F2/image10.png" class="img-fluid">). The producers accuracy for the non-forest class is 97.3%, calculated from <img src="images/F2/image11.png" class="img-fluid">).</p>
<p>The users accuracy (also called the “consumers accuracy”) is the accuracy of the map from the point of view of a map user, and is calculated as the number of correctly identified pixels of a given class divided by the total number of pixels claimed to be in that class. The users accuracy for a given class tells us the proportion of the pixels identified on the map as being in that class that are actually in that class on the ground.</p>
<p><img src="images/F2/image12.png" class="img-fluid"></p>
<p><img src="images/F2/image13.png" class="img-fluid"></p>
<p>In this case, the users accuracy for the forest class is 94.5%, calculated using <img src="images/F2/image14.png" class="img-fluid">). The users accuracy for the non-forest class is 97.9%, calculated from <img src="images/F2/image15.png" class="img-fluid">).</p>
<p>Fig. F2.2.1 helps visualize the rows and columns used to calculate each accuracy.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image43.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.2.1 Confusion matrix for a binary classification where the classes are “positive” (forest) and “negative” (non-forest), with accuracy metrics</figcaption><p></p>
</figure>
</div>
<p>It is very common to talk about two types of error when addressing remote-sensing classification accuracy: omission errors and commission errors. Omission errors refer to the reference pixels that were left out of (omitted from) the correct class in the classified map. In a two-class system, an error of omission in one class will be counted as an error of commission in another class. Omission errors are complementary to the producers accuracy.</p>
<p><img src="images/F2/image16.png" class="img-fluid"></p>
<p>Commission errors refer to the class pixels that were erroneously classified in the map and are complementary to the users accuracy.</p>
<p><img src="images/F2/image17.png" class="img-fluid"></p>
<p>Finally, another commonly used accuracy metric is the kappa coefficient, which evaluates how well the classification performed as compared to random. The value of the kappa coefficient can range from 1 to 1: a negative value indicates that the classification is worse than a random assignment of categories would have been; a value of 0 indicates that the classification is no better or worse than random; and a positive value indicates that the classification is better than random.</p>
<p><img src="images/F2/image18.png" class="img-fluid"></p>
<p>The chance agreement is calculated as the sum of the product of row and column totals for each class, and the observed accuracy is the overall accuracy. Therefore, for our example, the kappa coefficient is 0.927.</p>
<p><img src="images/F2/image19.png" class="img-fluid"></p>
<p>Now, lets go back to the script. In Earth Engine, there are API calls for these operations. Note that our confusion matrix will be a 4 x 4 table, since we have four different classes.</p>
<p>Copy and paste the code below to classify the testingSet and get a confusion matrix using the method errorMatrix. Note that the classifier automatically adds a property called “classification,” which is compared to the “class” property of the reference dataset.</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Now, to test the classification (verify model's accuracy), </span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="co">// we classify the testingSet and get a confusion matrix. </span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> confusionMatrix <span class="op">=</span> testingSet<span class="op">.</span><span class="fu">classify</span>(RFclassifier) </span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">errorMatrix</span>({ </span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">actual</span><span class="op">:</span> <span class="st">'class'</span><span class="op">,</span> </span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">predicted</span><span class="op">:</span> <span class="st">'classification'</span> })<span class="op">;</span></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Copy and paste the code below to print the confusion matrix and accuracy metrics. Expand the confusion matrix object to inspect it. The entries represent the number of pixels. Items on the diagonal represent correct classification. Items off the diagonal are misclassifications, where the class in row i is classified as column j (values from 0 to 3 correspond to our class codes: forest, developed, water, and herbaceous, respectively). Also expand the producers accuracy, users accuracy (consumers accuracy), and kappa coefficient objects to inspect them.</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Print the results. </span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Confusion matrix:'</span><span class="op">,</span> confusionMatrix)<span class="op">;</span> </span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Overall Accuracy:'</span><span class="op">,</span> confusionMatrix<span class="op">.</span><span class="fu">accuracy</span>())<span class="op">;</span> </span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Producers Accuracy:'</span><span class="op">,</span> confusionMatrix<span class="op">.</span><span class="fu">producersAccuracy</span>())<span class="op">;</span> </span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Consumers Accuracy:'</span><span class="op">,</span> confusionMatrix<span class="op">.</span><span class="fu">consumersAccuracy</span>())<span class="op">;</span> </span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(<span class="st">'Kappa:'</span><span class="op">,</span> confusionMatrix<span class="op">.</span><span class="fu">kappa</span>())<span class="op">;</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>How is the classification accuracy? Which classes have higher accuracy compared to the others? Can you think of any reasons why? (Hint: Check where the errors in these classes are in the confusion matrix—i.e., being committed and omitted.)</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F22a. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="hyperparameter-tuning" class="level3">
<h3 class="anchored" data-anchor-id="hyperparameter-tuning">Hyperparameter tuning</h3>
<p>We can also assess how the number of trees in the Random Forest classifier affects the classification accuracy. Copy and paste the code below to create a function that charts the overall accuracy versus the number of trees used. The code tests from 5 to 100 trees at increments of 5, producing Fig. F2.2.2. (Do not worry too much about fully understanding each item at this stage of your learning. If you want to find out how these operations work, you can see more in Chaps. F4.0 and F4.1.)</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode js code-with-copy"><code class="sourceCode javascript"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="co">// Hyperparameter tuning. </span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> numTrees <span class="op">=</span> ee<span class="op">.</span><span class="at">List</span><span class="op">.</span><span class="fu">sequence</span>(<span class="dv">5</span><span class="op">,</span> <span class="dv">100</span><span class="op">,</span> <span class="dv">5</span>)<span class="op">;</span> </span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> accuracies <span class="op">=</span> numTrees<span class="op">.</span><span class="fu">map</span>(<span class="kw">function</span>(t) { <span class="kw">var</span> classifier <span class="op">=</span> ee<span class="op">.</span><span class="at">Classifier</span><span class="op">.</span><span class="fu">smileRandomForest</span>(t) </span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">train</span>({ </span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">features</span><span class="op">:</span> trainingSet<span class="op">,</span> </span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">classProperty</span><span class="op">:</span> <span class="st">'class'</span><span class="op">,</span> </span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">inputProperties</span><span class="op">:</span> predictionBands </span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a> })<span class="op">;</span> <span class="cf">return</span> testingSet </span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">classify</span>(classifier) </span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">errorMatrix</span>(<span class="st">'class'</span><span class="op">,</span> <span class="st">'classification'</span>) </span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">accuracy</span>()<span class="op">;</span> </span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>})<span class="op">;</span> </span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a><span class="fu">print</span>(ui<span class="op">.</span><span class="at">Chart</span><span class="op">.</span><span class="at">array</span><span class="op">.</span><span class="fu">values</span>({ </span>
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">array</span><span class="op">:</span> ee<span class="op">.</span><span class="fu">Array</span>(accuracies)<span class="op">,</span> </span>
<span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">axis</span><span class="op">:</span> <span class="dv">0</span><span class="op">,</span> </span>
<span id="cb26-18"><a href="#cb26-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">xLabels</span><span class="op">:</span> numTrees </span>
<span id="cb26-19"><a href="#cb26-19" aria-hidden="true" tabindex="-1"></a>})<span class="op">.</span><span class="fu">setOptions</span>({ </span>
<span id="cb26-20"><a href="#cb26-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">hAxis</span><span class="op">:</span> { </span>
<span id="cb26-21"><a href="#cb26-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Number of trees'</span> }<span class="op">,</span> </span>
<span id="cb26-22"><a href="#cb26-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">vAxis</span><span class="op">:</span> { </span>
<span id="cb26-23"><a href="#cb26-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Accuracy'</span> }<span class="op">,</span> </span>
<span id="cb26-24"><a href="#cb26-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">title</span><span class="op">:</span> <span class="st">'Accuracy per number of trees'</span> </span>
<span id="cb26-25"><a href="#cb26-25" aria-hidden="true" tabindex="-1"></a>}))<span class="op">;</span></span>
<span id="cb26-26"><a href="#cb26-26" aria-hidden="true" tabindex="-1"></a></span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><img src="images/F2/image45.png" class="img-fluid figure-img"></p>
<p></p><figcaption class="figure-caption">Fig. F2.2.2 Chart showing accuracy per number of Random Forest trees</figcaption><p></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F22b. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
<p>Section 3. Spatial autocorrelation</p>
<p>We might also want to ensure that the samples from the training set are uncorrelated with the samples from the testing set. This might result from the spatial autocorrelation of the phenomenon being predicted. One way to exclude samples that might be correlated in this manner is to remove samples that are within some distance to any other sample. In Earth Engine, this can be accomplished with a spatial join. The following Code Checkpoint replicates Sect. 1 but with a spatial join that excludes training points that are less than 1000 meters distant from testing points.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Code Checkpoint F22c. The books repository contains a script that shows what your code should look like at this point.</p>
</div>
</div>
</section>
<section id="conclusion-2" class="level3 unnumbered">
<h3 class="unnumbered anchored" data-anchor-id="conclusion-2">Conclusion</h3>
<p>You should now understand how to calculate how well your classifier is performing on the data used to build the model. This is a useful way to understand how a classifier is performing, because it can help indicate which classes are performing better than others. A poorly modeled class can sometimes be improved by, for example, collecting more training points for that class.</p>
<p>Nevertheless, a model may work well on training data but work poorly in locations randomly chosen in the study area. To understand a models behavior on testing data, analysts employ protocols required to produce scientifically rigorous and transparent estimates of the accuracy and area of each class in the study region. We will not explore those practices in this chapter, but if you are interested, there are tutorials and papers available online that can guide you through the process. Links to some of those tutorials can be found in the “For Further Reading” section of this book.</p>
<p>References</p>
<p>Congalton R (1994) Accuracy assessment of remotely sensed data: Future needs and directions. In: Proceedings of Pecora 12 land information from space-based systems. pp 385388</p>
<p>Foody GM (2002) Status of land cover classification accuracy assessment. Remote Sens Environ 80:185201. https://doi.org/10.1016/S0034-4257(01)00295-4</p>
<p>Olofsson P, Foody GM, Herold M, et al (2014) Good practices for estimating area and assessing accuracy of land change. Remote Sens Environ 148:4257. https://doi.org/10.1016/j.rse.2014.02.015</p>
<p>Stehman SV (2013) Estimating area from an accuracy assessment error matrix. Remote Sens Environ 132:202211. https://doi.org/10.1016/j.rse.2013.01.016</p>
<p>Stehman SV (1997) Selecting and interpreting measures of thematic classification accuracy. Remote Sens Environ 62:7789. https://doi.org/10.1016/S0034-4257(97)00083-7</p>
<p>Stehman SV, Foody GM (2019) Key issues in rigorous accuracy assessment of land cover products. Remote Sens Environ 231:111199. https://doi.org/10.1016/j.rse.2019.05.018</p>
<p>Strahler AH, Boschetti L, Foody GM, et al (2006) Global land cover validation: Recommendations for evaluation and accuracy assessment of global land cover maps. Eur Communities, Luxemb 51:160</p>
</section>
</section>
</main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
toggleBodyColorPrimary();
const disableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'prefetch';
}
}
const enableStylesheet = (stylesheets) => {
for (let i=0; i < stylesheets.length; i++) {
const stylesheet = stylesheets[i];
stylesheet.rel = 'stylesheet';
}
}
const manageTransitions = (selector, allowTransitions) => {
const els = window.document.querySelectorAll(selector);
for (let i=0; i < els.length; i++) {
const el = els[i];
if (allowTransitions) {
el.classList.remove('notransition');
} else {
el.classList.add('notransition');
}
}
}
const toggleColorMode = (alternate) => {
// Switch the stylesheets
const alternateStylesheets = window.document.querySelectorAll('link.quarto-color-scheme.quarto-color-alternate');
manageTransitions('#quarto-margin-sidebar .nav-link', false);
if (alternate) {
enableStylesheet(alternateStylesheets);
for (const sheetNode of alternateStylesheets) {
if (sheetNode.id === "quarto-bootstrap") {
toggleBodyColorMode(sheetNode);
}
}
} else {
disableStylesheet(alternateStylesheets);
toggleBodyColorPrimary();
}
manageTransitions('#quarto-margin-sidebar .nav-link', true);
// Switch the toggles
const toggles = window.document.querySelectorAll('.quarto-color-scheme-toggle');
for (let i=0; i < toggles.length; i++) {
const toggle = toggles[i];
if (toggle) {
if (alternate) {
toggle.classList.add("alternate");
} else {
toggle.classList.remove("alternate");
}
}
}
// Hack to workaround the fact that safari doesn't
// properly recolor the scrollbar when toggling (#1455)
if (navigator.userAgent.indexOf('Safari') > 0 && navigator.userAgent.indexOf('Chrome') == -1) {
manageTransitions("body", false);
window.scrollTo(0, 1);
setTimeout(() => {
window.scrollTo(0, 0);
manageTransitions("body", true);
}, 40);
}
}
const isFileUrl = () => {
return window.location.protocol === 'file:';
}
const hasAlternateSentinel = () => {
let styleSentinel = getColorSchemeSentinel();
if (styleSentinel !== null) {
return styleSentinel === "alternate";
} else {
return false;
}
}
const setStyleSentinel = (alternate) => {
const value = alternate ? "alternate" : "default";
if (!isFileUrl()) {
window.localStorage.setItem("quarto-color-scheme", value);
} else {
localAlternateSentinel = value;
}
}
const getColorSchemeSentinel = () => {
if (!isFileUrl()) {
const storageValue = window.localStorage.getItem("quarto-color-scheme");
return storageValue != null ? storageValue : localAlternateSentinel;
} else {
return localAlternateSentinel;
}
}
let localAlternateSentinel = 'alternate';
// Dark / light mode switch
window.quartoToggleColorScheme = () => {
// Read the current dark / light value
let toAlternate = !hasAlternateSentinel();
toggleColorMode(toAlternate);
setStyleSentinel(toAlternate);
};
// Ensure there is a toggle, if there isn't float one in the top right
if (window.document.querySelector('.quarto-color-scheme-toggle') === null) {
const a = window.document.createElement('a');
a.classList.add('top-right');
a.classList.add('quarto-color-scheme-toggle');
a.href = "";
a.onclick = function() { try { window.quartoToggleColorScheme(); } catch {} return false; };
const i = window.document.createElement("i");
i.classList.add('bi');
a.appendChild(i);
window.document.body.appendChild(a);
}
// Switch to dark mode if need be
if (hasAlternateSentinel()) {
toggleColorMode(true);
} else {
toggleColorMode(false);
}
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const clipboard = new window.ClipboardJS('.code-copy-button', {
text: function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
});
clipboard.on('success', function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
});
function tippyHover(el, contentFn) {
const config = {
allowHTML: true,
content: contentFn,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start'
};
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
return note.innerHTML;
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
<nav class="page-navigation">
<div class="nav-page nav-page-previous">
<a href="./B1_Getting_Started.html" class="pagination-link">
<i class="bi bi-arrow-left-short"></i> <span class="nav-page-text"><span class="chapter-title">Getting Started</span></span>
</a>
</div>
<div class="nav-page nav-page-next">
<a href="./B3_Image_Series.html" class="pagination-link">
<span class="nav-page-text"><span class="chapter-title">Image Series</span></span> <i class="bi bi-arrow-right-short"></i>
</a>
</div>
</nav>
</div> <!-- /content -->
</body></html>