Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ datasets/
flask_session
synAnno.json
secrets/
tmp/

.vscode/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# SynAnno

## Live Demo
A demo is available [here](http://16.170.214.77/)

## Table of Contents

SynAnno is a tool designed for proofreading and correcting synaptic polarity annotation from electron microscopy (EM) volumes - specifically the [H01](https://h01-release.storage.googleapis.com/landing.html) dataset. SynAnno is aimed for integration with CAVE (Connectome Annotation Versioning Engine).

- [Key Components and Subjects](#key-components-and-subjects)
Expand Down
2 changes: 1 addition & 1 deletion synanno/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def initialize_global_variables(app):
app.source = None
app.cz1, app.cz2, app.cz, app.cy, app.cx = 0, 0, 0, 0, 0
app.n_pages = 0
app.per_page = 24 # Number of images per page
app.tiles_per_page = 24 # Number of images per page
# Neuron skeleton info/data
app.sections = None
app.neuron_ready = None
Expand Down
20 changes: 11 additions & 9 deletions synanno/backend/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -787,22 +787,24 @@ def calculate_number_of_pages(n_images: int) -> int:

Args:
n_images: Total number of images.
per_page: Number of images per page.
tiles_per_page: Number of images per page.

Returns:
Number of pages.
"""
number_pages = n_images // current_app.per_page
if n_images % current_app.per_page != 0:
number_pages = n_images // current_app.tiles_per_page
if n_images % current_app.tiles_per_page != 0:
number_pages += 1

current_app.synapse_data["page"] = -1 # Initialize pages to -1

# assign pages to synapses
for i, start_idx in enumerate(range(0, n_images, current_app.per_page), start=1):
for i, start_idx in enumerate(
range(0, n_images, current_app.tiles_per_page), start=1
):
current_app.synapse_data.loc[
current_app.synapse_data.iloc[
start_idx : start_idx + current_app.per_page # noqa: E203
start_idx : start_idx + current_app.tiles_per_page # noqa: E203
].index,
"page",
] = i
Expand All @@ -816,7 +818,7 @@ def calculate_number_of_pages_for_neuron_section_based_loading():

This function:
- Groups synapses by `section_index`.
- Assigns page numbers within each section based on `current_app.per_page`.
- Assigns page numbers within each section based on `current_app.tiles_per_page`.
- Adds an extra empty page per section.

Returns:
Expand All @@ -839,11 +841,11 @@ def calculate_number_of_pages_for_neuron_section_based_loading():

# Assign pages to synapses within the section
for i, start_idx in enumerate(
range(0, total_synapses, current_app.per_page), start=1
range(0, total_synapses, current_app.tiles_per_page), start=1
):
current_app.synapse_data.loc[
synapse_group.iloc[
start_idx : start_idx + current_app.per_page # noqa: E203
start_idx : start_idx + current_app.tiles_per_page # noqa: E203
].index,
"page",
] = (
Expand All @@ -856,7 +858,7 @@ def calculate_number_of_pages_for_neuron_section_based_loading():
)

# Increment by the number of pages needed
number_of_pages += int(np.ceil(total_synapses / current_app.per_page))
number_of_pages += int(np.ceil(total_synapses / current_app.tiles_per_page))

# Add one empty page for the section
number_of_pages += 1
Expand Down
8 changes: 5 additions & 3 deletions synanno/routes/false_negatives.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ def create_new_item(request) -> dict:
if current_page > -1:
item["Page"] = current_page
else:
if not (len(current_app.df_metadata) % current_app.per_page == 0):
item["Page"] = len(current_app.df_metadata) // current_app.per_page + 1
if not (len(current_app.df_metadata) % current_app.tiles_per_page == 0):
item["Page"] = (
len(current_app.df_metadata) // current_app.tiles_per_page + 1
)
else:
item["Page"] = len(current_app.df_metadata) // current_app.per_page
item["Page"] = len(current_app.df_metadata) // current_app.tiles_per_page

coordinate_order = list(current_app.coordinate_order.keys())

Expand Down
3 changes: 2 additions & 1 deletion synanno/routes/finish.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import logging
import zipfile
from typing import Optional

from flask import (
Blueprint,
Expand Down Expand Up @@ -131,7 +132,7 @@ def create_zip_with_masks() -> io.BytesIO:
return zip_buffer


def get_metadata_for_image_index(img_index: str) -> dict | None:
def get_metadata_for_image_index(img_index: str) -> Optional[dict]:
"""Retrieve metadata for a given image index."""
img_index_int = int(img_index) # noqa: F841
if current_app.df_metadata.query("Image_Index == @img_index_int").to_dict(
Expand Down
2 changes: 1 addition & 1 deletion synanno/routes/manual_annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def save_canvas() -> dict:
image = decode_image(request.form["imageBase64"])
page = int(request.form["page"])
index = int(request.form["data_id"])
viewed_instance_slice = int(request.form["viewed_instance_slice"])
viewed_instance_slice = int(request.form["viewedInstanceSlice"])
canvas_type = str(request.form["canvas_type"])

crop_axes = (
Expand Down
2 changes: 2 additions & 0 deletions synanno/routes/opendata.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,8 @@ def upload_file():
"""
current_app.view_style = request.form.get("view_style")

current_app.tiles_per_page = int(request.form.get("tiles_per_page"))

save_coordinate_order_and_crop_size(request.form)

source_url = request.form.get("source_url")
Expand Down
8 changes: 8 additions & 0 deletions synanno/static/annotation_module.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ $(document).ready(function () {
});

$('[data-bs-target="#neuroModel"]').on('click', function () {
$('#synapse-id-annotation').text(dataId);
toggleInert($detailsModal, true);
$neuroModel.modal('show');
});
Expand Down Expand Up @@ -120,6 +121,13 @@ $(document).ready(function () {
isModalScrollingLocked = true;

const newSlice = currentSlice + (event.originalEvent.deltaY > 0 ? 1 : -1);

// Restrict scrolling beyond available slices
if (newSlice < dataJson.Min_Slice || newSlice > dataJson.Max_Slice) {
isModalScrollingLocked = false;
return;
}

try {
const exists = await $.get(dataJson.Error_Description === "False Negative" ?
`/source_img_exists/${dataId}/${newSlice}` :
Expand Down
124 changes: 92 additions & 32 deletions synanno/static/css/style.css
Original file line number Diff line number Diff line change
@@ -1,40 +1,32 @@
#base-content-container {
max-height: 80vh;
max-height: 100vh;
max-width: 100vw;
}

#image-tile-container {
max-height: 70vh;
max-width: 70vw;
}

.correct {
background: #abebc6;
min-width: 60vw;
width: fit-content;
margin: 0 auto;
}

.annotate-item {
width: 16.5%;
text-align: center;
}

.card>.correct {
.correct,
.card > .correct {
background: #abebc6;
}

.incorrect {
background: #f08080;
}

.incorrect,
.card>.incorrect {
background: #f08080;
}

.unsure {
background: #d7dbdd;
}

.unsure,
.card>.unsure {
background: #d7dbdd;
background: #f6f699;
}

.card {
Expand All @@ -47,13 +39,13 @@
}

.legend {
position: sticky;
top: 0;
z-index: 900;
width: 100%;
flex-wrap: wrap;
display: flex;
gap: 1rem;
background-color: white;
padding: 1rem;
padding: 0.5rem;
box-sizing: border-box;
}

.legend-item {
Expand All @@ -71,10 +63,7 @@
.card-body-proof-read {
max-height: 23%;
max-width: 23%;
margin-left: 1%;
margin-right: 1%;
margin-top: 0.5%;
margin-bottom: 0.5%;
margin: 0.5% 1%;
}

.card-block>.main-image-categorize>.img_categorize {
Expand All @@ -94,8 +83,11 @@

.outsideWrapper {
margin: auto;
width: 384px;
height: 384px;
width: 452px;
height: 452px;
background-color: #fff;
border-radius: 12px;
overflow: hidden;
}

.insideWrapper {
Expand All @@ -108,8 +100,8 @@
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
top: 0;
left: 0;
pointer-events: none;
z-index: 0;
}
Expand All @@ -122,6 +114,60 @@
left: 0;
}

/* drawing modal content */
.modal-content {
background: #ffffff;
border-radius: 1rem;
padding: 0.5rem;
border: none;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}

.modal-dialog {
border: none !important;
box-shadow: none !important;
}

.modal-header,
.card-header {
background: #f8f9fa;
border-bottom: none !important;
border-top-left-radius: 1rem;
border-top-right-radius: 1rem;
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
box-shadow: none;
}

.btn {
transition: all 0.2s ease-in-out;
}

.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.modal-footer {
padding: 0rem 1rem;
max-height: 40px;
border-top: none;
}

#centralBadge {
z-index: 1; /* Ensure it's above the image, but below the modal header */
pointer-events: none; /* Prevent interaction issues */
font-size: 0.75rem;
opacity: 0.7;
}

#neuron-id-draw .badge.disabled {
opacity: 0.8;
cursor: default;
pointer-events: none;
padding: 10px;
}

/* css for the open data form */
.syn-id-container {
display: flex;
Expand Down Expand Up @@ -182,11 +228,11 @@
/* Initially hidden */
position: relative;
/* Default inside flex container */
width: 33vw;
width: 40vw;
/* Always take 33% of the screen width */
height: 90vh;
height: 91vh;
/* Always take 80% of the screen height */
margin-top: 5px;
margin-top: 4px;
background: #fff;
border: 1px solid #ccc;
box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2);
Expand Down Expand Up @@ -278,3 +324,17 @@
.toggle-btn:hover {
background: #888;
}

.metadata-overlay {
position: absolute;
top: 5px;
right: 5px;
background: rgba(0, 0, 0, 0.55);
color: white;
font-size: 0.65rem;
padding: 2px 4px;
border-radius: 3px;
z-index: 2;
font-family: monospace;
pointer-events: none;
}
Loading
Loading