From a8cf70aed28491a4ff6f9665449f9b6c64e55bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Thu, 7 Mar 2024 19:24:30 -0800 Subject: [PATCH 01/24] Put Tiled client refresh into its own callback --- callbacks/control_bar.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index 72c8915..15f10d9 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -853,6 +853,16 @@ def open_controls_drawer(n_clicks, is_opened): return no_update, no_update +@callback(Output("project-name-src", "data"), Input("refresh-tiled", "n_clicks")) +def refresh_data_client(refresh_tiled): + if refresh_tiled: + tiled_datasets.refresh_data_client() + data_options = [ + item for item in tiled_datasets.get_data_project_names() if "seg" not in item + ] + return data_options + + @callback( Output("result-selector", "data"), Output("result-selector", "value"), @@ -860,22 +870,15 @@ def open_controls_drawer(n_clicks, is_opened): Output("show-result-overlay-toggle", "checked"), Output("show-result-overlay-toggle", "disabled"), Output("seg-result-opacity-slider", "disabled"), - Output("project-name-src", "data"), Input("project-name-src", "value"), - Input("refresh-tiled", "n_clicks"), Input("show-result-overlay-toggle", "checked"), State("result-selector", "disabled"), State("seg-result-opacity-slider", "disabled"), ) def populate_classification_results( - image_src, refresh_tiled, toggle, dropdown_enabled, slider_enabled + image_src, toggle, dropdown_enabled, slider_enabled ): - if refresh_tiled: - tiled_datasets.refresh_data_client() - data_options = [ - item for item in tiled_datasets.get_data_project_names() if "seg" not in item - ] results = [] value = None checked = False @@ -910,7 +913,6 @@ def populate_classification_results( checked, disabled_toggle, disabled_slider, - data_options, ) From 192e90002c41ed119782dff919031fc39b1d85e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Thu, 7 Mar 2024 19:25:23 -0800 Subject: [PATCH 02/24] Remove unused stores --- components/control_bar.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/control_bar.py b/components/control_bar.py index 03d2343..d2e66da 100644 --- a/components/control_bar.py +++ b/components/control_bar.py @@ -758,8 +758,6 @@ def drawer_section(children): dmc.NotificationsProvider(html.Div(id="notifications-container")), dcc.Download(id="export-annotation-metadata"), dcc.Download(id="export-annotation-mask"), - dcc.Store(id="project-data"), - dcc.Store(id="submitted-job-id"), dcc.Interval( id="model-check", interval=5000 ), # TODO: May want to increase frequency From 6304ad3d10cc1b788ebe4a94f66c761da85d5a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 08:19:05 -0800 Subject: [PATCH 03/24] Adapt result retrieval to store element from the previously used dropdown that was populated by name match --- callbacks/control_bar.py | 54 ++++++++++++--------------------------- callbacks/image_viewer.py | 32 +++++++++++++++-------- components/control_bar.py | 14 ++-------- utils/data_utils.py | 9 +++++++ 4 files changed, 49 insertions(+), 60 deletions(-) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index 15f10d9..caa2875 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -28,7 +28,7 @@ from components.parameter_items import ParameterItems from constants import ANNOT_ICONS, ANNOT_NOTIFICATION_MSGS, KEY_MODES, KEYBINDS from utils.annotations import Annotations -from utils.data_utils import models, tiled_datasets, tiled_masks, tiled_results +from utils.data_utils import models, tiled_datasets, tiled_masks from utils.plot_utils import generate_notification, generate_notification_bg_icon_col # TODO - temporary local file path and user for annotation saving and exporting @@ -864,55 +864,33 @@ def refresh_data_client(refresh_tiled): @callback( - Output("result-selector", "data"), - Output("result-selector", "value"), - Output("result-selector", "disabled"), Output("show-result-overlay-toggle", "checked"), Output("show-result-overlay-toggle", "disabled"), Output("seg-result-opacity-slider", "disabled"), - Input("project-name-src", "value"), Input("show-result-overlay-toggle", "checked"), - State("result-selector", "disabled"), + Input("project-name-src", "value"), + Input("seg-result-store", "data"), State("seg-result-opacity-slider", "disabled"), ) -def populate_classification_results( - image_src, toggle, dropdown_enabled, slider_enabled -): - - results = [] - value = None +def update_result_controls(toggle, seg_result, slider_disabled, image_src): checked = False - disabled_dropdown = True - disabled_toggle = True - disabled_slider = True + disable_toggle = True + disable_slider = True + # Disable opacity slider if result overlay is unchecked if ctx.triggered_id == "show-result-overlay-toggle": - results = no_update - value = no_update checked = no_update - disabled_dropdown = dropdown_enabled - disabled_toggle = False - disabled_slider = slider_enabled + # Must have been enabled to be source of trigger + disable_toggle = no_update + disable_slider = not slider_disabled else: - # TODO: Match by mask uid instead of image_src - results = [ - item - for item in tiled_results.get_data_project_names() - if ("seg" in item and image_src in item) - ] - if results: - value = results[0] - disabled_dropdown = False - checked = False - disabled_toggle = False - disabled_slider = False - + if "project_name" in seg_result and seg_result["project_name"] == image_src: + checked = no_update + disable_toggle = False + disable_slider = False return ( - results, - value, - disabled_dropdown, checked, - disabled_toggle, - disabled_slider, + disable_toggle, + disable_slider, ) diff --git a/callbacks/image_viewer.py b/callbacks/image_viewer.py index ea29e75..260217d 100644 --- a/callbacks/image_viewer.py +++ b/callbacks/image_viewer.py @@ -16,7 +16,7 @@ from dash.exceptions import PreventUpdate from constants import ANNOT_ICONS, ANNOT_NOTIFICATION_MSGS, KEYBINDS -from utils.data_utils import tiled_datasets, tiled_masks, tiled_results +from utils.data_utils import tiled_datasets, tiled_results from utils.plot_utils import ( create_viewfinder, downscale_view, @@ -70,7 +70,7 @@ def hide_show_segmentation_overlay(toggle_seg_result, opacity): State("image-metadata", "data"), State("screen-size", "data"), State("current-class-selection", "data"), - State("result-selector", "value"), + State("seg-result-store", "data"), State("seg-result-opacity-slider", "value"), State("image-viewer", "figure"), prevent_initial_call=True, @@ -85,7 +85,7 @@ def render_image( image_metadata, screen_size, current_color, - seg_result_selection, + seg_result, opacity, fig, ): @@ -118,13 +118,25 @@ def render_image( and ctx.triggered_id == "show-result-overlay-toggle" ): return [dash.no_update] * 7 + ["hidden"] - annotation_indices = tiled_masks.get_annotated_segmented_results() - if str(image_idx + 1) in annotation_indices: - # Will not return an error since we already checked if image_idx+1 is in the list - mapped_index = annotation_indices.index(str(image_idx + 1)) - result = tiled_results.get_data_sequence_by_name(seg_result_selection)[ - mapped_index - ] + # Check if the stored results are for the current project and image + if ( + "project_name" in seg_result + and seg_result["project_name"] == project_name + ): + if "mask_idx" in seg_result: + annotation_indices = seg_result["mask_idx"] + if str(image_idx) in annotation_indices: + # Will not return an error since we already checked if image_idx is in the list + mapped_index = annotation_indices.index(str(image_idx)) + result = tiled_results.get_data_by_trimmed_uri( + seg_result["seg_result_trimmed_uri"], slice=mapped_index + ) + else: + result = None + else: + result = tiled_results.get_data_by_trimmed_uri( + seg_result["seg_result_trimmed_uri"], slice=image_idx + ) else: result = None else: diff --git a/components/control_bar.py b/components/control_bar.py index d2e66da..86b45c8 100644 --- a/components/control_bar.py +++ b/components/control_bar.py @@ -44,9 +44,7 @@ def layout(): """ Returns the layout for the control panel in the app UI """ - DATA_OPTIONS = [ - item for item in tiled_datasets.get_data_project_names() if "seg" not in item - ] + DATA_OPTIONS = [item for item in tiled_datasets.get_data_project_names()] return drawer_section( dmc.Stack( style={"width": "400px"}, @@ -658,15 +656,7 @@ def layout(): disabled=True, styles={"trackLabel": {"cursor": "pointer"}}, ), - dmc.Space(h=25), - ControlItem( - "Results", - "", - dmc.Select( - id="result-selector", - placeholder="Select an ML result...", - ), - ), + dcc.Store("seg-result-store"), dmc.Space(h=25), ControlItem( "Opacity", diff --git a/utils/data_utils.py b/utils/data_utils.py index de0c93c..3b5d198 100644 --- a/utils/data_utils.py +++ b/utils/data_utils.py @@ -99,6 +99,15 @@ def get_data_uri_by_name(self, project_name): return project_container.uri return None + def get_data_by_trimmed_uri(self, trimmed_uri, slice=None): + """ + Retrieve data by a trimmed uri (not containing the base uri) and slice id + """ + if slice is None: + return self.data_client[trimmed_uri] + else: + return self.data_client[trimmed_uri][slice] + tiled_datasets = TiledDataLoader( data_tiled_uri=DATA_TILED_URI, data_tiled_api_key=DATA_TILED_API_KEY From 8bb83a52ce6010e5573cbc18bad2853f0d79fd9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 08:22:14 -0800 Subject: [PATCH 04/24] Add place holder for dvc results link --- components/control_bar.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/control_bar.py b/components/control_bar.py index 86b45c8..b8ae508 100644 --- a/components/control_bar.py +++ b/components/control_bar.py @@ -1,3 +1,5 @@ +import os + import dash_bootstrap_components as dbc import dash_mantine_components as dmc from dash import dcc, html @@ -9,6 +11,8 @@ from constants import ANNOT_ICONS, KEYBINDS from utils.data_utils import models, tiled_datasets +RESULTS_DIR = os.getenv("RESULTS_DIR", "") + def _tooltip(text, children): """ @@ -629,6 +633,19 @@ def layout(): id="train-job-selector", ), ), + dmc.Space(h=25), + # Maybe add icon: fluent:window-new-20-filled + ControlItem( + "Training Stats", + "dvc-training-stats", + dmc.Anchor( + dmc.Text("Open in new window"), + # href=RESULTS_DIR + uid from store "report.html", + href="assets/report.html", + target="_blank", + size="sm", + ), + ), dmc.Space(h=10), dmc.Button( "Inference", From 0dd2ee447429e9da9e4ebcf4749f7fa80351e523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 08:27:15 -0800 Subject: [PATCH 05/24] Delete existing annotations json --- callbacks/control_bar.py | 2 +- exported_annotation_data.json | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 exported_annotation_data.json diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index caa2875..ded636e 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -32,7 +32,7 @@ from utils.plot_utils import generate_notification, generate_notification_bg_icon_col # TODO - temporary local file path and user for annotation saving and exporting -EXPORT_FILE_PATH = os.getenv("EXPORT_FILE_PATH", "exported_annotation_data.json") +EXPORT_FILE_PATH = os.getenv("EXPORT_FILE_PATH", "data/exported_annotation_data.json") USER_NAME = os.getenv("USER_NAME", "user1") # Create an empty file if it doesn't exist diff --git a/exported_annotation_data.json b/exported_annotation_data.json deleted file mode 100644 index 644c849..0000000 --- a/exported_annotation_data.json +++ /dev/null @@ -1 +0,0 @@ -{"user": "user1", "source": "rec20190524_085542_clay_testZMQ_8bit", "time": "2023-11-03-01:13:15", "data": "[{\"annotations\": {\"10\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#1616A7\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#1616A7\", \"fillrule\": \"evenodd\", \"type\": \"rect\", \"x0\": -63.20234136701797, \"y0\": -87.87419799023255, \"x1\": 1798.7075538711276, \"y1\": 1799.8159881050567}], \"201\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#1616A7\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#1616A7\", \"fillrule\": \"evenodd\", \"type\": \"rect\", \"x0\": -57.56973124678129, \"y0\": -32.338000404849645, \"x1\": 1810.1275356935803, \"y1\": 1788.4714690391008}], \"222\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#1616A7\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#1616A7\", \"fillrule\": \"evenodd\", \"type\": \"rect\", \"x0\": -51.58406154894103, \"y0\": -51.1284966129008, \"x1\": 1799.5567024892007, \"y1\": 1792.352374608532}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#1616A7\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#1616A7\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M851.3965863962227,841.9445529663897L842.7649344699761,838.4918921958911L839.3122736994775,836.1901183488919L828.9542913879816,843.670883351639Z\"}], \"493\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#1616A7\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#1616A7\", \"fillrule\": \"evenodd\", \"type\": \"rect\", \"x0\": -57.47338784320829, \"y0\": -59.229430371184144, \"x1\": 1792.978600347318, \"y1\": 1791.2225578193422}]}, \"color\": \"#1616A7\", \"label\": \"Background\", \"is_visible\": true, \"class_id\": 2}, {\"annotations\": {\"10\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M975.1847752778856,840.1471642358852L970.8219581631488,845.818826485043L968.2042678943067,849.3090801768325L967.5498453270961,855.417024137464L968.4224087500435,857.1621509833586L971.4763807303592,859.561700396464L971.2582398746224,861.7431089538323L967.3317044713593,869.5961797603586L965.1502959139909,873.3045743078849L962.314464789412,875.4859828652533L961.0056196549909,879.1943774127795L961.0056196549909,886.829307363569L961.0056196549909,888.5744342094637L959.2604928090963,890.3195610553583L957.0790842517279,892.5009696127267L953.8069714156752,897.7363501504109L955.3339574058331,899.6996178520425L955.1158165500963,902.7535898323582L951.40742200257,906.6801252356213L955.1158165500963,908.2071112257792L957.7335068189383,909.0796746487266L955.5520982615699,911.9155057733054L954.4613939828857,914.0969143306739L955.1158165500963,914.7513368978844L959.6967745205699,913.2243509077265L961.8781830779383,917.1508863109897L962.0963239336751,921.5137034257265L961.0056196549909,922.168125992937L961.4419013664647,917.1508863109897L961.2237605107277,923.2588302716212L959.6967745205699,923.6951119830949L955.1158165500963,921.7318442814633L953.5888305599384,920.6411400027791L951.8437037140436,923.6951119830949L952.0618445697805,925.8765205404633L954.6795348386225,930.6756193666737L957.9516476746752,934.1658730584631L959.4786336648331,940.2738170190947L958.6060702418857,944.2003524223578L957.2972251074647,943.982211566621L955.3339574058331,942.8915072879367L952.4981262812541,941.3645212977789L948.5715908779911,932.8570279240422L946.8264640320963,932.2026053568317L943.9906329075174,933.9477322027263L944.4269146189911,935.4747181928842L949.4441543009384,938.9649718846737L952.4981262812541,945.0729158453051L952.9344079927279,950.5264372387262L953.3706897042015,951.8352823731473L952.7162671369911,954.0166909305157L955.3339574058331,960.9971983140946L955.9883799730436,962.3060434485155L962.7507465008856,958.8157897567261L964.2777324910436,961.8697617370419L966.2410001926751,967.9777056976734L970.8219581631488,972.3405228124102L977.1480429795171,972.9949453796207L977.366183835254,970.8135368222523L973.221507576254,968.8502691206207L967.1135636156224,964.7055928616209L967.1135636156224,960.9971983140946L969.076831317254,958.3795080452525L971.9126624418329,956.6343811993577L974.9666344221487,955.5436769206735L975.4029161336224,955.1073952091998L979.5475923926224,959.9064940354103L980.8564375270433,960.9971983140946L989.8002126122539,963.8330294386734L991.7634803138855,965.5781562845682L992.8541845925696,969.2865508320945L998.0895651302537,986.5196784353049L1003.324945667938,992.6276223959363L1007.9059036384116,998.517425500831L1012.7050024646221,1002.2258200483574L1013.5775658875694,1002.662101759831L1063.9681035627796,1002.662101759831L1068.3309206775164,1001.7895383368837L1093.6352599429897,991.9731998287258L1097.561795346253,990.8824955500417L1105.414866152779,987.8285235697259L1114.3586412379896,984.992692445147L1121.5572894773054,980.6298753304102L1124.6112614576211,976.9214807828839L1130.7192054182526,960.7790574583577L1132.4643322641473,951.6171415174105L1132.4643322641473,943.3277889994105L1129.1922194280946,922.8225485601475L1123.0842754674632,903.4080123995687L1120.0303034871474,896.6456458717266L1112.8316552478318,885.0841805176742L1103.8878801626213,873.7408560193585L1098.2162179134634,868.5054754816744L1080.1105268873057,855.8533058489377L1079.8923860315688,855.8533058489377Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1045.4523655042005,825.4118682052382L1039.5226607419443,851.7249330877504L1033.222349432047,895.4565057093903L1032.4811363367648,929.1817015447228L1031.739923241483,949.5650616649788L1029.516283955637,957.7184057130811L1019.8805137169704,982.5490444050292L1017.6568744311243,988.4787491672855L1014.6920220499961,992.5554211913367L1013.5802024070731,997.3733063106698L1011.727169668868,1000.338158691798L1012.8389893117911,1004.0442241682082L1018.0274809787653,1009.2327158351824L1032.110529789124,1012.1975682163105L1038.7814476466622,1012.9387813115926L1046.9347916947645,1016.2742402403617L1055.4587422905079,1020.350912264413L1062.5002666956873,1023.686371193182L1068.0593649103025,1022.574551550259L1072.8772500296357,1019.6096991691309L1075.471495863123,1017.0154533356438L1090.2957577687635,1017.0154533356438L1105.8612327696862,1018.4978795262078L1110.6791178890194,1018.8684860738489L1113.2733637225065,1021.462731907336L1134.0273303904034,1021.462731907336L1141.0688547955829,1020.7215188120539L1160.7110018205567,1019.9803057167719L1175.5352637261974,1018.4978795262078L1182.5767881313766,1016.2742402403617L1193.3243780129662,1013.6799944068746L1201.1071155134275,1010.7151420257464L1211.854705395017,1002.9324045252852L1225.1965411100937,988.8493557149264L1230.0144262294268,984.7726836908753L1234.83231134876,977.731159285696L1240.3914095633752,970.6896348805167L1244.0974750397854,961.7950777371323L1248.9153601591186,955.865372974876L1251.8802125402467,950.3062747602607L1254.8450649213748,946.9708158314916L1256.69809765958,933.258373568774L1257.439310754862,925.4756360683126L1268.5575071840926,916.2104723772873L1274.4872119463487,904.3510628527747L1280.416916708605,892.8622598759032L1283.381769089733,890.6386205900571L1284.8641952802973,887.673768208929L1283.7523756373741,879.5204241608266L1280.0463101609641,867.2904080886731L1281.158129803887,862.8431295169809L1281.158129803887,860.2488836834938L1282.2699494468102,859.1370640405706L1280.416916708605,852.4661461830324L1278.934490518041,847.2776545160582L1279.675703613323,845.054015230212L1281.158129803887,842.0891628490839L1281.158129803887,835.0476384439046L1283.7523756373741,835.4182449915456L1283.7523756373741,834.3064253486226L1285.2348018279383,826.5236878481612L1284.4935887326562,822.076409276469L1282.6405559944512,817.2585241571359L1282.6405559944512,809.4757866566745L1280.0463101609641,803.9166884420592L1209.2604595615298,807.2521473708283L1083.9954464588664,813.5524586807256L1036.187201813175,815.7760979665718L1013.5802024070731,819.4821634429819Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1280.6525609289347,786.9699369509858L1279.9545127239699,767.0755631094884L1278.2093922115578,760.0950810598401L1274.3701270842512,753.1145990101919L1274.0211029817688,747.1811892679909L1275.4171993916984,741.5968036282724L1276.813295801628,739.502659013378L1274.0211029817688,736.0124179885538L1272.2759824693567,737.0594902960011L1265.9935486246734,727.2868154264936L1262.1542834973668,721.702429786775L1255.522825550201,714.7219477371268L1250.2874640129648,709.4865861998906L1233.1852829913269,699.7139113303831L1223.4126081218192,695.8746462030766L1218.875294789548,696.9217185105239L1210.49871632997,705.2982969701017L1202.1221378703922,709.4865861998906L1184.6709327462715,716.8160923520212L1172.8041132618696,722.7495020942222L1166.5216794171863,726.9377913240112L1163.729486597327,726.2397431190464L1159.1921732650555,721.3534056842926L1156.7490045476789,719.6082851718805L1148.3724260881008,717.1651164545036L1147.674377883136,717.8631646594685L1149.7685224980305,721.702429786775L1143.4860886533472,725.8907190165639L1137.9017030136285,729.0319359389056L1133.7134137838398,723.447550299187L1130.572196861498,724.4946226066343L1123.940738914332,728.3338877339407L1122.5446425044024,727.635839528976L1121.4975701969554,725.5416949140815L1121.148546094473,723.0985261967046L1114.8661122497895,720.3063333768454L1107.536606097659,719.2592610693981L1102.3012445604227,721.0043815818102L1097.4149071256688,726.2397431190464L1094.9717384082921,729.380960041388L1096.0188107157394,746.8321651655085L1097.4149071256688,741.5968036282724L1101.6031963554578,739.502659013378L1106.1405096877293,738.8046108084131L1106.1405096877293,750.3224061903327L1102.9992927653875,752.4165508052272L1096.3678348182218,752.4165508052272L1093.924666100845,754.5106954201216L1089.7363768710559,754.1616713176392L1087.6422322561614,752.7655749077095L1082.057846616443,753.1145990101919L1079.614677899066,747.1811892679909L1079.614677899066,741.24777952579L1076.1244368742418,740.5497313208251L1070.8890753370058,746.4831410630261L1071.2380994394882,751.3694784977799L1072.6341958494177,754.859719522604L1071.9361476444528,760.0950810598401L1077.171509181689,761.8402015722522L1084.5010153338196,769.5187318268653L1083.802967128855,771.6128764417597L1077.869557386654,774.0560451591366L1074.0302922593473,772.6599487492069L1072.6341958494177,768.8206836219005L1063.9085932873575,768.8206836219005L1063.9085932873575,771.6128764417597L1065.6537137997695,774.7540933641014L1065.3046896972871,776.8482379789959L1061.4654245699805,779.6404307988552L1057.2771353401918,781.0365272087848L1055.1829907252973,780.33847900382L1054.13591841785,776.1501897740311L1054.13591841785,774.0560451591366L1052.0417738029555,775.1031174665839L1027.6100866291868,778.2443343889255L1022.3747250919506,784.8757923360913L1020.6296045795385,789.0640815658803L1015.04521893982,793.6013948981516L1010.8569297100311,799.883828742835L1005.9705922752773,811.750648227237L1003.178399455418,815.5899133545435L1000.0371825330764,814.8918651495786L995.1508450983225,813.1467446371666L989.9154835610864,813.4957687396491L995.4998692008049,817.684057969438L996.5469415082522,820.1272266868149L995.4998692008049,822.2213713017093L987.821338946192,828.5038051463927L985.0291461263326,831.9940461712168L974.9074471543428,840.719648733277L988.8684112536391,847.0020825779604L994.4527968933577,853.2845164226438L1004.5744958653477,859.9159743698096L1015.04521893982,864.453287702081L1033.8925204738703,867.5945046244227L1053.0888461104028,868.64157693187L1113.1209917373774,864.453287702081L1142.4390163459,861.3120707797393L1174.898257876764,860.2649984722921L1182.9258122338597,857.4728056524327L1234.9304035037387,833.7391666836288L1256.9189219601308,825.362588224051L1271.5779342643918,815.9389374570259L1280.3035368264523,808.2604072024128L1281.3506091338995,805.817238485036L1281.3506091338995,795.3465154105637L1280.6525609289347,792.5543225907044L1281.3506091338995,790.1111538733275Z\"}], \"201\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M781.8459165618385,926.1131713425185L778.618042889031,949.353861786733L763.7698239941162,977.7591501074395L756.6685019139396,992.6073690023544L748.27603036464,1002.9365647553385L737.3012598770943,1023.594956261307L731.4910872660407,1042.9621982981523L736.0101104079713,1055.8736929893826L741.1747082844633,1075.2409350262278L744.4025819572709,1094.6081770630733L752.7950535065705,1104.9373728160574L763.1242492595547,1119.1400169764106L768.9344218706084,1122.3678906492182L768.9344218706084,1131.4059369330794L768.9344218706084,1138.507259013256L777.9724681544695,1147.5453052971172L786.3649397037691,1166.266972599401L792.1751123148227,1174.0138694141392L805.7321817406145,1183.0519156980004L832.846320592198,1196.608985123792L839.302067937813,1206.2926061422147L847.6945394871127,1205.6470314076532L842.5299416106207,1209.5204798150223L843.8210910797437,1213.3939282223914L859.9604594437815,1228.242147117306L903.8595413939643,1246.9638144195899L941.9484507330934,1266.3310564564354L962.6068422390617,1272.7868038020504L966.4802906464308,1275.3691027402965L980.6829348067841,1270.2045048638042L987.7842568869607,1270.850079598366L1000.0501768436294,1276.014677474858L1010.3793725966136,1275.3691027402965L1027.8098904297744,1272.141229067489L1047.1771324666197,1272.141229067489L1049.1138566703044,1276.014677474858L1061.3797766269731,1278.596976413104L1073.6456965836417,1274.0779532711733L1087.848340743995,1274.7235280057348L1105.9244333117174,1276.6602522094195L1130.4562732250547,1267.6222059255583L1156.2792626075152,1256.6474354380125L1173.0642057061145,1252.7739870306434L1189.2035740701524,1248.2549638887128L1213.7354139834897,1230.1788713209905L1235.684954958581,1216.6218018951988L1261.5079443410416,1203.064732469407L1275.7105885013948,1195.9634103892306L1295.0778305382403,1180.4696167597542L1309.2804746985935,1168.2036968030854L1324.7742683280696,1133.9882358713253L1329.2932914700002,1112.038694896234L1320.255245186139,1084.278981310089L1307.9893252294705,1061.0382908658746L1271.1915653594642,1011.9746110391998L1225.355759205597,959.0374828051556L1190.4947235392754,928.0498955462031L1118.8359280029476,880.9229399232128L1069.7722481762728,871.23931890479L1009.0882231274907,864.7835715591749L949.04977281327,853.1632263370677L916.7710360851945,843.4796053186451L880.6188509497498,838.3150074421529L859.9604594437815,839.606156911276L852.8591373636049,839.606156911276L843.8210910797437,834.441559034784L831.555171123075,834.441559034784L826.3905732465829,840.8973063803991L823.8082743083368,844.770754787768L823.8082743083368,857.0366747444368L816.7069522281602,873.1760431084746L810.8967796171066,877.0494915158437L810.251204882545,878.3406409849667Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1171.1274815024299,532.95815799456L1165.3173088913763,525.8568359143833L1161.4438604840072,523.9201117106988L1153.696963669269,521.9833875070143L1144.658917385408,517.4643643650837L1139.4943195089159,515.5276401613992L1133.0385721633006,520.0466633033298L1125.2916753485624,520.6922380378912L1124.646100614001,519.4010885687682L1115.6080543301398,529.7302843217524L1103.9877091080327,531.6670085254369L1103.342134373471,531.0214337908754L1104.6332838425942,529.0847095871909L1098.177536496979,536.831606401929L1080.1014439292567,549.7431010931592L1062.0253513615344,560.7178715807049L1052.3417303431117,567.8191936608815L1050.405006139427,567.8191936608815L1045.240408262935,561.3634463152664L1046.5315577320582,556.8444231733358L1039.4302356518815,565.2368947226355L1021.3541430841592,576.2116652101812L1003.2780505164369,595.5789072470266L988.4298316215221,616.2372987529949L965.1891411773076,634.9589660552788L953.5687959552005,652.3894838884396L940.6573012639702,676.2757490672154L936.7838528566012,686.6049448201996L925.163507634494,707.263336326168L907.7329898013331,731.7951762395054L891.5936214372954,752.4535677454737L874.1631036041346,778.9221318624957L863.8339078511505,809.9097191214482L854.1502868327277,826.6946622200476L855.4414363018508,833.7959843002242L868.352930993081,851.226502133385L880.6188509497497,868.6570199665458L889.0113224990494,875.1127673121609L925.163507634494,889.3154114725141L1005.8603494546829,916.4295503240977L1085.9116165403104,937.7335165646275L1131.7474226941777,945.4804133793657L1202.760643495944,950.6450112558578L1226.6469086747197,949.9994365212963L1246.0141507115652,946.1259881139272L1269.2548411557796,935.796792360943L1294.4322558036786,918.3662745277821L1339.6224872229843,873.1760431084764L1356.4074303215837,845.4163295223314L1364.7999018708833,826.0490874854861L1368.6733502782524,809.9097191214482L1370.6100744819369,786.0234539426724L1358.344154525268,668.5288522524772L1353.179556648776,610.4271261419414L1346.723809303161,589.1231599014114L1326.0654177971926,553.6165495005283L1317.0273715133314,541.9962042784211L1307.9893252294703,536.831606401929L1287.3309337235019,499.38827179736137L1277.0017379705178,489.0590760443772L1257.6344959336723,484.5400529024466L1231.811506551212,481.95775396420055L1205.9885171687515,476.14758135314696L1198.2416203540133,476.14758135314696L1178.2288035826064,483.8944781678851L1169.836332033307,492.2869497171847L1165.3173088913763,500.03384653192285L1175.000929909799,501.3249960010459L1182.1022519899755,498.09712232823836L1187.2668498664675,491.64137498262323L1189.8491488047136,489.0590760443772L1196.304896150329,490.3502255135002L1195.0137466812057,495.5148233899923L1185.9757003973446,501.3249960010459L1184.6845509282216,505.8440191429765L1186.621275131906,507.1351686120995L1197.5960456194518,509.07189281578405L1201.469494026821,515.5276401613992L1198.8871950885748,520.0466633033298L1188.5579993355907,523.2745369761373L1184.0389761936601,521.9833875070143L1180.8111025208525,516.8187896305221L1176.9376541134834,519.4010885687682L1172.4186309715528,525.8568359143833Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1388.6861670496592,782.7955802698631L1400.3065122717665,780.8588560661785L1452.598065771249,776.339832924248L1474.54760674634,775.0486834551249L1490.0414003758165,768.5929361095098L1496.4971477214315,758.2637403565255L1499.0794466596776,738.8964983196803L1499.0794466596776,727.276153097573L1488.104676172132,704.6810373879201L1482.9400782956398,683.3770711473903L1493.269274048624,666.592128048791L1498.4338719251161,658.8452312340529L1496.4971477214315,647.8704607465072L1491.3325498449394,627.2120692405389L1489.395825641255,607.199252469132L1488.104676172132,613.6549998147472L1488.104676172132,600.7435051235169L1481.6489288265168,579.439538882987L1477.1299056845862,568.4647683954413L1459.053813116864,545.224077951227L1450.6613415675642,522.628962241574L1429.3573753270343,487.7679265752524L1419.0281795740502,483.2489034333218L1410.6357080247506,480.02102976051424L1397.0786385989588,465.818385600161L1393.8507649261512,454.1980403780538L1386.103868111413,449.0334425015617L1373.192373420183,452.26131617436926L1334.4578893464923,465.818385600161L1314.4450725750853,465.1728108655995L1314.4450725750853,460.0082129891074L1317.672946247893,454.1980403780538L1317.672946247893,450.3245919706847L1313.7994978405238,449.0334425015617L1303.4703020875397,452.26131617436926L1301.533577883855,442.57769515594657L1267.9636916866566,439.34982148313907L1249.8875991189343,439.34982148313907L1236.976104427704,441.9321204213851L1227.2924834092814,449.67901723612323L1211.798689779805,463.8816613964765L1207.2796666378745,468.40068453840706L1200.1783445576978,484.5400529024448L1195.6593214157672,503.9072949392902L1195.6593214157672,536.1860316673658L1203.4062182305054,580.0851136175485L1227.938058143843,657.55408176493L1253.7610475263034,718.8836815482734L1262.7990938101645,732.4407509740652L1282.1663358470098,748.5801193381029L1346.0782345685993,775.0486834551249L1372.5467986856213,793.7703507574088L1376.4202470929904,795.7070749610915L1424.8383521851038,795.0615002265317L1421.6104785122964,786.0234539426706L1422.9016279814193,779.5677065970555Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1338.3313377538614,1140.4439832169421L1370.6100744819369,1108.1652464888666L1388.0405923150977,1091.3803033902673L1407.407834351943,1077.8232339644755L1417.0914553703656,1066.2028887423683L1440.9777205491416,1022.3038067921855L1449.3701920984413,1002.9365647553402L1457.762663647741,980.3414490456872L1460.9905373205484,955.1640343977883L1465.509560462479,920.3029987314667L1475.1931814809018,895.1255840835678L1489.395825641255,864.1379968246152L1496.4971477214315,838.3150074421548L1503.5984698016082,812.4920180596944L1506.8263434744158,787.3146034117954L1506.8263434744158,750.5168435417892L1498.4338719251161,738.2509235851205L1482.9400782956398,729.2128773012594L1473.2564572772171,726.6305783630133L1440.9777205491416,730.5040267703823L1393.8507649261512,738.2509235851205L1318.3185209824544,761.4916140293349L1289.2676579271865,773.7575339860036L1271.191565359464,784.0867297389879L1246.0141507115652,802.1628223067102L1206.634091903313,841.5428811149624L1191.785873008398,866.0747210282998L1182.747826724537,886.7331125342681L1169.1907572987452,929.9866197498894L1164.0261594222532,954.5184596632268L1161.4438604840072,983.5693227184948L1163.3805846876917,999.7086910825326L1169.1907572987452,1011.9746110392014L1212.4442645143665,1070.7219118842988L1231.811506551212,1099.1272002050055L1240.2039781005114,1111.393120161674L1255.0521969954264,1124.3046148529043L1267.9636916866566,1129.4692127293963L1290.5588073963095,1134.6338106058886L1299.5968536801706,1139.7984084823806L1313.7994978405238,1150.7731789699262L1308.6348999640318,1166.2669725994026Z\"}], \"222\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"circle\", \"x0\": 1260.1322693780608, \"y0\": 1198.9338661514662, \"x1\": 767.2733699806054, \"y1\": 876.95832667052}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M853.122916781472,840.2182225811404L841.0386040847268,835.6146748871422L839.3122736994775,835.6146748871422L828.3788479262317,841.3691095046399L811.6909875354884,852.3025352778856L799.0312313769934,873.0184999008774L789.824135988997,898.9134556796172L782.34337098625,912.7240987616117L765.6555105955066,958.7595757015935L762.202849825008,966.2403407043405L757.5993021310098,981.2018707098346L744.3641025107651,1000.1915049475771L731.1289028905203,1016.3039218765706L730.5534594287705,1027.2373476498162L727.6762421200216,1035.8689995760628L735.7324505845185,1037.5953299613122L1236.3682623068203,1037.5953299613122L1250.1789053888149,1031.2654518820648L1254.782453082813,1027.2373476498162L1270.8948700118067,1001.9178353328264L1277.2247480910542,995.5879572535789L1284.705513093801,992.1352964830802L1299.091599637544,990.4089660978309L1299.091599637544,992.71073994483L1298.5161561757957,697.5082440671969L1291.0353911730485,679.0940532912042L1276.6493046293044,658.9535321299621L1258.8105573150615,641.1147848157192L1228.3120538423234,625.0023678867256L1198.3889938313353,615.2198290369795L1178.8239161318431,612.3426117282306L1155.2307342001025,610.6162813429813L1098.2618314868748,603.710959801984L1036.6893810796494,601.409185954985L999.2855560659141,606.0127336489832L982.5976956751707,615.2198290369795L967.0607222079269,623.8514809632261L952.6746356641826,642.2656717392188L939.4394360439378,659.528975591712L921.0252452679451,690.0274790644498L918.1480279591963,700.3854613759457L912.3935933416985,709.5925567639421L912.9690368034483,714.1961044579402L916.421697573947,722.8277563841868L914.1199237269478,734.3366256191823L906.6391587242008,740.09106023668L898.582950259704,740.6665036984298L884.1968637159597,753.3262598569248L874.4143248662135,769.4386767859185L860.603681784219,789.5791979471604L856.0001340902209,804.5407279526545L853.122916781472,813.1723798789011Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1384.2572319765115,756.7789206274234L1417.0575092962486,736.6383994661813L1452.1595604629847,721.1014259989375L1465.3947600832296,713.0452175344408L1480.9317335504734,701.5363482994453L1488.98794201497,683.1221575234525L1492.4406027854689,675.0659490589558L1493.5914897089683,667.009740594459L1498.7704808647163,663.5570798239603L1506.826689329213,649.7464367419658L1510.2793500997118,643.9920021244681L1512.5811239467107,632.4831328894726L1512.005680484961,613.4934986517302L1505.1003589439638,595.6547513374873L1503.3740285587144,572.6370128674964L1501.6476981734652,564.5808044029995L1493.5914897089683,545.0157267035073L1466.545647006729,505.31012784277306L1450.4332300777355,478.83972860228357L1445.8296823837372,478.2642851405338L1441.801578151489,470.20807667603697L1444.6787954602378,470.20807667603697L1445.8296823837372,463.3027551350397L1442.3770216132386,454.0956597470433L1435.4717000722414,452.9447728235438L1428.566378531244,456.3974335940424L1428.566378531244,450.64299897654473L1429.7172654547437,447.1903382060461L1425.6891612224952,440.8604601267986L1419.9347266049974,436.8323558945502L1399.2187619820056,444.888564359047L1396.3415446732567,440.28501666504883L1392.8888839027582,432.8042516623018L1388.28533620876,431.6533647388022L1349.7306242715254,440.8604601267986L1329.5901031102833,431.6533647388022L1312.9022427195398,426.4743735830543L1305.4214777167929,424.748043197805L1281.2528523233025,413.8146174245593L1268.5930961648075,412.6637305010598L1253.6315661593135,416.69183473330816L1236.94370576857,427.04981704480406L1217.9540715308276,435.1060255093009L1208.1715326810813,444.3131208972972L1196.0872199843361,452.9447728235438L1172.4940380525954,470.20807667603697L1160.4097253558502,482.86783283453195L1153.504403814853,494.95214553127715L1150.6271865061042,500.1311366870251L1148.325412659105,504.15924091927354L1133.363882653611,508.7627886132717L1097.686388025125,525.4506490040151L1089.0547360988785,529.4787532362635L1076.9704234021333,540.987622471259L1057.4053457026412,555.949152476753L1049.3491372381443,563.4299174795001L1034.3876072326502,570.3352390204973L1032.085833385651,567.4580217117484L1029.784059538652,564.0053609412498L1023.4541814594046,564.0053609412498L1019.4260772271562,566.8825782499987L1014.822529533158,574.3633432527457L1003.8891037599122,587.5985428729905L999.8609995276639,595.6547513374873L987.7766868309187,607.7390640342325L977.9941479811725,615.2198290369795L975.6923741341735,616.9461594222288L980.2959218281717,638.8130109687202L992.9556779866666,672.7641752119567L1012.5207556861589,704.9890090699439L1041.2929287736474,736.0629560044316L1075.8195364786338,760.231581397922L1130.486665344862,782.6738764061631L1484.384394320972,783.249319867913L1490.1388289384697,768.2877898624189L1492.4406027854689,751.5999294716755L1492.4406027854689,718.7996521519384L1498.1950374029666,704.9890090699439L1502.7985850969646,695.2064702201978L1502.7985850969646,687.7257052174508L1499.9213677882158,681.9712705999531L1490.7142724002194,675.0659490589558L1474.601855471226,691.1783659879494L1471.724638162477,703.2626786846946L1480.9317335504734,699.2345744524462Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1489.42068682396,669.5112376833831L1499.3462489953572,683.1203083837843L1509.1274574541312,692.9064945567866L1514.3092579658785,706.1422366900589L1518.336228776641,717.650711986082L1520.0634956138902,728.5817724316126L1527.545000099151,723.9773867703577L1526.3901703981658,734.9134249301168L1526.3901703981658,745.8444853756474L1530.4221189231566,751.598723023659L1533.1399508918541,765.038551440295L1528.1174372354149,790.7285345729833L1519.4860807633975,797.0601870714873L1514.8816951021424,802.814424719499L1516.6089619393917,806.2689583939972L1516.0315470888993,810.8683663410238L1518.336228776641,824.6815233247886L1513.6870436873305,835.6125837703192L1506.827753480618,847.6984739168349L1504.5230717928762,851.1530075913332L1497.618982158108,889.7054042901651L1495.8917153208588,907.6849080830871L1493.014596496853,919.0540073807155L1483.8108028885715,956.4565520927906L1479.2064172273167,966.242738265793L1481.50612120083,963.9380565780514L1478.629002376824,977.9204558455813L1470.5750607552993,996.1637784926075L1453.3123478112645,1027.2396484204069L1438.9267536912355,1051.40645099921L1427.9907155314768,1069.2465787937372L1407.2734689129436,1086.509291737772L1399.2195272914187,1092.8359665220476L1383.6840811846332,1095.7130853460535L1218.5285008010899,1096.8679150470384L1212.7742631530784,1094.5632333592966L1206.4475883688026,1080.7550540897603L1201.8432027075478,1066.3694599697315L1196.0889650595361,1054.2835698232159L1188.6074605742754,1024.3625295964014L1189.757312561032,992.7092448181091L1195.5115502090437,959.3336709167965L1211.0469963158293,920.2038593674721L1247.8771038916404,802.2370098690064L1255.358608376901,778.0702072902036L1277.7981441184547,737.7905437541225L1296.7881238997388,720.5278308100878L1322.687171030019,699.8105841915549L1367.571220227355,661.8306246289872L1348.0038255955787,675.6437816127519L1323.2596081662832,688.3021088955317Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1117.2514657246172,1289.639566207712L1128.7603349596127,1289.0641227459623L1135.0902130388602,1285.6114619754637L1148.9008561208548,1284.460575051964L1155.806177661852,1282.7342446667149L1164.4378295880986,1282.7342446667149L1181.7011334405918,1279.2815838962163L1193.2100026755872,1271.800818893469L1207.5960892193314,1264.3200538907222L1219.6804019160766,1256.2638454262253L1238.6700361538192,1244.1795327294801L1251.9052357740638,1240.1514284972318L1265.1404353943087,1229.7934461857358L1272.6212003970556,1221.1617942594892L1276.0738611675542,1214.8319161802417L1294.488051943547,1202.7476034834965L1300.2424865610446,1193.5405080955002L1305.4214777167927,1190.6632907867513L1318.0812338752876,1186.635186554503L1324.4111119545353,1173.399986934258L1330.7409900337827,1165.3437784697612L1341.0989723452785,1158.438456928764L1348.5797373480257,1157.2875700052646L1356.0605023507726,1151.5331353877668L1368.7202585092675,1135.996161920523L1371.0220323562667,1127.3645099942764L1371.5974758180164,1121.6100753767787L1385.408118900011,1122.1855188385284L1390.587110055759,1115.2801972975312L1393.4643273645079,1108.9503192182838L1399.7942054437553,1102.0449976772863L1407.2749704465023,1091.6870153657906L1419.3592831432475,1080.7535895925448L1419.9347266049974,1078.4518157455457L1411.8785181405005,1070.9710507427988L1383.6817885147616,1058.8867380460536L1371.0220323562667,1054.858633813805L1342.249859268778,1050.255086119807L1241.547253462568,1046.8024253493084L1194.3608895990867,1049.1041991963075L1127.6094480361132,1058.3112945843038L1080.423084172632,1068.0938334340499L1039.566598388398,1081.3290330542945L1010.2189818391597,1097.4414499832883L975.6923741341733,1120.459188453279L961.3062875904291,1138.297935767522L953.2500791259322,1157.2875700052646L946.344757584935,1185.4842996310033L931.9586710411907,1213.1055857949923L926.7796798854428,1232.6706634944846L924.4779060384436,1260.8673931202234L929.0814537324418,1270.6499319699697L934.2604448881898,1277.5552535109668L950.3728618171833,1289.639566207712L973.966043748924,1294.81855736346L1045.3210330058957,1294.81855736346L1062.584336858389,1294.81855736346L1091.3565099458774,1293.0922269782106L1099.4127184103743,1291.9413400547112L1115.525135339368,1289.639566207712Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M724.7990248112728,1036.4444430378123L722.4972509642738,1041.0479907318106L721.921807502524,1054.8586338138052L726.5253551965221,1066.9429465105504L732.2797898140199,1070.9710507427988L735.7324505845186,1079.0272592072956L732.2797898140199,1089.9606849805411L733.4306767375194,1103.7713280625358L734.0061201992692,1114.7047538357815L752.9957544370117,1126.2136230707767L754.1466413605112,1129.6662838412756L748.9676502047632,1141.7505965380208L748.9676502047632,1152.6840223112663L757.0238586692601,1172.8245434725084L765.6555105955067,1185.4842996310033L777.1643798305021,1193.5405080955002L788.0978056037478,1196.9931688659988L802.483892147492,1212.5301423332428L826.6525175409826,1230.9443331092355L836.4350563907286,1237.274211188483L846.2175952404748,1243.6040892677304L858.30190793722,1249.3585238852281L869.8107771722155,1259.716506196724L876.1406552514629,1266.0463842759714L905.4882718007012,1281.5833577432154L922.7515756531944,1284.4605750519643L944.6184271996858,1286.7623488989632L967.6361656696766,1295.3940008252098L987.7766868309187,1297.695774672209L992.3802345249169,1295.9694442869597L1014.2470860714081,1271.2253754317194L1036.1139376178996,1240.1514284972318L1045.8964764676457,1211.9546988714928L1045.8964764676457,1194.11595155725L1038.4157114648985,1176.277204243007L1026.906842229903,1160.1647873140134L1000.4364429894136,1136.5716053822728L945.1938706614355,1098.016893445038L917.5725844974465,1084.2062503630434L888.8004114099579,1074.4237115132973L852.5474733197223,1059.4621815078033L823.7753002322337,1047.9533122728078L801.9084486857423,1036.4444430378123L778.8907102157514,1013.4267045678215L765.0800671337569,1007.6722699503238L758.1747455927597,1008.8231568738233L742.6377721255158,1015.1530349530708L729.9780159670208,1018.0302522618197L727.100798658272,1021.4829130323184L728.2516855817715,1026.6619041880663L727.100798658272,1027.237347649816Z\"}], \"493\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ffa200\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ffa200\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M694.6883358827529,938.4637951882811L683.6745783997985,947.6419264240765L670.8251946696851,953.1488051655537L657.9758109395715,975.1763201314625L646.9620534566171,1004.5463400860076L626.7701647378673,1026.5738550519166L604.7426497719584,1149.560813611574L597.4001447833222,1215.6433585093007L612.0851547605947,1279.890277159868L623.0989122435492,1311.0959233615722L648.7976797037762,1369.8359632706624L683.6745783997985,1390.0278519894123L742.4146183088887,1424.9047506854347L780.9627694992292,1457.9460231342978L832.3603044196832,1489.151669336002L949.8403842378636,1516.686063043388L1014.087302888431,1522.1929417848653L1063.649211561726,1524.0285680320244L1104.0329889992256,1507.5079318075927L1138.9098876952478,1474.4666593587294L1238.0337050418377,1395.5347307308896L1318.8012599168367,1309.260297114413L1337.1575223884274,1283.5615296541862L1395.8975622975177,1235.8352472280503L1447.2950972179717,1162.4101973416875L1484.007622161153,1120.190793657029L1555.5970458003567,991.6969563558941L1586.802692002061,923.7787852110085L1599.6520757321744,874.2168765377137L1608.8302069679698,820.9837153701006L1634.5289744281968,751.229917978056L1647.3783581583102,633.7498381598756L1640.035853169674,452.0228396911277L1630.8577219338786,422.65281973658256L1540.9120358230841,318.02212364851556L1524.3913995986525,288.65210369397045L1460.1444809480852,226.24081129056208L1428.938834746381,217.06268005476673L1373.870047331609,220.73393254908487L1313.2943811753596,239.09019502067557L1285.7599874679734,248.2683262564709L1201.3211800986562,299.66586117692486L1148.0880189310433,321.6933761428337L1070.9917165503623,369.4196585689695L995.7310404168404,446.51596094965043L891.1003443287734,552.9822832848765L826.853425678206,639.2567169013528L771.7846382634339,738.3805342479426L747.9214970503659,789.7780691683965L736.9077395674116,837.5043515945323L700.1952146242302,938.4637951882811Z\"}]}, \"color\": \"#ffa200\", \"label\": \"Clay\", \"is_visible\": true, \"class_id\": 3}, {\"annotations\": {\"10\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1006.0602758157335,912.5509980954744L1006.0602758157335,916.0260139673273L1005.6514504190449,919.909855235869L1005.6514504190449,922.7716330126892L1004.2205615306348,924.2025219010993L1003.4029107372576,925.0201726944765L1002.3808472455361,931.1525536448054L1000.7455456587817,933.8099187232813L999.9278948654045,936.0584584050686L997.0661170885844,931.1525536448054L991.5469742332883,930.1304901530839L991.1381488365997,927.2687123762638L993.1822758200427,926.4510615828866L998.7014186753388,925.8378234878537L1002.1764345471918,920.3186806325576L1004.4249742289791,917.4569028557374Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M991.7513869316326,914.1862996822288L993.5911012167313,915.6171885706387L997.2705297869287,916.4348393640159L1000.5411329604374,911.5289346037529L1003.1984980389133,907.0318552401783L1004.4249742289791,902.5347758766037L1003.6073234356019,901.7171250832265L1000.3367202620931,904.783315558391L996.6572916918958,906.4186171451454L995.0219901051414,904.5789028600467L993.9999266134199,902.3303631782594L992.1602123283212,904.783315558391Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M989.7957962805144,995.3554594630775L991.061968047013,1000.420146529072L993.5943115800103,1005.4848335950664L997.3928268795061,1010.5495206610609L1002.0354566900011,1012.6598069385586L1003.7236857119992,1013.0818641940582L1004.1457429674987,1008.8612916390628L999.9251704125033,1005.4848335950664Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M983.9104095073592,869.8143206160954L982.6015643729381,871.3413066062533L983.4741277958855,872.8682925964112L986.0918180647276,874.1771377308322L988.0550857663591,873.7408560193585L987.8369449106223,871.1231657505165L986.3099589204644,869.5961797603586L985.2192546417803,868.9417571931481Z\"}], \"201\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1392.4161115006975,544.9795270673765L1391.7958501135472,562.967107294734L1394.8971570492986,574.752073650589L1394.2768956621483,578.4736419734904L1388.694543177796,586.5370400064438L1383.1121906934436,614.4488024282056L1395.517418436449,625.6135073969102L1400.4795095336508,632.4363826555631L1407.3023847923037,632.4363826555631L1406.6821234051536,621.2716776868584L1408.5429075666043,615.0690638153558L1417.226566986708,615.0690638153558L1413.5049986638064,592.7396538779465L1422.18865808391,566.6886756176356L1426.5304877939618,559.8658003589827L1422.80891947106,561.1063231332832L1415.3657828252572,551.8024023260293L1409.7834303409047,554.9037092617806Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1344.6559846901275,472.4089447707961L1337.2128480443243,475.51025170654736L1340.3141549800757,480.4723428037495L1351.4788599487804,485.43443390095155L1356.4409510459825,490.3965249981536L1358.3017352074332,494.7383547082054L1359.5422579817337,481.0926041908997L1356.4409510459825,477.37103586799816Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M995.6348353762899,732.9631417742826L997.0261943871968,734.9869366992382L996.7732200215773,735.998834161716L993.1050917200953,738.1491162694814L991.840219891998,740.1729111944371L990.9548096123299,742.5761676678219L990.1958865154714,747.7621421630208L989.8164249670423,753.9600141206975L990.4488608810909,757.3751680565601L991.7137327091882,761.0432963580423L993.3580660857147,761.0432963580423L995.2553738278606,759.5254501643255L997.405655935626,755.098398765985L997.405655935626,752.1891935613613L996.014296924719,749.4064755395473L994.4964507310023,747.5091677974013L993.1050917200953,743.46157794749L993.1050917200953,741.6907573881538L993.7375276341439,740.4258855600565L996.1407841075287,739.5404752803885L997.0261943871968,738.9080393663398L998.7970149465331,738.6550650007204L999.3029636777719,735.4928854304771Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1003.0975791620638,710.5749104169606L1001.3267586027276,713.6106028043941L1002.3386560652054,715.8873720949692L1004.6154253557805,718.290628568354L1006.7657074635459,720.0614491276903L1008.7895023885015,721.0733465901681L1010.1808613994086,726.1328339025572L1010.0543742165988,728.1566288275129L1009.54842548536,728.7890647415616L1009.4219383025502,734.2280136023799L1007.5246305604043,738.6550650007204L1004.6154253557805,742.5761676678219L1002.2121688823956,745.9913216036846L1000.4413483230595,747.8886293458305L999.3029636777719,751.4302704645029L998.9235021293428,757.2486808737505L999.0499893121524,759.778424529945L998.6705277637233,762.3081681861396L998.9235021293428,762.4346553689493L997.2791687528163,762.8141169173784L990.9548096123299,764.5849374767147L987.160194128038,767.2411683157189L984.377476106224,768.0000914125774L982.6066555468877,769.0119888750552L980.5828606219321,771.5417325312497L979.4444759766445,772.5536299937276L982.3536811812684,771.5417325312497L985.2628863858921,771.1622709828206L987.2866813108477,771.2887581656303L988.6780403217548,769.6444247891038L990.1958865154714,766.8617067672898L991.7137327091882,766.6087324016703L992.3461686232368,766.7352195844801L993.6110404513341,768.8855016922454L995.1288866450509,769.6444247891038L996.6467328387677,770.023886337533L998.4175533981038,769.0119888750552L1000.694322688679,766.1027836704314L1002.8446047964443,763.4465528314271L1005.1213740870195,760.9168091752325L1005.1213740870195,759.5254501643255L1004.1094766245416,756.7427321425115L1004.4889381729708,754.7189372175559L1007.1451690119751,751.5567576473127L1007.6511177432141,749.532962722357L1007.2716561947848,746.1178087864943L1006.7657074635459,742.5761676678219L1006.7657074635459,741.0583214741051L1009.9278870337891,739.7934496460078L1011.5722204103156,737.6431675382426L1012.5841178727934,733.3426033227117L1012.3311435071739,727.9036544618934L1011.4457332275059,723.0971415151238L1011.4457332275059,722.4647056010751L1013.8489897008907,719.3025260308319L1012.5841178727934,718.5436029339735L1009.6749126681697,719.1760388480221L1005.6273228182583,717.7846798371152L1004.4889381729708,717.1522439230665L1004.4889381729708,716.2668336433984L1004.6154253557805,716.1403464605887Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1195.8067732866414,1138.968797414106L1198.3229924937527,1139.5279572379084L1200.2800518770616,1142.7431262247728L1200.2800518770616,1144.4206056961805L1195.8067732866414,1145.3991353878348L1193.709923947382,1145.3991353878348L1192.0324444759744,1144.4206056961805L1191.6130746081226,1142.323756356921L1192.591604299777,1141.3452266652664L1194.5486636830858,1140.9258567974146L1195.247613462839,1140.2269070176615L1195.3874034187895,1139.9473271057602Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1029.9777529864773,1204.8894972198789L1026.8721044301612,1204.8894972198789L1026.412008347744,1204.4294011374616L1025.261768141701,1202.24394474598L1024.456599997471,1204.199353096253L1024.3415759768666,1207.995145776195L1023.9965039150536,1209.2604100028423L1026.066936285931,1211.1007943325112L1027.3322005125783,1212.2510345385542L1028.5974647392256,1213.6313227858059L1029.1725848422473,1210.295626188281L1030.3228250482903,1209.7205060852596L1032.163209377959,1209.2604100028423L1032.163209377959,1207.4200256731735L1031.1279931925203,1206.2697854671305Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1031.4730652543333,1194.1922633036786L1030.8979451513117,1194.8824074273045L1028.0223446362043,1196.8378157775776L1027.9073206156,1197.9880559836206L1029.862728965873,1200.2885363957066L1032.0481853573547,1201.0937045399369L1033.7735456664193,1201.2087285605412L1035.8439780372967,1199.8284403132895L1036.4190981403183,1198.7932241278509L1036.5341221609226,1197.2979118599949L1035.0388098930666,1196.2626956745562L1032.7383294809806,1195.6875755715346L1032.3932574191676,1195.3425035097216L1032.3932574191676,1195.2274794891173Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1064.9450552501853,1203.8542810344402L1061.7243826732647,1205.2345692816916L1059.883998343596,1205.9247134053176L1059.9990223642003,1206.7298815495476L1060.9192145290347,1208.2251938174036L1061.379310611452,1209.7205060852596L1062.4145267968906,1211.2158183531155L1063.1046709205164,1208.340217838008L1064.2549111265594,1207.7650977349863L1065.175103291394,1207.995145776195L1066.0952954562283,1208.340217838008L1068.0507038065014,1207.3050016525692L1068.8558719507316,1206.0397374259219L1068.8558719507316,1204.3143771168573L1067.820655765293,1203.6242329932315L1065.5201753532067,1203.1641369108142Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1432.8131356009405,941.8083892988257L1431.5827408423324,943.3227213094204L1430.3523460837241,945.4049278239881L1429.689825829089,945.4995735746503L1429.216597075778,947.0139055852451L1429.3112428264403,948.9068205984885L1430.5416375850484,953.2605251289483L1431.2041578396836,954.3016283862321L1432.7184898502783,952.7872963756374L1434.138176110211,949.6639866037858L1436.2203826247787,947.1085513359072L1436.5043198767653,945.7835108266369L1436.1257368741165,943.3227213094204L1435.273925118157,942.2816180521365L1433.9488846088866,940.9565775428662Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M849.6817320673572,876.5186234022045L846.6474590521442,878.2753077794332L845.8489661534039,879.6327457072916L846.0086647331519,881.4692793743942L847.0467055015143,882.5073201427566L849.6817320673572,883.3058130414969L850.6399235458455,883.3058130414969L851.6779643142079,882.4274708528826L851.5981150243339,881.7886765338903L849.202636328113,881.1498822148982L847.3661026610104,880.910334345276L847.3661026610104,880.3513893161579L848.3242941394988,879.3931978376695L849.282485617987,878.6745542288033L850.4003756762235,877.7962120401891Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1187.9243109914275,526.7573947128348L1186.6168096068016,527.4765204743792L1185.440058360638,528.52252158208L1184.1979320452433,530.0261481744L1184.328682183706,530.9413991436383L1185.2439331529442,532.3142755974957L1186.289934260645,532.3142755974957L1186.9436849529582,531.9220251821079L1188.0550611298902,530.2876484513253L1188.9703120991285,529.6992728282435L1190.2778134837545,529.3070224128556L1190.6700638991424,528.9147719974678L1190.6700638991424,527.9341459589983L1189.8855630683668,526.8227697820661L1189.1664373068224,526.8227697820661Z\"}], \"222\": [{\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M960.5001077459817,777.49910962684L956.1842661196371,772.4639610627712L955.1053057130509,766.7095055609784L956.5439195884991,759.8760896525994L958.3421869328095,756.9988619017029L959.0614938705335,754.1216341508065L960.1404542771197,751.9637133376341L964.0966424346022,732.9020794879452L968.0528305920849,727.5072774550144L978.1231277202224,719.5949011400493L983.1582762842911,708.8052970741876L986.0355040351876,706.2877227921532L993.9478803501528,705.5684158544291L996.8251081010492,707.7263366676015L999.7023358519457,722.8317823598077L995.746147694463,726.7879705172903L989.6320387238081,732.9020794879452L989.9916921926701,736.8582676454279L992.1496130058425,739.7354953963243L996.4654546321872,742.6127231472208L1001.860256665118,752.6830202753582L1001.500603196256,755.5602480262547L993.9478803501528,763.4726243412199L986.7548109729117,767.7884659675645L980.6407020022568,769.9463867807368L974.1669395627398,772.1043075939092L971.2897118118434,773.5429214693575Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M958.1647764286322,1216.5947725588926L954.2614673247174,1221.1143936265833L951.1799075058373,1222.552454875394L951.1799075058373,1225.6340146942741L952.001656790872,1227.0720759430849L952.4125314333893,1230.5645104044822L950.3581582208026,1233.851507544621L950.3581582208026,1238.5765659335705L950.9744701845786,1244.534248250072L953.0288433971654,1248.0266827114694L955.9049658947868,1250.4919305665735L957.9593391073735,1251.7245544941256L960.0137123199603,1253.7789277067122L960.4245869624776,1253.7789277067122L962.4789601750643,1251.7245544941256L966.1768319577204,1251.7245544941256L969.0529544553418,1254.8061143130058L969.2583917766005,1255.216988955523L973.367138201774,1252.5463037791603L975.6269487356194,1252.5463037791603L977.0650099844302,1248.6429946752455L979.7356951607928,1246.7940587839175L980.3520071245689,1244.7396855713307L983.6390042647076,1244.534248250072L984.2553162284837,1243.5070616437788L983.6390042647076,1239.603752539864L983.6390042647076,1238.1656912910532L984.8716281922597,1236.7276300422425L984.666190871001,1235.4950061146903L980.1465698033102,1233.0297582595863L979.5302578395342,1230.5645104044822L974.8051994505847,1229.7427611194476L971.9290769529633,1228.0992625493782L970.4910157041526,1225.2231400517567L970.9018903466699,1224.401390766722L972.5453889167393,1224.8122654092394Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1046.5028245698616,1211.4588395274257L1048.146323139931,1213.5132127400125L1049.378947067483,1213.3077754187536L1051.8441949225871,1211.869714169943L1054.9257547414672,1211.253402206167L1055.9529413477605,1211.253402206167L1056.5692533115366,1212.8969007762364L1054.1040054564326,1215.773023273858L1053.4876934926565,1217.2110845226684L1048.762635103707,1220.2926443415486L1046.5028245698616,1221.1143936265833L1044.448451357275,1221.1143936265833L1044.2430140360161,1221.1143936265833L1044.2430140360161,1218.6491457714792L1044.6538886785336,1216.5947725588926L1044.2430140360161,1215.567585952599Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1418.420679912958,458.2615289461498L1416.0702217321082,460.0896630868105L1414.7644116316362,464.7905794485096L1415.286735671825,472.364278031247L1419.204165973241,474.4535741920022L1422.5992722344681,474.1924121719078L1424.1662443550345,472.6254400513414L1422.3381102143737,464.00709338822645L1417.6371938526747,458.2615289461498L1418.1595178928635,457.739204905961Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1364.882465793607,518.5899555879547L1362.793169632852,518.8511176080491L1363.3154936730407,522.2462238692762L1366.188275894079,524.074358009937L1368.799896095023,525.3801681104089L1372.9784884165333,525.3801681104089L1373.7619744768165,527.2083022510697L1375.0677845772884,527.7306262912584L1376.3735946777604,525.9024921505977L1375.3289465973828,523.5520339697482Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1340.0720738846396,460.61198712699934L1341.6390460052062,456.9557188456778L1345.0341522664332,457.21688086577217L1348.168096507566,458.2615289461498L1349.4739066080379,455.38874672511145L1351.3020407486986,455.64990874520583L1353.9136609496427,458.52269096624417L1354.174822969737,460.0896630868105L1350.257392668321,459.5673390466217L1347.9069344874715,458.52269096624417L1343.2060181257725,459.8285010667161L1342.6836940855837,461.9177972274713Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1385.3149323979258,597.6278773779507L1384.8178512590634,601.2731390629418L1387.1375632404213,603.9242384702081L1388.4631129440545,604.7527070349788L1390.28574378655,605.7468693127037L1391.2799060642749,606.4096441645203L1393.1025369067704,605.7468693127037L1393.4339243326788,604.4213196090706L1392.9368431938165,603.4271573313457L1391.1142123513207,602.4329950536209L1390.7828249254126,601.1074453499876L1391.1142123513207,600.6103642111252L1386.9718695274673,602.2673013406667L1387.1375632404213,602.1016076277125Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1075.7358727221813,534.4047134612556L1074.1915116493458,530.8012042913061L1073.8483202998268,528.2272691699137L1075.2210856979027,527.5408864708756L1076.4222554212192,525.4817383737617L1076.9370424454978,523.7657816261667L1080.5405516154472,527.3692907961162L1081.913317013523,527.3692907961162L1086.5464002320296,523.7657816261667L1087.2327829310675,522.3930162280908L1089.120335353422,527.1976951213567L1092.895440198131,531.8307783398632L1092.3806531738526,534.9195004855341L1083.9724651106371,541.6117318011545L1082.5996997125612,543.499284223509L1081.226934314485,547.6175804177369L1079.5109775668902,549.6767285148509L1068.3572587075228,556.3689598304713L1062.0082187414214,562.7179997965727L1054.629604726763,568.8954440879146L1052.0556696053704,571.4693792093071L1043.9906728916742,574.558101354978L1040.5587593964842,575.7592710782945L1033.0085497070663,574.558101354978L1030.9494016099522,570.7829965102691L1030.434614585674,567.5226786898387L1027.5174881147623,565.6351262674842L1024.4287659690915,561.0020430489777L1025.1151486681295,559.2860863013827L1028.0322751390408,556.0257684809523L1031.4641886342308,559.1144906266233L1033.8665280808639,559.6292776509017L1035.5824848284587,556.3689598304713L1038.1564199498512,552.0790679614838L1040.9019507460032,549.1619414905724L1040.9019507460032,548.3039631167749L1041.2451420955222,552.5938549857624L1042.617907493598,557.0553425295093L1043.6474815421552,557.5701295537878L1046.049820988788,556.3689598304713L1047.9373734111425,553.2802376848003L1050.3397128577756,556.3689598304713L1050.8544998820541,556.1973641557119L1052.570456629649,554.1382160585978L1053.7716263529655,553.9666203838383L1056.5171571491176,555.8541728061928L1058.2331138967124,554.9961944323953L1060.9786446928645,554.3098117333574L1063.209388464738,551.5642809372054L1064.2389625132948,550.0199198643699L1066.9844933094469,546.7596020439394L1067.8424716832444,543.842475573028L1070.2448111298772,541.096944776876Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1220.6318736184646,455.66103237810995L1214.7399467645296,456.0538275017056L1212.9723687083492,454.6790445691208L1209.8300077195838,457.4286104342904L1207.2768394162122,458.80339336687524L1207.0804418544142,461.5529592320448L1209.4372125959883,468.03407877137323L1210.615597966775,469.21246414216023L1212.7759711465512,469.21246414216023L1215.1327418881253,468.03407877137323L1219.0606931240818,463.71333241182094L1220.4354760566666,461.356561670247L1222.399451674645,461.5529592320448L1228.094980966782,463.5169348500231L1233.5941126971213,463.71333241182094L1243.6103883488106,451.53668358035554L1244.7887737195977,448.0015274679946L1244.1995810342041,445.4483591646228L1241.84281029263,443.28798598484667L1232.4157273263343,450.1619006477707L1222.0066565510492,455.66103237810995L1218.0787053150927,456.2502250635034L1216.5075248207102,455.8574299399078L1216.3111272589122,455.66103237810995Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M1456.5668553451058,523.1718273563406L1454.4020400151999,522.5057303317542L1452.5702731975873,520.6739635141416L1452.0707004291476,520.1743907457019L1450.7385063799748,522.5057303317542L1451.904176173001,524.67054566166L1453.5694187344668,526.6688367354192L1455.0681370397863,529.4997490899113L1454.5685642713465,533.4963312374297L1455.234661295933,535.3280980550422L1456.2338068328124,536.1607193357752L1461.396058773357,537.492913384948L1466.5583107139014,537.9924861533877L1469.222698812247,538.325534665681L1470.2218443491267,536.6602921042149L1470.5548928614198,534.3289525181626L1470.5548928614198,530.831943139084L1471.0544656298596,528.6671278091783L1470.5548928614198,527.5014580161521L1470.5548928614198,525.5031669423929L1469.3892230683937,528.1675550407385L1467.2244077384878,530.3323703706443L1465.7256894331686,528.5006035530317L1464.8930681524355,525.8362154546861L1464.5600196401424,523.6714001247803L1462.5617285663832,522.6722545879009L1461.7291072856501,522.5057303317542Z\"}, {\"editable\": true, \"visible\": true, \"showlegend\": false, \"legend\": \"legend\", \"legendgroup\": \"\", \"legendgrouptitle\": {\"text\": \"\"}, \"legendrank\": 1000, \"label\": {\"text\": \"\", \"texttemplate\": \"\"}, \"xref\": \"x\", \"yref\": \"y\", \"layer\": \"above\", \"opacity\": 1, \"line\": {\"color\": \"#ff1472\", \"width\": 4, \"dash\": \"solid\"}, \"fillcolor\": \"#ff1472\", \"fillrule\": \"evenodd\", \"type\": \"path\", \"path\": \"M747.8301406271211,1123.8707690300691L747.3165454600147,1125.796750906718L745.005367208036,1129.7771134517925L743.9781768738233,1132.6018868708775L743.2077841231637,1134.1426723721966L742.4373913725042,1138.765028876154L741.2818022465148,1139.9206180021433L736.787844534334,1140.5626119610263L733.3210771563661,1137.9946361254945L732.679083197483,1137.4810409583881L731.6518928632703,1138.2514337090477L732.4222856139298,1140.305814377473L734.3482674905788,1143.0021890047815L738.2002312438766,1145.5701648403135L742.822587747834,1146.8541527580794L744.4917720409296,1147.8813430922921L748.0869382106742,1147.7529443005155L749.6277237119933,1146.4689563827496L751.0401104215359,1144.1577781307708L752.4524971310784,1139.5354216268136L754.8920741748336,1137.4810409583881L756.4328596761528,1134.7846663310795L757.7168475939187,1128.4931255340266L757.9736451774719,1126.8239412409307L759.5144306787911,1125.6683521149414L761.6972101389931,1124.3843641971755L762.5960016814292,1121.5595907780905L762.2108053060995,1117.8360258165692L761.3120137636633,1116.29524031525L760.9268173883336,1116.29524031525L759.6428294705677,1117.8360258165692L758.6156391363548,1118.3496209836758L753.9932826323975,1118.9916149425587L750.0129200873231,1120.789198027431L749.6277237119933,1121.5595907780905Z\"}]}, \"color\": \"#ff1472\", \"label\": \"Clay Anomalies\", \"is_visible\": true, \"class_id\": 4}]"} From 2525f4aa5e8dad29f48a7ecd10917c20574128f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 10:52:53 -0800 Subject: [PATCH 06/24] Assemble parameters for training job Includes some better error handling for mask export and parameter retrieval --- .dockerignore | 1 + callbacks/segmentation.py | 62 ++++++++++++++++++++++++++++++--------- constants.py | 1 + utils/data_utils.py | 30 +++++++++++++++---- 4 files changed, 75 insertions(+), 19 deletions(-) diff --git a/.dockerignore b/.dockerignore index 1fafbd5..b9a8ec0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ .env .git .gitignore +*-env/ diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index 7aad256..7565362 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -7,7 +7,12 @@ from dash import ALL, Input, Output, State, callback, no_update from constants import ANNOT_ICONS -from utils.data_utils import extract_parameters_from_html, tiled_masks +from utils.data_utils import ( + assemble_io_parameters_from_uris, + extract_parameters_from_html, + tiled_datasets, + tiled_masks, +) from utils.plot_utils import generate_notification from utils.prefect import ( get_flow_run_name, @@ -76,7 +81,12 @@ prevent_initial_call=True, ) def run_train( - n_clicks, global_store, all_annotations, project_name, model_parameters, job_name + n_clicks, + global_store, + all_annotations, + project_name, + model_parameter_container, + job_name, ): """ This callback collects parameters from the UI and submits a training job to Prefect. @@ -85,22 +95,46 @@ def run_train( # TODO: Appropriately paramaterize the job json depending on user inputs and relevant file paths """ - input_params = {} if n_clicks: - input_params = extract_parameters_from_html(model_parameters) - # return the input values in dictionary and save to the model parameter store - print(f"input_param:\n{input_params}") - if MODE == "dev": - mask_uri = tiled_masks.save_annotations_data( - global_store, all_annotations, project_name + model_parameters, parameter_errors = extract_parameters_from_html( + model_parameter_container + ) + # Check if the model parameters are valid + if parameter_errors: + notification = generate_notification( + "Model Parameters", + "red", + ANNOT_ICONS["parameters"], + "Model parameters are not valid!", + ) + return notification, no_update + mask_uri, mask_error_message = tiled_masks.save_annotations_data( + global_store, all_annotations, project_name + ) + if mask_uri is None: + notification = generate_notification( + "Mask Export", "red", ANNOT_ICONS["export"], mask_error_message ) + return notification, model_parameters + + # Set io_parameters for both training and partial inference + # Uid retrieve is set to None because the partial inference job will be + # populated with the the uid_save of the training job + # This is handled in the Prefect worker + data_uri = tiled_datasets.get_data_uri_by_name(project_name) + io_parameters = assemble_io_parameters_from_uris(data_uri, mask_uri) + io_parameters["uid_retrieve"] = None + + TRAIN_PARAMS_EXAMPLE[0]["params"]["io_parameters"] = io_parameters + TRAIN_PARAMS_EXAMPLE[1]["params"]["io_parameters"] = io_parameters + TRAIN_PARAMS_EXAMPLE[0]["params"]["model_parameters"] = model_parameters + TRAIN_PARAMS_EXAMPLE[1]["params"]["model_parameters"] = model_parameters + + if MODE == "dev": job_uid = str(uuid.uuid4()) - job_message = f"Workflow has been succesfully submitted with uid: {job_uid} and mask uri: {mask_uri}" + job_message = f"Dev Mode: Job has been succesfully submitted with uid: {job_uid} and mask uri: {mask_uri}" notification_color = "indigo" else: - mask_uri = tiled_masks.save_annotations_data( - global_store, all_annotations, project_name - ) try: # Schedule job current_time = datetime.now(pytz.timezone(TIMEZONE)).strftime( @@ -125,7 +159,7 @@ def run_train( "Job Submission", notification_color, ANNOT_ICONS["submit"], job_message ) - return notification, input_params + return notification, model_parameters return no_update, no_update diff --git a/constants.py b/constants.py index f7b441f..1a97187 100644 --- a/constants.py +++ b/constants.py @@ -30,6 +30,7 @@ "no-more-slices": "pajamas:warning-solid", "export": "entypo:export", "submit": "formkit:submit", + "parameter-valid": "fluent-mdl2:machine-learning", } ANNOT_NOTIFICATION_MSGS = { diff --git a/utils/data_utils.py b/utils/data_utils.py index 3b5d198..04118ce 100644 --- a/utils/data_utils.py +++ b/utils/data_utils.py @@ -187,7 +187,7 @@ def save_annotations_data(self, global_store, all_annotations, project_name): else None ) if data_shape is None: - return "Image shape could not be determined." + return None, "Image shape could not be determined." image_shape = (data_shape[1], data_shape[2]) annotations = Annotations(all_annotations, image_shape) @@ -215,7 +215,7 @@ def save_annotations_data(self, global_store, all_annotations, project_name): try: mask = np.stack(mask) except ValueError: - return "No annotations to process." + return None, "No annotations to process." # Store the mask in the Tiled server under /username/project_name/uuid/mask" container_keys = [USER_NAME, project_name] @@ -235,7 +235,7 @@ def save_annotations_data(self, global_store, all_annotations, project_name): mask = last_container.write_array(key="mask", array=mask) else: last_container = last_container[annotations_hash] - return last_container.uri + return last_container.uri, "Annotations saved successfully." tiled_masks = TiledMaskHandler( @@ -271,8 +271,10 @@ def __getitem__(self, key): def extract_parameters_from_html(model_parameters_html): """ - Extracts parameters from the children component of a + Extracts parameters from the children component of a ParameterItems component, + if there are any errors in the input, it will return an error status """ + errors = False input_params = {} for param in model_parameters_html["props"]["children"]: # param["props"]["children"][0] is the label @@ -285,5 +287,23 @@ def extract_parameters_from_html(model_parameters_html): value = parameter_item["value"] elif "checked" in parameter_item: value = parameter_item["checked"] + if "error" in parameter_item: + if parameter_item["error"] is not False: + errors = True input_params[key] = value - return input_params + return input_params, errors + + +def assemble_io_parameters_from_uris(data_uri, mask_uri): + """ + Assembles input and output Tiled information for the model + """ + io_parameters = { + "data_tiled_uri": data_uri, + "data_tiled_api_key": DATA_TILED_API_KEY, + "mask_tiled_uri": mask_uri, + "mask_tiled_api_key": MASK_TILED_API_KEY, + "seg_tiled_uri": SEG_TILED_URI, + "seg_tiled_api_key": SEG_TILED_API_KEY, + } + return io_parameters From d92b046acc4c72f94767eecc3e78a83088045918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 11:27:06 -0800 Subject: [PATCH 07/24] :bug: Fix icon typo and parameter assembly --- callbacks/segmentation.py | 18 +++++++++++++----- constants.py | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index 7565362..0fbb5a4 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -123,12 +123,20 @@ def run_train( # This is handled in the Prefect worker data_uri = tiled_datasets.get_data_uri_by_name(project_name) io_parameters = assemble_io_parameters_from_uris(data_uri, mask_uri) - io_parameters["uid_retrieve"] = None + io_parameters["uid_retrieve"] = "" - TRAIN_PARAMS_EXAMPLE[0]["params"]["io_parameters"] = io_parameters - TRAIN_PARAMS_EXAMPLE[1]["params"]["io_parameters"] = io_parameters - TRAIN_PARAMS_EXAMPLE[0]["params"]["model_parameters"] = model_parameters - TRAIN_PARAMS_EXAMPLE[1]["params"]["model_parameters"] = model_parameters + TRAIN_PARAMS_EXAMPLE["params_list"][0]["params"][ + "io_parameters" + ] = io_parameters + TRAIN_PARAMS_EXAMPLE["params_list"][1]["params"][ + "io_parameters" + ] = io_parameters + TRAIN_PARAMS_EXAMPLE["params_list"][0]["params"][ + "model_parameters" + ] = model_parameters + TRAIN_PARAMS_EXAMPLE["params_list"][1]["params"][ + "model_parameters" + ] = model_parameters if MODE == "dev": job_uid = str(uuid.uuid4()) diff --git a/constants.py b/constants.py index 1a97187..79f3cd7 100644 --- a/constants.py +++ b/constants.py @@ -30,7 +30,7 @@ "no-more-slices": "pajamas:warning-solid", "export": "entypo:export", "submit": "formkit:submit", - "parameter-valid": "fluent-mdl2:machine-learning", + "parameters": "fluent-mdl2:machine-learning", } ANNOT_NOTIFICATION_MSGS = { From 8f3400e0635a0c0d86e0421e4e16451213da4aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 12:30:06 -0800 Subject: [PATCH 08/24] Add parameters to inference --- callbacks/segmentation.py | 42 +++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index 0fbb5a4..c2ecc67 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -15,6 +15,7 @@ ) from utils.plot_utils import generate_notification from utils.prefect import ( + get_children_flow_run_ids, get_flow_run_name, get_flow_runs_by_name, schedule_prefect_flow, @@ -92,8 +93,6 @@ def run_train( This callback collects parameters from the UI and submits a training job to Prefect. If the app is run from "dev" mode, then only a placeholder job_uid will be created. - # TODO: Appropriately paramaterize the job json depending on user inputs - and relevant file paths """ if n_clicks: model_parameters, parameter_errors = extract_parameters_from_html( @@ -176,9 +175,10 @@ def run_train( Input("run-inference", "n_clicks"), State("train-job-selector", "value"), State("project-name-src", "value"), + State("model-parameters", "children"), prevent_initial_call=True, ) -def run_inference(n_clicks, train_job_id, project_name): +def run_inference(n_clicks, train_job_id, project_name, model_parameter_container): """ This callback collects parameters from the UI and submits an inference job to Prefect. If the app is run from "dev" mode, then only a placeholder job_uid will be created. @@ -187,6 +187,31 @@ def run_inference(n_clicks, train_job_id, project_name): and relevant file paths """ if n_clicks: + model_parameters, parameter_errors = extract_parameters_from_html( + model_parameter_container + ) + # Check if the model parameters are valid + if parameter_errors: + notification = generate_notification( + "Model Parameters", + "red", + ANNOT_ICONS["parameters"], + "Model parameters are not valid!", + ) + return notification, no_update + + # Set io_parameters for inference, there will be no mask + data_uri = tiled_datasets.get_data_uri_by_name(project_name) + io_parameters = assemble_io_parameters_from_uris(data_uri, "") + io_parameters["uid_retrieve"] = "" + + INFERENCE_PARAMS_EXAMPLE["params_list"][0]["params"][ + "io_parameters" + ] = io_parameters + INFERENCE_PARAMS_EXAMPLE["params_list"][0]["params"][ + "model_parameters" + ] = model_parameters + if MODE == "dev": job_uid = str(uuid.uuid4()) job_message = f"Job has been succesfully submitted with uid: {job_uid}" @@ -195,6 +220,15 @@ def run_inference(n_clicks, train_job_id, project_name): if train_job_id is not None: job_name = get_flow_run_name(train_job_id) if job_name is not None: + children_flows = get_children_flow_run_ids(train_job_id) + # The first child flow is the training portion of the parent flow + # TODO: Maybe check number of children and type in the future + train_job_id = children_flows[0] + # Set the uid_retrieve of the inference job to the uid of the training job + INFERENCE_PARAMS_EXAMPLE["params_list"][0]["params"][ + "io_parameters" + ]["uid_retrieve"] = train_job_id + # TODO: Check if the architecture parameters are the same, as the one used in training try: # Schedule job current_time = datetime.now(pytz.timezone(TIMEZONE)).strftime( @@ -273,7 +307,7 @@ def check_inference_job(n_intervals, train_job_id, project_name): {"label": "🕑 DLSIA XYC 03/11/2024 14:21PM", "value": "uid0002"}, {"label": "✅ DLSIA CBA 03/11/2024 10:02AM", "value": "uid0003"}, ] - return data, None + return data, no_update else: if train_job_id is not None: job_name = get_flow_run_name(train_job_id) From 6987c5a72c5f3ba1a94239336f95a9aa10ebac11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 14:14:13 -0800 Subject: [PATCH 09/24] Replace single result store by 2 --- callbacks/control_bar.py | 12 +++++++----- callbacks/image_viewer.py | 16 +++++++++------- components/control_bar.py | 3 ++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index ded636e..b7d5b8d 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -868,11 +868,13 @@ def refresh_data_client(refresh_tiled): Output("show-result-overlay-toggle", "disabled"), Output("seg-result-opacity-slider", "disabled"), Input("show-result-overlay-toggle", "checked"), - Input("project-name-src", "value"), - Input("seg-result-store", "data"), + Input("seg-results-train-store", "data"), + Input("seg-results-inference-store", "data"), State("seg-result-opacity-slider", "disabled"), ) -def update_result_controls(toggle, seg_result, slider_disabled, image_src): +def update_result_controls( + toggle, seg_result_train, seg_result_inference, slider_disabled +): checked = False disable_toggle = True disable_slider = True @@ -883,8 +885,8 @@ def update_result_controls(toggle, seg_result, slider_disabled, image_src): disable_toggle = no_update disable_slider = not slider_disabled else: - if "project_name" in seg_result and seg_result["project_name"] == image_src: - checked = no_update + if seg_result_train or seg_result_inference: + checked = False disable_toggle = False disable_slider = False return ( diff --git a/callbacks/image_viewer.py b/callbacks/image_viewer.py index 260217d..a253e45 100644 --- a/callbacks/image_viewer.py +++ b/callbacks/image_viewer.py @@ -70,7 +70,8 @@ def hide_show_segmentation_overlay(toggle_seg_result, opacity): State("image-metadata", "data"), State("screen-size", "data"), State("current-class-selection", "data"), - State("seg-result-store", "data"), + State("seg-results-train-store", "data"), + State("seg-results-inference-store", "data"), State("seg-result-opacity-slider", "value"), State("image-viewer", "figure"), prevent_initial_call=True, @@ -85,7 +86,8 @@ def render_image( image_metadata, screen_size, current_color, - seg_result, + seg_result_train, + seg_result_inference, opacity, fig, ): @@ -119,11 +121,11 @@ def render_image( ): return [dash.no_update] * 7 + ["hidden"] # Check if the stored results are for the current project and image - if ( - "project_name" in seg_result - and seg_result["project_name"] == project_name - ): - if "mask_idx" in seg_result: + if seg_result_train or seg_result_inference: + seg_result = ( + seg_result_inference if seg_result_inference else seg_result_train + ) + if "mask_idx" in seg_result and seg_result["mask_idx"] is not None: annotation_indices = seg_result["mask_idx"] if str(image_idx) in annotation_indices: # Will not return an error since we already checked if image_idx is in the list diff --git a/components/control_bar.py b/components/control_bar.py index b8ae508..dd40942 100644 --- a/components/control_bar.py +++ b/components/control_bar.py @@ -673,7 +673,8 @@ def layout(): disabled=True, styles={"trackLabel": {"cursor": "pointer"}}, ), - dcc.Store("seg-result-store"), + dcc.Store("seg-results-train-store"), + dcc.Store("seg-results-inference-store"), dmc.Space(h=25), ControlItem( "Opacity", From 2b960e3e5ae570b931fca339eeb707a9f097880b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 14:14:54 -0800 Subject: [PATCH 10/24] Populate result stores on dropdown change --- callbacks/segmentation.py | 80 +++++++++++++++++++++++++++++++++++++++ constants.py | 1 + 2 files changed, 81 insertions(+) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index c2ecc67..8eae09c 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -12,6 +12,7 @@ extract_parameters_from_html, tiled_datasets, tiled_masks, + tiled_results, ) from utils.plot_utils import generate_notification from utils.prefect import ( @@ -335,3 +336,82 @@ def check_inference_job(n_intervals, train_job_id, project_name): selected_value = None if len(data) == 0 else no_update return data, selected_value return [], None + + +def populate_segmentation_results( + job_id, + project_name, + job_type="training", +): + """ + This function populates the segmentation results store based on the uids + of the training job orinference job. + """ + data_uri = tiled_datasets.get_data_uri_by_name(project_name) + if job_id is not None: + job_name = get_flow_run_name(job_id) + if job_name is not None: + if job_type == "training": + # Get second child to retrieve results + children_flows = get_children_flow_run_ids(job_id) + job_id = children_flows[1] + expected_result_uri = f"{job_id}/seg_result" + try: + result_container = tiled_results.get_data_by_trimmed_uri( + expected_result_uri + ) + except Exception: + notification = generate_notification( + "Segmentation Results", + "red", + ANNOT_ICONS["results"], + f"Could not retrieve result from {job_type} job!", + ) + return notification, None + result_metadata = result_container.metadata + if result_metadata["data_uri"] == data_uri: + result_store = { + "seg_result_trimmed_uri": expected_result_uri, + "mask_idx": result_metadata["mask_idx"], + "data_uri": result_metadata["data_uri"], + } + notification = generate_notification( + "Segmentation Results", + "green", + ANNOT_ICONS["results"], + f"Retrieved result from {job_type} job!", + ) + return notification, result_store + else: + return no_update, None + return no_update, no_update + + +@callback( + Output("notifications-container", "children", allow_duplicate=True), + Output("seg-results-train-store", "data"), + Input("train-job-selector", "value"), + Input("project-name-src", "value"), + prevent_initial_call=True, +) +def populate_segmentation_results_train(train_job_id, project_name): + """ + This callback populates the segmentation results store based on the uids + if the training job and the inference job. + """ + return populate_segmentation_results(train_job_id, project_name, "training") + + +@callback( + Output("notifications-container", "children", allow_duplicate=True), + Output("seg-results-inference-store", "data"), + Input("inference-job-selector", "value"), + Input("project-name-src", "value"), + prevent_initial_call=True, +) +def populate_segmentation_results_inference(inference_job_id, project_name): + """ + This callback populates the segmentation results store based on the uids + if the training job and the inference job. + """ + return populate_segmentation_results(inference_job_id, project_name, "inference") diff --git a/constants.py b/constants.py index 79f3cd7..59044c7 100644 --- a/constants.py +++ b/constants.py @@ -31,6 +31,7 @@ "export": "entypo:export", "submit": "formkit:submit", "parameters": "fluent-mdl2:machine-learning", + "results": "entypo:download", } ANNOT_NOTIFICATION_MSGS = { From 46d5c3fa67155a399f9bb64c32a3021df6cbfe02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 14:25:00 -0800 Subject: [PATCH 11/24] Add number of classes to model parameters --- callbacks/segmentation.py | 3 ++- utils/data_utils.py | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index 8eae09c..990fb6f 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -108,9 +108,10 @@ def run_train( "Model parameters are not valid!", ) return notification, no_update - mask_uri, mask_error_message = tiled_masks.save_annotations_data( + mask_uri, num_classes, mask_error_message = tiled_masks.save_annotations_data( global_store, all_annotations, project_name ) + model_parameters["num_classes"] = num_classes if mask_uri is None: notification = generate_notification( "Mask Export", "red", ANNOT_ICONS["export"], mask_error_message diff --git a/utils/data_utils.py b/utils/data_utils.py index 04118ce..9fd2cac 100644 --- a/utils/data_utils.py +++ b/utils/data_utils.py @@ -187,7 +187,7 @@ def save_annotations_data(self, global_store, all_annotations, project_name): else None ) if data_shape is None: - return None, "Image shape could not be determined." + return None, None, "Image shape could not be determined." image_shape = (data_shape[1], data_shape[2]) annotations = Annotations(all_annotations, image_shape) @@ -215,7 +215,7 @@ def save_annotations_data(self, global_store, all_annotations, project_name): try: mask = np.stack(mask) except ValueError: - return None, "No annotations to process." + return None, None, "No annotations to process." # Store the mask in the Tiled server under /username/project_name/uuid/mask" container_keys = [USER_NAME, project_name] @@ -235,7 +235,11 @@ def save_annotations_data(self, global_store, all_annotations, project_name): mask = last_container.write_array(key="mask", array=mask) else: last_container = last_container[annotations_hash] - return last_container.uri, "Annotations saved successfully." + return ( + last_container.uri, + len(annotation_classes), + "Annotations saved successfully.", + ) tiled_masks = TiledMaskHandler( From b6fe3bd4ac1b927c3e07f65dcb52e20a90673e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 15:42:34 -0800 Subject: [PATCH 12/24] Add example script for copying mask as result --- examples/copy_mask_as_result.py | 67 +++++++++++++++++++++++++++++++++ examples/plot_mask.py | 2 +- 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 examples/copy_mask_as_result.py diff --git a/examples/copy_mask_as_result.py b/examples/copy_mask_as_result.py new file mode 100644 index 0000000..f6b3af0 --- /dev/null +++ b/examples/copy_mask_as_result.py @@ -0,0 +1,67 @@ +import os +import sys + +import numpy as np +from dotenv import load_dotenv +from tiled.client import from_uri + +load_dotenv() + +MASK_TILED_API_KEY = os.getenv("MASK_TILED_API_KEY") +SEG_TILED_URI = os.getenv("SEG_TILED_URI") +SEG_TILED_API_KEY = os.getenv("SEG_TILED_API_KEY") + + +def copy_mask_as_result(mask_uri, job_id, quick_inference=False): + mask_client = from_uri(mask_uri, api_key=SEG_TILED_API_KEY) + mask = mask_client["mask"] + mask_metadata = mask_client.metadata + + result_client = from_uri(SEG_TILED_URI, api_key=SEG_TILED_API_KEY) + print(result_client) + result_container = result_client.create_container(key=job_id) + result_metadata = { + "data_uri": mask_metadata["data_uri"], + "mask_uri": mask_uri, + "mask_idx": mask_metadata["mask_idx"], + } + + if not quick_inference: + mask_shape = mask.shape + image_shape = mask_metadata["image_shape"] + + repeats_needed = image_shape[0] // mask_shape[0] + partial_repeats_needed = image_shape[0] % mask_shape[0] + + if partial_repeats_needed > 0: + full_repeats_mask = np.repeat(mask, repeats_needed, axis=0) + # Take a slice of the mask for the partial repeat + partial_mask = mask[:partial_repeats_needed, :, :] + # Concatenate the full repeats and the partial repeat + mask = np.concatenate((full_repeats_mask, partial_mask), axis=0) + else: + # If no partial repeats needed, just use full repeats + mask = np.repeat(mask, repeats_needed, axis=0) + + result_container.write_array(key="seg_result", array=mask, metadata=result_metadata) + + +if __name__ == "__main__": + """ + Example usage: python3 copy_mask_as_result.py http://localhost:8000/api/v1/metadata/mlex_store/username/dataset/hash job_id + """ + + if len(sys.argv) < 3: + print( + "Usage: python3 copy_mask_as_result [quick_inference]" + ) + sys.exit(1) + + mask_uri = sys.argv[1] + job_id = sys.argv[2] + if len(sys.argv) < 4: + quick_inference = False + else: + quick_inference = False if sys.argv[3] == 0 else True + + copy_mask_as_result(mask_uri, job_id, quick_inference=False) diff --git a/examples/plot_mask.py b/examples/plot_mask.py index fa4735d..8533905 100644 --- a/examples/plot_mask.py +++ b/examples/plot_mask.py @@ -60,7 +60,7 @@ def plot_mask(mask_uri, api_key, slice_idx, output_path): if __name__ == "__main__": """ - Example usage: python3 plot_mask.py http://localhost:8000/api/v1/metadata/mlex_store/mlex_store/username/dataset/uuid + Example usage: python3 plot_mask.py http://localhost:8000/api/v1/metadata/mlex_store/username/dataset/hash """ load_dotenv() From bce4bcd59959b5736c4e63bb064a912ae436387b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 15:42:57 -0800 Subject: [PATCH 13/24] :bug: Fix default export location --- callbacks/control_bar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index b7d5b8d..b445224 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -32,7 +32,7 @@ from utils.plot_utils import generate_notification, generate_notification_bg_icon_col # TODO - temporary local file path and user for annotation saving and exporting -EXPORT_FILE_PATH = os.getenv("EXPORT_FILE_PATH", "data/exported_annotation_data.json") +EXPORT_FILE_PATH = os.getenv("EXPORT_FILE_PATH", "exported_annotation_data.json") USER_NAME = os.getenv("USER_NAME", "user1") # Create an empty file if it doesn't exist From 5696eefd28018c469f2808baaa32c306000e8d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 16:28:52 -0800 Subject: [PATCH 14/24] :bug: Fix copy mask script (all bools inverted) --- examples/copy_mask_as_result.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/copy_mask_as_result.py b/examples/copy_mask_as_result.py index f6b3af0..80ce92f 100644 --- a/examples/copy_mask_as_result.py +++ b/examples/copy_mask_as_result.py @@ -12,7 +12,7 @@ SEG_TILED_API_KEY = os.getenv("SEG_TILED_API_KEY") -def copy_mask_as_result(mask_uri, job_id, quick_inference=False): +def copy_mask_as_result(mask_uri, job_id, quick_inference=True): mask_client = from_uri(mask_uri, api_key=SEG_TILED_API_KEY) mask = mask_client["mask"] mask_metadata = mask_client.metadata @@ -60,8 +60,8 @@ def copy_mask_as_result(mask_uri, job_id, quick_inference=False): mask_uri = sys.argv[1] job_id = sys.argv[2] if len(sys.argv) < 4: - quick_inference = False + quick_inference = True else: - quick_inference = False if sys.argv[3] == 0 else True + quick_inference = True if sys.argv[3] == 0 else False - copy_mask_as_result(mask_uri, job_id, quick_inference=False) + copy_mask_as_result(mask_uri, job_id, quick_inference) From 8b53e25e19a72c2f4d7a11ce8c2870420359e8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 16:33:42 -0800 Subject: [PATCH 15/24] :bug: Always check subflows for results In case of training, we find initial partial results under the flow id of the first child. In case of inference, we find final fully segmented results under the flow id of the second child --- callbacks/segmentation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index 990fb6f..e0d0da2 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -352,10 +352,12 @@ def populate_segmentation_results( if job_id is not None: job_name = get_flow_run_name(job_id) if job_name is not None: + children_flows = get_children_flow_run_ids(job_id) if job_type == "training": # Get second child to retrieve results - children_flows = get_children_flow_run_ids(job_id) job_id = children_flows[1] + else: + job_id = children_flows[0] expected_result_uri = f"{job_id}/seg_result" try: result_container = tiled_results.get_data_by_trimmed_uri( From a27b6cdaab20de6f3b0ce2d6c88cfb729368ad6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 16:44:59 -0800 Subject: [PATCH 16/24] :bug: Slice into array when copying --- examples/copy_mask_as_result.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/copy_mask_as_result.py b/examples/copy_mask_as_result.py index 80ce92f..ec006fc 100644 --- a/examples/copy_mask_as_result.py +++ b/examples/copy_mask_as_result.py @@ -14,11 +14,15 @@ def copy_mask_as_result(mask_uri, job_id, quick_inference=True): mask_client = from_uri(mask_uri, api_key=SEG_TILED_API_KEY) - mask = mask_client["mask"] + mask = mask_client["mask"][:] mask_metadata = mask_client.metadata + print( + f"Copying mask with unique values: {np.unique(mask)} and shape: {mask.shape}." + ) + result_client = from_uri(SEG_TILED_URI, api_key=SEG_TILED_API_KEY) - print(result_client) + result_container = result_client.create_container(key=job_id) result_metadata = { "data_uri": mask_metadata["data_uri"], From cc5c0048774516fabde4ec4d7e0c3bbf2617d023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 17:00:47 -0800 Subject: [PATCH 17/24] Add conda Prefect flows --- callbacks/segmentation.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index e0d0da2..e10382e 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -26,6 +26,10 @@ RESULTS_DIR = os.getenv("RESULTS_DIR", "") FLOW_NAME = os.getenv("FLOW_NAME", "") PREFECT_TAGS = os.getenv("PREFECT_TAGS", ["high-res-segmentation"]) +CONDA_ENV_NAME = os.getenv("CONDA_ENV_NAME", "dlsia") +TRAIN_SCRIPT_PATH = os.getenv("TRAIN_SCRIPT_PATH", "scr/train.py") +SEGMENT_SCRIPT_PATH = os.getenv("SEGMENT_SCRIPT_PATH", "scr/segment.py") + # TODO: Retrieve timezone from browser TIMEZONE = os.getenv("TIMEZONE", "US/Pacific") @@ -70,6 +74,39 @@ ], } +TRAIN_PARAMS_EXAMPLE = { + "flow_type": "conda", + "params_list": [ + { + "conda_env_name": f"{CONDA_ENV_NAME}", + "python_file_name": f"{TRAIN_SCRIPT_PATH}", + "params": { + "io_parameters": {"uid_save": "uid0001", "uid_retrieve": "uid0001"} + }, + }, + { + "conda_env_name": f"{CONDA_ENV_NAME}", + "python_file_name": f"{SEGMENT_SCRIPT_PATH}", + "params": { + "io_parameters": {"uid_save": "uid0001", "uid_retrieve": "uid0001"} + }, + }, + ], +} + +INFERENCE_PARAMS_EXAMPLE = { + "flow_type": "conda", + "params_list": [ + { + "conda_env_name": f"{CONDA_ENV_NAME}", + "python_file_name": f"{SEGMENT_SCRIPT_PATH}", + "params": { + "io_parameters": {"uid_save": "uid0001", "uid_retrieve": "uid0001"} + }, + }, + ], +} + @callback( Output("notifications-container", "children", allow_duplicate=True), From 13a9951345afbde8f99db355b40666e167ad8cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 17:02:00 -0800 Subject: [PATCH 18/24] :bug: Guard against `None` for string params --- callbacks/control_bar.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index b445224..43c1b61 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -943,6 +943,10 @@ def update_model_parameters(model_name): ), ) def validate_class_weights(all_annotation_classes, weights): + + if weights is None: + return "Provide a list with a float for each class" + parsed_weights = weights.strip("[]").split(",") try: parsed_weights = [float(weight.strip()) for weight in parsed_weights] @@ -978,11 +982,17 @@ def validate_class_weights(all_annotation_classes, weights): ), ) def validate_dilation_array(dilation_array): + + if dilation_array is None: + return "Provide a list of ints for dilation" + parsed_dilation_array = dilation_array.strip("[]").split(",") try: parsed_dilation_array = [ int(array_entry.strip()) for array_entry in parsed_dilation_array ] + if len(parsed_dilation_array) == 0: + return "Provide a list of ints for dilation" # Check if all elements in the list are floats return False except ValueError: From aaff3de18c491d6dcce63de96caa63e7f78c9297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 17:29:08 -0800 Subject: [PATCH 19/24] Add missing `network` parameter --- assets/models.json | 6 +++--- callbacks/segmentation.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/assets/models.json b/assets/models.json index a191691..4b4ad76 100755 --- a/assets/models.json +++ b/assets/models.json @@ -1,7 +1,7 @@ { "contents": [ { - "model_name": "DSLIA MSDNet", + "model_name": "MSDNet", "version": "0.0.1", "type": "supervised", "user": "mlexchange team", @@ -434,7 +434,7 @@ "reference": "https://dlsia.readthedocs.io/en/latest/" }, { - "model_name": "DSLIA TUNet", + "model_name": "TUNet", "version": "0.0.1", "type": "supervised", "user": "mlexchange team", @@ -853,7 +853,7 @@ "reference": "https://dlsia.readthedocs.io/en/latest/" }, { - "model_name": "DSLIA TUNet3+", + "model_name": "TUNet3+", "version": "0.0.1", "type": "supervised", "user": "mlexchange team", diff --git a/callbacks/segmentation.py b/callbacks/segmentation.py index e10382e..20227f0 100644 --- a/callbacks/segmentation.py +++ b/callbacks/segmentation.py @@ -116,6 +116,7 @@ State({"type": "annotation-class-store", "index": ALL}, "data"), State("project-name-src", "value"), State("model-parameters", "children"), + State("model-list", "value"), State("job-name", "value"), prevent_initial_call=True, ) @@ -125,6 +126,7 @@ def run_train( all_annotations, project_name, model_parameter_container, + model_name, job_name, ): """ @@ -149,6 +151,7 @@ def run_train( global_store, all_annotations, project_name ) model_parameters["num_classes"] = num_classes + model_parameters["network"] = model_name if mask_uri is None: notification = generate_notification( "Mask Export", "red", ANNOT_ICONS["export"], mask_error_message @@ -192,7 +195,7 @@ def run_train( flow_run_name=f"{job_name} {current_time}", tags=PREFECT_TAGS + ["train", project_name], ) - job_message = f"Job has been succesfully submitted with uid: {job_uid}" + job_message = f"Job has been succesfully submitted with uid: {job_uid} and mask uri: {mask_uri}" notification_color = "indigo" except Exception as e: # Print the traceback to the console @@ -215,9 +218,12 @@ def run_train( State("train-job-selector", "value"), State("project-name-src", "value"), State("model-parameters", "children"), + State("model-list", "value"), prevent_initial_call=True, ) -def run_inference(n_clicks, train_job_id, project_name, model_parameter_container): +def run_inference( + n_clicks, train_job_id, project_name, model_parameter_container, model_name +): """ This callback collects parameters from the UI and submits an inference job to Prefect. If the app is run from "dev" mode, then only a placeholder job_uid will be created. @@ -238,7 +244,7 @@ def run_inference(n_clicks, train_job_id, project_name, model_parameter_containe "Model parameters are not valid!", ) return notification, no_update - + model_parameters["network"] = model_name # Set io_parameters for inference, there will be no mask data_uri = tiled_datasets.get_data_uri_by_name(project_name) io_parameters = assemble_io_parameters_from_uris(data_uri, "") From 9b91f7e25b4738211fa8be251d2a6c3de16928f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Fri, 8 Mar 2024 17:30:53 -0800 Subject: [PATCH 20/24] Update `.env.example` for conda flows --- .env.example | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env.example b/.env.example index 35f00f4..0129bdd 100644 --- a/.env.example +++ b/.env.example @@ -47,3 +47,8 @@ USER_PASSWORD= PREFECT_API_URL=http://prefect:4200/api FLOW_NAME="Parent flow/launch_parent_flow" TIMEZONE="US/Pacific" + +# Environment variables for conda-based Prefect flows +CONDA_ENV_NAME="dlsia" +TRAIN_SCRIPT_PATH="src/train.py" +SEGMENT_SCRIPT_PATH="src/segment.py" From cc02a53a217e953ea549f2ec53a1bda9c0cc5d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Sat, 9 Mar 2024 18:24:53 -0800 Subject: [PATCH 21/24] Add default values for weights and dilation array --- assets/models.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/models.json b/assets/models.json index 4b4ad76..093e6bf 100755 --- a/assets/models.json +++ b/assets/models.json @@ -48,6 +48,7 @@ "name": "dilation_array", "title": "Dilation Array", "param_key": "dilation_array", + "value": "[1, 2, 4]", "placeholder": "e.g. [1, 2, 4]", "error": "Provide a list of ints for dilation", "debounce": 1000, @@ -230,6 +231,7 @@ "name": "weights", "title": "Class Weights", "param_key": "weights", + "value": "[1]", "placeholder": "e.g [0.1, 0.4, 0.5]", "error": "Provide a list with a float for each class", "debounce": 1000, @@ -653,6 +655,7 @@ "name": "weights", "title": "Class Weights", "param_key": "weights", + "value": "[1]", "placeholder": "e.g [0.1, 0.4, 0.5]", "error": "Provide a list with a float for each class", "debounce": 1000, @@ -1080,6 +1083,7 @@ "name": "weights", "title": "Class Weights", "param_key": "weights", + "value": "[1]", "placeholder": "e.g [0.1, 0.4, 0.5]", "error": "Provide a list with a float for each class", "debounce": 1000, From 9b06cc508bd4fcbc34e78fc8a8ee49bf709f04f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Sat, 9 Mar 2024 18:40:42 -0800 Subject: [PATCH 22/24] Change `val_pct` range from 0-100 to 0-1 --- assets/models.json | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/assets/models.json b/assets/models.json index 093e6bf..ec41659 100755 --- a/assets/models.json +++ b/assets/models.json @@ -314,20 +314,21 @@ "title": "Validation %", "param_key": "val_pct", "min": 0, - "max": 100, - "step": 5, - "value": 20, + "max": 1, + "step": 0.05, + "value": 0.2, + "precision": 2, "marks": [ { "value": 0, "label": "0%" }, { - "value": 50, + "value": 0.5, "label": "50%" }, { - "value": 100, + "value": 1, "label": "100%" } ], @@ -738,16 +739,21 @@ "title": "Validation %", "param_key": "val_pct", "min": 0, - "max": 100, - "step": 5, - "value": 20, + "max": 1, + "step": 0.05, + "value": 0.2, + "precision": 2, "marks": [ { "value": 0, "label": "0%" }, { - "value": 100, + "value": 0.5, + "label": "50%" + }, + { + "value": 1, "label": "100%" } ], @@ -1166,16 +1172,21 @@ "title": "Validation %", "param_key": "val_pct", "min": 0, - "max": 100, - "step": 5, - "value": 20, + "max": 1, + "step": 0.05, + "value": 0.2, + "precision": 2, "marks": [ { "value": 0, "label": "0%" }, { - "value": 100, + "value": 0.5, + "label": "50%" + }, + { + "value": 1, "label": "100%" } ], From affc0c9c04ba313d217568e2687b1c9933235180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Sat, 9 Mar 2024 22:04:04 -0800 Subject: [PATCH 23/24] :bug: Test if `shapes` exist before editing them Switching to pan-and-zoom gave a key error for `shapes` when no annotation had been added yet --- callbacks/control_bar.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/callbacks/control_bar.py b/callbacks/control_bar.py index 43c1b61..b10f57e 100644 --- a/callbacks/control_bar.py +++ b/callbacks/control_bar.py @@ -230,16 +230,18 @@ def annotation_mode( patched_figure["layout"]["dragmode"] = "drawrect" annotation_store["dragmode"] = "drawrect" styles[trigger] = active - elif trigger == "pan-and-zoom" and pan_and_zoom > 0: patched_figure["layout"]["dragmode"] = "pan" annotation_store["dragmode"] = "pan" styles[trigger] = active # disable shape editing when in pan/zoom mode - for shape in fig["layout"]["shapes"]: - shape["editable"] = trigger != "pan-and-zoom" and pan_and_zoom > 0 - patched_figure["layout"]["shapes"] = fig["layout"]["shapes"] + # if no shapes have been added yet, + # none need to be set to not editable + if "shapes" in fig["layout"]: + for shape in fig["layout"]["shapes"]: + shape["editable"] = trigger != "pan-and-zoom" and pan_and_zoom > 0 + patched_figure["layout"]["shapes"] = fig["layout"]["shapes"] return ( patched_figure, styles["closed-freeform"], From b7d5f2f487b2fe9fd273c5a2c853d77a251b5d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiebke=20K=C3=B6pp?= Date: Mon, 11 Mar 2024 11:53:31 +0000 Subject: [PATCH 24/24] Change slider and switch styles --- components/parameter_items.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/parameter_items.py b/components/parameter_items.py index f165125..4666545 100644 --- a/components/parameter_items.py +++ b/components/parameter_items.py @@ -107,6 +107,8 @@ def __init__( self.input = dmc.Slider( id={**base_id, "name": name, "param_key": param_key, "layer": "input"}, labelAlwaysOn=False, + color="gray", + size="sm", **kwargs, ) @@ -206,6 +208,9 @@ def __init__( self.input = dmc.Switch( id={**base_id, "name": name, "param_key": param_key, "layer": "input"}, label=title, + size="sm", + radius="lg", + color="gray", **kwargs, )