DynamicPacific commited on
Commit
824dd08
·
1 Parent(s): 67cd09b

add area detection

Browse files
Files changed (2) hide show
  1. app.py +9 -1
  2. utils/advanced_extraction.py +26 -5
app.py CHANGED
@@ -28,7 +28,7 @@ logger = logging.getLogger('forestai')
28
 
29
  # Feature styles for trees only
30
  FEATURE_STYLES = {
31
- 'trees': {"color": "green", "fillColor": "yellow", "fillOpacity": 0.3, "weight": 2}
32
  }
33
 
34
  # Example file path
@@ -134,6 +134,14 @@ def create_split_view_map(geojson_data, bounds):
134
  )
135
  geojson_layer.add_to(m)
136
 
 
 
 
 
 
 
 
 
137
  # Add split view plugin
138
  plugins.SideBySideLayers(
139
  layer_left=left_layer,
 
28
 
29
  # Feature styles for trees only
30
  FEATURE_STYLES = {
31
+ 'trees': {"color": "yellow", "fillColor": "yellow", "fillOpacity": 0.3, "weight": 2}
32
  }
33
 
34
  # Example file path
 
134
  )
135
  geojson_layer.add_to(m)
136
 
137
+ # Add red outline for the region bounding box
138
+ folium.Rectangle(
139
+ bounds=[[south, west], [north, east]],
140
+ color='red',
141
+ weight=3,
142
+ fill=False
143
+ ).add_to(m)
144
+
145
  # Add split view plugin
146
  plugins.SideBySideLayers(
147
  layer_left=left_layer,
utils/advanced_extraction.py CHANGED
@@ -12,26 +12,38 @@ def extract_features_from_geotiff(image_path, output_folder, feature_type="trees
12
  logging.info(f"Extracting {feature_type} from {image_path}")
13
 
14
  with rasterio.open(image_path) as src:
 
 
15
  # Enhanced NDVI calculation
16
  if src.count >= 3:
17
  red = src.read(1).astype(float)
18
  green = src.read(2).astype(float)
19
  nir = src.read(4).astype(float) if src.count >= 4 else green
20
 
 
 
 
 
21
  # Improved NDVI calculation
22
  ndvi = np.divide(nir - red, nir + red + 1e-10)
23
 
24
  # Adaptive thresholding based on image statistics
25
  ndvi_mean = np.mean(ndvi)
26
  ndvi_std = np.std(ndvi)
27
- threshold = max(0.3, ndvi_mean + 0.5 * ndvi_std) # More adaptive threshold
 
 
28
 
29
  mask = ndvi > threshold
 
30
  else:
31
  band = src.read(1)
 
32
  # Adaptive threshold for single-band images
33
- threshold = np.percentile(band, 70) # Increased from 60
 
34
  mask = band > threshold
 
35
 
36
  # Get bounds
37
  bounds = src.bounds
@@ -45,30 +57,38 @@ def extract_features_from_geotiff(image_path, output_folder, feature_type="trees
45
 
46
  # Convert to uint8 for OpenCV processing
47
  mask_uint8 = (mask * 255).astype(np.uint8)
 
48
 
49
  # Morphological operations to clean up the mask
50
  kernel = np.ones((3, 3), np.uint8)
51
  mask_cleaned = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel)
52
  mask_cleaned = cv2.morphologyEx(mask_cleaned, cv2.MORPH_OPEN, kernel)
53
 
 
 
54
  # Find contours instead of using grid
55
  contours, _ = cv2.findContours(mask_cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
56
 
57
  # Filter contours by area and create features
58
  features = []
59
  height, width = mask.shape
60
- min_area = (height * width) * 0.001 # Minimum 0.1% of image area
61
- max_area = (height * width) * 0.1 # Maximum 10% of image area
 
 
62
 
63
  for i, contour in enumerate(contours):
64
  area = cv2.contourArea(contour)
 
65
 
66
  # Filter by area
67
  if area < min_area or area > max_area:
 
68
  continue
69
 
70
  # Simplify contour for better polygon shape
71
- epsilon = 0.02 * cv2.arcLength(contour, True)
72
  approx = cv2.approxPolyDP(contour, epsilon, True)
73
 
74
  # Convert contour points to geographic coordinates
@@ -109,6 +129,7 @@ def extract_features_from_geotiff(image_path, output_folder, feature_type="trees
109
  }
110
 
111
  features.append(feature)
 
112
 
113
  logging.info(f"Extracted {len(features)} tree features")
114
 
 
12
  logging.info(f"Extracting {feature_type} from {image_path}")
13
 
14
  with rasterio.open(image_path) as src:
15
+ logging.info(f"Image shape: {src.shape}, bands: {src.count}")
16
+
17
  # Enhanced NDVI calculation
18
  if src.count >= 3:
19
  red = src.read(1).astype(float)
20
  green = src.read(2).astype(float)
21
  nir = src.read(4).astype(float) if src.count >= 4 else green
22
 
23
+ logging.info(f"Red band range: {red.min():.3f} to {red.max():.3f}")
24
+ logging.info(f"Green band range: {green.min():.3f} to {green.max():.3f}")
25
+ logging.info(f"NIR band range: {nir.min():.3f} to {nir.max():.3f}")
26
+
27
  # Improved NDVI calculation
28
  ndvi = np.divide(nir - red, nir + red + 1e-10)
29
 
30
  # Adaptive thresholding based on image statistics
31
  ndvi_mean = np.mean(ndvi)
32
  ndvi_std = np.std(ndvi)
33
+ threshold = max(0.05, ndvi_mean + 0.1 * ndvi_std) # More lenient threshold
34
+
35
+ logging.info(f"NDVI mean: {ndvi_mean:.3f}, std: {ndvi_std:.3f}, threshold: {threshold:.3f}")
36
 
37
  mask = ndvi > threshold
38
+ logging.info(f"Mask pixels above threshold: {np.sum(mask)} out of {mask.size}")
39
  else:
40
  band = src.read(1)
41
+ logging.info(f"Single band range: {band.min():.3f} to {band.max():.3f}")
42
  # Adaptive threshold for single-band images
43
+ threshold = np.percentile(band, 40) # Reduced from 70 to be more lenient
44
+ logging.info(f"Single band threshold: {threshold:.3f}")
45
  mask = band > threshold
46
+ logging.info(f"Mask pixels above threshold: {np.sum(mask)} out of {mask.size}")
47
 
48
  # Get bounds
49
  bounds = src.bounds
 
57
 
58
  # Convert to uint8 for OpenCV processing
59
  mask_uint8 = (mask * 255).astype(np.uint8)
60
+ logging.info(f"Mask uint8 range: {mask_uint8.min()} to {mask_uint8.max()}")
61
 
62
  # Morphological operations to clean up the mask
63
  kernel = np.ones((3, 3), np.uint8)
64
  mask_cleaned = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel)
65
  mask_cleaned = cv2.morphologyEx(mask_cleaned, cv2.MORPH_OPEN, kernel)
66
 
67
+ logging.info(f"After morphological ops: {np.sum(mask_cleaned > 0)} pixels")
68
+
69
  # Find contours instead of using grid
70
  contours, _ = cv2.findContours(mask_cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
71
+ logging.info(f"Found {len(contours)} contours")
72
 
73
  # Filter contours by area and create features
74
  features = []
75
  height, width = mask.shape
76
+ min_area = (height * width) * 0.0001 # Minimum 0.01% of image area (reduced from 0.1%)
77
+ max_area = (height * width) * 0.2 # Maximum 20% of image area (increased from 10%)
78
+
79
+ logging.info(f"Area filter: min={min_area:.0f}, max={max_area:.0f}")
80
 
81
  for i, contour in enumerate(contours):
82
  area = cv2.contourArea(contour)
83
+ logging.info(f"Contour {i}: area={area:.0f}")
84
 
85
  # Filter by area
86
  if area < min_area or area > max_area:
87
+ logging.info(f"Contour {i}: filtered out (area {area:.0f} not in range)")
88
  continue
89
 
90
  # Simplify contour for better polygon shape
91
+ epsilon = 0.005 * cv2.arcLength(contour, True)
92
  approx = cv2.approxPolyDP(contour, epsilon, True)
93
 
94
  # Convert contour points to geographic coordinates
 
129
  }
130
 
131
  features.append(feature)
132
+ logging.info(f"Contour {i}: added as feature with {len(polygon_coords)} points")
133
 
134
  logging.info(f"Extracted {len(features)} tree features")
135