VisionLanguageGroup commited on
Commit
c204e64
·
1 Parent(s): f6846ac

update interface functions

Browse files
.gitattributes CHANGED
@@ -37,3 +37,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
37
  1977_Well_F-5_Field_1.png filter=lfs diff=lfs merge=lfs -text
38
  example_imgs/1977_Well_F-5_Field_1_seg.png filter=lfs diff=lfs merge=lfs -text
39
  example_imgs/1977_Well_F-5_Field_1.png filter=lfs diff=lfs merge=lfs -text
 
 
37
  1977_Well_F-5_Field_1.png filter=lfs diff=lfs merge=lfs -text
38
  example_imgs/1977_Well_F-5_Field_1_seg.png filter=lfs diff=lfs merge=lfs -text
39
  example_imgs/1977_Well_F-5_Field_1.png filter=lfs diff=lfs merge=lfs -text
40
+ *.png filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -27,7 +27,7 @@ cache_path = os.path.expanduser("~/.cache/huggingface/gradio")
27
  if os.path.exists(cache_path):
28
  try:
29
  shutil.rmtree(cache_path)
30
- print("✅ Deleted ~/.cache")
31
  except:
32
  pass
33
 
@@ -203,13 +203,14 @@ def segment_with_choice(use_box_choice, annot_value):
203
  # ==== Color Overlay (每个实例一个颜色) ====
204
  overlay = img_np.copy()
205
  alpha = 0.5
206
- cmap = cm.get_cmap("nipy_spectral", num_instances + 1)
207
 
208
  for inst_id in np.unique(inst_mask):
209
  if inst_id == 0:
210
  continue
211
  binary_mask = (inst_mask == inst_id).astype(np.uint8)
212
- color = np.array(cmap(inst_id / (num_instances + 1))[:3]) # RGB only, ignore alpha
 
213
  overlay[binary_mask == 1] = (1 - alpha) * overlay[binary_mask == 1] + alpha * color
214
 
215
  # 绘制轮廓
@@ -439,6 +440,19 @@ def create_ctc_results_zip(output_dir):
439
  print(f"✅ ZIP created: {zip_path} ({os.path.getsize(zip_path) / 1024:.1f} KB)")
440
  return zip_path
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  def extract_first_frame(tif_dir):
443
  """
444
  Extract the first frame from a directory of TIF files
@@ -495,14 +509,14 @@ def create_tracking_visualization(tif_dir, output_dir, valid_tif_files):
495
 
496
  # Create color map for consistent track IDs
497
  # Use a colormap with many distinct colors
498
- try:
499
- cmap = colormaps.get_cmap("nipy_spectral")
500
- except:
501
- from matplotlib import cm
502
- cmap = cm.get_cmap("nipy_spectral")
503
 
504
  frames = []
505
- alpha = 0.5 # Transparency for overlay
506
 
507
  # Process each frame
508
  num_frames = min(len(valid_tif_files), len(mask_files))
@@ -570,7 +584,8 @@ def create_tracking_visualization(tif_dir, output_dir, valid_tif_files):
570
  binary_mask = (mask == track_id)
571
 
572
  # Get consistent color for this track ID
573
- color = np.array(cmap(int(track_id) % 256)[:3])
 
574
 
575
  # Blend color onto image
576
  overlay[binary_mask] = (1 - alpha) * overlay[binary_mask] + alpha * color
@@ -798,7 +813,10 @@ def track_video_handler(use_box_choice, first_frame_annot, zip_file_obj):
798
 
799
 
800
  # ===== 示例图像 =====
801
- example_images = ["003_img.png", "1977_Well_F-5_Field_1.png"]
 
 
 
802
 
803
  # ===== Gradio UI =====
804
  with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as demo:
@@ -815,12 +833,22 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
815
 
816
  # 全局状态
817
  current_query_id = gr.State(str(uuid.uuid4()))
818
- user_uploaded_examples = gr.State(example_images.copy()) # 初始化时包含原始示例
819
 
820
  with gr.Tabs():
821
  # ===== Tab 1: Segmentation =====
822
  with gr.Tab("🎨 分割 (Segmentation)"):
823
- gr.Markdown("## 细胞实例分割 - 每个细胞一个颜色")
 
 
 
 
 
 
 
 
 
 
824
 
825
  with gr.Row():
826
  with gr.Column(scale=1):
@@ -832,16 +860,13 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
832
  # 示例图片Gallery
833
  example_gallery = gr.Gallery(
834
  label="📁 示例图片",
835
- columns=3,
 
 
836
  object_fit="cover",
837
- height=150
838
  )
839
 
840
- # 上传示例图片
841
- image_uploader = gr.Image(
842
- label="➕ 上传新示例到Gallery",
843
- type="filepath"
844
- )
845
 
846
  with gr.Row():
847
  use_box_radio = gr.Radio(
@@ -852,15 +877,13 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
852
 
853
  run_seg_btn = gr.Button("▶️ 运行分割", variant="primary", size="lg")
854
 
855
- gr.Markdown(
856
- """
857
- **使用说明:**
858
- 1. 上传图像或从Gallery选择示例
859
- 2. (可选) 标注边界框并选择 "Yes"
860
- 3. 点击 "运行分割"
861
- """
862
  )
863
 
 
864
  with gr.Column(scale=2):
865
  seg_output = gr.Image(
866
  type="pil",
@@ -871,7 +894,8 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
871
  # 下载原始预测结果
872
  download_mask_btn = gr.File(
873
  label="📥 下载原始预测 (.tif 格式)",
874
- visible=True
 
875
  )
876
 
877
  # 满意度评分
@@ -908,7 +932,7 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
908
 
909
  # 初始化Gallery显示
910
  demo.load(
911
- fn=lambda: example_images.copy(),
912
  outputs=example_gallery
913
  )
914
 
@@ -970,7 +994,17 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
970
 
971
  # ===== Tab 2: Counting =====
972
  with gr.Tab("🔢 计数 (Counting)"):
973
- gr.Markdown("## 细胞计数分析 - 基于密度图")
 
 
 
 
 
 
 
 
 
 
974
 
975
  with gr.Row():
976
  with gr.Column(scale=1):
@@ -979,19 +1013,18 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
979
  categories=["cell"]
980
  )
981
 
982
- # 示例图片Gallery (与Segmentation相同)
983
- count_example_gallery = gr.Gallery(
984
- label="📁 示例图片",
985
- columns=3,
986
- object_fit="cover",
987
- height=150
988
- )
 
 
 
 
989
 
990
- # 上传示例图片
991
- count_image_uploader = gr.Image(
992
- label="➕ 上传新示例到Gallery",
993
- type="filepath"
994
- )
995
 
996
  with gr.Row():
997
  count_use_box_radio = gr.Radio(
@@ -1002,14 +1035,14 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1002
 
1003
  count_btn = gr.Button("▶️ 运行计数", variant="primary", size="lg")
1004
 
1005
- gr.Markdown(
1006
- """
1007
- **使用说明:**
1008
- 1. 上传图像或从Gallery选择示例
1009
- 2. (可选) 标注边界框并选择 "Yes"
1010
- 3. 点击 "运行计数"
1011
- """
1012
- )
1013
 
1014
  with gr.Column(scale=2):
1015
  count_output = gr.Image(
@@ -1021,53 +1054,69 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1021
  label="📊 统计信息",
1022
  lines=2
1023
  )
1024
-
1025
- # 下载原始预测结果
1026
  download_density_btn = gr.File(
1027
  label="📥 下载原始预测 (.npy 格式)",
1028
  visible=True
1029
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1030
 
1031
- # 绑定事件
1032
- count_btn.click(
1033
- fn=count_cells_handler,
1034
- inputs=[count_use_box_radio, count_annotator],
1035
- outputs=[count_output, download_density_btn, count_status]
1036
- )
1037
-
1038
- # 初始化Gallery显示
1039
- demo.load(
1040
- fn=lambda: example_images.copy(),
1041
- outputs=count_example_gallery
1042
- )
1043
-
1044
- # 绑定事件: 上传示例图片到Counting Gallery
1045
- count_user_examples = gr.State(example_images.copy())
1046
 
1047
- def add_to_count_gallery(img_path, current_imgs):
1048
- if not img_path:
1049
- return current_imgs
 
 
 
1050
  try:
1051
- if img_path not in current_imgs:
1052
- current_imgs.append(img_path)
1053
- return current_imgs
1054
- except:
1055
- return current_imgs
 
 
 
1056
 
1057
- count_image_uploader.change(
 
1058
  fn=add_to_count_gallery,
1059
  inputs=[count_image_uploader, count_user_examples],
1060
- outputs=count_user_examples
1061
- ).then(
1062
- fn=lambda imgs: imgs,
1063
- inputs=count_user_examples,
1064
- outputs=count_example_gallery
1065
  )
1066
 
1067
- # 绑定事件: 点击Gallery加载到count_annotator
1068
  def load_from_count_gallery(evt: gr.SelectData, all_imgs):
 
1069
  if evt.index is not None and evt.index < len(all_imgs):
1070
- return all_imgs[evt.index]
 
 
1071
  return None
1072
 
1073
  count_example_gallery.select(
@@ -1075,10 +1124,51 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1075
  inputs=count_user_examples,
1076
  outputs=count_annotator
1077
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1078
 
1079
  # ===== Tab 3: Tracking =====
1080
  with gr.Tab("🎬 跟踪 (Tracking)"):
1081
- gr.Markdown("## 视频细胞跟踪 - 支持 ZIP 压缩包上传")
 
 
 
 
 
 
 
 
 
 
 
1082
 
1083
  with gr.Row():
1084
  with gr.Column(scale=1):
@@ -1086,11 +1176,22 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1086
  label="📦 上传视频帧 ZIP 文件",
1087
  file_types=[".zip"]
1088
  )
1089
-
1090
  # First frame annotation for bounding box
1091
  track_first_frame_annotator = BBoxAnnotator(
1092
  label="🖼️ 首帧边界框标注 (可选)",
1093
- categories=["cell"]
 
 
 
 
 
 
 
 
 
 
 
1094
  )
1095
 
1096
  with gr.Row():
@@ -1102,27 +1203,19 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1102
 
1103
  track_btn = gr.Button("▶️ 运行跟踪", variant="primary", size="lg")
1104
 
1105
- gr.Markdown(
1106
- '''
1107
- **使用说明:**
1108
- 1. 上传包含 `.tif` 图像序列的 ZIP 文件
1109
- 2. (可选) 在首帧上标注边界框并选择 "Yes"
1110
- 3. 点击 "运行跟踪"
1111
- 4. 下载CTC格式结果
1112
-
1113
- **注意:**
1114
- - 确保TIFF文件按时间顺序命名 (如: t000.tif, t001.tif...)
1115
- - 边界框将应用于整个视频序列
1116
- - 避免使用macOS的压缩工具
1117
- '''
1118
  )
1119
 
1120
  with gr.Column(scale=2):
1121
- # Preview of first frame
1122
  track_first_frame_preview = gr.Image(
1123
- label="📸 首帧预览",
1124
- type="pil",
1125
- height=300
 
1126
  )
1127
 
1128
  track_output = gr.Textbox(
@@ -1131,18 +1224,138 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1131
  interactive=False
1132
  )
1133
 
1134
- # Download button for CTC results
1135
  track_download = gr.File(
1136
  label="📥 下载跟踪结果 (CTC格式)",
1137
  visible=False
1138
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1140
  def load_first_frame_for_annotation(zip_file_obj):
1141
  '''Load and normalize first frame from ZIP for annotation'''
1142
  if zip_file_obj is None:
1143
- return None
1144
 
1145
  import tifffile
 
1146
 
1147
  try:
1148
  temp_dir = tempfile.mkdtemp()
@@ -1158,7 +1371,7 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1158
  if tif_dir:
1159
  first_frame = extract_first_frame(tif_dir)
1160
  if first_frame:
1161
- # ===== NORMALIZATION STARTS HERE =====
1162
  try:
1163
  img_np = tifffile.imread(first_frame)
1164
 
@@ -1195,22 +1408,24 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1195
  print(f" Original dtype: {tifffile.imread(first_frame).dtype}")
1196
  print(f" Normalized to uint8 RGB for annotation")
1197
 
1198
- return temp_img.name
1199
- # ===== NORMALIZATION ENDS HERE =====
1200
  except Exception as e:
1201
  print(f"⚠️ Error normalizing first frame: {e}")
 
 
1202
  # Fallback to original file
1203
- return first_frame
1204
  except Exception as e:
1205
  print(f"⚠️ Error loading first frame: {e}")
1206
- pass
1207
- return None
 
1208
 
1209
- # Load first frame into annotator when ZIP uploaded
1210
  track_zip_upload.change(
1211
  fn=load_first_frame_for_annotation,
1212
  inputs=track_zip_upload,
1213
- outputs=track_first_frame_annotator
1214
  )
1215
 
1216
  # Run tracking
@@ -1219,6 +1434,29 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1219
  inputs=[track_use_box_radio, track_first_frame_annotator, track_zip_upload],
1220
  outputs=[track_download, track_output, track_download, track_first_frame_preview]
1221
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1222
 
1223
  gr.Markdown(
1224
  """
@@ -1232,7 +1470,7 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
1232
  if __name__ == "__main__":
1233
  demo.queue().launch(
1234
  server_name="0.0.0.0",
1235
- server_port=7860,
1236
  share=True,
1237
  show_error=True
1238
  )
 
27
  if os.path.exists(cache_path):
28
  try:
29
  shutil.rmtree(cache_path)
30
+ print("✅ Deleted ~/.cache/huggingface/gradio")
31
  except:
32
  pass
33
 
 
203
  # ==== Color Overlay (每个实例一个颜色) ====
204
  overlay = img_np.copy()
205
  alpha = 0.5
206
+ # cmap = cm.get_cmap("hsv", num_instances + 1)
207
 
208
  for inst_id in np.unique(inst_mask):
209
  if inst_id == 0:
210
  continue
211
  binary_mask = (inst_mask == inst_id).astype(np.uint8)
212
+ # color = np.array(cmap(inst_id / (num_instances + 1))[:3]) # RGB only, ignore alpha
213
+ color = get_well_spaced_color(inst_id)
214
  overlay[binary_mask == 1] = (1 - alpha) * overlay[binary_mask == 1] + alpha * color
215
 
216
  # 绘制轮廓
 
440
  print(f"✅ ZIP created: {zip_path} ({os.path.getsize(zip_path) / 1024:.1f} KB)")
441
  return zip_path
442
 
443
+ # 使用更智能的颜色分配 - 让相邻的ID颜色差异更大
444
+ def get_well_spaced_color(track_id, num_colors=256):
445
+ """生成间隔良好的颜色,相邻ID使用对比色"""
446
+ # 使用质数跳跃来分散颜色
447
+ golden_ratio = 0.618033988749895
448
+ hue = (track_id * golden_ratio) % 1.0
449
+
450
+ # 使用高饱和度和明度
451
+ import colorsys
452
+ rgb = colorsys.hsv_to_rgb(hue, 0.9, 0.95)
453
+ return np.array(rgb)
454
+
455
+
456
  def extract_first_frame(tif_dir):
457
  """
458
  Extract the first frame from a directory of TIF files
 
509
 
510
  # Create color map for consistent track IDs
511
  # Use a colormap with many distinct colors
512
+ # try:
513
+ # cmap = colormaps.get_cmap("hsv")
514
+ # except:
515
+ # from matplotlib import cm
516
+ # cmap = cm.get_cmap("hsv")
517
 
518
  frames = []
519
+ alpha = 0.3 # Transparency for overlay
520
 
521
  # Process each frame
522
  num_frames = min(len(valid_tif_files), len(mask_files))
 
584
  binary_mask = (mask == track_id)
585
 
586
  # Get consistent color for this track ID
587
+ # color = np.array(cmap(int(track_id) % 256)[:3])
588
+ color = get_well_spaced_color(int(track_id))
589
 
590
  # Blend color onto image
591
  overlay[binary_mask] = (1 - alpha) * overlay[binary_mask] + alpha * color
 
813
 
814
 
815
  # ===== 示例图像 =====
816
+ example_images_seg = [f for f in glob("example_imgs/seg/*")]
817
+ # ["example_imgs/seg/003_img.png", "example_imgs/seg/1977_Well_F-5_Field_1.png"]
818
+ example_images_cnt = [f for f in glob("example_imgs/cnt/*")]
819
+ example_tracking_zips = [f for f in glob("example_imgs/tra/*.zip")]
820
 
821
  # ===== Gradio UI =====
822
  with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as demo:
 
833
 
834
  # 全局状态
835
  current_query_id = gr.State(str(uuid.uuid4()))
836
+ user_uploaded_examples = gr.State(example_images_seg.copy()) # 初始化时包含原始示例
837
 
838
  with gr.Tabs():
839
  # ===== Tab 1: Segmentation =====
840
  with gr.Tab("🎨 分割 (Segmentation)"):
841
+ gr.Markdown("## 显微镜物体实例分割")
842
+ gr.Markdown(
843
+ """
844
+ **使用说明:**
845
+ 1. 上传图像或选择示例图片(支持多种格式: .png, .jpg, .tif)
846
+ 2. (可选) 标注一个目标物体的边界框并选择 "Yes",或直接点击 "运行分割"
847
+ 3. 点击 "运行分割"
848
+ 4. 查看分割结果,下载原始预测mask (.tif格式)
849
+ 5. 评分并提交反馈以帮助我们改进模型!
850
+ """
851
+ )
852
 
853
  with gr.Row():
854
  with gr.Column(scale=1):
 
860
  # 示例图片Gallery
861
  example_gallery = gr.Gallery(
862
  label="📁 示例图片",
863
+ columns=len(example_images_seg),
864
+ rows=1,
865
+ height=120,
866
  object_fit="cover",
867
+ show_download_button=False
868
  )
869
 
 
 
 
 
 
870
 
871
  with gr.Row():
872
  use_box_radio = gr.Radio(
 
877
 
878
  run_seg_btn = gr.Button("▶️ 运行分割", variant="primary", size="lg")
879
 
880
+ # 上传示例图片
881
+ image_uploader = gr.Image(
882
+ label="➕ 上传新示例到Gallery",
883
+ type="filepath"
 
 
 
884
  )
885
 
886
+
887
  with gr.Column(scale=2):
888
  seg_output = gr.Image(
889
  type="pil",
 
894
  # 下载原始预测结果
895
  download_mask_btn = gr.File(
896
  label="📥 下载原始预测 (.tif 格式)",
897
+ visible=True,
898
+ height=40,
899
  )
900
 
901
  # 满意度评分
 
932
 
933
  # 初始化Gallery显示
934
  demo.load(
935
+ fn=lambda: example_images_seg.copy(),
936
  outputs=example_gallery
937
  )
938
 
 
994
 
995
  # ===== Tab 2: Counting =====
996
  with gr.Tab("🔢 计数 (Counting)"):
997
+ gr.Markdown("## 显微镜物体计数分析")
998
+ gr.Markdown(
999
+ """
1000
+ **使用说明:**
1001
+ 1. 上传图像或选择示例图片(支持多种格式: .png, .jpg, .tif)
1002
+ 2. (可选) 标注边界框并选择 "Yes",或直接点击 "运行计数"
1003
+ 3. 点击 "运行计数"
1004
+ 4. 查看密度图,下载原始预测 (.npy格式)
1005
+ 5. 评分并提交反馈以帮助我们改进模型!
1006
+ """
1007
+ )
1008
 
1009
  with gr.Row():
1010
  with gr.Column(scale=1):
 
1013
  categories=["cell"]
1014
  )
1015
 
1016
+ # Example gallery with "add" functionality
1017
+ with gr.Row():
1018
+ count_example_gallery = gr.Gallery(
1019
+ label="📁 示例图片",
1020
+ columns=len(example_images_cnt),
1021
+ rows=1,
1022
+ object_fit="cover",
1023
+ height=120,
1024
+ value=example_images_cnt.copy(), # Initialize with examples
1025
+ show_download_button=False
1026
+ )
1027
 
 
 
 
 
 
1028
 
1029
  with gr.Row():
1030
  count_use_box_radio = gr.Radio(
 
1035
 
1036
  count_btn = gr.Button("▶️ 运行计数", variant="primary", size="lg")
1037
 
1038
+ # Add button to upload new examples
1039
+ with gr.Row():
1040
+ count_image_uploader = gr.File(
1041
+ label="➕ 添加示例图片",
1042
+ file_types=["image"],
1043
+ type="filepath"
1044
+ )
1045
+
1046
 
1047
  with gr.Column(scale=2):
1048
  count_output = gr.Image(
 
1054
  label="📊 统计信息",
1055
  lines=2
1056
  )
 
 
1057
  download_density_btn = gr.File(
1058
  label="📥 下载原始预测 (.npy 格式)",
1059
  visible=True
1060
  )
1061
+
1062
+ # 满意度评分
1063
+ score_slider = gr.Slider(
1064
+ minimum=1,
1065
+ maximum=5,
1066
+ step=1,
1067
+ value=5,
1068
+ label="🌟 满意度评分 (1-5)"
1069
+ )
1070
+
1071
+ # 反馈文本框
1072
+ feedback_box = gr.Textbox(
1073
+ placeholder="请输入您的反馈意见...",
1074
+ lines=2,
1075
+ label="💬 反馈意见"
1076
+ )
1077
+
1078
+ # 提交按钮
1079
+ submit_feedback_btn = gr.Button("💾 提交反馈", variant="secondary")
1080
+
1081
+ feedback_status = gr.Textbox(
1082
+ label="✅ 提交状态",
1083
+ lines=1,
1084
+ visible=False
1085
+ )
1086
 
1087
+ # State for managing gallery images
1088
+ count_user_examples = gr.State(example_images_cnt.copy())
 
 
 
 
 
 
 
 
 
 
 
 
 
1089
 
1090
+ # Function to add image to gallery
1091
+ def add_to_count_gallery(new_img_file, current_imgs):
1092
+ """Add uploaded image to gallery"""
1093
+ if new_img_file is None:
1094
+ return current_imgs, current_imgs
1095
+
1096
  try:
1097
+ # Add new image path to list
1098
+ if new_img_file not in current_imgs:
1099
+ current_imgs.append(new_img_file)
1100
+ print(f"✅ Added image to gallery: {new_img_file}")
1101
+ except Exception as e:
1102
+ print(f"⚠️ Failed to add image: {e}")
1103
+
1104
+ return current_imgs, current_imgs
1105
 
1106
+ # When user uploads a new image file
1107
+ count_image_uploader.upload(
1108
  fn=add_to_count_gallery,
1109
  inputs=[count_image_uploader, count_user_examples],
1110
+ outputs=[count_user_examples, count_example_gallery]
 
 
 
 
1111
  )
1112
 
1113
+ # When user selects from gallery, load into annotator
1114
  def load_from_count_gallery(evt: gr.SelectData, all_imgs):
1115
+ """Load selected image from gallery into annotator"""
1116
  if evt.index is not None and evt.index < len(all_imgs):
1117
+ selected_img = all_imgs[evt.index]
1118
+ print(f"📸 Loading image from gallery: {selected_img}")
1119
+ return selected_img
1120
  return None
1121
 
1122
  count_example_gallery.select(
 
1124
  inputs=count_user_examples,
1125
  outputs=count_annotator
1126
  )
1127
+
1128
+ # Run counting
1129
+ count_btn.click(
1130
+ fn=count_cells_handler,
1131
+ inputs=[count_use_box_radio, count_annotator],
1132
+ outputs=[count_output, download_density_btn, count_status]
1133
+ )
1134
+
1135
+ # 绑定事件: 提交反馈
1136
+ def submit_user_feedback(query_id, score, comment, annot_val):
1137
+ try:
1138
+ img_path = annot_val[0] if annot_val and len(annot_val) > 0 else None
1139
+ bboxes = annot_val[1] if annot_val and len(annot_val) > 1 else []
1140
+
1141
+ save_feedback(
1142
+ query_id=query_id,
1143
+ feedback_type=f"score_{int(score)}",
1144
+ feedback_text=comment,
1145
+ img_path=img_path,
1146
+ bboxes=bboxes
1147
+ )
1148
+ return "✅ 反馈已提交,感谢您的评价!", gr.update(visible=True)
1149
+ except Exception as e:
1150
+ return f"❌ 提交失败: {str(e)}", gr.update(visible=True)
1151
+
1152
+ submit_feedback_btn.click(
1153
+ fn=submit_user_feedback,
1154
+ inputs=[current_query_id, score_slider, feedback_box, annotator],
1155
+ outputs=[feedback_status, feedback_status]
1156
+ )
1157
 
1158
  # ===== Tab 3: Tracking =====
1159
  with gr.Tab("🎬 跟踪 (Tracking)"):
1160
+ gr.Markdown("## 显微镜物体视频跟踪 - 支持 ZIP 压缩包上传")
1161
+ gr.Markdown(
1162
+ """
1163
+ **使用说明:**
1164
+ 1. 上传ZIP文件或从示例库选择,ZIP内应包含按时间顺序命名的TIF图像序列 (如: t000.tif, t001.tif...)
1165
+ 2. (可选) 在首帧上标注边界框并选择 "Yes"
1166
+ 3. 点击 "运行跟踪"
1167
+ 4. 下载CTC格式结果
1168
+ 5. 评分并提交反馈以帮助我们改进模型!
1169
+
1170
+ """
1171
+ )
1172
 
1173
  with gr.Row():
1174
  with gr.Column(scale=1):
 
1176
  label="📦 上传视频帧 ZIP 文件",
1177
  file_types=[".zip"]
1178
  )
1179
+
1180
  # First frame annotation for bounding box
1181
  track_first_frame_annotator = BBoxAnnotator(
1182
  label="🖼️ 首帧边界框标注 (可选)",
1183
+ categories=["cell"],
1184
+ visible=False # Hidden initially
1185
+ )
1186
+
1187
+ # Example ZIP gallery
1188
+ track_example_gallery = gr.Gallery(
1189
+ label="📁 示例视频库 (点击选择)",
1190
+ columns=10,
1191
+ rows=1,
1192
+ height=120,
1193
+ object_fit="contain",
1194
+ show_download_button=False
1195
  )
1196
 
1197
  with gr.Row():
 
1203
 
1204
  track_btn = gr.Button("▶️ 运行跟踪", variant="primary", size="lg")
1205
 
1206
+ # Add to gallery button
1207
+ track_gallery_upload = gr.File(
1208
+ label="➕ 添加ZIP到示例库",
1209
+ file_types=[".zip"],
1210
+ type="filepath"
 
 
 
 
 
 
 
 
1211
  )
1212
 
1213
  with gr.Column(scale=2):
 
1214
  track_first_frame_preview = gr.Image(
1215
+ label="📸 跟踪可视化 (动画预览)",
1216
+ type="filepath",
1217
+ height=400,
1218
+ interactive=False
1219
  )
1220
 
1221
  track_output = gr.Textbox(
 
1224
  interactive=False
1225
  )
1226
 
 
1227
  track_download = gr.File(
1228
  label="📥 下载跟踪结果 (CTC格式)",
1229
  visible=False
1230
  )
1231
+
1232
+ # 满意度评分
1233
+ score_slider = gr.Slider(
1234
+ minimum=1,
1235
+ maximum=5,
1236
+ step=1,
1237
+ value=5,
1238
+ label="🌟 满意度评分 (1-5)"
1239
+ )
1240
+
1241
+ # 反馈文本框
1242
+ feedback_box = gr.Textbox(
1243
+ placeholder="请输入您的反馈意见...",
1244
+ lines=2,
1245
+ label="💬 反馈意见"
1246
+ )
1247
+
1248
+ # 提交按钮
1249
+ submit_feedback_btn = gr.Button("💾 提交反馈", variant="secondary")
1250
+
1251
+ feedback_status = gr.Textbox(
1252
+ label="✅ 提交状态",
1253
+ lines=1,
1254
+ visible=False
1255
+ )
1256
 
1257
+ # State for tracking examples
1258
+ track_user_examples = gr.State(example_tracking_zips.copy())
1259
+
1260
+ # Function to get preview image from ZIP
1261
+ def get_zip_preview(zip_path):
1262
+ """Extract first frame from ZIP for gallery preview"""
1263
+ try:
1264
+ temp_dir = tempfile.mkdtemp()
1265
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
1266
+ for member in zip_ref.namelist():
1267
+ basename = os.path.basename(member)
1268
+ if ('__MACOSX' not in member and
1269
+ not basename.startswith('._') and
1270
+ basename.lower().endswith(('.tif', '.tiff', '.png', '.jpg'))):
1271
+ zip_ref.extract(member, temp_dir)
1272
+ extracted_path = os.path.join(temp_dir, member)
1273
+
1274
+ # Load and normalize for preview
1275
+ import tifffile
1276
+ import numpy as np
1277
+
1278
+ img_np = tifffile.imread(extracted_path)
1279
+ if img_np.dtype == np.uint16:
1280
+ img_min, img_max = img_np.min(), img_np.max()
1281
+ if img_max > img_min:
1282
+ img_np = ((img_np.astype(np.float32) - img_min) / (img_max - img_min) * 255).astype(np.uint8)
1283
+
1284
+ if img_np.ndim == 2:
1285
+ img_np = np.stack([img_np]*3, axis=-1)
1286
+
1287
+ # Save preview
1288
+ preview_path = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
1289
+ Image.fromarray(img_np).save(preview_path.name)
1290
+ return preview_path.name
1291
+ except:
1292
+ pass
1293
+ return None
1294
+
1295
+ # Initialize gallery with previews
1296
+ def init_tracking_gallery():
1297
+ """Create preview images for ZIP examples"""
1298
+ previews = []
1299
+ for zip_path in example_tracking_zips:
1300
+ if os.path.exists(zip_path):
1301
+ preview = get_zip_preview(zip_path)
1302
+ if preview:
1303
+ previews.append(preview)
1304
+ return previews
1305
+
1306
+ # Load gallery on startup
1307
+ demo.load(
1308
+ fn=init_tracking_gallery,
1309
+ outputs=track_example_gallery
1310
+ )
1311
+
1312
+ # Add ZIP to gallery
1313
+ def add_zip_to_gallery(zip_path, current_zips):
1314
+ if not zip_path:
1315
+ return current_zips, track_example_gallery
1316
+ try:
1317
+ if zip_path not in current_zips:
1318
+ current_zips.append(zip_path)
1319
+ print(f"✅ Added ZIP to gallery: {zip_path}")
1320
+ # Regenerate previews
1321
+ previews = []
1322
+ for zp in current_zips:
1323
+ preview = get_zip_preview(zp)
1324
+ if preview:
1325
+ previews.append(preview)
1326
+ return current_zips, previews
1327
+ except Exception as e:
1328
+ print(f"⚠️ Error: {e}")
1329
+ return current_zips, []
1330
+
1331
+ track_gallery_upload.upload(
1332
+ fn=add_zip_to_gallery,
1333
+ inputs=[track_gallery_upload, track_user_examples],
1334
+ outputs=[track_user_examples, track_example_gallery]
1335
+ )
1336
+
1337
+ # Select ZIP from gallery
1338
+ def load_zip_from_gallery(evt: gr.SelectData, all_zips):
1339
+ if evt.index is not None and evt.index < len(all_zips):
1340
+ selected_zip = all_zips[evt.index]
1341
+ print(f"📁 Selected ZIP from gallery: {selected_zip}")
1342
+ return selected_zip
1343
+ return None
1344
+
1345
+ track_example_gallery.select(
1346
+ fn=load_zip_from_gallery,
1347
+ inputs=track_user_examples,
1348
+ outputs=track_zip_upload
1349
+ )
1350
+
1351
+ # Load first frame when ZIP is uploaded
1352
  def load_first_frame_for_annotation(zip_file_obj):
1353
  '''Load and normalize first frame from ZIP for annotation'''
1354
  if zip_file_obj is None:
1355
+ return None, gr.update(visible=False)
1356
 
1357
  import tifffile
1358
+ import numpy as np
1359
 
1360
  try:
1361
  temp_dir = tempfile.mkdtemp()
 
1371
  if tif_dir:
1372
  first_frame = extract_first_frame(tif_dir)
1373
  if first_frame:
1374
+ # Load and normalize the first frame
1375
  try:
1376
  img_np = tifffile.imread(first_frame)
1377
 
 
1408
  print(f" Original dtype: {tifffile.imread(first_frame).dtype}")
1409
  print(f" Normalized to uint8 RGB for annotation")
1410
 
1411
+ return temp_img.name, gr.update(visible=True)
 
1412
  except Exception as e:
1413
  print(f"⚠️ Error normalizing first frame: {e}")
1414
+ import traceback
1415
+ traceback.print_exc()
1416
  # Fallback to original file
1417
+ return first_frame, gr.update(visible=True)
1418
  except Exception as e:
1419
  print(f"⚠️ Error loading first frame: {e}")
1420
+ import traceback
1421
+ traceback.print_exc()
1422
+ return None, gr.update(visible=False)
1423
 
1424
+ # Load first frame when ZIP is uploaded
1425
  track_zip_upload.change(
1426
  fn=load_first_frame_for_annotation,
1427
  inputs=track_zip_upload,
1428
+ outputs=[track_first_frame_annotator, track_first_frame_annotator]
1429
  )
1430
 
1431
  # Run tracking
 
1434
  inputs=[track_use_box_radio, track_first_frame_annotator, track_zip_upload],
1435
  outputs=[track_download, track_output, track_download, track_first_frame_preview]
1436
  )
1437
+
1438
+ # 绑定事件: 提交反馈
1439
+ def submit_user_feedback(query_id, score, comment, annot_val):
1440
+ try:
1441
+ img_path = annot_val[0] if annot_val and len(annot_val) > 0 else None
1442
+ bboxes = annot_val[1] if annot_val and len(annot_val) > 1 else []
1443
+
1444
+ save_feedback(
1445
+ query_id=query_id,
1446
+ feedback_type=f"score_{int(score)}",
1447
+ feedback_text=comment,
1448
+ img_path=img_path,
1449
+ bboxes=bboxes
1450
+ )
1451
+ return "✅ 反馈已提交,感谢您的评价!", gr.update(visible=True)
1452
+ except Exception as e:
1453
+ return f"❌ 提交失败: {str(e)}", gr.update(visible=True)
1454
+
1455
+ submit_feedback_btn.click(
1456
+ fn=submit_user_feedback,
1457
+ inputs=[current_query_id, score_slider, feedback_box, annotator],
1458
+ outputs=[feedback_status, feedback_status]
1459
+ )
1460
 
1461
  gr.Markdown(
1462
  """
 
1470
  if __name__ == "__main__":
1471
  demo.queue().launch(
1472
  server_name="0.0.0.0",
1473
+ server_port=7861,
1474
  share=True,
1475
  show_error=True
1476
  )
example_imgs/{1977_Well_F-5_Field_1.png → cnt/00_01.png} RENAMED
File without changes
example_imgs/{1977_Well_F-5_Field_1_seg.png → cnt/047cell.png} RENAMED
File without changes
example_imgs/cnt/6800-17000_GTEX-XQ3S_Adipose-Subcutaneous.png ADDED

Git LFS Details

  • SHA256: 467319789c5b5b6c370a23c126c33044a841a115cf24b79f75106b5521cd5c44
  • Pointer size: 130 Bytes
  • Size of remote file: 79.9 kB
003_img.png → example_imgs/seg/003_img.png RENAMED
File without changes
example_imgs/seg/1-23 [Scan I08].png ADDED

Git LFS Details

  • SHA256: a96dfccdd794a95c9907b0eedecbd53dee078943d9a3dcdb43e11a36d34f5a1f
  • Pointer size: 132 Bytes
  • Size of remote file: 1.42 MB
example_imgs/seg/10X_B2_Tile-15.aligned.png ADDED

Git LFS Details

  • SHA256: e8dce16565ccfb055438b0b65d9e70b5be6cc36c61a964eed53d7ec782b5afa3
  • Pointer size: 132 Bytes
  • Size of remote file: 1.52 MB
1977_Well_F-5_Field_1.png → example_imgs/seg/1977_Well_F-5_Field_1.png RENAMED
File without changes
example_imgs/seg/200972823[5179]_RhoGGG_YAP_TAZ [200972823 Well K6 Field #2].png ADDED

Git LFS Details

  • SHA256: 56bd7a8df07d66ff5f8dac67aa116efe0869f6c46d9ce77e595535a6acd60ae9
  • Pointer size: 132 Bytes
  • Size of remote file: 1.39 MB
example_imgs/seg/A172_Phase_C7_1_00d00h00m_1.png ADDED

Git LFS Details

  • SHA256: f57430b87923f5de9a5799cc84016aeb5d99cd5068481a9fedae2a68fa9bba43
  • Pointer size: 131 Bytes
  • Size of remote file: 159 kB
example_imgs/seg/JE2NileRed_oilp22_PMP_101220_011_NR.png ADDED

Git LFS Details

  • SHA256: bdf31a4eab7826435407f2f88bfeee8f95c2b04d8f579cf6281b7f5838195b03
  • Pointer size: 130 Bytes
  • Size of remote file: 64.4 kB
example_imgs/seg/OpenTest_031.png ADDED

Git LFS Details

  • SHA256: 973ecd4ca18c650d630491c1f3531ba4ff20c12a37728dc79f279b26651d0c82
  • Pointer size: 131 Bytes
  • Size of remote file: 966 kB
example_imgs/seg/X_24.png ADDED

Git LFS Details

  • SHA256: 514b2df4bdcdd1d09d1f032284a5c2aaa0572d2f1ec148b256e4bbf5d68eb3c7
  • Pointer size: 131 Bytes
  • Size of remote file: 102 kB
example_imgs/seg/exp_A01_G002_0001.oir.png ADDED

Git LFS Details

  • SHA256: 9c22531659320908a688da277b7f67b70aafb450e035f56e3962ebfd3423140f
  • Pointer size: 132 Bytes
  • Size of remote file: 1.69 MB
example_imgs/tra/tracking_test_sequence.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bda69434e3de8103c98313777640acd35fc7501eec4b1528456304142b18797f
3
+ size 10392163
example_imgs/tra/tracking_test_sequence2.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:120cc2a75a4dd571b8f8ee7ea363a9b82a2b4c516376ccf4f287b6864d2dd576
3
+ size 2288296