ReyaLabColumbia commited on
Commit
99c6bf5
·
verified ·
1 Parent(s): d574d6f

Upload 3 files

Browse files
Colony_Analyzer_AI2_HF.py CHANGED
@@ -170,7 +170,7 @@ def contours_overlap_using_mask(contour1, contour2, image_shape=(1536, 2048)):
170
 
171
  return np.any(overlap)
172
 
173
- def analyze_colonies(mask, size_cutoff, circ_cutoff):
174
  colonies = find_colonies(mask, size_cutoff, circ_cutoff)
175
  necrosis = find_necrosis(mask)
176
 
@@ -181,7 +181,9 @@ def analyze_colonies(mask, size_cutoff, circ_cutoff):
181
  centroid = compute_centroid(colony)
182
  if colony_area <= 50:
183
  continue
184
-
 
 
185
  # Check if any necrosis contour is inside the colony
186
  necrosis_area = 0
187
  nec_list =[]
@@ -194,80 +196,30 @@ def analyze_colonies(mask, size_cutoff, circ_cutoff):
194
 
195
  data.append({
196
  "colony_area": colony_area,
197
- "necrosis_area": necrosis_area,
198
  "centroid": centroid,
199
- "percent_necrosis": necrosis_area/colony_area,
200
  "contour": colony,
201
- "nec_contours": nec_list
 
202
  })
203
 
204
  # Convert results to a DataFrame
205
  df = pd.DataFrame(data)
206
  df.index = range(1,len(df.index)+1)
207
  return(df)
208
-
209
-
210
- def contour_overlap(contour1, contour2, centroid1, centroid2, area1, area2, centroid_thresh=25, area_thresh = 85, img_shape = (1536, 2048)):
211
- """
212
- Determines the overlap between two contours.
213
- Returns:
214
- 0: No overlap
215
- 1: Overlap but does not meet strict conditions
216
- 2: Overlap >= 80% of the larger contour and centroids are close
217
- """
218
- # Create blank images
219
- img1 = np.zeros(img_shape, dtype=np.uint8)
220
- img2 = np.zeros(img_shape, dtype=np.uint8)
221
-
222
- # Draw filled contours
223
- cv2.drawContours(img1, [contour1], -1, 255, thickness=cv2.FILLED)
224
- cv2.drawContours(img2, [contour2], -1, 255, thickness=cv2.FILLED)
225
-
226
- # Compute overlap
227
- intersection = cv2.bitwise_and(img1, img2)
228
- intersection_area = np.count_nonzero(intersection)
229
-
230
- if intersection_area == 0:
231
- return 0 # No overlap
232
-
233
- # Compute centroid distance
234
- centroid_distance = float(np.sqrt(abs(centroid1[0]-centroid2[0])**2 + abs(centroid1[1]-centroid2[1])**2))
235
- # Check percentage overlap relative to the larger contour
236
- overlap_ratio = intersection_area/max(area1, area2)
237
-
238
- if overlap_ratio >= area_thresh and centroid_distance <= centroid_thresh:
239
- if area1 > area2:
240
- return(2)
241
- else:
242
- return(3)
243
- else:
244
- return 1 # Some overlap but not meeting strict criteria
245
-
246
- def compare_frames(frame1, frame2):
247
- for i in range(1, len(frame1)+1):
248
- for j in range(1, len(frame2)+1):
249
- temp = contour_overlap(frame1.loc[i, "contour"], frame2.loc[j, "contour"], frame1.loc[i, "centroid"], frame2.loc[j, "centroid"], frame1.loc[i, "colony_area"], frame2.loc[j, "colony_area"])
250
- if temp ==2:
251
- frame2.loc[j,"exclude"] = True
252
- elif temp ==3:
253
- frame1.loc[j, "exclude"] = True
254
- break
255
- frame1 = frame1[frame1["exclude"]==False]
256
- frame2 = frame2[frame2["exclude"]==False]
257
- df = pd.concat([frame1, frame2], axis=0)
258
- df.index = range(1,len(df.index)+1)
259
- return(df)
260
 
261
  def main(args):
262
  min_size = args[1]
263
  min_circ = args[2]
 
264
  colonies = {}
265
  img_map = cut_img(args[0])
266
  for z in img_map:
267
  img_map[z] = eval_img(img_map[z])
268
  del z
269
  p = stitch(img_map)
270
- colonies = analyze_colonies(p, min_size, min_circ)
271
 
272
  img = np.array(args[0])
273
  img = cv2.copyMakeBorder(img,top=0, bottom=10,left=0,right=10, borderType=cv2.BORDER_CONSTANT, value=[255, 255, 255])
@@ -279,7 +231,8 @@ def main(args):
279
 
280
  for i in range(len(colonies)):
281
  cv2.drawContours(img, [list(colonies["contour"])[i]], -1, (0, 255, 0), 2)
282
- cv2.drawContours(img, list(colonies['nec_contours'])[i], -1, (0, 0, 255), 2)
 
283
  coords = list(list(colonies["centroid"])[i])
284
  if coords[0] > 1950:
285
  #if a colony is too close to the right edge, makes the label move to left
@@ -293,15 +246,27 @@ def main(args):
293
  total_area_dark = sum(colonies['necrosis_area'])
294
  total_area_light = sum(colonies['colony_area'])
295
  ratio = total_area_dark/(abs(total_area_light)+1)
296
-
297
- colonies.loc[len(colonies)+1] = ["Total", total_area_light, total_area_dark, None, ratio]
 
 
 
 
 
 
 
 
 
298
  Parameters = pd.DataFrame({"Minimum colony size in pixels":[min_size], "Minimum colony circularity":[min_circ]})
299
  with pd.ExcelWriter('results.xlsx') as writer:
300
  colonies.to_excel(writer, sheet_name="Colony data", index=False)
301
  Parameters.to_excel(writer, sheet_name="Parameters", index=False)
302
  caption = np.ones((150, 2068, 3), dtype=np.uint8) * 255 # Multiply by 255 to make it white
303
- cv2.putText(caption, "Total area necrotic: "+str(total_area_dark)+ ", Total area living: "+str(total_area_light)+", Ratio: "+str(ratio), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
304
-
 
 
 
305
 
306
 
307
  cv2.imwrite('results.png', np.vstack((img, caption)))
 
170
 
171
  return np.any(overlap)
172
 
173
+ def analyze_colonies(mask, size_cutoff, circ_cutoff, img):
174
  colonies = find_colonies(mask, size_cutoff, circ_cutoff)
175
  necrosis = find_necrosis(mask)
176
 
 
181
  centroid = compute_centroid(colony)
182
  if colony_area <= 50:
183
  continue
184
+ mask = np.zeros(img.shape, np.uint8)
185
+ cv2.drawContours(mask, [colony], -1, 255, cv2.FILLED)
186
+ pix = img[mask == 255]
187
  # Check if any necrosis contour is inside the colony
188
  necrosis_area = 0
189
  nec_list =[]
 
196
 
197
  data.append({
198
  "colony_area": colony_area,
199
+ "necrotic_area": necrosis_area,
200
  "centroid": centroid,
201
+ "percent_necrotic": necrosis_area/colony_area,
202
  "contour": colony,
203
+ "nec_contours": nec_list,
204
+ 'mean_pixel_value':np.mean(pix)
205
  })
206
 
207
  # Convert results to a DataFrame
208
  df = pd.DataFrame(data)
209
  df.index = range(1,len(df.index)+1)
210
  return(df)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
  def main(args):
213
  min_size = args[1]
214
  min_circ = args[2]
215
+ do_necrosis = args[3]
216
  colonies = {}
217
  img_map = cut_img(args[0])
218
  for z in img_map:
219
  img_map[z] = eval_img(img_map[z])
220
  del z
221
  p = stitch(img_map)
222
+ colonies = analyze_colonies(p, min_size, min_circ, np.array(args[0]))
223
 
224
  img = np.array(args[0])
225
  img = cv2.copyMakeBorder(img,top=0, bottom=10,left=0,right=10, borderType=cv2.BORDER_CONSTANT, value=[255, 255, 255])
 
231
 
232
  for i in range(len(colonies)):
233
  cv2.drawContours(img, [list(colonies["contour"])[i]], -1, (0, 255, 0), 2)
234
+ if do_necrosis == True:
235
+ cv2.drawContours(img, list(colonies['nec_contours'])[i], -1, (0, 0, 255), 2)
236
  coords = list(list(colonies["centroid"])[i])
237
  if coords[0] > 1950:
238
  #if a colony is too close to the right edge, makes the label move to left
 
246
  total_area_dark = sum(colonies['necrosis_area'])
247
  total_area_light = sum(colonies['colony_area'])
248
  ratio = total_area_dark/(abs(total_area_light)+1)
249
+ radii = [np.sqrt(x/3.1415) for x in list(colonies['colony_area'])]
250
+ volumes = [4.189*(x**3) for x in radii]
251
+ colonies['Colony volume'] = volumes
252
+ del radii, volumes
253
+ meanpix = sum(colonies['mean_pixel_value'] * colonies['colony_area'])/total_area_light
254
+ colonies.loc[len(colonies)+1] = ["Total", total_area_light, total_area_dark, None, ratio, meanpix, sum(colonies['Colony volume'])]
255
+ del meanpix
256
+ colonies = colonies[["Colony Number", 'Colony volume', "colony_area",'mean_pixel_value', "centroid", "necrotic_area","percent_necrotic"]]
257
+ if do_necrosis == False:
258
+ colonies = colonies.drop('necrotic_area', axis=1)
259
+ colonies = colonies.drop('percent_necrotic', axis=1)
260
  Parameters = pd.DataFrame({"Minimum colony size in pixels":[min_size], "Minimum colony circularity":[min_circ]})
261
  with pd.ExcelWriter('results.xlsx') as writer:
262
  colonies.to_excel(writer, sheet_name="Colony data", index=False)
263
  Parameters.to_excel(writer, sheet_name="Parameters", index=False)
264
  caption = np.ones((150, 2068, 3), dtype=np.uint8) * 255 # Multiply by 255 to make it white
265
+ if do_necrosis == True:
266
+ cv2.putText(caption, "Total area necrotic: "+str(total_area_dark)+ ", Total area living: "+str(total_area_light)+", Ratio: "+str(ratio), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
267
+ else:
268
+ cv2.putText(caption, "Total area: "+str(total_area_light), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
269
+ cv2.putText(caption, "Total number of colonies: "+str(len(colonies)-1), (40, 110), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
270
 
271
 
272
  cv2.imwrite('results.png', np.vstack((img, caption)))
Colony_Analyzer_AI_zstack2_HF.py CHANGED
@@ -177,9 +177,9 @@ def analyze_colonies(mask, size_cutoff, circ_cutoff):
177
 
178
  data.append({
179
  "colony_area": colony_area,
180
- "necrosis_area": necrosis_area,
181
  "centroid": centroid,
182
- "percent_necrosis": necrosis_area/colony_area,
183
  "contour": colony,
184
  "nec_contours": nec_list
185
  })
@@ -247,6 +247,7 @@ def compare_frames(frame1, frame2):
247
  def main(args):
248
  min_size = args[1]
249
  min_circ = args[2]
 
250
  colonies = {}
251
  files = args[0]
252
  for idx,x in enumerate(files):
@@ -307,7 +308,8 @@ def main(args):
307
  img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
308
  for i in range(len(colonies)):
309
  cv2.drawContours(img, [list(colonies["contour"])[i]], -1, (0, 255, 0), 2)
310
- cv2.drawContours(img, list(colonies['nec_contours'])[i], -1, (0, 0, 255), 2)
 
311
  coords = list(list(colonies["centroid"])[i])
312
  if coords[0] > 1950:
313
  #if a colony is too close to the right edge, makes the label move to left
@@ -350,15 +352,27 @@ def main(args):
350
  total_area_dark = sum(colonies['necrosis_area'])
351
  total_area_light = sum(colonies['colony_area'])
352
  ratio = total_area_dark/(abs(total_area_light)+1)
353
-
354
- colonies.loc[len(colonies)+1] = ["Total", total_area_light, total_area_dark, None, ratio, None]
 
 
 
 
 
 
355
  Parameters = pd.DataFrame({"Minimum colony size in pixels":[min_size], "Minimum colony circularity":[min_circ]})
 
 
 
356
  with pd.ExcelWriter("Group_analysis_results.xlsx") as writer:
357
  colonies.to_excel(writer, sheet_name="Colony data", index=False)
358
  Parameters.to_excel(writer, sheet_name="Parameters", index=False)
359
  caption = np.ones((150, 2068, 3), dtype=np.uint8) * 255 # Multiply by 255 to make it white
360
- cv2.putText(caption, "Total area necrotic: "+str(total_area_dark)+ ", Total area living: "+str(total_area_light)+", Ratio: "+str(ratio), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
361
-
 
 
 
362
  print('img ndim: ' +str(img.ndim))
363
  print('caption ndim: ' +str(caption.ndim))
364
 
 
177
 
178
  data.append({
179
  "colony_area": colony_area,
180
+ "necrotic_area": necrosis_area,
181
  "centroid": centroid,
182
+ "percent_necrotic": necrosis_area/colony_area,
183
  "contour": colony,
184
  "nec_contours": nec_list
185
  })
 
247
  def main(args):
248
  min_size = args[1]
249
  min_circ = args[2]
250
+ do_necrosis = args[3]
251
  colonies = {}
252
  files = args[0]
253
  for idx,x in enumerate(files):
 
308
  img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
309
  for i in range(len(colonies)):
310
  cv2.drawContours(img, [list(colonies["contour"])[i]], -1, (0, 255, 0), 2)
311
+ if do_necrosis == True:
312
+ cv2.drawContours(img, list(colonies['nec_contours'])[i], -1, (0, 0, 255), 2)
313
  coords = list(list(colonies["centroid"])[i])
314
  if coords[0] > 1950:
315
  #if a colony is too close to the right edge, makes the label move to left
 
352
  total_area_dark = sum(colonies['necrosis_area'])
353
  total_area_light = sum(colonies['colony_area'])
354
  ratio = total_area_dark/(abs(total_area_light)+1)
355
+ radii = [np.sqrt(x/3.1415) for x in list(colonies['colony_area'])]
356
+ volumes = [4.189*(x**3) for x in radii]
357
+ colonies['Colony volume'] = volumes
358
+ del radii, volumes
359
+ meanpix = sum(colonies['mean_pixel_value'] * colonies['colony_area'])/total_area_light
360
+ colonies.loc[len(colonies)+1] = ["Total", total_area_light, total_area_dark, None, ratio, meanpix, sum(colonies['Colony volume'])]
361
+ del meanpix
362
+ colonies = colonies[["Colony Number", 'Colony volume', "colony_area",'mean_pixel_value', "centroid", "necrotic_area","percent_necrotic"]]
363
  Parameters = pd.DataFrame({"Minimum colony size in pixels":[min_size], "Minimum colony circularity":[min_circ]})
364
+ if do_necrosis == False:
365
+ colonies = colonies.drop('necrotic_area', axis=1)
366
+ colonies = colonies.drop('percent_necrotic', axis=1)
367
  with pd.ExcelWriter("Group_analysis_results.xlsx") as writer:
368
  colonies.to_excel(writer, sheet_name="Colony data", index=False)
369
  Parameters.to_excel(writer, sheet_name="Parameters", index=False)
370
  caption = np.ones((150, 2068, 3), dtype=np.uint8) * 255 # Multiply by 255 to make it white
371
+ if do_necrosis == True:
372
+ cv2.putText(caption, "Total area necrotic: "+str(total_area_dark)+ ", Total area living: "+str(total_area_light)+", Ratio: "+str(ratio), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
373
+ else:
374
+ cv2.putText(caption, "Total area: "+str(total_area_light), (40, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
375
+ cv2.putText(caption, "Total number of colonies: "+str(len(colonies)-1), (40, 110), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 3)
376
  print('img ndim: ' +str(img.ndim))
377
  print('caption ndim: ' +str(caption.ndim))
378
 
app.py CHANGED
@@ -2,25 +2,26 @@ import gradio as gr
2
  from PIL import Image
3
 
4
  # Single image analysis function (your existing logic)
5
- def analyze_image(image, min_size, circularity):
6
  import Colony_Analyzer_AI2_HF as analyzer
7
- processed_img, picname, excelname = analyzer.main([image, min_size, circularity])
8
  return Image.fromarray(processed_img), picname, excelname
9
 
10
  # Z-stack analysis function (adapt with your own logic)
11
- def analyze_zstack(images, min_size, circularity):
12
  # images: list of PIL images
13
  # Plug in your own z-stack segmentation logic here
14
  # Example stub: pass images as a list to your analyzer
15
  import Colony_Analyzer_AI_zstack2_HF as analyzer
16
  images = [Image.open(f.name) for f in images]
17
- processed_img, picname, excelname = analyzer.main([images, min_size, circularity])
18
  return Image.fromarray(processed_img), picname, excelname
19
 
20
  with gr.Blocks() as demo:
21
  gr.Markdown("# AI Colony Analyzer\nUpload an image (or Z-Stack) to run colony analysis.")
22
 
23
  z_stack_checkbox = gr.Checkbox(label="Enable Z-Stack", value=False)
 
24
  image_input_single = gr.Image(type="pil", label="Upload Image", visible=True)
25
  image_input_multi = gr.File(file_count="multiple", type="filepath", label="Upload Z-Stack Images", visible=False)
26
  min_size_input = gr.Number(label="Minimum Colony Size (pixels)", value=1000)
@@ -42,16 +43,16 @@ with gr.Blocks() as demo:
42
  outputs=[image_input_single, image_input_multi]
43
  )
44
 
45
- def conditional_analyze(z_stack, single_image, multi_images, min_size, circularity):
46
  if z_stack:
47
- return analyze_zstack(multi_images, min_size, circularity)
48
  else:
49
- return analyze_image(single_image, min_size, circularity)
50
 
51
- process_btn.click(
52
- conditional_analyze,
53
- inputs=[z_stack_checkbox, image_input_single, image_input_multi, min_size_input, circularity_input],
54
- outputs=[output_image, output_file_img, output_file_excel]
55
- )
56
 
57
  demo.launch()
 
2
  from PIL import Image
3
 
4
  # Single image analysis function (your existing logic)
5
+ def analyze_image(image, min_size, circularity, do_necrosis=True):
6
  import Colony_Analyzer_AI2_HF as analyzer
7
+ processed_img, picname, excelname = analyzer.main([image, min_size, circularity,do_necrosis])
8
  return Image.fromarray(processed_img), picname, excelname
9
 
10
  # Z-stack analysis function (adapt with your own logic)
11
+ def analyze_zstack(images, min_size, circularity, do_necrosis=True):
12
  # images: list of PIL images
13
  # Plug in your own z-stack segmentation logic here
14
  # Example stub: pass images as a list to your analyzer
15
  import Colony_Analyzer_AI_zstack2_HF as analyzer
16
  images = [Image.open(f.name) for f in images]
17
+ processed_img, picname, excelname = analyzer.main([images, min_size, circularity,do_necrosis])
18
  return Image.fromarray(processed_img), picname, excelname
19
 
20
  with gr.Blocks() as demo:
21
  gr.Markdown("# AI Colony Analyzer\nUpload an image (or Z-Stack) to run colony analysis.")
22
 
23
  z_stack_checkbox = gr.Checkbox(label="Enable Z-Stack", value=False)
24
+ do_necrosis_checkbox = gr.Checkbox(label="Enable necrosis detection", value=False)
25
  image_input_single = gr.Image(type="pil", label="Upload Image", visible=True)
26
  image_input_multi = gr.File(file_count="multiple", type="filepath", label="Upload Z-Stack Images", visible=False)
27
  min_size_input = gr.Number(label="Minimum Colony Size (pixels)", value=1000)
 
43
  outputs=[image_input_single, image_input_multi]
44
  )
45
 
46
+ def conditional_analyze(z_stack, single_image, multi_images, min_size, circularity, do_necrosis=True):
47
  if z_stack:
48
+ return analyze_zstack(multi_images, min_size, circularity,do_necrosis)
49
  else:
50
+ return analyze_image(single_image, min_size, circularity,do_necrosis)
51
 
52
+ process_btn.click(
53
+ conditional_analyze,
54
+ inputs=[z_stack_checkbox, image_input_single, image_input_multi, min_size_input, circularity_input, do_necrosis_checkbox],
55
+ outputs=[output_image, output_file_img, output_file_excel]
56
+ )
57
 
58
  demo.launch()