fusion-tools 2.2.3__tar.gz → 2.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/PKG-INFO +1 -1
  2. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/setup.py +1 -1
  3. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/components/maps.py +1 -3
  4. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/components/segmentation.py +142 -5
  5. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/feature_extraction.py +1 -1
  6. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/utils/shapes.py +9 -22
  7. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/visualization/components.py +6 -7
  8. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools.egg-info/PKG-INFO +1 -1
  9. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/LICENSE +0 -0
  10. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/README.md +0 -0
  11. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/pyproject.toml +0 -0
  12. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/setup.cfg +0 -0
  13. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/__init__.py +0 -0
  14. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/components/__init__.py +0 -0
  15. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/components/tools.py +0 -0
  16. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/dataset.py +0 -0
  17. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/handler.py +0 -0
  18. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/tileserver.py +0 -0
  19. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/utils/__init__.py +0 -0
  20. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/utils/omics.py +0 -0
  21. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/utils/stats.py +0 -0
  22. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/visualization/__init__.py +0 -0
  23. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools/visualization/vis_utils.py +0 -0
  24. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools.egg-info/SOURCES.txt +0 -0
  25. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools.egg-info/dependency_links.txt +0 -0
  26. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools.egg-info/requires.txt +0 -0
  27. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/src/fusion_tools.egg-info/top_level.txt +0 -0
  28. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_classification_model.py +0 -0
  29. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_custom_component.py +0 -0
  30. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_dataset_classes.py +0 -0
  31. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_extract_nested_prop.py +0 -0
  32. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_feature_annotation.py +0 -0
  33. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_feature_extraction.py +0 -0
  34. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_generate_property_dict.py +0 -0
  35. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_get_gene_info.py +0 -0
  36. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_hra_viewer.py +0 -0
  37. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_image_overlay.py +0 -0
  38. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_model_train.py +0 -0
  39. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_multiframe_rgb.py +0 -0
  40. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_multiframeslidemap.py +0 -0
  41. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_property_plotter.py +0 -0
  42. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_read_anndata.py +0 -0
  43. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_remote_slide_annotations.py +0 -0
  44. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_remote_tileserver.py +0 -0
  45. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_segmentation.py +0 -0
  46. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_slide_annotations.py +0 -0
  47. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_spatial_aggregation.py +0 -0
  48. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_tile_server.py +0 -0
  49. {fusion_tools-2.2.3 → fusion_tools-2.2.5}/tests/test_visualization_component.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fusion-tools
3
- Version: 2.2.3
3
+ Version: 2.2.5
4
4
  Summary: Modular visualization and analysis dashboard creation for high-resolution microscopy images
5
5
  Home-page: https://github.com/spborder/fusion-tools
6
6
  Author: Sam Border
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="fusion-tools",
8
- version="2.2.3",
8
+ version="2.2.5",
9
9
  author="Sam Border",
10
10
  author_email="sam.border2256@gmail.com",
11
11
  description="Modular visualization and analysis dashboard creation for high-resolution microscopy images",
@@ -314,7 +314,7 @@ class SlideMap(MapComponent):
314
314
 
315
315
  """
316
316
  self.js_namespace = Namespace(
317
- "fusionTools","default"
317
+ "fusionTools","slideMap"
318
318
  )
319
319
 
320
320
  self.js_namespace.add(
@@ -1346,8 +1346,6 @@ class MultiFrameSlideMap(SlideMap):
1346
1346
  else:
1347
1347
  frame_names = [f'Frame {i}' for i in range(len(image_metadata['frames']))]
1348
1348
 
1349
- print(frame_names)
1350
- print(len(image_metadata['frames']))
1351
1349
  # This is a multi-frame image
1352
1350
  if len(image_metadata['frames'])==3:
1353
1351
  # Treat this as an RGB image by default
@@ -23,10 +23,11 @@ from skimage.measure import label
23
23
  import dash
24
24
  dash._dash_renderer._set_react_version('18.2.0')
25
25
  import dash_leaflet as dl
26
- from dash import dcc, callback, ctx, ALL, MATCH, exceptions, Patch, no_update, dash_table
26
+ from dash import dcc, callback, ctx, ALL, MATCH, exceptions, Patch, no_update, dash_table, ClientsideFunction
27
27
  import dash_bootstrap_components as dbc
28
28
  import dash_mantine_components as dmc
29
29
  from dash_extensions.enrich import DashBlueprint, html, Input, Output, State, PrefixIdTransform, MultiplexerTransform
30
+ from dash_extensions.javascript import Namespace
30
31
 
31
32
  # fusion-tools imports
32
33
  from fusion_tools.visualization.vis_utils import get_pattern_matching_value
@@ -917,6 +918,9 @@ class BulkLabels(Tool):
917
918
  self.ignore_list = ignore_list
918
919
  self.property_depth = property_depth
919
920
 
921
+ self.assets_folder = os.getcwd()+'/.fusion_assets/'
922
+ self.get_namespace()
923
+
920
924
  def __str__(self):
921
925
  return 'Bulk Labels'
922
926
 
@@ -954,6 +958,112 @@ class BulkLabels(Tool):
954
958
 
955
959
  return x_scale, y_scale
956
960
 
961
+ def get_namespace(self):
962
+ """Adding JavaScript functions to the BulkLabels Namespace
963
+ """
964
+ self.js_namespace = Namespace(
965
+ "fusionTools","bulkLabels"
966
+ )
967
+
968
+ self.js_namespace.add(
969
+ name = "propertyFilter",
970
+ src = """
971
+ function(butt_click, structure_names, filter_json, map_annotations, slide_information) {
972
+ var returnFeature = true;
973
+ // Defining function to extract property value
974
+ function checkFilter(feature, filterItem) {
975
+ if (filterItem.name) {
976
+ var filterSubProps = filterItem.name.split(" --> ");
977
+ var prop_dict = feature.properties;
978
+ for (let i=0; i<filterSubProps.length; i++) {
979
+ if (filterSubProps[j] in prop_dict) {
980
+ var prop_dict = prop_dict[filterSubProps[j]];
981
+ var testVal = prop_dict;
982
+ } else {
983
+ var testVal = false;
984
+ }
985
+ }
986
+
987
+ if (filterItem.range & testVal) {
988
+ if (typeof filterItem.range[0]==='number') {
989
+ if (testVal < filterItem.range[0]) {
990
+ returnFeature = returnFeature & false;
991
+ }
992
+ if (testVal > filterItem.range[1]) {
993
+ returnFeature = returnFeature & false;
994
+ }
995
+ } else {
996
+ if (filterItem.range.includes(testVal)) {
997
+ returnFeature = returnFeature & true;
998
+ } else {
999
+ returnFeature = returnFeature & false;
1000
+ }
1001
+ }
1002
+ } else {
1003
+ returnFeature = returnFeature & false;
1004
+ }
1005
+ }
1006
+
1007
+ return returnFeature;
1008
+ }
1009
+
1010
+ // Reading in filters dictionary
1011
+ const filters = JSON.parse(filter_json);
1012
+ console.log(filters);
1013
+
1014
+ // Reading in annotations
1015
+ const annotations = JSON.parse(map_annotations);
1016
+ var namesList = [];
1017
+ for (let i = 0; i<annotations.length; i++) {
1018
+ namesList.push(annotations[i]["properties"]["name"]);
1019
+ }
1020
+ console.log(namesList);
1021
+
1022
+ // Getting desired annotation for left joins
1023
+ var baseAnnotations = {
1024
+ "type": "FeatureCollection",
1025
+ "features": []
1026
+ };
1027
+
1028
+ for (let i = 0; i<structure_names.length; i++) {
1029
+ baseAnnotations["features"].push(annotations[namesList.indexOf(structure_names[i])]);
1030
+ }
1031
+
1032
+ // Performing filtration
1033
+ for (let j = 0; j < filters.length; j++) {
1034
+ // Checking if this is a spatial query
1035
+ if ("structure" in filters[j]) {
1036
+ var sqAnnotation = annotations[namesList.indexOf(filters[j]["structure"])];
1037
+ baseAnnotations.splice(i,1,sjoin(baseAnnotations[i],sqAnnotation, { how: "inner", op: filters[j]["type"] }));
1038
+ } else {
1039
+ for (let k = 0; k < baseAnnotations['features'].length; k++) {
1040
+ var check = checkFilter(baseAnnotations["features"], filters[j]);
1041
+ if (!check) {
1042
+ baseAnnotations['features'].splice(k,1);
1043
+ }
1044
+ }
1045
+ }
1046
+ }
1047
+ console.log(baseAnnotations['features'].length);
1048
+
1049
+ // Creating L.Marker() objects
1050
+ var marker_list = [];
1051
+ for (let j = 0; j < baseAnnotations['features'].length; j++) {
1052
+ var centroid = turf.centroid(turf.polygon(baseAnnotations['features'][j]['geometry']));
1053
+
1054
+ marker_list.push(
1055
+ L.Marker(
1056
+ centroid
1057
+ ).bindPopup()
1058
+ );
1059
+
1060
+ }
1061
+ }
1062
+ """
1063
+ )
1064
+
1065
+
1066
+
957
1067
  def gen_layout(self, session_data:dict):
958
1068
  """Generating layout for BulkLabels component
959
1069
 
@@ -1254,6 +1364,32 @@ class BulkLabels(Tool):
1254
1364
  Output({'type': 'bulk-labels-apply-labels','index': ALL},'disabled'),
1255
1365
  Output({'type': 'bulk-labels-download-labels','index': ALL},'disabled')
1256
1366
  ],
1367
+ [
1368
+ State({'type': 'bulk-labels-include-structures','index': ALL},'value'),
1369
+ State({'type': 'bulk-labels-filter-data','index': ALL},'data'),
1370
+ State({'type': 'map-annotations-store','index': ALL},'data'),
1371
+ State({'type': 'map-slide-information','index': ALL},'data')
1372
+ ]
1373
+ )(self.update_label_structures)
1374
+
1375
+ self.blueprint.clinetside_callback(
1376
+ ClientsideFunction(
1377
+ namespace = 'fusionTools',
1378
+ function_name = ''
1379
+ ),
1380
+ [
1381
+ Input({'type': 'bulk-labels-update-structures','index': ALL},'n_clicks')
1382
+ ],
1383
+ [
1384
+ Output({'type': 'bulk-labels-current-structures','index': ALL},'children'),
1385
+ Output({'type': 'map-marker-div','index': ALL},'children'),
1386
+ Output({'type': 'bulk-labels-label-source','index': ALL},'children'),
1387
+ Output({'type': 'bulk-labels-label-type','index': ALL},'disabled'),
1388
+ Output({'type': 'bulk-labels-label-text','index': ALL},'disabled'),
1389
+ Output({'type': 'bulk-labels-label-rationale','index': ALL},'disabled'),
1390
+ Output({'type': 'bulk-labels-apply-labels','index': ALL},'disabled'),
1391
+ Output({'type': 'bulk-labels-download-labels','index': ALL},'disabled')
1392
+ ],
1257
1393
  [
1258
1394
  State({'type': 'bulk-labels-include-structures','index': ALL},'value'),
1259
1395
  State({'type': 'bulk-labels-add-property-div','index': ALL},'children'),
@@ -1261,7 +1397,7 @@ class BulkLabels(Tool):
1261
1397
  State({'type': 'map-annotations-store','index': ALL},'data'),
1262
1398
  State({'type': 'map-slide-information','index': ALL},'data')
1263
1399
  ]
1264
- )(self.update_label_structures)
1400
+ )
1265
1401
 
1266
1402
  # Adding new property
1267
1403
  self.blueprint.callback(
@@ -2097,9 +2233,10 @@ class BulkLabels(Tool):
2097
2233
  n_marked = len(get_pattern_matching_value(current_markers))
2098
2234
 
2099
2235
  patched_list = Patch()
2100
- values_to_remove = np.where(clear_click)[0].tolist()
2101
- for v in values_to_remove:
2102
- del patched_list[v]
2236
+ for v_idx,v in enumerate(clear_click):
2237
+ if v:
2238
+ del patched_list[v_idx]
2239
+ break
2103
2240
 
2104
2241
  new_structures_div = [
2105
2242
  dbc.Alert(
@@ -156,7 +156,7 @@ class ParallelFeatureExtractor:
156
156
  width = int(bbox[2] - bbox[0])
157
157
 
158
158
  # polygon2mask expects y,x coordinates, scaled to within shape bounding box
159
- scaled_coords = np.flip(np.squeeze(coords_array)-np.array([bbox[0], bbox[1], 0]),axis=1)
159
+ scaled_coords = np.flip(np.squeeze(coords_array)-np.array([bbox[0], bbox[1]]),axis=1)
160
160
  if np.shape(scaled_coords)[-1]==3:
161
161
  scaled_coords = scaled_coords[:,1:]
162
162
 
@@ -67,33 +67,30 @@ def load_geojson(geojson_path: str, name:Union[str,None]=None) -> dict:
67
67
  with open(geojson_path,'r') as f:
68
68
  geojson_anns = json.load(f)
69
69
  f.close()
70
-
71
- print(type(geojson_anns))
72
- print(len(geojson_anns))
73
70
 
74
71
  if type(geojson_anns)==list:
75
72
  for g in geojson_anns:
76
73
  if not 'properties' in g:
77
74
  g['properties'] = {}
75
+ else:
76
+ if 'name' in g['properties'] and name is None:
77
+ name = g['properties']['name']
78
78
 
79
79
  geo_id = uuid.uuid4().hex[:24] if not '_id' in g['properties'] else g['properties']['_id']
80
80
  g['properties'] = g['properties'] | {'name': name if not name is None else geo_id, '_id': geo_id}
81
81
 
82
-
83
82
  for f_idx, f in enumerate(g['features']):
84
83
  f['properties'] = f['properties'] | {'name': name if not name is None else geo_id, '_id': uuid.uuid4().hex[:24], '_index': f_idx}
85
84
 
86
85
  elif type(geojson_anns)==dict:
87
86
  if not 'properties' in geojson_anns:
88
87
  geojson_anns['properties'] = {}
89
- geo_id = uuid.uuid4().hex[:24] if not '_id' in g['properties'] else geojson_anns['properties']['_id']
90
- geojson_anns['properties'] = geojson_anns['properties'] | {'name': name if not name is None else geo_id, '_id': geo_id}
91
-
92
-
93
- for f_idx, f in enumerate(geojson_anns['features']):
94
- f['properties'] = f['properties'] | {'name': name if not name is None else geo_id, '_id': uuid.uuid4().hex[:24], '_index': f_idx}
95
88
 
89
+ geo_id = uuid.uuid4().hex[:24] if not '_id' in g['properties'] else geojson_anns['properties']['_id']
90
+ geojson_anns['properties'] = geojson_anns['properties'] | {'name': name if not name is None else geo_id, '_id': geo_id}
96
91
 
92
+ for f_idx, f in enumerate(geojson_anns['features']):
93
+ f['properties'] = f['properties'] | {'name': name if not name is None else geo_id, '_id': uuid.uuid4().hex[:24], '_index': f_idx}
97
94
 
98
95
  return geojson_anns
99
96
 
@@ -1016,8 +1013,6 @@ def process_filters_queries(filter_list:list, spatial_list:list, structures:list
1016
1013
  structure_filtered = [gpd.GeoDataFrame.from_features(i['features']) for i in all_geo_list]
1017
1014
  name_order = [i['properties']['name'] for i in all_geo_list]
1018
1015
 
1019
- #all_names = [i['properties']['name'] for i in all_geo_list]
1020
-
1021
1016
  # Now going through spatial queries
1022
1017
  filter_reference_list = {
1023
1018
  n: {}
@@ -1048,6 +1043,8 @@ def process_filters_queries(filter_list:list, spatial_list:list, structures:list
1048
1043
  )
1049
1044
 
1050
1045
  intermediate_gdf = intermediate_gdf.drop([i for i in ['index_left','index_right'] if i in intermediate_gdf], axis = 1)
1046
+ intermediate_gdf = intermediate_gdf.drop([i for i in intermediate_gdf if '_right' in i],axis=1)
1047
+ intermediate_gdf.columns = [i.replace('_left','') if '_left' in i else i for i in intermediate_gdf ]
1051
1048
 
1052
1049
  remainder_structures.append(intermediate_gdf)
1053
1050
  else:
@@ -1060,21 +1057,11 @@ def process_filters_queries(filter_list:list, spatial_list:list, structures:list
1060
1057
  }
1061
1058
  for g,name in zip(remainder_structures,name_order):
1062
1059
  g_json = g.to_geo_dict(show_bbox=True)
1063
- #feature_geos = [i['geometry'] for i in g_json['features']]
1064
-
1065
-
1066
1060
  filter_reference_list[name] = {
1067
1061
  i+len(combined_geojson['features']):j['properties']['_index']
1068
1062
  for i,j in enumerate(g_json['features'])
1069
1063
  }
1070
1064
  combined_geojson['features'].extend(g_json['features'])
1071
- """
1072
- if len(g_json['features'])>0:
1073
- for idx, i in enumerate(all_geo_list[all_names.index(name)]['features']):
1074
- if i['geometry'] in feature_geos:
1075
- filter_reference_list[name][len(combined_geojson['features'])] = idx
1076
- combined_geojson['features'].append(g_json['features'][feature_geos.index(i['geometry'])])
1077
- """
1078
1065
 
1079
1066
  # Going through property filters:
1080
1067
  if len(filter_list)>0:
@@ -29,15 +29,12 @@ class Visualization:
29
29
 
30
30
  components = [
31
31
  [
32
- SlideMap(
33
- tile_server = LocalTileServer("/path/to/slide.svs"),
34
- annotations = geojson_list
35
- )
32
+ SlideMap()
36
33
  ],
37
34
  [
38
35
  [
39
- OverlayOptions(geojson_list),
40
- PropertyViewer(geojson_list)
36
+ OverlayOptions(),
37
+ PropertyViewer()
41
38
  ]
42
39
  ]
43
40
  ]
@@ -95,7 +92,9 @@ class Visualization:
95
92
  MultiplexerTransform()
96
93
  ],
97
94
  'external_scripts': [
98
- 'https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js'
95
+ 'https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.1.0/chroma.min.js',
96
+ 'https://cdn.jsdelivr.net/npm/spatialmerge',
97
+ 'https://cdn.jsdelivr.net/npm/@turf/turf@7/turf.min.js'
99
98
  ]
100
99
  }
101
100
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fusion-tools
3
- Version: 2.2.3
3
+ Version: 2.2.5
4
4
  Summary: Modular visualization and analysis dashboard creation for high-resolution microscopy images
5
5
  Home-page: https://github.com/spborder/fusion-tools
6
6
  Author: Sam Border
File without changes
File without changes
File without changes