diff --git a/README.md b/README.md index 1cb18c7..01d713b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ SynAnno is a tool designed for interactive guided proofreading and correction of ## Live Demo -A demo is available [here](http://16.170.214.77/reset). +A simplified version of the app, starting directly in the Error Categorization view, is available [here](http://16.170.214.77/demo). + +For the full functionality and configuration options of SynAnno, try the complete version [here](http://16.170.214.77/reset). ## Table of Contents diff --git a/synanno/__init__.py b/synanno/__init__.py index 9900514..d08b80f 100644 --- a/synanno/__init__.py +++ b/synanno/__init__.py @@ -182,6 +182,7 @@ def register_routes(app): from synanno.routes.annotation import blueprint as annotation_blueprint from synanno.routes.auto_annotate import blueprint as auto_annotate_blueprint from synanno.routes.categorize import blueprint as categorize_blueprint + from synanno.routes.demo import blueprint as demo_blueprint from synanno.routes.false_negatives import blueprint as fn_blueprint from synanno.routes.file_access import blueprint as file_access_blueprint from synanno.routes.finish import blueprint as finish_blueprint @@ -199,6 +200,7 @@ def register_routes(app): app.register_blueprint(manual_annotate_blueprint) app.register_blueprint(auto_annotate_blueprint) app.register_blueprint(fn_blueprint) + app.register_blueprint(demo_blueprint) def setup_context_processors(app): diff --git a/synanno/backend/ng_util.py b/synanno/backend/ng_util.py index 2d49d32..00f6c4a 100644 --- a/synanno/backend/ng_util.py +++ b/synanno/backend/ng_util.py @@ -263,7 +263,7 @@ def setup_ng( # if a neuron id is already set in the app context, explicitly select it if getattr(app, "selected_neuron_id", None) is not None: with app.ng_viewer.txn() as s: - s.layer.layers["neuropil"].segments = frozenset( + s.layers["neuropil"].segments = frozenset( [getattr(app, "selected_neuron_id", None)] ) diff --git a/synanno/routes/demo.py b/synanno/routes/demo.py new file mode 100644 index 0000000..4bf2cac --- /dev/null +++ b/synanno/routes/demo.py @@ -0,0 +1,104 @@ +import logging + +import pandas as pd +from flask import Blueprint, current_app, render_template, session + +import synanno.backend.ng_util as ng_util +from synanno import initialize_global_variables +from synanno.backend.processing import ( + calculate_number_of_pages_for_neuron_section_based_loading, + determine_volume_dimensions, + load_cloud_volumes, +) +from synanno.routes.opendata import ( + calculate_scale_factor, + handle_neuron_view, + set_coordinate_resolution, +) + +logging.basicConfig(level="INFO") +logger = logging.getLogger(__name__) + +blueprint = Blueprint("demo", __name__) + + +@blueprint.route("/demo", methods=["GET"]) +def demo(): + """Displays a loading page and resets the session storage. + + Returns: + Renders the loading page. + """ + + # Reset logic + session.clear() + initialize_global_variables(current_app) + + return render_template("demo_setup.html", next_route="/demo_annotation") + + +@blueprint.route("/demo_annotation", methods=["GET"]) +def demo_init(): + """Initializes the demo data and renders the annotation view. + + Returns: + Renders the annotation view after initializing the demo data. + """ + # Upload logic + current_app.view_style = "view_style" + current_app.tiles_per_page = 12 + + current_app.crop_size_x = 256 + current_app.crop_size_y = 256 + current_app.crop_size_z = 6 + + current_app.coordinate_order = {"x": (4, 8), "y": (4, 8), "z": (33, 33)} + + source_url = "gs://h01-release/data/20210601/4nm_raw" + target_url = "gs://h01-release/data/20210729/c3/synapses/whole_ei_onlyvol" + neuropil_url = "gs://h01-release/data/20210601/proofread_104" + + current_app.synapse_data = pd.read_csv("/app/h01/h01_104_materialization.csv") + + load_cloud_volumes(source_url, target_url, neuropil_url, "~/.cloudvolume/secrets") + + current_app.vol_dim = determine_volume_dimensions() + + set_coordinate_resolution() + calculate_scale_factor() + + current_app.vol_dim_scaled = tuple( + int(a * b) for a, b in zip(current_app.vol_dim, current_app.scale.values()) + ) + + current_app.selected_neuron_id = 2325998949 + + handle_neuron_view(neuropil_url) + current_app.neuron_ready = "true" + current_app.n_pages = calculate_number_of_pages_for_neuron_section_based_loading() + + if current_app.ng_version is None: + ng_util.setup_ng( + app=current_app._get_current_object(), + source="precomputed://" + source_url, + target="precomputed://" + target_url, + neuropil="precomputed://" + neuropil_url, + ) + + page = 1 + return render_template( + "annotation.html", + page=page, + n_pages=current_app.n_pages, + grid_opacity=current_app.grid_opacity, + neuron_id=current_app.selected_neuron_id, + neuronReady=current_app.neuron_ready, + neuronSections=current_app.sections, + synapsePointCloud=current_app.snapped_point_cloud, + activeNeuronSection=( + current_app.page_section_mapping[page][0] + if page in current_app.page_section_mapping + else 0 + ), + activeSynapseIDs=current_app.synapse_data.query("page == @page").index.tolist(), + ) diff --git a/synanno/static/annotation.js b/synanno/static/annotation.js index e138ffb..d30e768 100755 --- a/synanno/static/annotation.js +++ b/synanno/static/annotation.js @@ -3,6 +3,22 @@ let currentRequestController = new AbortController(); $(document).ready(() => { const currentPage = $("script[src*='annotation.js']").data("current-page"); reloadImages(currentPage); + + // Bind hotkeys for label opacity and toggle-label button + $(document).on("keydown", (event) => { + const hotkey = event.key.toLowerCase(); + const $button = $(`[data-hotkey="${hotkey}"]`); + + if ($button.length && !$button.prop("disabled")) { + event.preventDefault(); + $button.trigger("click"); + } + }); + + window.check_gt = function () { + $imgTarget.toggle(); + $toggleLabel.toggleClass("btn-secondary"); + }; }); $(document).on("shown.bs.modal", "#drawModalFN", () => { @@ -11,7 +27,7 @@ $(document).on("shown.bs.modal", "#drawModalFN", () => { const handleSaveBboxClick = async () => { const currentPage = $("script[src*='annotation.js']").data("current-page"); - $('#loading-bar').css('display', 'flex'); + $("#loading-bar").css("display", "flex"); $(".text-white").text("Saving new instance..."); @@ -21,7 +37,7 @@ const handleSaveBboxClick = async () => { z1: $("#d_z1").val(), z2: $("#d_z2").val(), my: $("#m_y").val(), - mx: $("#m_x").val() + mx: $("#m_x").val(), }); $("#drawModalFNSave, #drawModalFN").modal("hide"); @@ -29,7 +45,7 @@ const handleSaveBboxClick = async () => { } catch (error) { console.error("Error saving bbox:", error); } - $('#loading-bar').css('display', 'none'); + $("#loading-bar").css("display", "none"); }; const reloadImages = async (currentPage) => { @@ -97,6 +113,16 @@ const hideCardGroupLoadingBar = () => { }; const toggleNavigationButtons = (disable) => { - $("#prev-page, #next-page").toggleClass("disabled", disable) - .find("a").attr("aria-disabled", disable ? "true" : null); + $("#prev-page, #next-page") + .toggleClass("disabled", disable) + .find("a") + .attr("aria-disabled", disable ? "true" : null); +}; + +window.toggleAllSources = function () { + const $allImgSources = $("[id^='imgTarget-']"); + $allImgSources.each((_, img) => { + const $img = $(img); + $img.toggle(); + }); }; diff --git a/synanno/static/draw.js b/synanno/static/draw.js index 1e4c7cb..3bbcb6e 100755 --- a/synanno/static/draw.js +++ b/synanno/static/draw.js @@ -475,6 +475,17 @@ $(document).ready(() => { } } + // Bind hotkeys to buttons + $(document).on("keydown", (event) => { + const hotkey = event.key.toLowerCase(); + const $button = $(`[data-hotkey="${hotkey}"]`); + + if ($button.length && !$button.prop("disabled")) { + event.preventDefault(); + $button.trigger("click"); + } + }); + $("canvas.curveCanvas").mousemove((event) => { if (split_mask) { if ((mousePosition.x != event.clientX || mousePosition.y != event.clientY) && event.buttons == 1) { diff --git a/synanno/templates/annotation.html b/synanno/templates/annotation.html index 2c36922..61611eb 100644 --- a/synanno/templates/annotation.html +++ b/synanno/templates/annotation.html @@ -40,6 +40,20 @@ {{grid_opacity or '0.5'}} + +
Please wait while the demo data is being initialized.
+{% endblock %} + +{% block content %} +Resetting Data and Loading Demo...
+