diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4e10eb4ed0d..96e1dede4c5 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -257,12 +257,8 @@ jobs:
- run:
name: Run screenshot-tests
command: |
- for i in {1..5}; do # retry
- URL=https://master.webknossos.xyz/ \
- yarn test-screenshot && s=0 && break || s=$?
- sleep 30
- done
- (exit $s)
+ URL=https://master.webknossos.xyz/ \
+ yarn test-screenshot
- store_artifacts:
path: frontend/javascripts/test/screenshots
diff --git a/.eslintrc.json b/.eslintrc.json
index 44dce351f96..874d66ec8bf 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -82,7 +82,16 @@
"flow-header/flow-header": "error",
"react/sort-comp": [
"error",
- { "order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"] }
+ {
+ "order": [
+ "type-annotations",
+ "instance-variables",
+ "static-methods",
+ "lifecycle",
+ "everything-else",
+ "render"
+ ]
+ }
]
}
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ff4d357710..f537c82064e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,13 +16,18 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
- Neuroglancer precomputed datasets can now be added to webKnossos using the webknossos-connect (wk-connect) service. To setup a wk-connect datastore follow the instructions in the [Readme](https://github.com/scalableminds/webknossos-connect). Afterwards, datasets can be added through "Add Dataset" - "Add Dataset via wk-connect". [#3843](https://github.com/scalableminds/webknossos/pull/3843)
- The dataset settings within the tracing view allow to select between different loading strategies now ("best quality first" and "progressive quality"). Additionally, the rendering can use different magnifications as a fallback (instead of only one magnification). [#3801](https://github.com/scalableminds/webknossos/pull/3801)
- The mapping selection dropbown is now sorted alphabetically. [#3864](https://github.com/scalableminds/webknossos/pull/3864)
+- Added the possibility to filter datasets in the dashboard according to their availability. By default, datasets which are missing on disk (e.g., when the datastore was deleted) are not shown anymore. This behavior can be configured via the settings icon next to the search box in the dashboard. [#3883](https://github.com/scalableminds/webknossos/pull/3883)
+- Added merger mode for skeleton and hybrid tracings. It allows to merge segments from e.g. generated segmentations. [#3619](https://github.com/scalableminds/webknossos/pull/3619)
- The HTML template now includes SEO tags for demo instances and hides internal instances from search engines.
-- A maximize-button was added to the viewports in the annotation view. [#3876](https://github.com/scalableminds/webknossos/pull/3876)
+- A maximize-button was added to the viewports in the annotation view. Maximization can also be toggled with the `.` shortcut. [#3876](https://github.com/scalableminds/webknossos/pull/3876)
+- [webknossos-connect](https://github.com/scalableminds/webknossos-connect) now starts with webKnossos on local and development instances by default. [#3913](https://github.com/scalableminds/webknossos/pull/3913)
### Changed
- Improved the flight mode performance for tracings with very large trees (>80.000 nodes). [#3880](https://github.com/scalableminds/webknossos/pull/3880)
- Tweaked the highlighting of the active node. The inner node looks exactly as a non-active node and is not round, anymore. An active node is circled by a "halo". In arbitrary mode, the halo is hidden and the active node is round. [#3868](https://github.com/scalableminds/webknossos/pull/3868)
+- Improved the performance of moving through a dataset which should make the overall interaction smoother. [#3902](https://github.com/scalableminds/webknossos/pull/3902)
- Brush size is independent of zoom value, now. This change simplifies volume annotations, as brush sizes can be adapted to certain structures (e.g., vesicles) and don't need to be changed when zooming. [#3868](https://github.com/scalableminds/webknossos/pull/3889)
+- Reworked the search in the trees tab. [#3878](https://github.com/scalableminds/webknossos/pull/3878)
### Fixed
- Fixed a bug where failed large save requests lead to inconsistent tracings on the server. [#3829](https://github.com/scalableminds/webknossos/pull/3829)
@@ -31,6 +36,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md).
- Fixed interpolation along z-axis. [#3888](https://github.com/scalableminds/webknossos/pull/3888)
- Fixed that the halo of the active node could cover other nodes. [#3919](https://github.com/scalableminds/webknossos/pull/3919)
- Fixed that the 3D viewport was partially occluded due to clipping distance issues. [#3919](https://github.com/scalableminds/webknossos/pull/3919)
+- Fixed that scrolling with the mouse wheel over a data viewport also scrolled the page. This bug appeared with the new Chrome version 73. [#3939](https://github.com/scalableminds/webknossos/pull/3939)
### Removed
- Removed FPS meter in Annotation View. [#3916](https://github.com/scalableminds/webknossos/pull/3916)
diff --git a/README.md b/README.md
index 4e9e793e0a2..92929c46d27 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,13 @@
# webKnossos
-Cellular-resolution connectomics is currently substantially limited by the throughput and efficiency of data analysis.
-Current solutions require an efficient integration of automated image analysis with massive manual data annotation.
-To scale such annotation efforts it is decisive to be able to crowd source data analysis online.
-Here we present **webKnossos**.
+
+webKnossos is an open-source tool for annotating and exploring large 3D image datasets.
-> Boergens, Berning, Bocklisch, Bräunlein, Drawitsch, Frohnhofen, Herold, Otto, Rzepka, Werkmeister, Werner, Wiese, Wissler and Helmstaedter
-webKnossos: efficient online 3D data annotation for connectomics.
-[Nature Methods (2017) DOI:10.1038/NMETH.4331.](https://www.nature.com/articles/nmeth.4331)
-
-![webKnossos logo](https://webknossos.org/images/oxalis.svg)
+* Fly through your data for fast skeletonization and proof-reading
+* Create 3D training data for automated segmentations efficiently
+* Scale data reconstruction projects with crowdsourcing workflows
+* Share datasets and annotations with collaborating scientists
+[Start using webKnossos](https://webknossos.org) - [User Documentation](https://docs.webknossos.org) - [Contact us](mailto:hello@scalableminds.com)
[![]( https://img.shields.io/circleci/project/github/scalableminds/webknossos/master.svg?logo=circleci)](https://circleci.com/gh/scalableminds/webknossos)
[![](https://img.shields.io/github/release/scalableminds/webknossos.svg)](https://github.com/scalableminds/webknossos/releases/latest)
@@ -28,7 +26,7 @@ webKnossos: efficient online 3D data annotation for connectomics.
* User and task management for high-throughput crowdsourcing
* Sharing and collaboration features
* [Standalone datastore component](https://github.com/scalableminds/webknossos/tree/master/webknossos-datastore) for flexible deployments
-* [Supported dataset formats: WKW (Optimized), KNOSSOS cubes](https://github.com/scalableminds/webknossos/wiki/Datasets), [Neuroglancer Precomputed, and BossDB](https://github.com/scalableminds/webknossos-connect)
+* [Supported dataset formats: WKW, KNOSSOS cubes](https://github.com/scalableminds/webknossos/wiki/Datasets), [Neuroglancer Precomputed, and BossDB](https://github.com/scalableminds/webknossos-connect)
* Supported image formats: Grayscale, Segmentation Maps, RGB, Multi-Channel
* [Support for 3D mesh rendering and on-the-fly isosurface generation](https://docs.webknossos.org/guides/mesh_visualization)
* [Documented frontend API for user scripts](https://webknossos.org/assets/docs/frontend-api/index.html), REST API for backend access
@@ -36,8 +34,15 @@ webKnossos: efficient online 3D data annotation for connectomics.
* [Docker-based deployment](https://hub.docker.com/r/scalableminds/webknossos/) for production and development
* [Detailed Documentation](https://docs.webknossos.org)
+## Publication
+> Boergens, Berning, Bocklisch, Bräunlein, Drawitsch, Frohnhofen, Herold, Otto, Rzepka, Werkmeister, Werner, Wiese, Wissler and Helmstaedter
+> webKnossos: efficient online 3D data annotation for connectomics.
+> [Nature Methods (2017) DOI:10.1038/NMETH.4331.](https://www.nature.com/articles/nmeth.4331)
+
+[Read more about the original publication.](https://publication.webknossos.org)
-## Development setup
+
+## Development installation
### Docker
This is the fastest way to try webKnossos.
Docker CE 17+ and Docker Compose 1.18+ is required.
@@ -141,9 +146,7 @@ yarn start
Will fetch all Scala, Java and node dependencies and run the application on Port 9000.
Make sure that the PostgreSQL and Redis services are running before you start the application.
-## Production setup
-[See wiki](https://github.com/scalableminds/webknossos/wiki/Production-setup) for recommended production setup.
-
+## Upgrades
For upgrades, please check the [changelog](CHANGELOG.md) & [migration guide](MIGRATIONS.md).
## Tests
diff --git a/app/controllers/AnnotationIOController.scala b/app/controllers/AnnotationIOController.scala
index 7c8cbc8b35b..4de688f9fea 100755
--- a/app/controllers/AnnotationIOController.scala
+++ b/app/controllers/AnnotationIOController.scala
@@ -125,10 +125,10 @@ class AnnotationIOController @Inject()(nmlWriter: NmlWriter,
dataSetName <- assertAllOnSameDataSet(skeletonTracings, volumeTracingsWithDataLocations.headOption.map(_._1)) ?~> "nml.file.differentDatasets"
organizationNameOpt <- assertAllOnSameOrganization(parseSuccesses.flatMap(s => s.organizationName)) ?~> "nml.file.differentDatasets"
organizationIdOpt <- Fox.runOptional(organizationNameOpt) {
- organizationDAO.findOneByName(_).map(_._id)
- } ?~> Messages("dataSet.noAccess", dataSetName) ~> FORBIDDEN
+ organizationDAO.findOneByName(_)(GlobalAccessContext).map(_._id)
+ } ?~> Messages("dataSet.notFound", dataSetName) ~> FORBIDDEN
organizationId <- Fox.fillOption(organizationIdOpt) {
- dataSetDAO.getOrganizationForDataSet(dataSetName)
+ dataSetDAO.getOrganizationForDataSet(dataSetName)(GlobalAccessContext)
} ?~> Messages("dataSet.noAccess", dataSetName) ~> FORBIDDEN
dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, organizationId) ?~> Messages(
"dataSet.noAccess",
diff --git a/app/controllers/DataSetController.scala b/app/controllers/DataSetController.scala
index fa90e2ba6ed..5c4bad0ec99 100755
--- a/app/controllers/DataSetController.scala
+++ b/app/controllers/DataSetController.scala
@@ -156,7 +156,7 @@ class DataSetController @Inject()(userService: UserService,
def accessList(organizationName: String, dataSetName: String) = sil.SecuredAction.async { implicit request =>
for {
- dataSet <- dataSetDAO.findOneByNameAndOrganization(dataSetName, request.identity._organization) ?~> Messages(
+ dataSet <- dataSetDAO.findOneByNameAndOrganizationName(dataSetName, organizationName) ?~> Messages(
"dataSet.notFound",
dataSetName) ~> NOT_FOUND
allowedTeams <- dataSetService.allowedTeamIdsFor(dataSet._id)
diff --git a/app/controllers/InitialDataController.scala b/app/controllers/InitialDataController.scala
index d835bc670bb..bc19efd576e 100644
--- a/app/controllers/InitialDataController.scala
+++ b/app/controllers/InitialDataController.scala
@@ -94,6 +94,7 @@ Samplecountry
for {
_ <- insertLocalDataStoreIfEnabled
_ <- insertLocalTracingStoreIfEnabled
+ _ <- insertConnectDataStoreIfEnabled
_ <- assertInitialDataEnabled
_ <- assertNoOrganizationsPresent
_ <- insertOrganization
@@ -211,6 +212,16 @@ Samplecountry
}
} else Fox.successful(())
+ def insertConnectDataStoreIfEnabled: Fox[Any] =
+ if (conf.Application.insertLocalConnectDatastore) {
+ dataStoreDAO.findOneByName("connect").futureBox.map { maybeStore =>
+ if (maybeStore.isEmpty) {
+ logger.info("inserting connect datastore")
+ dataStoreDAO.insertOne(DataStore("connect", "http://localhost:8000", "secret-key", isConnector = true))
+ }
+ }
+ } else Fox.successful(())
+
def insertLocalTracingStoreIfEnabled: Fox[Any] =
if (conf.Tracingstore.enabled) {
tracingStoreDAO.findOneByName("localhost").futureBox.map { maybeStore =>
diff --git a/app/models/binary/DataSet.scala b/app/models/binary/DataSet.scala
index 27c0d4c7ca7..debd5ed7c8a 100755
--- a/app/models/binary/DataSet.scala
+++ b/app/models/binary/DataSet.scala
@@ -259,7 +259,10 @@ class DataSetDAO @Inject()(sqlClient: SQLClient,
_ <- dataSetDataLayerDAO.updateLayers(old._id, source)
} yield ()
- def deactivateUnreported(names: List[String], organizationId: ObjectId, dataStoreName: String): Fox[Unit] = {
+ def deactivateUnreported(names: List[String],
+ organizationId: ObjectId,
+ dataStoreName: String,
+ unreportedStatus: String): Fox[Unit] = {
val inclusionPredicate =
if (names.isEmpty) "true" else s"name not in ${writeStructTupleWithQuotes(names.map(sanitize))}"
val deleteResolutionsQuery =
@@ -272,7 +275,7 @@ class DataSetDAO @Inject()(sqlClient: SQLClient,
and #${inclusionPredicate})"""
val setToUnusableQuery =
sqlu"""update webknossos.datasets
- set isUsable = false, status = 'No longer available on datastore.', scale = NULL
+ set isUsable = false, status = $unreportedStatus, scale = NULL
where _dataStore = ${dataStoreName} and _organization = ${organizationId}
and #${inclusionPredicate}"""
for {
diff --git a/app/models/binary/DataSetService.scala b/app/models/binary/DataSetService.scala
index 11da1bb859a..db387ebccf0 100644
--- a/app/models/binary/DataSetService.scala
+++ b/app/models/binary/DataSetService.scala
@@ -45,6 +45,8 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
extends FoxImplicits
with LazyLogging {
+ val unreportedStatus = "No longer available on datastore."
+
def isProperDataSetName(name: String): Boolean =
name.matches("[A-Za-z0-9_\\-]*")
@@ -171,7 +173,8 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
case Full(organization) =>
dataSetDAO.deactivateUnreported(dataSourcesByOrganizationName(organizationName).map(_.id.name),
organization._id,
- dataStoreName)
+ dataStoreName,
+ unreportedStatus)
case _ => {
logger.info(s"Ignoring reported dataset for non-existing organization $organizationName")
Fox.successful(())
@@ -252,11 +255,16 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
case _ => Fox.successful(0L)
}
- def allowedTeamIdsFor(_dataSet: ObjectId)(implicit ctx: DBAccessContext) =
- dataSetAllowedTeamsDAO.findAllForDataSet(_dataSet)(GlobalAccessContext) ?~> "allowedTeams.notFound"
+ def allowedTeamIdsFor(_dataSet: ObjectId)(implicit ctx: DBAccessContext): Fox[List[ObjectId]] =
+ dataSetAllowedTeamsDAO.findAllForDataSet(_dataSet) ?~> "allowedTeams.notFound"
- def allowedTeamsFor(_dataSet: ObjectId)(implicit ctx: DBAccessContext) =
- teamDAO.findAllForDataSet(_dataSet)(GlobalAccessContext) ?~> "allowedTeams.notFound"
+ def allowedTeamsFor(_dataSet: ObjectId, requestingUser: Option[User])(
+ implicit ctx: DBAccessContext): Fox[List[Team]] =
+ for {
+ teams <- teamDAO.findAllForDataSet(_dataSet) ?~> "allowedTeams.notFound"
+ // dont leak team names of other organizations
+ teamsFiltered = teams.filter(team => requestingUser.map(_._organization).contains(team._organization))
+ } yield teamsFiltered
def isEditableBy(
dataSet: DataSet,
@@ -271,22 +279,22 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
}
def publicWrites(dataSet: DataSet,
- userOpt: Option[User],
+ requestingUserOpt: Option[User],
skipResolutions: Boolean = false,
- requestingUserTeamManagerMemberships: Option[List[TeamMembership]] = None): Fox[JsObject] = {
- implicit val ctx = GlobalAccessContext
+ requestingUserTeamManagerMemberships: Option[List[TeamMembership]] = None)(
+ implicit ctx: DBAccessContext): Fox[JsObject] =
for {
- organization <- organizationDAO.findOne(dataSet._organization) ?~> "organization.notFound"
- teams <- allowedTeamsFor(dataSet._id)
+ organization <- organizationDAO.findOne(dataSet._organization)(GlobalAccessContext) ?~> "organization.notFound"
+ teams <- allowedTeamsFor(dataSet._id, requestingUserOpt)
teamsJs <- Fox.serialCombined(teams)(t => teamService.publicWrites(t))
logoUrl <- logoUrlFor(dataSet, Some(organization))
- isEditable <- isEditableBy(dataSet, userOpt, requestingUserTeamManagerMemberships)
- lastUsedByUser <- lastUsedTimeFor(dataSet._id, userOpt)
+ isEditable <- isEditableBy(dataSet, requestingUserOpt, requestingUserTeamManagerMemberships)
+ lastUsedByUser <- lastUsedTimeFor(dataSet._id, requestingUserOpt)
dataStore <- dataStoreFor(dataSet)
dataStoreJs <- dataStoreService.publicWrites(dataStore)
dataSource <- dataSourceFor(dataSet, Some(organization), skipResolutions)
publicationOpt <- Fox.runOptional(dataSet._publication)(publicationDAO.findOne(_))
- publicationJson <- Fox.runOptional(publicationOpt)(publicationService.publicWrites(_))
+ publicationJson <- Fox.runOptional(publicationOpt)(publicationService.publicWrites)
} yield {
Json.obj(
"name" -> dataSet.name,
@@ -305,9 +313,9 @@ class DataSetService @Inject()(organizationDAO: OrganizationDAO,
"sortingKey" -> dataSet.sortingKey,
"details" -> dataSet.details,
"publication" -> publicationJson,
+ "isUnreported" -> Json.toJson(dataSource.statusOpt.contains(unreportedStatus)),
"isForeign" -> dataStore.isForeign
)
}
- }
}
diff --git a/app/utils/WkConf.scala b/app/utils/WkConf.scala
index 8da45400f06..88abe2be36c 100644
--- a/app/utils/WkConf.scala
+++ b/app/utils/WkConf.scala
@@ -12,6 +12,7 @@ class WkConf @Inject()(configuration: Configuration) extends ConfigReader {
object Application {
val insertInitialData = get[Boolean]("application.insertInitialData")
+ val insertLocalConnectDatastore = get[Boolean]("application.insertLocalConnectDatastore")
val title = get[String]("application.title")
object Authentication {
@@ -125,5 +126,16 @@ class WkConf @Inject()(configuration: Configuration) extends ConfigReader {
}
val children =
- List(Application, Http, Mail, WebKnossos, Datastore, User, Braintracing, Features, Silhouette, Airbrake, Google)
+ List(Application,
+ Http,
+ Mail,
+ WebKnossos,
+ Datastore,
+ Tracingstore,
+ User,
+ Braintracing,
+ Features,
+ Silhouette,
+ Airbrake,
+ Google)
}
diff --git a/conf/application.conf b/conf/application.conf
index adfc452ccfa..b6897125780 100644
--- a/conf/application.conf
+++ b/conf/application.conf
@@ -64,6 +64,7 @@ braintracing {
application {
insertInitialData = true
+ insertLocalConnectDatastore = true
authentication {
enableDevAutoLogin = false
enableDevAutoAdmin = false
diff --git a/conf/connect/.gitignore b/conf/connect/.gitignore
new file mode 100644
index 00000000000..0ca687ac137
--- /dev/null
+++ b/conf/connect/.gitignore
@@ -0,0 +1 @@
+datasets.json
diff --git a/conf/connect/config.json b/conf/connect/config.json
new file mode 100644
index 00000000000..0b758441137
--- /dev/null
+++ b/conf/connect/config.json
@@ -0,0 +1,7 @@
+{
+ "server": {"host": "0.0.0.0", "port": 8000, "url": "http://localhost:8000"},
+ "datastore": {"name": "connect", "key": "secret-key"},
+ "webknossos": {"url": "http://localhost:9000"},
+ "backends": {"neuroglancer": {}},
+ "datasets_path": "data/datasets.json"
+}
diff --git a/docker-compose.yml b/docker-compose.yml
index 6623d558908..cf0b0d13f3c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -12,11 +12,14 @@ services:
- "fossildb-persisted:fossildb"
- "postgres-persisted:postgres"
- redis
+ - webknossos-connect
depends_on:
postgres-persisted:
condition: service_healthy
fossildb-persisted:
condition: service_healthy
+ webknossos-connect:
+ condition: service_healthy
command:
- -Dconfig.file=conf/application.conf
- -Djava.net.preferIPv4Stack=true
@@ -122,11 +125,14 @@ services:
- "fossildb-dev:fossildb"
- "postgres-dev:postgres"
- redis
+ - webknossos-connect
depends_on:
postgres-dev:
condition: service_healthy
fossildb-dev:
condition: service_healthy
+ webknossos-connect:
+ condition: service_healthy
environment:
- POSTGRES_URL=jdbc:postgresql://postgres/webknossos
command:
@@ -166,11 +172,14 @@ services:
- postgres
- fossildb
- redis
+ - webknossos-connect
depends_on:
postgres:
condition: service_healthy
fossildb:
condition: service_healthy
+ webknossos-connect:
+ condition: service_healthy
environment:
- POSTGRES_URL=jdbc:postgresql://postgres/webknossos_testing
command:
@@ -195,6 +204,13 @@ services:
- ".:/home/pptruser/webknossos"
user: ${USER_UID:-1000}:${USER_GID:-1000}
+ # webKnossos-connect
+ webknossos-connect:
+ image: scalableminds/webknossos-connect:master__190
+ volumes:
+ - "./conf/connect:/app/data"
+ network_mode: host
+
# Postgres
postgres:
image: postgres:10-alpine
diff --git a/docs/hello.md b/docs/hello.md
index ed9f24ac54a..e0cd5f87cfe 100644
--- a/docs/hello.md
+++ b/docs/hello.md
@@ -5,7 +5,7 @@ The web-based tool is powered by a specialized data-delivery backend that stores
webKnossos has a GPU-accelerated viewer that includes tools for creating and sharing annotations (skeletons and volumes).
Powerful [user](./users.md) and [task](./tasks.md) management features automatically distribute tasks to human annotators.
There are a lot of productivity improvements to make the human part as efficient as possible.
-webKnossos is also a platform for [showcasing datasets](https://demo.webknossos.org) alongside a paper publication.
+webKnossos is also a platform for [showcasing datasets](https://webknossos.org) alongside a paper publication.
> Boergens, Berning, Bocklisch, Bräunlein, Drawitsch, Frohnhofen, Herold, Otto, Rzepka, Werkmeister, Werner, Wiese, Wissler and Helmstaedter
webKnossos: efficient online 3D data annotation for connectomics.
@@ -14,24 +14,21 @@ webKnossos: efficient online 3D data annotation for connectomics.
![webKnossos logo](https://webknossos.org/images/oxalis.svg)
## Demo
-Dataset gallery: [https://demo.webknossos.org/](https://demo.webknossos.org/)
-Try webKnossos yourself: [https://try.webknossos.org/](https://try.webknossos.org/)
+Try webKnossos on a large selection of published datasets: [https://webknossos.org/](https://webknossos.org/)
## Features
* Exploration of large 3D image datasets
* Fully browser-based user experience with efficient data streaming
-* Creation/editing of [skeleton and volume annotations](./tracing_ui.md)
+* Creation/editing of skeleton and volume annotations
* [Innovative flight mode for fast skeleton tracing](https://www.nature.com/articles/nmeth.4331)
-* Optimized performance for large datasets and annotations
-* [User](./users.md) and [task](./tasks.md) management for high-throughput crowdsourcing
-* [Sharing and collaboration](./sharing.md) features
-* Efficient [keyboard shortcuts](./keyboard_shortcuts.md)
-* Undo/Redo functionality
+* Optimized performance for large tracings
+* User and task management for high-throughput crowdsourcing
+* Sharing and collaboration features
* [Standalone datastore component](https://github.com/scalableminds/webknossos/tree/master/webknossos-datastore) for flexible deployments
-* [Supported dataset formats](./datasets.md): WKW (Optimized), KNOSSOS cubes
+* [Supported dataset formats: WKW (Optimized), KNOSSOS cubes](https://github.com/scalableminds/webknossos/wiki/Datasets), [Neuroglancer Precomputed, and BossDB](https://github.com/scalableminds/webknossos-connect)
* [Supported image formats](./data_formats.md): Grayscale, Segmentation Maps, RGB, Multi-Channel
* [Mesh Visualization](./mesh_visualization.md)
-* [Documented frontend API for user scripts](https://demo.webknossos.org/assets/docs/frontend-api/index.html), REST API for backend access
+* [Documented frontend API for user scripts](https://webknossos.org/assets/docs/frontend-api/index.html), REST API for backend access
* Open-source development with [automated test suite](https://circleci.com/gh/scalableminds/webknossos)
* [Docker-based deployment](https://hub.docker.com/r/scalableminds/webknossos/) for production and development
diff --git a/docs/keyboard_shortcuts.md b/docs/keyboard_shortcuts.md
index 9372a6452e1..407447b58a3 100644
--- a/docs/keyboard_shortcuts.md
+++ b/docs/keyboard_shortcuts.md
@@ -18,6 +18,8 @@ Find all available keyboard shortcuts for webKnossos listed below.
| 3 | Toggle Segmentation Opacity |
| H | Increase the Move Value |
| G | Decrease the Move Value |
+| Q | Download Screenshot(s) of Viewport(s) |
+| . | Toggle Viewport Maximization |
## Skeleton Tracings
diff --git a/docs/sharing.md b/docs/sharing.md
index b46a0dc9c3f..af51af8e489 100644
--- a/docs/sharing.md
+++ b/docs/sharing.md
@@ -9,7 +9,7 @@ To manage access rights to certain datasets for webKnossos users check out [the
Dataset sharing allows outside users to view your datasets and segmentation layers within webKnossos.
Shared resources can be accessed through a direct URL or can be featured on a spotlight gallery for showcasing your work.
-[Please contact us](mailto:hello@scalableminds.com) to feature your dataset on https://demo.webknossos.org.
+[Please contact us](mailto:hello@scalableminds.com) to feature your dataset on https://webknossos.org.
Sharing a dataset is useful for multiple scenarios:
- You recorded a novel microscopy dataset and want to include links to it in your paper or for reviewers. Use wklink.org to shorten these URLs, e.g. https://wklink.org/5386 ([contact us](mailto:hello@scalableminds.com)).
diff --git a/docs/tracing_ui.md b/docs/tracing_ui.md
index 95df635a736..ced0b8a1807 100644
--- a/docs/tracing_ui.md
+++ b/docs/tracing_ui.md
@@ -19,7 +19,7 @@ The most common buttons are:
- `Archive`: Only available for Explorative Annotations. Closes the annotation and archives it, removing it from a user's dashboard. Archived annotations can be found on a user's dashboard under "Explorative Annotations" and by clicking on "Show Archived Annotations". Use this to declutter your dashboard.
- `Download`: Starts the download of the current annotation. Skeleton annotations are downloaded as [NML](./data_formats.md#nml) files. Volume annotation downloads contain the raw segmentation data as [WKW](./data_formats.md#wkw) files.
- `Share`: Create a shareable link to your dataset containing the current position, rotation, zoom level etc. Use this to collaboratively work with colleagues. Read more about this feature in the [Sharing guide](./sharing.md).
-- `Add Script`: Using the [webKnossos frontend API](https://demo.webknossos.org/assets/docs/frontend-api/index.html) users can interact with webKnossos programmatically. User scripts can be executed from here. Admins can add often used scripts to webKnossos to make them available to all users for easy access.
+- `Add Script`: Using the [webKnossos frontend API](https://webknossos.org/assets/docs/frontend-api/index.html) users can interact with webKnossos programmatically. User scripts can be executed from here. Admins can add often used scripts to webKnossos to make them available to all users for easy access.
- `Restore Older Version`: Opens a view that shows all previous versions of a tracing. From this view, any older version can be selected, previewed, and restored.
A user can directly jump to positions within their datasets by entering them in the position input field.
@@ -209,6 +209,10 @@ In the `Segmentation` tab on the right-hand side, you can see the cell IDs which
![Adding labels with the Brush tool](./images/volume_brush.gif)
![Removing labels with the Brush tool](./images/volume_delete.gif)
+### Merging Segments
+
+Segments from e.g. automatic segmentations can be merged with the merger mode to refine the result. The merger mode is available in skeleton and hybrid tracings. Each tree represents a merged segment and its nodes define the segments that are merged together. So each new merged segment needs its own tree. The merger mode can be enabled in the settings in the category Nodes & Trees under the option Enable Merger Mode. As soon as you enable it, all already existing trees will be used to form merged segments.
+
## Hybrid Annotations
Hybrid annotations combine the functionality of skeleton and volume annotations.
@@ -284,3 +288,4 @@ For multi-layer datasets, each layer can be adjusted separately.
- `4 Bit`: Toggles data download from the server using only 4 Bit instead of 8 Bit for each pixel. Use this to reduce the amount of necessary internet bandwidth for webKnossos. Useful for showcasing data on the go over cellular networks, e.g 3G.
- `Interpolation`: When interpolation is enabled, bilinear filtering is applied while rendering pixels between two voxels. As a result, data may look "smoother" (or blurry when being zoomed in very far). Without interpolation, data may look more "crisp" (or pixelated when being zomed in very far).
- `Render Missing Data Black`: If a dataset doesn't contain data at a specific position, webKnossos can either render "black" at that position or it can try to render data from another magnification.
+
diff --git a/frontend/javascripts/admin/api_flow_types.js b/frontend/javascripts/admin/api_flow_types.js
index 2f7d0f9217e..2a58b4e5738 100644
--- a/frontend/javascripts/admin/api_flow_types.js
+++ b/frontend/javascripts/admin/api_flow_types.js
@@ -111,6 +111,7 @@ export type APIDatasetDetails = {
};
type APIDatasetBase = APIDatasetId & {
+ +isUnreported: boolean,
+allowedTeams: Array,
+created: number,
+dataStore: APIDataStore,
diff --git a/frontend/javascripts/admin/auth/change_password_view.js b/frontend/javascripts/admin/auth/change_password_view.js
index 0b488d3f238..96ed10301f5 100644
--- a/frontend/javascripts/admin/auth/change_password_view.js
+++ b/frontend/javascripts/admin/auth/change_password_view.js
@@ -58,7 +58,7 @@ class ChangePasswordView extends React.PureComponent {
checkPassword = (rule, value, callback) => {
const form = this.props.form;
if (value && value !== form.getFieldValue("password.password1")) {
- callback(messages["auth.registration_password_missmatch"]);
+ callback(messages["auth.registration_password_mismatch"]);
} else {
callback();
}
diff --git a/frontend/javascripts/admin/auth/finish_reset_password_view.js b/frontend/javascripts/admin/auth/finish_reset_password_view.js
index bf654408962..1d954451354 100644
--- a/frontend/javascripts/admin/auth/finish_reset_password_view.js
+++ b/frontend/javascripts/admin/auth/finish_reset_password_view.js
@@ -44,21 +44,21 @@ class FinishResetPasswordView extends React.PureComponent {
};
handleConfirmBlur = (e: SyntheticInputEvent<>) => {
- const value = e.target.value;
+ const { value } = e.target;
this.setState(prevState => ({ confirmDirty: prevState.confirmDirty || !!value }));
};
checkPassword = (rule, value, callback) => {
- const form = this.props.form;
+ const { form } = this.props;
if (value && value !== form.getFieldValue("password.password1")) {
- callback(messages["auth.registration_password_missmatch"]);
+ callback(messages["auth.registration_password_mismatch"]);
} else {
callback();
}
};
checkConfirm = (rule, value, callback) => {
- const form = this.props.form;
+ const { form } = this.props;
if (value && this.state.confirmDirty) {
form.validateFields(["confirm"], { force: true });
}
diff --git a/frontend/javascripts/admin/auth/registration_form.js b/frontend/javascripts/admin/auth/registration_form.js
index 6c82b005d52..f3244729f8e 100644
--- a/frontend/javascripts/admin/auth/registration_form.js
+++ b/frontend/javascripts/admin/auth/registration_form.js
@@ -103,7 +103,7 @@ class RegistrationForm extends React.PureComponent {
checkPassword = (rule, value, callback) => {
const { form } = this.props;
if (value && value !== form.getFieldValue("password.password1")) {
- callback(messages["auth.registration_password_missmatch"]);
+ callback(messages["auth.registration_password_mismatch"]);
} else {
callback();
}
diff --git a/frontend/javascripts/admin/help/keyboardshortcut_view.js b/frontend/javascripts/admin/help/keyboardshortcut_view.js
index 33bb91b4d1b..1f2d1f1f692 100644
--- a/frontend/javascripts/admin/help/keyboardshortcut_view.js
+++ b/frontend/javascripts/admin/help/keyboardshortcut_view.js
@@ -99,7 +99,7 @@ const KeyboardShortcutView = () => {
},
{
keybinding: "M",
- action: "Toggle Mode (orthogonal, Flight, Oblique)",
+ action: "Toggle Mode (Orthogonal, Flight, Oblique)",
},
{
keybinding: "1",
@@ -125,6 +125,14 @@ const KeyboardShortcutView = () => {
keybinding: "Ctrl + Shift + F",
action: "Open Tree Search (if Tree List is visible)",
},
+ {
+ keybinding: "Q",
+ action: "Download Screenshot(s) of Viewport(s)",
+ },
+ {
+ keybinding: ".",
+ action: "Toggle Viewport Maximization",
+ },
];
const flightModeShortcuts = [
diff --git a/frontend/javascripts/components/brain_spinner.js b/frontend/javascripts/components/brain_spinner.js
index 41dad46cc6d..3a09d114529 100644
--- a/frontend/javascripts/components/brain_spinner.js
+++ b/frontend/javascripts/components/brain_spinner.js
@@ -19,7 +19,7 @@ export default function BrainSpinner() {
+ }
+ >
+ You just enabled the merger mode. This mode allows to merge segmentation cells by creating
+ trees and nodes. Each tree maps the marked segments (the ones where nodes were created in) to
+ one new segment. Create separate trees for different segements.
+
+
+ Additionally available keyboard shortcuts:
+
+
+
+
8
+
+ Replace the color of the current active tree and its mapped segments with a new one
+