NaseefNazrul commited on
Commit
9d8ff0f
·
verified ·
1 Parent(s): 3605545

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -26
app.py CHANGED
@@ -30,8 +30,8 @@ SCALER_FILE = Path("mil_scaler.joblib")
30
  FEATURES_FILE = Path("mil_features.joblib")
31
  PHENO_FILE = Path("phenologythingy.csv")
32
  SPECIES_STATS_FILE = Path("species_stats.csv")
33
- MIN_BLOOM_THRESHOLD = float(os.environ.get("MIN_BLOOM_THRESHOLD", 40.0)) # minimum probability to predict species
34
- MIN_PEAK_FOR_BELL = float(os.environ.get("MIN_PEAK_FOR_BELL", 60.0))
35
 
36
  ELEV_IMAGE_ID = "USGS/SRTMGL1_003"
37
  BUFFER_METERS = int(os.environ.get("BUFFER_METERS", 200))
@@ -119,40 +119,71 @@ def gaussian_kernel(length=12, sigma=1.2):
119
  kern = kern / kern.sum()
120
  return kern
121
 
122
- def smooth_monthly_probs(raw_probs, alpha=ALPHA, sigma=SMOOTH_SIGMA):
123
  """
124
- raw_probs: length-12 array of ML probabilities in [0,100]
125
- Steps:
126
- 1. scale to [0,1]
127
- 2. apply exponent alpha to sharpen
128
- 3. pad circularly then convolve with gaussian kernel
129
- 4. normalize to sum to 1 and return percentages (0-100)
130
  """
131
- a = np.asarray(raw_probs, dtype=float) / 100.0
132
- # apply floor to avoid all zeros
133
- if a.sum() == 0:
134
- a = np.ones_like(a) * (MIN_CURVE_PROB / 100.0)
135
- # sharpen
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  if alpha != 1.0:
137
- a = np.power(a, alpha)
138
- # circular padding for smoothing wrap-around months
139
- pad = int(max(6, 3 * sigma))
140
- padded = np.concatenate([a[-pad:], a, a[:pad]])
141
- # build kernel centered
142
- xs = np.arange(-pad, pad + len(a) + pad) # not used directly
 
 
143
  kern_range = np.arange(-pad, pad + 1)
144
  kern = np.exp(-0.5 * (kern_range / sigma) ** 2)
145
  kern = kern / kern.sum()
146
- smoothed = np.convolve(padded, kern, mode='same')
147
- # extract center portion
148
  center = smoothed[pad:pad+12]
149
- # clip small negatives or tiny numbers
150
  center = np.clip(center, 0.0, None)
151
- if center.sum() == 0:
 
152
  center = np.ones_like(center) / len(center)
153
- # normalize to percentages
 
154
  norm = center / center.sum()
155
- perc = (norm * 100.0).round(3)
 
 
 
 
 
 
 
156
  return perc.tolist()
157
 
158
  def is_bell_shaped(perc_list):
@@ -746,8 +777,12 @@ async def predict_bloom(req: BloomPredictionRequest):
746
  for mr in monthly_results
747
  ], dtype=float)
748
 
 
 
 
749
  # Compute smoothed curve
750
  monthly_perc = smooth_monthly_probs(raw_probs.tolist(), alpha=ALPHA, sigma=SMOOTH_SIGMA)
 
751
  monthly_curve = {i+1: float(monthly_perc[i]) for i in range(12)}
752
 
753
  # Check bell shape
 
30
  FEATURES_FILE = Path("mil_features.joblib")
31
  PHENO_FILE = Path("phenologythingy.csv")
32
  SPECIES_STATS_FILE = Path("species_stats.csv")
33
+ MIN_BLOOM_THRESHOLD = float(os.environ.get("MIN_BLOOM_THRESHOLD", 20.0)) # minimum probability to predict species
34
+ MIN_PEAK_FOR_BELL = float(os.environ.get("MIN_PEAK_FOR_BELL", 25.0))
35
 
36
  ELEV_IMAGE_ID = "USGS/SRTMGL1_003"
37
  BUFFER_METERS = int(os.environ.get("BUFFER_METERS", 200))
 
119
  kern = kern / kern.sum()
120
  return kern
121
 
122
+ def smooth_monthly_probs(raw_probs, alpha=ALPHA, sigma=SMOOTH_SIGMA, min_curve_prob=MIN_CURVE_PROB):
123
  """
124
+ Robust smoothing + contrast stretch for raw monthly ML probabilities.
125
+
126
+ raw_probs: list/array length 12, values expected in [0,100]
127
+ Returns: list length 12, percentages summing to ~100
 
 
128
  """
129
+ a = np.asarray(raw_probs, dtype=float) / 100.0 # scale to 0-1
130
+
131
+ # If all zeros => return uniform (but avoid strict uniform by small floor)
132
+ if a.sum() == 0 or np.allclose(a, 0.0):
133
+ base = np.ones_like(a) * (min_curve_prob / 100.0)
134
+ base = base / base.sum()
135
+ return (base * 100.0).round(3).tolist()
136
+
137
+ # contrast stretch (min-max), but avoid tiny denominator
138
+ amin = float(a.min())
139
+ amax = float(a.max())
140
+ rng = amax - amin
141
+ if rng < 1e-4:
142
+ # values almost identical -> amplify relative differences using sqrt of original
143
+ a_cs = np.sqrt(a)
144
+ a_cs = a_cs - a_cs.min()
145
+ rng2 = a_cs.max() - a_cs.min()
146
+ if rng2 <= 1e-8:
147
+ a_cs = np.ones_like(a) / len(a)
148
+ else:
149
+ a_cs = a_cs / rng2
150
+ else:
151
+ a_cs = (a - amin) / (rng + 1e-12)
152
+
153
+ # small floor to avoid zeros everywhere
154
+ floor = min_curve_prob / 100.0
155
+ a_cs = np.clip(a_cs, floor * 0.001, None)
156
+
157
+ # apply sharpening exponent
158
  if alpha != 1.0:
159
+ a_sh = np.power(a_cs, alpha)
160
+ else:
161
+ a_sh = a_cs
162
+
163
+ # circular pad and gaussian smooth
164
+ sigma = float(max(0.6, sigma))
165
+ pad = max(3, int(round(3 * sigma)))
166
+ padded = np.concatenate([a_sh[-pad:], a_sh, a_sh[:pad]])
167
  kern_range = np.arange(-pad, pad + 1)
168
  kern = np.exp(-0.5 * (kern_range / sigma) ** 2)
169
  kern = kern / kern.sum()
170
+ smoothed = np.convolve(padded, kern, mode="same")
 
171
  center = smoothed[pad:pad+12]
 
172
  center = np.clip(center, 0.0, None)
173
+
174
+ if center.sum() <= 0:
175
  center = np.ones_like(center) / len(center)
176
+
177
+ # Normalize & to percentages
178
  norm = center / center.sum()
179
+ perc = (norm * 100.0)
180
+ # tiny rounding
181
+ perc = np.round(perc, 3)
182
+ # final safety: ensure sum ~= 100
183
+ s = float(perc.sum())
184
+ if abs(s - 100.0) > 0.001 and s > 0:
185
+ perc = perc * (100.0 / s)
186
+
187
  return perc.tolist()
188
 
189
  def is_bell_shaped(perc_list):
 
777
  for mr in monthly_results
778
  ], dtype=float)
779
 
780
+ print("DEBUG raw_probs (months 1..12):", raw_probs.tolist())
781
+ print("DEBUG raw_probs stats -> min, max, mean:", float(raw_probs.min()), float(raw_probs.max()), float(raw_probs.mean()))
782
+
783
  # Compute smoothed curve
784
  monthly_perc = smooth_monthly_probs(raw_probs.tolist(), alpha=ALPHA, sigma=SMOOTH_SIGMA)
785
+ print("DEBUG monthly_perc:", monthly_perc)
786
  monthly_curve = {i+1: float(monthly_perc[i]) for i in range(12)}
787
 
788
  # Check bell shape