├── Data_processing.R ├── Data_processing.Rmd ├── Landuse_ID.csv └── README.md /Data_processing.R: -------------------------------------------------------------------------------- 1 | # Data Processing: Image Classification using Deep Neural Network 2 | # Author: "Zia Ahmed, PhD, University at Buffalo" 3 | 4 | ### Load R packages 5 | library(rgdal) # spatial data processing 6 | library(raster) # raster processing 7 | library(plyr) # data manipulation 8 | library(dplyr) # data manipulation 9 | library(RStoolbox) # ploting spatial data with ggplot2 10 | library(RColorBrewer) 11 | library(sp) 12 | library(ggplot2) 13 | 14 | ### Set working directory 15 | setwd("F:\\My_GitHub\\DNN_H20_R") 16 | dsn<-("F:\\My_GitHub\\DNN_H20_R\\Data") 17 | 18 | ### Import training polygon layer 19 | poly <- readOGR(dsn=dsn, "Training_layer") 20 | ID<-read.csv("Data\\Landuse_ID.csv", header=T) 21 | 22 | ### Convert polygon to raster 23 | #### Raster extent (we will use any band of Sentinel-2 to set raster extent, we use B2 for define extent) 24 | b2<-raster("Data\\B2.tif") # 25 | crs(b2) <- "+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs" 26 | extent=extent(b2) 27 | 28 | #### Convert to raster (2.5 m grid size) 29 | r<-raster(extent, resolution=2.5, 30 | crs = '+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs ') 31 | extent(r) <- extent(poly) 32 | rp <- rasterize(poly, r, 'Class_ID') 33 | plot(rp) 34 | 35 | #### Convert raster to data.frame and rename colum to "layer"" to Class_ID 36 | rp.df <- as.data.frame(rasterToPoints(rp)) 37 | colnames(rp.df)[3] <- 'Class_ID' 38 | 39 | #### Create a Spatial point Data frame 40 | xy <- rp.df[,c(1,2)] 41 | point.SPDF <- SpatialPointsDataFrame(coords = xy, 42 | data=rp.df, 43 | proj4string = CRS("+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs")) 44 | #### Load multi-band raster 45 | multi=stack("Data\\multi_bands.tif") 46 | # rename the bands 47 | names(multi) <- c("B2", "B3","B4","B5","B6","B7","B8","B8A","B11","B12") 48 | 49 | ### Plot map, to check everything working well 50 | 51 | # Natural Color 52 | p1<-ggRGB(multi, r=3, g=2, b=1, stretch = "lin")+ 53 | theme(axis.title.x=element_blank(), 54 | axis.text.x=element_blank(), 55 | axis.ticks.x=element_blank(), 56 | axis.title.y=element_blank(), 57 | axis.text.y=element_blank(), 58 | axis.ticks.y=element_blank())+ 59 | ggtitle("Natural Color\n(R= Red, G= Green, B= Blue)") 60 | # False Color image 61 | p2<-ggRGB(multi, r=7, g=3, b=2, stretch = "lin")+ 62 | theme(axis.title.x=element_blank(), 63 | axis.text.x=element_blank(), 64 | axis.ticks.x=element_blank(), 65 | axis.title.y=element_blank(), 66 | axis.text.y=element_blank(), 67 | axis.ticks.y=element_blank())+ 68 | ggtitle("False Color Infrared\n(R= NIR, G= Red, B=Green)") 69 | 70 | source("multi_plot_function.r") # We need this function to plot multiple figures in one page 71 | #(http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/) 72 | multiplot(p1, p2, cols=2) 73 | 74 | #### Extract raster values to point file 75 | point.df <- extract(multi, point.SPDF, df=TRUE, method='simple') 76 | 77 | #### Combine with data frame 78 | point.mf<-cbind(rp.df,point.df) 79 | head(point.mf) 80 | 81 | #### Keep values belong only to 5 classes 82 | point.train<-point.mf %>% 83 | select(x,y,Class_ID, B2, B3, B4, B5, B6,B7,B8,B8A,B11,B12) %>% 84 | filter(Class_ID >0) 85 | 86 | #### Add class ID and save as a CSV file 87 | point<-join(point.train, ID, by="Class_ID", type="inner") 88 | write.csv(point, "point_data.csv", row.names=F) 89 | 90 | ### Convert raster stack to CSV file 91 | #### First creat XY data-frame, will use Band B2 92 | grid.point <- data.frame(rasterToPoints(b2)) 93 | # Remove B2 column 94 | grid.point$B2<-NULL 95 | names(grid.point) 96 | 97 | #### Convert to sptial point data frame and define projection 98 | coordinates(grid.point) <- ~x + y 99 | projection(grid.point) <- CRS("+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs") 100 | 101 | #### Extract all bands values to grird.point 102 | df.grid<- extract(multi, grid.point, df=TRUE, method='simple') 103 | 104 | #### Combine with grid.df (we need to add xy coordinated for mapping) and write as a CSV file 105 | grid<-cbind(as.data.frame(grid.point),df.grid) 106 | head(grid) 107 | write.csv(grid, "grid_data.csv", row.names=F) 108 | 109 | -------------------------------------------------------------------------------- /Data_processing.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: ' Data processing' 3 | author: "Zia Ahmed, PhD, University at Buffalo" 4 | date: "April 5, 2018" 5 | output: 6 | html_document: default 7 | word_document: default 8 | --- 9 | 10 | #### Load R packages 11 | 12 | ```{r message=F, warning=F} 13 | library(rgdal) # spatial data processing 14 | library(raster) # raster processing 15 | library(plyr) # data manipulation 16 | library(dplyr) # data manipulation 17 | library(RStoolbox) # ploting spatial data 18 | library(RColorBrewer) 19 | library(ggplot2) 20 | library(sp) 21 | ``` 22 | 23 | #### Set working directory 24 | 25 | ```{r} 26 | setwd("F:\\My_GitHub\\DNN_H20_R") 27 | dsn<-("F:\\My_GitHub\\DNN_H20_R\\Data") 28 | ``` 29 | 30 | #### Import training polygon layer 31 | 32 | ```{r} 33 | poly <- readOGR(dsn=dsn, "Training_layer") 34 | ID<-read.csv("Data\\Landuse_ID.csv", header=T) 35 | ID 36 | ``` 37 | 38 | #### Convert polygon to raster 39 | #### Raster extent (we will use any band of Sentinel-2 to set raster extent, we use B2 for define extent) 40 | 41 | ```{r} 42 | b2<-raster("Data\\B2.tif") # 43 | crs(b2) <- "+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs" 44 | extent=extent(b2) 45 | ``` 46 | 47 | #### Convert to raster (2.5 m grid size) 48 | 49 | ```{r} 50 | r <- raster(extent, resolution=2.5, 51 | crs = '+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs ') 52 | extent(r) <- extent(poly) 53 | rp <- rasterize(poly, r, 'Class_ID') 54 | plot(rp) 55 | ``` 56 | 57 | #### Convert raster to data.frame and rename colum to "layer"" to Class_ID 58 | 59 | ```{r} 60 | rp.df <- as.data.frame(rasterToPoints(rp)) 61 | colnames(rp.df)[3] <- 'Class_ID' 62 | ``` 63 | 64 | #### Create a Spatial point Data frame 65 | 66 | ```{r} 67 | xy <- rp.df[,c(1,2)] 68 | point.SPDF <- SpatialPointsDataFrame(coords = xy, 69 | data=rp.df, 70 | proj4string = CRS("+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs")) 71 | ``` 72 | 73 | #### Load multi-band raster 74 | 75 | ```{r} 76 | multi=stack("Data//multi_bands.tif") 77 | # rename the bands 78 | names(multi) <- c("B2", "B3","B4","B5","B6","B7","B8","B8A","B11","B12") 79 | ``` 80 | 81 | #### Plot map 82 | 83 | ```{r} 84 | 85 | # Natural Color 86 | p1<-ggRGB(multi, r=3, g=2, b=1, stretch = "lin")+ 87 | theme(axis.title.x=element_blank(), 88 | axis.text.x=element_blank(), 89 | axis.ticks.x=element_blank(), 90 | axis.title.y=element_blank(), 91 | axis.text.y=element_blank(), 92 | axis.ticks.y=element_blank())+ 93 | ggtitle("Natural Color\n(R= Red, G= Green, B= Blue)") 94 | # False Color image 95 | p2<-ggRGB(multi, r=7, g=3, b=2, stretch = "lin")+ 96 | theme(axis.title.x=element_blank(), 97 | axis.text.x=element_blank(), 98 | axis.ticks.x=element_blank(), 99 | axis.title.y=element_blank(), 100 | axis.text.y=element_blank(), 101 | axis.ticks.y=element_blank())+ 102 | ggtitle("False Color Infrared\n(R= NIR, G= Red, B=Green)") 103 | source("multi_plot_function.r") # We need this function to plot multiple figures in one page 104 | #(http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/) 105 | multiplot(p1, p2, cols=2) 106 | ``` 107 | 108 | #### Save as a tiff file 109 | 110 | ```{r} 111 | windows(width=6, height=4) 112 | tiff( file="FIGURE_Natural_False.tif", 113 | width=6, 114 | height=4, 115 | units = "in", 116 | pointsize = 14, 117 | res=1600, bg = "white", 118 | restoreConsole = TRUE, 119 | compression = "lzw") 120 | par(mar=c(5,5,2,1), oma=c(0,0,0,0)) 121 | multiplot(p1, p2, cols=2) 122 | dev.off() 123 | 124 | ``` 125 | 126 | #### Extract raster values to point file 127 | 128 | ```{r} 129 | point.df <- extract(multi, point.SPDF, df=TRUE, method='simple') 130 | ``` 131 | 132 | #### Combine with data frame 133 | 134 | ```{r} 135 | point.mf<-cbind(rp.df,point.df) 136 | head(point.mf) 137 | ``` 138 | 139 | #### Keep values belong only to 5 classes 140 | 141 | ```{r} 142 | point.train<-point.mf %>% 143 | select(x,y,Class_ID, B2, B3, B4, B5, B6,B7,B8,B8A,B11,B12) %>% 144 | filter(Class_ID >0) 145 | ``` 146 | 147 | #### Add class ID and save as a CSV file 148 | 149 | ```{r} 150 | point<-join(point.train, ID, by="Class_ID", type="inner") 151 | write.csv(point, "point_data.csv", row.names=F) 152 | ``` 153 | 154 | ### Convert raster stack to CSV file 155 | #### First creat XY data-frame, will use Band B2 156 | 157 | ```{r} 158 | grid.point <- data.frame(rasterToPoints(b2)) 159 | # Remove B2 column 160 | grid.point$B2<-NULL 161 | ``` 162 | 163 | #### Convert to sptial point data frame and define projection 164 | 165 | ```{r} 166 | coordinates(grid.point) <- ~x + y 167 | projection(grid.point) <- CRS("+proj=utm +zone=17N +ellps=WGS84 +datum=WGS84 +units=m +no_defs") 168 | ``` 169 | 170 | #### Extract all bands values to grird.point 171 | 172 | ```{r} 173 | df.grid<- extract(multi, grid.point, df=TRUE, method='simple') 174 | ``` 175 | 176 | #### Combine with grid.df (we need to add xy coordinated for mapping) and write as a CSV file 177 | 178 | ```{r} 179 | grid<-cbind(as.data.frame(grid.point),df.grid) 180 | write.csv(grid, "grid_data.csv", row.names=F) 181 | head(grid) 182 | ``` 183 | 184 | 185 | -------------------------------------------------------------------------------- /Landuse_ID.csv: -------------------------------------------------------------------------------- 1 | Class_ID,Class,Description 2 | 1,Class_1,Parking/road/pavement 3 | 2,Class_2,Building 4 | 3,Class_3,Tree/bushes 5 | 4,Class_4,Grass 6 | 5,Class_5,Water 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sentinel 2-Data Processing 2 | We have downloaded one scene of [Sentinel-2](https://www.esa.int/Our_Activities/Observing_the_Earth/Copernicus/Sentinel-2) multi-spectral images from [Copernicus Open Data Hub](https://scihub.copernicus.eu/dhus/#/home) that was acquired on September 21st, 2017. It covers most of the regions in Western New York area.The [spatial](https://earth.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/spatial) and [radiometric](https://earth.esa.int/web/sentinel/user-guides/sentinel-2-msi/resolutions/spatial) resolution of Sentinel-2 imagery varies by spectral band. Sentinel-2 data are acquired in 13 spectral bands in the VNIR and SWIR: four bands at 10 m: Band 2, Band 3, Band 4, and Band 8; six bands at 20 m: Band 5, Band 6, Band 7, Band 8A, Band 11, and Band 12; three bands at 60 m: Band 1, Band 9, and Band 10. 3 | 4 | we have used [Sen2Cor](https://www.esa.int/Our_Activities/Observing_the_Earth/Copernicus/Sentinel-2), a python based processor develop by [ESA](http://www.esa.int/ESA) for [Sentinel-2 Level 2A](https://sentinel.esa.int/web/sentinel/user-guides/document-library/-/asset_publisher/xlslt4309D5h/content/sentinel-2-msi-level-2a-product-definition;jsessionid=012C32D7FCD607BAB2110EBDA053BBC7.jvm1?redirect=https%3A%2F%2Fsentinel.esa.int%2Fweb%2Fsentinel%2Fuser-guides%2Fdocument-library%3Bjsessionid%3D012C32D7FCD607BAB2110EBDA053BBC7.jvm1%3Fp_p_id%3D101_INSTANCE_xlslt4309D5h%26p_p_lifecycle%3D0%26p_p_state%3Dnormal%26p_p_mode%3Dview%26p_p_col_id%3Dcolumn-1%26p_p_col_count%3D1%26_101_INSTANCE_xlslt4309D5h_keywords%3D%26_101_INSTANCE_xlslt4309D5h_advancedSearch%3Dfalse%26_101_INSTANCE_xlslt4309D5h_delta%3D75%26_101_INSTANCE_xlslt4309D5h_andOperator%3Dtrue) product for formatting and processing (such as atmospheric correction, aerosol optical thickness correction, water vapor retrieval, surface reflectance retrieval from TOA, geometric correction with DEM). All bands were resampled at 10 m resolution in [Sentinel Toolboxes](http://step.esa.int/main/download/).In this tutorial we will use a sub-set of images with bands 2, 3, 4, 5, 6, 7, 8, 8A, 11 and 12. 5 | Prior to go for classification of [Sentinel-2](https://www.esa.int/Our_Activities/Observing_the_Earth/Copernicus/Sentinel-2) satellite image using a Deep Neural Network, we need to process the spatial data. Then we will train our Deep Neural Network model with 5 feature classes extracted from Google Earth imagery: (1) parking/road/pavement, (2) building, (3) trees/bushes, (4) grass, and (5) water bodies. We used on-screen digitization in [QGIS](https://www.qgis.org/en/site/) to create polygons representing members of these feature classes. 6 | A subset of atmospheric corrected surface reflectance of Sentinel-2 bands and feature class shape files are avilable for download as [rar](https://www.dropbox.com/s/wcllg9tkxsvge7r/Data.rar?dl=0), [7z](https://www.dropbox.com/s/fz3b8thtm3ogw19/Data.7z?dl=0) and [zip](https://www.dropbox.com/s/9neml6r8wtm8w6g/Data.zip?dl=0) formats. 7 | 8 | 9 | First, we will convert the polygons to 2.5-meter x 2.5 meter raster grid, and then convert them to spatial points. We will next extract values from Sentinel-2 Band's B2, B3, B4, B5, B6, B7, B8, B8A, B11 and B12 bands and add them to the point data set. We also convert all raster bands to spatial point data frame and then to a CSV file. This grid-point data file will be used for prediction of landuse classes. 10 | 11 | --------------------------------------------------------------------------------