Spectral range | Name | Range, nm | Spatial resolution in nadir, m |
---|---|---|---|
Panchromatic (1 channel; covers the visible part of the spectrum) | 450-800 | 0.31 | |
Multispectral (8 channels) | Coastal | 400-450 | 1.24 |
Blue | 450-510 | ||
Green | 510-580 | ||
Yellow | 585-625 | ||
Red | 630-690 | ||
Red edge | 705-745 | ||
Near infrared | 770-895 | ||
Near infrared | 860-1040 | ||
Multiband in the short infrared range (8 channels) | SWIR-1 | 1195-1225 | 3.70 |
SWIR-2 | 1550-1590 | ||
SWIR-3 | 1640-1680 | ||
SWIR-4 | 1710-1750 | ||
SWIR-5 | 2145-2185 | ||
SWIR-6 | 2185-2225 | ||
SWIR-7 | 2235-2285 | ||
SWIR-8 | 2295-2365 |
Index name | Formula | Application |
---|---|---|
Iron Oxide Index | Red Blue | To detect the content of iron oxides |
Clay Minerals Index | The ratio of the brightness values ​​within the middle infrared channel (CIC). CIC1 / CIC2, where CIC1 is the range from 1.55 to 1.75 microns, CIC2 is the range from 2.08 to 2.35 microns | To identify the content of clay minerals |
Ferrous Minerals Index | The ratio of the brightness value in the average infrared (SIK1; from 1.55 to 1.75 ÎĽm) channel to the brightness value in the near infrared channel (BIC). SIK1 / BIK | To detect glandular minerals |
Redness Index (RI) | Based on the difference in reflectivity of red minerals in the red (K) and green (W) ranges. RI = ( - ) / ( + ) | To detect the content of iron oxide in the soil |
Normalized differential snow index (NDSI) | NDSI is a relative value characterized by the difference in snow reflectivity in the red (K) and shortwave infrared (CIR) ranges. NDSI = (K - KIK) / (K + KIK) | Used to highlight areas covered with snow. For snow NDSI> 0.4 |
Water Index (WI) | WI = 0.90 µm / 0.97 µm | It is used to determine the water content in vegetation from hyperspectral images |
Normalized Differential Vegetation Index (NDVI) | Chlorophyll of plant leaves reflects radiation in the near infrared (NIR) range of the electromagnetic spectrum and absorbs it in red (K). The ratio of brightness values ​​in these two channels allows you to clearly separate and analyze plant from other natural objects. NDVI = (BIK - K) / (BIK + K) | Shows the presence and condition of vegetation. NDVI values ​​range from -1 to 1; Dense vegetation: 0.7; Sparse vegetation: 0.5; Open soil: 0.025; Clouds: 0; Snow and ice: -0.05; Water: -0.25; Artificial materials (concrete, asphalt): -0.5 |
conda install -c conda-forge gdal conda install -c conda-forge rasterio
from osgeo import gdal import numpy as np src_ds = gdal.Open('image.tif') img = src_ds.ReadAsArray() #height, width, band img = np.rollaxis(img, 0, 3) width = src_ds.RasterXSize height = src_ds.RasterYSize gt = src_ds.GetGeoTransform() # minx = gt[0] miny = gt[3] + width*gt[4] + height*gt[5] maxx = gt[0] + width*gt[1] + height*gt[2] maxy = gt[3]
import numpy as np import rasterio #fiona , shp geojson import fiona import cv2 from shapely.geometry import shape from shapely.ops import cascaded_union from shapely.ops import transform from shapely.geometry import MultiPolygon def rasterize_polygons(img_mask, polygons): if not polygons: return img_mask if polygons.geom_type == 'Polygon': polygons = MultiPolygon([polygons]) int_coords = lambda x: np.array(x).round().astype(np.int32) exteriors = [int_coords(poly.exterior.coords) for poly in polygons] interiors = [int_coords(pi.coords) for poly in polygons for pi in poly.interiors] cv2.fillPoly(img_mask, exteriors, 255) cv2.fillPoly(img_mask, interiors, 0) return img_mask def get_polygons(shapefile): geoms = [feature["geometry"] for feature in shapefile] polygons = list() for g in geoms: s = shape(g) if s.geom_type == 'Polygon': if s.is_valid: polygons.append(s) else: # polygons.append(s.buffer(0)) elif s.geom_type == 'MultiPolygon': for p in s: if p.is_valid: polygons.append(p) else: # polygons.append(p.buffer(0)) mpolygon = cascaded_union(polygons) return mpolygon # geojson src = rasterio.open('image.tif') shapefile = fiona.open('buildings.geojson', "r") # ( ) left = src.bounds.left right = src.bounds.right bottom = src.bounds.bottom top = src.bounds.top height,width = src.shape # mpolygon = get_polygons(shapefile) # , mpolygon = transform(lambda x, y, z=None: (width * (x - left) / float(right - left),\ height - height * (y - bottom) / float(top - bottom)), mpolygon) # real_mask = np.zeros((height,width), np.uint8) real_mask = rasterize_polygons(real_mask, mpolygon)
# epsg:4326 epsg:32637 ( ) def wgs84_to_32637(lon, lat): from pyproj import Proj, transform inProj = Proj(init='epsg:4326') outProj = Proj(init='epsg:32637') x,y = transform(inProj,outProj,lon,lat) return x,y
from osgeo import gdal from sklearn.decomposition import IncrementalPCA from sklearn import preprocessing import numpy as np import matplotlib.pyplot as plt # , height, width, band , PCA src_ds = gdal.Open('0.tif') img = src_ds.ReadAsArray() img = np.rollaxis(img, 0, 3)[300:1000, 700:1700, :] height, width, bands = img.shape # PCA data = img.reshape((height * width, 4)).astype(np.float) num_data, dim = data.shape pca = IncrementalPCA(n_components = 3, whiten = True) #c ( ) x = preprocessing.scale(data) # y = pca.fit_transform(x) y = y.reshape((height, width, 3)) # for idx in range(0, 3): band_max = y[:, :, idx].max() y[:, :, idx] = np.around(y[:, :, idx] * 255.0 / band_max).astype(np.uint8)
inputs = Input((channel_number, None, None))
import pandas as pd import numpy as np import rasterio import math import cv2 from shapely.geometry import Point # ( ) # remove_small_objects remove_small_holes skimage def filterByLength(input, length, more=True): import Queue as queue copy = input.copy() output = np.zeros_like(input) for i in range(input.shape[0]): for j in range(input.shape[1]): if (copy[i][j] == 255): q_coords = queue.Queue() output_coords = list() copy[i][j] = 100 q_coords.put([i,j]) output_coords.append([i,j]) while(q_coords.empty() == False): currentCenter = q_coords.get() for idx1 in range(3): for idx2 in range(3): offset1 = - 1 + idx1 offset2 = - 1 + idx2 currentPoint = [currentCenter[0] + offset1, currentCenter[1] + offset2] if (currentPoint[0] >= 0 and currentPoint[0] < input.shape[0]): if (currentPoint[1] >= 0 and currentPoint[1] < input.shape[1]): if (copy[currentPoint[0]][currentPoint[1]] == 255): copy[currentPoint[0]][currentPoint[1]] = 100 q_coords.put(currentPoint) output_coords.append(currentPoint) if (more == True): if (len(output_coords) >= length): for coord in output_coords: output[coord[0]][coord[1]] = 255 else: if (len(output_coords) < length): for coord in output_coords: output[coord[0]][coord[1]] = 255 return output # epsg:32637 epsg:4326 def getLongLat(x1, y1): from pyproj import Proj, transform inProj = Proj(init='epsg:32637') outProj = Proj(init='epsg:4326') x2,y2 = transform(inProj,outProj,x1,y1) return x2,y2 # epsg:4326 epsg:32637 def get32637(x1, y1): from pyproj import Proj, transform inProj = Proj(init='epsg:4326') outProj = Proj(init='epsg:32637') x2,y2 = transform(inProj,outProj,x1,y1) return x2,y2 # epsg:32637 () def crsToPixs(width, height, left, right, bottom, top, coords): x = coords.xy[0][0] y = coords.xy[1][0] x = width * (x - left) / (right - left) y = height - height * (y - bottom) / (top - bottom) x = int(math.ceil(x)) y = int(math.ceil(y)) return x,y # def shadowSegmentation(roi, threshold = 60): thresh = cv2.equalizeHist(roi) ret, thresh = cv2.threshold(thresh,threshold,255,cv2.THRESH_BINARY_INV) tmp = filterByLength(thresh, 50) if np.count_nonzero(tmp) != 0: thresh = tmp return thresh # () ; x,y - thresh def getShadowSize(thresh, x, y): # min_dist = thresh.shape[0] min_dist_coords = (0, 0) for i in range(thresh.shape[0]): for j in range(thresh.shape[1]): if (thresh[i,j] == 255) and (math.sqrt( (i - y) * (i - y) + (j - x) * (j - x) ) < min_dist): min_dist = math.sqrt( (i - y) * (i - y) + (j - x) * (j - x) ) min_dist_coords = (i, j) #y,x # , import Queue as queue q_coords = queue.Queue() q_coords.put(min_dist_coords) mask = thresh.copy() output_coords = list() output_coords.append(min_dist_coords) while q_coords.empty() == False: currentCenter = q_coords.get() for idx1 in range(3): for idx2 in range(3): offset1 = - 1 + idx1 offset2 = - 1 + idx2 currentPoint = [currentCenter[0] + offset1, currentCenter[1] + offset2] if (currentPoint[0] >= 0 and currentPoint[0] < mask.shape[0]): if (currentPoint[1] >= 0 and currentPoint[1] < mask.shape[1]): if (mask[currentPoint[0]][currentPoint[1]] == 255): mask[currentPoint[0]][currentPoint[1]] = 100 q_coords.put(currentPoint) output_coords.append(currentPoint) # mask = np.zeros_like(mask) for i in range(len(output_coords)): mask[output_coords[i][0]][output_coords[i][1]] = 255 # () erode kernel = np.ones((3,3),np.uint8) i = 0 while np.count_nonzero(mask) != 0: mask = cv2.erode(mask,kernel,iterations = 1) i += 1 return i + 1 # , def getNoCloudArea(b, g, r, n): gray = (b + g + r + n) / 4.0 band_max = np.max(gray) gray = np.around(gray * 255.0 / band_max).astype(np.uint8) gray[gray == 0] = 255 ret, no_cloud_area = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY) kernel = np.ones((50, 50), np.uint8) no_cloud_area = cv2.morphologyEx(no_cloud_area, cv2.MORPH_OPEN, kernel) kernel = np.ones((100, 100), np.uint8) no_cloud_area = cv2.morphologyEx(no_cloud_area, cv2.MORPH_DILATE, kernel) no_cloud_area = cv2.morphologyEx(no_cloud_area, cv2.MORPH_DILATE, kernel) no_cloud_area = 255 - no_cloud_area return no_cloud_area # csv lat,long,height df = pd.read_csv('buildings.csv') # csv image_df = pd.read_csv('geotiff.csv') # sun_elevation = image_df['sun_elevation'].values[0] with rasterio.open('image.tif') as src: # # epsg:32637 left = src.bounds.left right = src.bounds.right bottom = src.bounds.bottom top = src.bounds.top height,width = src.shape b, g, r, n = map(src.read, (1, 2, 3, 4)) # , .. band_max = g.max() img = np.around(g * 255.0 / band_max).astype(np.uint8) # , no_cloud_area = getNoCloudArea(b, g, r, n) heights = list() # (size, size) size = 30 for idx in range(0, df.shape[0]): # lat = df.loc[idx]['lat'] lon = df.loc[idx]['long'] build_height = int(df.loc[idx]['height']) # epsg:32637 #( ) build_coords = Point(get32637(lon, lat)) # , x,y = crsToPixs(width, height, left, right, bottom, top, build_coords) # , if no_cloud_area[y][x] == 255: # , # roi = img[y-size:y,x-size:x].copy() shadow = shadowSegmentation(roi) #(size, size) - roi # #( 3 ) shadow_length = getShadowSize(shadow, size, size) * 3 est_height = shadow_length * math.tan(sun_elevation * 3.14 / 180) est_height = int(est_height) heights.append((est_height, build_height)) MAPE = 0 for i in range(len(heights)): MAPE += ( abs(heights[i][0] - heights[i][1]) / float(heights[i][1]) ) MAPE *= (100 / float(len(heights)))
Source: https://habr.com/ru/post/342028/
All Articles