├── .Rhistory ├── minerva_analysis ├── test.py ├── client │ ├── .gitignore │ ├── src │ │ ├── js │ │ │ ├── services │ │ │ │ ├── passVariablesToFrontend.js │ │ │ │ ├── simpleEventHandler.js │ │ │ │ ├── colorScheme.js │ │ │ │ └── numericData.js │ │ │ ├── vendor.js │ │ │ ├── views │ │ │ │ └── lensingFilters │ │ │ │ │ ├── lfDataColors.js │ │ │ │ │ └── lfSegmentationOutlines.js │ │ │ └── main.js │ │ ├── img │ │ │ ├── logl.ai │ │ │ ├── logo_with_text.ai │ │ │ ├── logo.svg │ │ │ └── logo_with_text.svg │ │ ├── shaders │ │ │ └── vert.glsl │ │ └── css │ │ │ ├── lensing.css │ │ │ ├── gating.css │ │ │ └── viewer.css │ ├── test │ │ ├── fixtures │ │ │ ├── passVariablesToFrontend.js │ │ │ ├── context.html │ │ │ └── main.html │ │ ├── data │ │ │ ├── 1024x1024_logo.png │ │ │ ├── 1024x1024_clear.png │ │ │ ├── cell_hoechst.bin.gz │ │ │ ├── 1024x1024_not_clear.png │ │ │ ├── cell_id_center.bin.gz │ │ │ ├── get_channel_names │ │ │ │ └── short.json │ │ │ ├── formData │ │ │ │ ├── download_channels.json │ │ │ │ ├── gated_cell_encodings.json │ │ │ │ └── gated_channel_ranges.json │ │ │ ├── get_gating_gmm │ │ │ │ └── Hoechst0.json │ │ │ ├── config.json │ │ │ └── get_channel_gmm │ │ │ │ └── Hoechst0.json │ │ ├── tsconfig.json │ │ └── globals │ │ │ └── set_globals.js │ ├── external │ │ └── openseadragon-bin-2.4.0 │ │ │ ├── images │ │ │ ├── flip_rest.png │ │ │ ├── home_rest.png │ │ │ ├── next_rest.png │ │ │ ├── button_rest.png │ │ │ ├── flip_hover.png │ │ │ ├── home_hover.png │ │ │ ├── next_hover.png │ │ │ ├── zoomin_rest.png │ │ │ ├── button_hover.png │ │ │ ├── button_pressed.png │ │ │ ├── flip_pressed.png │ │ │ ├── fullpage_hover.png │ │ │ ├── fullpage_rest.png │ │ │ ├── home_pressed.png │ │ │ ├── next_pressed.png │ │ │ ├── previous_hover.png │ │ │ ├── previous_rest.png │ │ │ ├── zoomin_hover.png │ │ │ ├── zoomin_pressed.png │ │ │ ├── zoomout_hover.png │ │ │ ├── zoomout_rest.png │ │ │ ├── flip_grouphover.png │ │ │ ├── fullpage_pressed.png │ │ │ ├── home_grouphover.png │ │ │ ├── next_grouphover.png │ │ │ ├── previous_pressed.png │ │ │ ├── rotateleft_hover.png │ │ │ ├── rotateleft_rest.png │ │ │ ├── rotateright_rest.png │ │ │ ├── zoomout_pressed.png │ │ │ ├── button_grouphover.png │ │ │ ├── fullpage_grouphover.png │ │ │ ├── previous_grouphover.png │ │ │ ├── rotateleft_pressed.png │ │ │ ├── rotateright_hover.png │ │ │ ├── rotateright_pressed.png │ │ │ ├── zoomin_grouphover.png │ │ │ ├── zoomout_grouphover.png │ │ │ ├── rotateleft_grouphover.png │ │ │ └── rotateright_grouphover.png │ │ │ ├── openseadragon-flat-toolbar-icons-master │ │ │ ├── Photoshop │ │ │ │ ├── home.psd │ │ │ │ ├── next.psd │ │ │ │ ├── zoom.psd │ │ │ │ ├── Toolbar.png │ │ │ │ ├── fullpage.psd │ │ │ │ ├── previous.psd │ │ │ │ ├── rotateleft.psd │ │ │ │ └── rotateright.psd │ │ │ ├── images │ │ │ │ ├── home_rest.png │ │ │ │ ├── next_rest.png │ │ │ │ ├── home_hover.png │ │ │ │ ├── next_hover.png │ │ │ │ ├── zoomin_rest.png │ │ │ │ ├── fullpage_hover.png │ │ │ │ ├── fullpage_rest.png │ │ │ │ ├── home_pressed.png │ │ │ │ ├── next_pressed.png │ │ │ │ ├── previous_hover.png │ │ │ │ ├── previous_rest.png │ │ │ │ ├── zoomin_hover.png │ │ │ │ ├── zoomin_pressed.png │ │ │ │ ├── zoomout_hover.png │ │ │ │ ├── zoomout_rest.png │ │ │ │ ├── fullpage_pressed.png │ │ │ │ ├── home_grouphover.png │ │ │ │ ├── next_grouphover.png │ │ │ │ ├── previous_pressed.png │ │ │ │ ├── rotateleft_hover.png │ │ │ │ ├── rotateleft_rest.png │ │ │ │ ├── rotateright_rest.png │ │ │ │ ├── zoomout_pressed.png │ │ │ │ ├── fullpage_grouphover.png │ │ │ │ ├── previous_grouphover.png │ │ │ │ ├── rotateleft_pressed.png │ │ │ │ ├── rotateright_hover.png │ │ │ │ ├── rotateright_pressed.png │ │ │ │ ├── zoomin_grouphover.png │ │ │ │ ├── zoomout_grouphover.png │ │ │ │ ├── rotateleft_grouphover.png │ │ │ │ └── rotateright_grouphover.png │ │ │ ├── .gitattributes │ │ │ ├── README.md │ │ │ └── LICENSE.txt │ │ │ ├── openseadragonrgb.js │ │ │ ├── LICENSE.txt │ │ │ ├── openseadragon-svg-overlay.js │ │ │ ├── openSeadragonGL.js │ │ │ ├── canvas-overlay-hd.js │ │ │ └── viaWebGL.js │ ├── dist │ │ ├── 713c134ff47cd328a5b711526f151082.svg │ │ ├── 693b935cb1f907814be9c6199a68c217.svg │ │ └── ff743f408972e0e96ca9153ed5ae4c83.svg │ ├── webpack.config.js │ ├── package.json │ ├── templates │ │ ├── channel_match.html │ │ ├── base.html │ │ ├── index.html │ │ └── upload.html │ └── karma.conf.js ├── server │ ├── utils │ │ ├── addHEColumns.py │ │ ├── mostFrequentLongestSubstring.py │ │ ├── fullConversion.py │ │ ├── pre_normalization.py │ │ ├── smallestenclosingcircle.py │ │ └── pyramid_upgrade.py │ ├── routes │ │ └── page_routes.py │ └── models │ │ └── database_model.py └── __init__.py ├── .flaskenv ├── icon.ico ├── requirements.yml ├── package_win.bat ├── Dockerfile ├── package_mac.sh ├── .gitignore ├── run.py ├── LICENSE ├── .github └── workflows │ ├── dockerhub.yml │ └── release.yml ├── minerva_analysis_mac.spec └── README.md /.Rhistory: -------------------------------------------------------------------------------- 1 | q() 2 | -------------------------------------------------------------------------------- /minerva_analysis/test.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.flaskenv: -------------------------------------------------------------------------------- 1 | FLASK_APP=app.py 2 | FLASK_ENV=development 3 | FLASK_RUN_PORT=5005 -------------------------------------------------------------------------------- /icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/icon.ico -------------------------------------------------------------------------------- /minerva_analysis/client/.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .editorconfig 3 | .eslintignore 4 | .eslintrc.json 5 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/services/passVariablesToFrontend.js: -------------------------------------------------------------------------------- 1 | function passVariablesToFrontend(vars) { 2 | return vars 3 | } -------------------------------------------------------------------------------- /minerva_analysis/client/test/fixtures/passVariablesToFrontend.js: -------------------------------------------------------------------------------- 1 | const flaskVariables = { 2 | datasource: "karma-test" 3 | } 4 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/img/logl.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/src/img/logl.ai -------------------------------------------------------------------------------- /minerva_analysis/client/src/img/logo_with_text.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/src/img/logo_with_text.ai -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/1024x1024_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/test/data/1024x1024_logo.png -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/1024x1024_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/test/data/1024x1024_clear.png -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/cell_hoechst.bin.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/test/data/cell_hoechst.bin.gz -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/1024x1024_not_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/test/data/1024x1024_not_clear.png -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/cell_id_center.bin.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/test/data/cell_id_center.bin.gz -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/flip_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/home_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/next_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/button_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/fullpage_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/previous_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomin_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/zoomout_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateleft_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/images/rotateright_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/src/shaders/vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | in vec2 a_uv; 3 | out vec2 uv; 4 | 5 | void main() { 6 | // Texture coordinates 7 | uv = a_uv; 8 | 9 | // Clip coordinates 10 | vec2 full_pos = 2. * a_uv - 1.; 11 | gl_Position = vec4(full_pos, 0., 1.); 12 | } 13 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/labsyspharm/minerva_analysis/HEAD/minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png -------------------------------------------------------------------------------- /minerva_analysis/client/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "es6", 5 | "moduleResolution": "node", 6 | "esModuleInterop": true, 7 | "noImplicitAny": false, 8 | "module": "commonjs", 9 | "strict": false, 10 | "noEmit": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /minerva_analysis/server/utils/addHEColumns.py: -------------------------------------------------------------------------------- 1 | #%% 2 | import pandas as pd 3 | 4 | data = pd.read_csv('./unmicst-WD-76845-097-ij_subtracted_50.csv', encoding="utf-8-sig") 5 | data.head() 6 | #%% 7 | data['HE_r'] = 0 8 | data['HE_g'] = 0 9 | data['HE_b'] = 0 10 | data.columns 11 | #%% 12 | data.to_csv('./unmicst-WD-76845-097-ij_subtracted_50-jj.csv', index=False) 13 | #%% 14 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/globals/set_globals.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an annendum to ../../src/js/main.js 3 | * This reassigns all defined global variables 4 | */ 5 | //EVENTHANDLER 6 | window.eventHandler = eventHandler; 7 | window.datasource = datasource; 8 | //VIEWS 9 | window.__minervaAnalysis = __minervaAnalysis; 10 | //DATA MANAGEMENT 11 | window.dataSrcIndex = dataSrcIndex; 12 | //CHANNELS 13 | window.imageChannels = imageChannels; 14 | window.imageChannelsIdx = imageChannelsIdx; 15 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/get_channel_names/short.json: -------------------------------------------------------------------------------- 1 | ["Hoechst0","AF488","AF555","AF647","Hoechst1","AF488","A555","A647","Hoechst2","anti_CD3","anti_NaKATPase","anti_CD45RO","Hoechst3","Ki67_488","Keratin_570","aSMA_660","Hoechst4","CD4_488","CD45_PE","PD1_647","Hoechst5","CD20_488","CD68_555","CD8a_660","Hoechst6","CD163_488","FOXP3_570","PDL1_647","Hoechst7","Ecad_488","Vimentin_555","CDX2_647","Hoechst8","LaminABC_488","Desmin_555","CD31_647","Hoechst9","PCNA_488","Ki67_570","CollagenIV_647","HE_r","HE_g","HE_b"] 2 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md: -------------------------------------------------------------------------------- 1 | # OpenSeadragon Flat Toolbar Icons 2 | Simple, flat design toolbar icons for OpenSeadragon 3 | [https://github.com/openseadragon/openseadragon](https://github.com/openseadragon/openseadragon) 4 | 5 | Demo: 6 | [http://droneservices.getyournet.ch/beispiele-luftaufnahmen-mit-drohne](http://droneservices.getyournet.ch/beispiele-luftaufnahmen-mit-drohne) 7 | 8 | ![alt tag](https://cloud.githubusercontent.com/assets/5383762/9150698/70f23bfe-3de8-11e5-961e-5239fa67e94e.png) 9 | 10 | -------------------------------------------------------------------------------- /requirements.yml: -------------------------------------------------------------------------------- 1 | name: minerva_analysis 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - appdirs 6 | - flask=2.2.2 7 | - flask-sqlalchemy=3.0.2 8 | - itsdangerous=2.1.2 9 | - jinja2 10 | - matplotlib 11 | - numpy 12 | - ome-types 13 | - orjson 14 | - pandas 15 | - pillow=8.1 16 | - pip 17 | - pyinstaller 18 | - python=3.9.15 19 | - requests 20 | - scikit-image 21 | - scikit-learn 22 | - scipy 23 | - tifffile=2021.4.8 24 | - waitress 25 | - werkzeug=2.2.2 26 | - zarr=2.10 27 | 28 | - pip: 29 | - opencv-python==4.7.0.68 30 | 31 | 32 | -------------------------------------------------------------------------------- /package_win.bat: -------------------------------------------------------------------------------- 1 | pyinstaller -F --paths $env:CONDA_PREFIX --add-data "minerva_analysis/client;minerva_analysis/client" --add-data "minerva_analysis/server;minerva_analysis/server" --add-data "%CONDA_PREFIX%/Lib/site-packages/xmlschema/schemas;xmlschema/schemas" --add-data "%CONDA_PREFIX%/Lib/site-packages/ome_types;ome_types" --hidden-import "scipy.spatial.transform._rotation_groups" --hidden-import cmath --hidden-import="sqlalchemy.sql.default_comparator" --hidden-import="sklearn.utils._typedefs" --hidden-import="sklearn.neighbors._partition_nodes" --hidden-import="sklearn.neighbors.ball_tree" --icon icon.ico --name minerva_analysis_windows run.py 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y python3-opencv && \ 5 | rm -rf /var/lib/apt/lists/* 6 | 7 | RUN python -m pip install \ 8 | Flask==1.1.2 \ 9 | jinja2==3.0.3 \ 10 | werkzeug==2.0.3 \ 11 | itsdangerous==2.0.1 \ 12 | flask-sqlalchemy \ 13 | numpy \ 14 | opencv-python \ 15 | orjson \ 16 | pandas \ 17 | pillow==8.1.1 \ 18 | requests \ 19 | scikit-learn \ 20 | scikit-image \ 21 | scipy \ 22 | tifffile==2021.4.8 \ 23 | waitress \ 24 | zarr==2.10.3 \ 25 | ome-types \ 26 | opencv-python==4.5.3.56 27 | 28 | COPY . /app 29 | 30 | CMD ["python", "/app/run.py"] 31 | -------------------------------------------------------------------------------- /package_mac.sh: -------------------------------------------------------------------------------- 1 | pyinstaller -F --paths $CONDA_PREFIX --add-data "minerva_analysis/client:minerva_analysis/client" --add-data "minerva_analysis/__init__.py:minerva_analysis/" --add-data "minerva_analysis/server:minerva_analysis/server" --add-data "$CONDA_PREFIX/lib/python3.7/site-packages/xmlschema/schemas:xmlschema/schemas" --add-data "$CONDA_PREFIX/lib/python3.7/site-packages/ome_types:ome_types" --hidden-import "scipy.spatial.transform._rotation_groups" --hidden-import "sqlalchemy.sql.default_comparator" --hidden-import "sklearn.neighbors._partition_nodes" --hidden-import "sklearn.neighbors.ball_tree" --hidden-import "sklearn.utils._typedefs" --hidden-import cmath --name minerva_analysis_mac run.py 2 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/services/simpleEventHandler.js: -------------------------------------------------------------------------------- 1 | 2 | class SimpleEventHandler { 3 | 4 | constructor(subject) { 5 | this.subject = subject; 6 | this.eventListeners = []; 7 | } 8 | 9 | bind(eventNames, eventFunction) { 10 | for (const eventName of eventNames.split(' ')) { 11 | this.eventListeners.push({eventName, eventFunction}); 12 | const eventFunctionWrap = e => eventFunction(e.detail, e); 13 | this.subject.addEventListener(eventName, eventFunctionWrap, false); 14 | } 15 | } 16 | 17 | getListeners() { 18 | return this.eventListeners; 19 | } 20 | 21 | trigger(eventName, detail) { 22 | this.subject.dispatchEvent(new CustomEvent(eventName, {detail})); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | */node_modules 6 | static/frontend/node_modules 7 | minerva_analysis/client/node_modules/ 8 | /.pnp 9 | .pnp.js 10 | *.sqlite3 11 | */.ipynb_checkpoints/ 12 | minerva_analysis/data/ 13 | # testing 14 | /coverage 15 | /dist 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | .env.local 23 | .env.development.local 24 | .env.test.local 25 | .env.production.local 26 | 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | .idea/ 31 | /data 32 | /data/ 33 | minerva_analysis/data 34 | 35 | /minerva_analysis//data 36 | /static/data 37 | /static/data/ 38 | /__pycache__/ 39 | /__pycache__/* 40 | */__pycache__/* 41 | *__pycache__* 42 | *.pyc 43 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from waitress import serve 4 | 5 | from minerva_analysis import app 6 | 7 | if __name__ == '__main__': 8 | # use port 8000 if no port is specified via command line argument 9 | port = 8000 if len(sys.argv) < 2 or not str.isdigit(sys.argv[1]) else sys.argv[1] 10 | 11 | 12 | def str2bool(v): 13 | return v.lower() in ("yes", "true", "t", "1") 14 | 15 | 16 | if len(sys.argv) > 2 and str2bool(sys.argv[2]): 17 | is_docker = True 18 | else: 19 | is_docker = False 20 | app.config['IS_DOCKER'] = is_docker 21 | 22 | print('Serving on 0.0.0.0:' + str(port) + ' or http://localhost:' + str(port)) 23 | serve(app, host='0.0.0.0', port=port, max_request_body_size=1073741824000000, 24 | max_request_header_size=85899345920000) 25 | -------------------------------------------------------------------------------- /minerva_analysis/client/dist/713c134ff47cd328a5b711526f151082.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/services/colorScheme.js: -------------------------------------------------------------------------------- 1 | class ColorScheme { 2 | constructor(dataLayer) { 3 | this.dataLayer = dataLayer; 4 | this.colorMap = {}; 5 | } 6 | 7 | async init() { 8 | await this.getColorScheme(false); 9 | } 10 | 11 | async getColorScheme(refresh) { 12 | try { 13 | this.colorMap = await this.dataLayer.getColorScheme(refresh); 14 | // console.log("Loaded Color Scheme"); 15 | } catch (e) { 16 | console.log("Error Getting Color Palette", e) 17 | 18 | } 19 | } 20 | } 21 | 22 | // colorScheme.classColors = { 23 | // '-': '#ffa500', 24 | // 0: '#1f78b4', 25 | // 1: '#e31a1c', 26 | // 2: '#33a02c', 27 | // 3: '#6a3d9a', 28 | // 4: '#b15928', 29 | // 5: '#ffff99', 30 | // 6: '#a6cee3', 31 | // 7: '#b2df8a', 32 | // 8: '#fb9a99', 33 | // 9: '#cab2d6' 34 | // } 35 | // 36 | // colorScheme. 37 | // //colorScheme.classrColors = {'-' : [255,165,0], "0" : [141,211,199], "1" : [255,255,179], "2" : [190,186,218], "3": [251,128,114] } 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /minerva_analysis/client/dist/693b935cb1f907814be9c6199a68c217.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/vendor.js: -------------------------------------------------------------------------------- 1 | import {Buffer} from 'buffer/'; 2 | import {PNG} from 'pngjs' 3 | import 'popper.js' 4 | import 'jquery' 5 | import 'bootstrap' 6 | import 'bootstrap/dist/css/bootstrap.min.css'; 7 | import 'pngjs' 8 | import {regeneratorRuntime} from "regenerator-runtime"; 9 | import * as d3 from 'd3'; 10 | import {sliderBottom} from 'd3-simple-slider'; 11 | import 'lodash' 12 | import 'jquery-form' 13 | import '@fortawesome/fontawesome-free/js/all' 14 | import Sortable from 'sortablejs'; 15 | import Mark from 'mark.js' 16 | import $ from 'jquery' 17 | import 'node-fetch' 18 | import convert from 'color-convert' 19 | import 'viawebgl' 20 | import * as viaWebGL from 'viawebgl'; 21 | import {ViewerManager} from './views/viewerManager'; 22 | import Dropzone from 'dropzone'; 23 | 24 | window.convert = convert; 25 | window.$ = $; 26 | window.d3 = d3; 27 | window.d3.sliderBottom = sliderBottom; 28 | window.PNG = PNG; 29 | window.Buffer = Buffer; 30 | window.Sortable = Sortable; 31 | window.Mark = Mark; 32 | window.OpenSeadragon = viaWebGL.OpenSeadragon; 33 | window.Dropzone = Dropzone; 34 | window.viaWebGL = viaWebGL; 35 | window.ViewerManager = ViewerManager; 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Laboratory of Systems Pharmacology, Harvard Medical School 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /minerva_analysis/server/utils/mostFrequentLongestSubstring.py: -------------------------------------------------------------------------------- 1 | from itertools import combinations 2 | from difflib import SequenceMatcher 3 | 4 | 5 | def find_substring(header_list): 6 | substring_frequencies = {} 7 | for combo in combinations(header_list, 2): 8 | substring = longest_substring(combo[0], combo[1]) 9 | if substring in substring_frequencies: 10 | substring_frequencies[substring] += 1 11 | else: 12 | substring_frequencies[substring] = 1 13 | # return the most frequent substring between evey combination of strings 14 | return max(substring_frequencies, key=substring_frequencies.get) 15 | 16 | 17 | # via geeksforgeeks 18 | def longest_substring(str1, str2): 19 | # initialize SequenceMatcher object with 20 | # input string 21 | seqMatch = SequenceMatcher(None, str1, str2) 22 | 23 | # find match of longest sub-string 24 | # output will be like Match(a=0, b=0, size=5) 25 | match = seqMatch.find_longest_match(0, len(str1), 0, len(str2)) 26 | 27 | # print longest substring 28 | if (match.size != 0): 29 | return (str1[match.a: match.a + match.size]) 30 | else: 31 | return '' 32 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/css/lensing.css: -------------------------------------------------------------------------------- 1 | 2 | /* Bootstrap Overrides */ 3 | #topBar { 4 | padding: 0 16px; 5 | } 6 | 7 | #title { 8 | font-size: 16px; 9 | } 10 | 11 | #navbarDropdown, #navbarUpload { 12 | font-size: 16px; 13 | } 14 | 15 | #boxHeading1, #boxHeading2 { 16 | font-size: 13px; 17 | } 18 | 19 | #boxChannels1A, #boxChannels1B { 20 | font-size: 10px; 21 | } 22 | 23 | #container, #row, #lowRow, #lowRow div, #channel_list_wrapper, #openseadragon_wrapper { 24 | border: 0; 25 | box-sizing: border-box; 26 | margin: 0; 27 | padding: 0; 28 | } 29 | 30 | #container { 31 | width: 100vw; 32 | max-width: 100vw; 33 | } 34 | 35 | #row { 36 | padding: 0 16px; 37 | width: 100%; 38 | } 39 | 40 | #channel_list_wrapper, #openseadragon_wrapper { 41 | height: 87.5vh; 42 | min-height: 570px; 43 | } 44 | 45 | #channel_list_wrapper { 46 | padding: 0 6px 0 0; 47 | max-width: 20%; 48 | min-width: 20%; 49 | width: 20%; 50 | } 51 | 52 | #openseadragon_wrapper { 53 | padding: 0 0 0 6px; 54 | max-width: 80%; 55 | min-width: 80%; 56 | width: 80%; 57 | } 58 | 59 | #openseadragon, #channel_list { 60 | height: 100%; 61 | width: 100%; 62 | } -------------------------------------------------------------------------------- /minerva_analysis/server/routes/page_routes.py: -------------------------------------------------------------------------------- 1 | from minerva_analysis import app, get_config_names 2 | from flask import render_template, send_from_directory 3 | from pathlib import Path 4 | import json 5 | import os 6 | 7 | 8 | @app.route("/") 9 | def my_index(): 10 | return render_template("index.html", data={'datasource': '', 'datasources': get_config_names(), 11 | 'is_docker': app.config['IS_DOCKER']}) 12 | 13 | 14 | @app.route('/') 15 | def image_viewer(datasource): 16 | datasources = get_config_names() 17 | if datasource not in datasources: 18 | datasource = '' 19 | return render_template('index.html', data={'datasource': datasource, 'datasources': datasources, 20 | 'is_docker': app.config['IS_DOCKER']}) 21 | 22 | 23 | 24 | @app.route("/upload_page") 25 | def upload_page(): 26 | return render_template("upload.html", data={'datasource': '', 'datasources': get_config_names(), 27 | 'is_docker': app.config['IS_DOCKER']}) 28 | 29 | 30 | 31 | 32 | @app.route('/client/') 33 | def serveClient(filename): 34 | return send_from_directory(app.config['CLIENT_PATH'], filename, conditional=True) 35 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragonrgb.js: -------------------------------------------------------------------------------- 1 | !function(e){"use strict";function t(e){this.onCanvasHover(this.getValueAt(e.position))}if(!e.version||e.version.major<2)throw new Error("This version of OpenSeadragonRGB requires OpenSeadragon version 2.0.0+");e.Viewer.prototype.rgb=function(t){return(!this.rgbInstance||t)&&(t=t||{},t.viewer=this,this.rgbInstance=new e.RGB(t)),this.rgbInstance},e.RGB=function(n){e.extend(!0,this,{viewer:null,onCanvasHover:null},n),this.onCanvasHover&&(this.tracker=new e.MouseTracker({element:this.viewer.canvas,moveHandler:e.delegate(this,t)}))},e.extend(e.RGB.prototype,e.ControlDock.prototype,{getValueAt:function(t,n){var r=1===arguments.length?t:new e.Point(t,n),o=this.viewer,i=o.drawer.getRgbAt(r);if(i)for(var a,s,g,v=0;v=0&&s.y>=0&&s.x<=g.x&&s.y<=g.y&&(i.image=a,i.imageCoordinates=s);return i}}),e.Drawer.prototype.getRgbAt=function(t){if(!this.useCanvas)return!1;var n=e.pixelDensityRatio,r=this._getContext().getImageData(t.x*n,t.y*n,1,1).data;return{r:r[0],g:r[1],b:r[2],a:r[3]}}}(OpenSeadragon); 2 | //# sourceMappingURL=openseadragonrgb.js.map 3 | -------------------------------------------------------------------------------- /.github/workflows/dockerhub.yml: -------------------------------------------------------------------------------- 1 | name: dockerhub 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | docker: 10 | if: github.repository != 'labsyspharm/minerva_analysis' 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Branch name 14 | id: branch_name 15 | run: | 16 | echo ::set-output name=SOURCE_BRANCH::${GITHUB_REF#refs/heads/} 17 | echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} 18 | - name: Clean up the release tag test 19 | id: cleantag 20 | run: | 21 | tag=$(echo ${{ steps.branch_name.outputs.SOURCE_TAG }} | tr -d v) 22 | echo "::set-output name=tag::${{ github.repository }}:${tag%"_gating"}" 23 | - name: Set up QEMU 24 | uses: docker/setup-qemu-action@v1 25 | - name: Set up Docker Buildx 26 | uses: docker/setup-buildx-action@v1 27 | - name: Login to DockerHub 28 | uses: docker/login-action@v1 29 | with: 30 | username: ${{ secrets.DOCKERHUB_USERNAME }} 31 | password: ${{ secrets.DOCKERHUB_TOKEN }} 32 | - name: Build and push 33 | id: docker_build 34 | uses: docker/build-push-action@v2 35 | with: 36 | push: true 37 | tags: ${{steps.cleantag.outputs.tag}} 38 | - name: Image digest 39 | run: echo ${{ steps.docker_build.outputs.digest }} -------------------------------------------------------------------------------- /minerva_analysis_mac.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python ; coding: utf-8 -*- 2 | 3 | 4 | block_cipher = None 5 | 6 | 7 | a = Analysis(['run.py'], 8 | pathex=['/opt/homebrew/anaconda3/envs/viewer', '/Users/swarchol/Research/minerva_analysis'], 9 | binaries=[], 10 | datas=[('minerva_analysis/client', 'minerva_analysis/client'), ('minerva_analysis/__init__.py', 'minerva_analysis/'), ('minerva_analysis/server', 'minerva_analysis/server'), ('/opt/homebrew/anaconda3/envs/viewer/lib/python3.7/site-packages/xmlschema/schemas', 'xmlschema/schemas')], 11 | hiddenimports=['scipy.spatial.transform._rotation_groups', 'sqlalchemy.sql.default_comparator', 'cmath'], 12 | hookspath=[], 13 | hooksconfig={}, 14 | runtime_hooks=[], 15 | excludes=[], 16 | win_no_prefer_redirects=False, 17 | win_private_assemblies=False, 18 | cipher=block_cipher, 19 | noarchive=False) 20 | pyz = PYZ(a.pure, a.zipped_data, 21 | cipher=block_cipher) 22 | 23 | exe = EXE(pyz, 24 | a.scripts, 25 | a.binaries, 26 | a.zipfiles, 27 | a.datas, 28 | [], 29 | name='minerva_analysis_mac', 30 | debug=False, 31 | bootloader_ignore_signals=False, 32 | strip=False, 33 | upx=True, 34 | upx_exclude=[], 35 | runtime_tmpdir=None, 36 | console=True, 37 | disable_windowed_traceback=False, 38 | target_arch=None, 39 | codesign_identity=None, 40 | entitlements_file=None ) 41 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2009 CodePlex Foundation 2 | Copyright (C) 2010-2013 OpenSeadragon contributors 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | - Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | - Neither the name of CodePlex Foundation nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /minerva_analysis/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 12 | 13 | 14 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/css/gating.css: -------------------------------------------------------------------------------- 1 | #csv_gating_list { 2 | width: 100%; 3 | height: 80vh; 4 | font-size: 10pt; 5 | overflow-y: auto; 6 | overflow-x: hidden; 7 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); 8 | 9 | } 10 | 11 | .input{ 12 | font-size:8px; 13 | width:35px; 14 | margin-top:15px; 15 | border: 1px solid gray; 16 | box-sizing: border-box; 17 | } 18 | 19 | #csv_gating_list .list-group-item { 20 | padding: .20rem 0.5rem; 21 | border: 0; 22 | } 23 | 24 | #csv_gating_list .active { 25 | background-color: rgba(0, 0, 0, 0.75); 26 | border-top: 1px solid rgba(255, 255, 255, 0.5); 27 | border-right: none; 28 | border-bottom: 1px solid rgba(255, 255, 255, 0.5); 29 | border-left: none; 30 | height: 90px; 31 | } 32 | 33 | #csv_gating_list .list-group { 34 | border-radius: 0rem; 35 | } 36 | 37 | #openseadragon_loader, #channel_list_loader, #csv_gating_list_loader { 38 | margin-left: 33px; 39 | } 40 | 41 | .auto-btn { 42 | float: right; 43 | padding-top: 0; 44 | padding-bottom: 0; 45 | border: 1px; 46 | } 47 | 48 | .distribution_line { 49 | stroke: rgba(130, 130, 130, 0.5); 50 | stroke-width: 4px; 51 | fill: none; 52 | } 53 | 54 | .gmm_line { 55 | stroke-width: 1px; 56 | stroke-opacity: 0.75; 57 | fill: none; 58 | } 59 | 60 | line.track { 61 | stroke-width: 4px !important; 62 | } 63 | 64 | 65 | #drag-and-drop-text { 66 | font-size: 30pt; 67 | text-align: center; 68 | } 69 | 70 | #drag-and-drop-info { 71 | display: none; 72 | } 73 | 74 | .dz-preview, .dz-file-preview { 75 | display: none; 76 | } 77 | 78 | #channels_arrow-upload-form, #channels_arrow-upload-form * { 79 | display: none 80 | } 81 | 82 | #arrow-upload-form, #arrow-upload-form * { 83 | display: none 84 | } 85 | -------------------------------------------------------------------------------- /minerva_analysis/client/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | 3 | module.exports = { 4 | mode: 'development', 5 | entry: { 6 | vendor: './src/js/vendor.js' 7 | }, 8 | output: { 9 | filename: '[name]_bundle.js' 10 | }, 11 | node: { 12 | fs: 'empty' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | use: [ 19 | 'style-loader', 20 | 'css-loader' 21 | ] 22 | }, 23 | { 24 | test: /\.(png|svg|jpg|gif|dzi)$/, 25 | use: [ 26 | 'file-loader', 27 | ], 28 | }, 29 | { 30 | test: /\.ts$/, 31 | exclude: /(node_modules|bower_components)/, 32 | use: { 33 | loader: 'babel-loader', 34 | options: { 35 | "presets": [ 36 | "@babel/typescript" 37 | ], 38 | "plugins": [ 39 | "dynamic-import-node" 40 | ], 41 | } 42 | } 43 | }, 44 | { 45 | test: /\.m?js$/, 46 | exclude: /(node_modules|bower_components)/, 47 | use: { 48 | loader: 'babel-loader', 49 | options: { 50 | plugins: [ 51 | ["@babel/plugin-transform-runtime", { "corejs": 2 }], 52 | '@babel/plugin-proposal-class-properties' 53 | ], 54 | presets: ['@babel/preset-env'] 55 | } 56 | } 57 | }, 58 | ], 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/formData/download_channels.json: -------------------------------------------------------------------------------- 1 | { 2 | "active_channels": "{}", 3 | "datasource": "karma-test", 4 | "filename": "karma-test_channel_list", 5 | "list_channels": "{\"Hoechst0\":[765,41036],\"AF488\":[758,32538],\"AF555\":[387,59399],\"AF647\":[667,15105],\"Hoechst1\":[827,59065],\"A555\":[320,10263],\"A647\":[743,3897],\"Hoechst2\":[904,61968],\"anti_CD3\":[1185,46565],\"anti_NaKATPase\":[694,25379],\"anti_CD45RO\":[546,45152],\"Hoechst3\":[650,59040],\"Ki67_488\":[1287,17472],\"Keratin_570\":[480,64311],\"aSMA_660\":[566,42751],\"Hoechst4\":[1131,65536],\"CD4_488\":[1346,13776],\"CD45_PE\":[643,13262],\"PD1_647\":[951,7954],\"Hoechst5\":[931,65535],\"CD20_488\":[1999,57991],\"CD68_555\":[509,20277],\"CD8a_660\":[1190,35372],\"Hoechst6\":[1084,65536],\"CD163_488\":[1256,64032],\"FOXP3_570\":[619,25318],\"PDL1_647\":[1977,7351],\"Hoechst7\":[1098,65536],\"Ecad_488\":[1300,38672],\"Vimentin_555\":[749,9849],\"CDX2_647\":[2817,65434],\"Hoechst8\":[772,65536],\"LaminABC_488\":[1956,34295],\"Desmin_555\":[530,52665],\"CD31_647\":[766,50352],\"Hoechst9\":[880,65536],\"PCNA_488\":[1296,42317],\"Ki67_570\":[893,12404],\"CollagenIV_647\":[533,61923],\"HE_r\":[2558,65535],\"HE_g\":[1042,65535],\"HE_b\":[13158,65535]}", 6 | "map_channels": "{\"1\":\"Hoechst0\",\"2\":\"AF488\",\"3\":\"AF555\",\"4\":\"AF647\",\"5\":\"Hoechst1\",\"6\":\"AF488\",\"7\":\"A555\",\"8\":\"A647\",\"9\":\"Hoechst2\",\"10\":\"anti_CD3\",\"11\":\"anti_NaKATPase\",\"12\":\"anti_CD45RO\",\"13\":\"Hoechst3\",\"14\":\"Ki67_488\",\"15\":\"Keratin_570\",\"16\":\"aSMA_660\",\"17\":\"Hoechst4\",\"18\":\"CD4_488\",\"19\":\"CD45_PE\",\"20\":\"PD1_647\",\"21\":\"Hoechst5\",\"22\":\"CD20_488\",\"23\":\"CD68_555\",\"24\":\"CD8a_660\",\"25\":\"Hoechst6\",\"26\":\"CD163_488\",\"27\":\"FOXP3_570\",\"28\":\"PDL1_647\",\"29\":\"Hoechst7\",\"30\":\"Ecad_488\",\"31\":\"Vimentin_555\",\"32\":\"CDX2_647\",\"33\":\"Hoechst8\",\"34\":\"LaminABC_488\",\"35\":\"Desmin_555\",\"36\":\"CD31_647\",\"37\":\"Hoechst9\",\"38\":\"PCNA_488\",\"39\":\"Ki67_570\",\"40\":\"CollagenIV_647\",\"41\":\"HE_r\",\"42\":\"HE_g\",\"43\":\"HE_b\"}", 7 | "list_colors": "{}", 8 | "list_ranges": "{}" 9 | } 10 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/views/lensingFilters/lfDataColors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class LfDataColors 3 | */ 4 | export class LfDataColors { 5 | 6 | // Class vars 7 | data = []; 8 | load = []; 9 | 10 | /** 11 | * @constructor 12 | */ 13 | constructor() { 14 | 15 | // Init 16 | this.init() 17 | } 18 | 19 | 20 | /** 21 | * @function init 22 | * 23 | * @return void 24 | */ 25 | init() { 26 | 27 | this.data = [ 28 | { 29 | index: 0, 30 | name: 'black', 31 | r: 0, 32 | g: 0, 33 | b: 0 34 | }, 35 | { 36 | index: 1, 37 | name: 'white', 38 | r: 255, 39 | g: 255, 40 | b: 255 41 | }, 42 | { 43 | index: 2, 44 | name: 'red', 45 | r: 255, 46 | g: 0, 47 | b: 0 48 | }, 49 | { 50 | index: 3, 51 | name: 'green', 52 | r: 0, 53 | g: 255, 54 | b: 0 55 | }, 56 | { 57 | index: 4, 58 | name: 'blue', 59 | r: 0, 60 | g: 0, 61 | b: 255 62 | }, 63 | { 64 | index: 5, 65 | name: 'light red', 66 | r: 255, 67 | g: 128, 68 | b: 128 69 | }, 70 | { 71 | index: 6, 72 | name: 'light green', 73 | r: 128, 74 | g: 255, 75 | b: 128 76 | }, 77 | { 78 | index: 7, 79 | name: 'light blue', 80 | r: 128, 81 | g: 128, 82 | b: 255 83 | }, 84 | ]; 85 | 86 | this.load = { 87 | data: this.data, 88 | config: { 89 | type: 'color-index', 90 | filter: 'fil_data_rgb' 91 | } 92 | } 93 | 94 | } 95 | 96 | } -------------------------------------------------------------------------------- /minerva_analysis/client/test/fixtures/context.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 47 | 48 | 49 | 50 | 51 | 57 | %SCRIPTS% 58 | 61 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/formData/gated_cell_encodings.json: -------------------------------------------------------------------------------- 1 | { 2 | "filename": "{{ data.datasource }}_gated_cell_encodings.csv", 3 | "fullCsv": "true", 4 | "encoding": "binary", 5 | "filter": "{}", 6 | "channels": "{\"Area\":[25,385],\"Hoechst0\":[7.913521017283895,10.677038913891868],\"AF488\":[6.089044875446846,10.630432112679363],\"AF555\":[4.875197323201151,10.21159705705293],\"AF647\":[4.07753744390572,9.08839870117094],\"Hoechst1\":[7.80994708647679,10.9073861501318],\"A555\":[4.007333185232471,7.623153068476902],\"A647\":[4.31748811353631,7.591861714889934],\"Hoechst2\":[7.830425617820331,10.904266480855474],\"anti_CD3\":[4.919980925828125,10.82808369877337],\"anti_NaKATPase\":[4.30406509320417,9.833815879286808],\"anti_CD45RO\":[4.477336814478207,10.783197284147285],\"Hoechst3\":[7.63433723562832,10.647351611387782],\"Ki67_488\":[5.017279836814924,9.56500329315396],\"Keratin_570\":[4.23410650459726,10.976234875009183],\"aSMA_660\":[4.007333185232471,10.479342202122597],\"Hoechst4\":[8.237214703349489,10.99807508606562],\"CD4_488\":[5.181783550292085,9.33034316437088],\"CD45_PE\":[3.871201010907891,9.529303064460995],\"PD1_647\":[4.875197323201151,8.980801413573113],\"Hoechst5\":[7.963807953231451,10.84271325569278],\"CD20_488\":[4.624972813284271,10.902905460984163],\"CD68_555\":[4.189654742026425,9.899730503658416],\"CD8a_660\":[4.663439094112067,10.602691354678605],\"Hoechst6\":[8.000684784514748,11.019825148966754],\"CD163_488\":[4.691347882229144,8.490849216076635],\"FOXP3_570\":[4.31748811353631,9.834726796965336],\"PDL1_647\":[4.875197323201151,8.085794701281568],\"Hoechst7\":[8.09040229659332,11.015542416900727],\"Ecad_488\":[4.770684624465665,10.12470925828764],\"Vimentin_555\":[4.30406509320417,8.081475040137052],\"CDX2_647\":[6.333279628139691,10.809889551659912],\"Hoechst8\":[7.473637108496206,10.786759101953049],\"LaminABC_488\":[5.971261839790462,10.416790106177013],\"Desmin_555\":[4.3694478524670215,10.389272253058634],\"CD31_647\":[4.248495242049359,10.657518144952888],\"Hoechst9\":[7.448333860897476,10.9408802507624],\"PCNA_488\":[4.700480365792417,10.523526261275435],\"Ki67_570\":[4.406719247264253,8.445482343862237],\"CollagenIV_647\":[3.9889840465642745,10.35685354062884],\"HE_r\":[0,0],\"HE_g\":[0,0],\"HE_b\":[0,0]}", 7 | "datasource": "karma-test" 8 | } 9 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/formData/gated_channel_ranges.json: -------------------------------------------------------------------------------- 1 | { 2 | "filename": "{{ data.datasource }}_gated_channel_ranges.csv", 3 | "fullCsv": "false", 4 | "encoding": "binary", 5 | "filter": "{}", 6 | "channels": "{\"Area\":[25,385],\"Hoechst0\":[7.913521017283895,10.677038913891868],\"AF488\":[6.089044875446846,10.630432112679363],\"AF555\":[4.875197323201151,10.21159705705293],\"AF647\":[4.07753744390572,9.08839870117094],\"Hoechst1\":[7.80994708647679,10.9073861501318],\"A555\":[4.007333185232471,7.623153068476902],\"A647\":[4.31748811353631,7.591861714889934],\"Hoechst2\":[7.830425617820331,10.904266480855474],\"anti_CD3\":[4.919980925828125,10.82808369877337],\"anti_NaKATPase\":[4.30406509320417,9.833815879286808],\"anti_CD45RO\":[4.477336814478207,10.783197284147285],\"Hoechst3\":[7.63433723562832,10.647351611387782],\"Ki67_488\":[5.017279836814924,9.56500329315396],\"Keratin_570\":[4.23410650459726,10.976234875009183],\"aSMA_660\":[4.007333185232471,10.479342202122597],\"Hoechst4\":[8.237214703349489,10.99807508606562],\"CD4_488\":[5.181783550292085,9.33034316437088],\"CD45_PE\":[3.871201010907891,9.529303064460995],\"PD1_647\":[4.875197323201151,8.980801413573113],\"Hoechst5\":[7.963807953231451,10.84271325569278],\"CD20_488\":[4.624972813284271,10.902905460984163],\"CD68_555\":[4.189654742026425,9.899730503658416],\"CD8a_660\":[4.663439094112067,10.602691354678605],\"Hoechst6\":[8.000684784514748,11.019825148966754],\"CD163_488\":[4.691347882229144,8.490849216076635],\"FOXP3_570\":[4.31748811353631,9.834726796965336],\"PDL1_647\":[4.875197323201151,8.085794701281568],\"Hoechst7\":[8.09040229659332,11.015542416900727],\"Ecad_488\":[4.770684624465665,10.12470925828764],\"Vimentin_555\":[4.30406509320417,8.081475040137052],\"CDX2_647\":[6.333279628139691,10.809889551659912],\"Hoechst8\":[7.473637108496206,10.786759101953049],\"LaminABC_488\":[5.971261839790462,10.416790106177013],\"Desmin_555\":[4.3694478524670215,10.389272253058634],\"CD31_647\":[4.248495242049359,10.657518144952888],\"Hoechst9\":[7.448333860897476,10.9408802507624],\"PCNA_488\":[4.700480365792417,10.523526261275435],\"Ki67_570\":[4.406719247264253,8.445482343862237],\"CollagenIV_647\":[3.9889840465642745,10.35685354062884],\"HE_r\":[0,0],\"HE_g\":[0,0],\"HE_b\":[0,0]}", 7 | "datasource": "karma-test" 8 | } 9 | -------------------------------------------------------------------------------- /minerva_analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from pathlib import Path 3 | from flask_sqlalchemy import SQLAlchemy 4 | from appdirs import user_data_dir 5 | 6 | from numcodecs import compat_ext # Needed for pyinstaller 7 | from numcodecs import blosc # Needed for pyinstaller 8 | import xmlschema # Needed for pyinstaller 9 | 10 | import os 11 | import json 12 | import sys 13 | import multiprocessing 14 | 15 | # If you're running the pyinstaller version of the code, create a 16 | # new directory for the data (this will be at ~/ on mac) 17 | 18 | #centralizing path across app 19 | cwd_path = Path.cwd() 20 | 21 | ## uncomment block if not on O2 22 | if getattr(sys, 'frozen', False): 23 | data_path = Path(Path(sys.executable).parent / 'data') 24 | multiprocessing.freeze_support() 25 | else: 26 | data_path = Path("minerva_analysis/data").resolve() 27 | 28 | ## uncomment block if on O2 29 | # appname = "minerva_analysis" 30 | # appauthor = "lsp" 31 | # data_path = Path(user_data_dir(appname, appauthor)+'/data').resolve() 32 | # if getattr(sys, 'frozen', False): 33 | # multiprocessing.freeze_support() 34 | 35 | # print('Data Path', str(data_path), str((data_path).resolve())) 36 | # Make the Data Path 37 | data_path.mkdir(parents=True, exist_ok=True) 38 | app = Flask(__name__, template_folder=Path('client/templates'), static_folder='data') 39 | app.config['TEMPLATES_AUTO_RELOAD'] = True 40 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + str(data_path) + '/db.sqlite3' 41 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 42 | app.config['CLIENT_PATH'] = app.root_path + '/client/' 43 | config_json_path = data_path / "config.json" 44 | db = SQLAlchemy(app) 45 | 46 | 47 | def get_config(): 48 | if not Path.is_dir(data_path): 49 | Path.mkdir(data_path) 50 | 51 | if not Path.is_file(config_json_path): 52 | with open(config_json_path, 'w') as f: 53 | json.dump({}, f) 54 | return [] 55 | else: 56 | with open(config_json_path, 'r+') as f: 57 | data = json.load(f) 58 | return data 59 | 60 | 61 | def get_config_names(): 62 | data = get_config() 63 | try: 64 | return [key for key in data.keys()] 65 | except AttributeError: 66 | return [] 67 | 68 | 69 | from minerva_analysis.server.routes import page_routes, data_routes, import_routes 70 | from minerva_analysis.server.models import data_model, database_model 71 | -------------------------------------------------------------------------------- /minerva_analysis/server/utils/fullConversion.py: -------------------------------------------------------------------------------- 1 | import os 2 | # set Libvips path for windows. If mac, please run `brew install vips` 3 | if os.name == 'nt': 4 | vipshome = os.path.dirname(os.path.abspath(__file__)) + r"\..\static\external\vips-dev-8.10\bin" 5 | os.environ['PATH'] = vipshome + ';' + os.environ['PATH'] 6 | 7 | from PIL import Image 8 | 9 | Image.MAX_IMAGE_PIXELS = 1000000000 10 | 11 | # def convertTifToPyramid(channel_img, destFile, isLabelImg): 12 | # img_dim = channel_img.shape[:2] 13 | # print('img shape', img_dim, channel_img.dtype) 14 | # channel_img = channel_img.astype('uint32') 15 | # imgR = ((channel_img >> 16) % 256).astype('uint8') 16 | # imgG = ((channel_img >> 8) % 256).astype('uint8') # high bits 17 | # imgB = (channel_img % 256).astype('uint8') # low bits 18 | # channel_img = cv2.merge((imgB, imgG, imgR)) 19 | # 20 | # if channel_img is not None: 21 | # print('Destfile', destFile) 22 | # cv2.imwrite(destFile + '.png', channel_img) # [cv2.IMWRITE_PNG_COMPRESSION, 9] 23 | # image = pyvips.Image.new_from_file(destFile + '.png', access='sequential') 24 | # # Remove any existing directory with the name of the channel 25 | # if os.path.exists(destFile): 26 | # shutil.rmtree(destFile) 27 | # if not isLabelImg: 28 | # image.dzsave(destFile, tile_size=128, overlap=2, suffix='.png') 29 | # else: 30 | # image.dzsave(destFile, tile_size=128, overlap=2, region_shrink='nearest', suffix='.png') 31 | 32 | # 33 | # def convertOmeTiff(filePath, fileName, isLabelImg=False): 34 | # channelNames = [] 35 | # file_path = str(Path(filePath) / fileName) 36 | # ome = tf.imread(file_path, is_ome=True) 37 | # for channel in range(np.shape(ome)[0]): 38 | # img = ome[channel, :, :] 39 | # channelName = re.sub(r'\.ome|\.tiff|\.tif|\.png', '', fileName) + "_" + str(channel) 40 | # channel_path = str(Path(filePath) / channelName) 41 | # convertTifToPyramid(img, channel_path, isLabelImg) 42 | # channelNames.append(channelName) 43 | # return channelNames 44 | 45 | # 46 | # def convertChannel(filePath, isLabelImg): 47 | # channel_img = imread(filePath) 48 | # channel_img = np.squeeze(channel_img) # Remove any unnecessary single dimensions 49 | # channelPath = re.sub(r'\.ome|\.tiff|\.tif|\.png', '', filePath) 50 | # convertTifToPyramid(channel_img, channelPath, isLabelImg) 51 | -------------------------------------------------------------------------------- /minerva_analysis/client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "start": "webpack", 7 | "watch": "webpack --watch", 8 | "test:test": "tsc --project test/tsconfig.json", 9 | "test:src": "mockttp -c karma start karma.conf.js", 10 | "test": "npm run test:test && npm run test:src" 11 | }, 12 | "license": "MIT", 13 | "dependencies": { 14 | "@fortawesome/fontawesome-free": "^5.14.0", 15 | "bootstrap": "^4.5.0", 16 | "buffer": "^5.6.0", 17 | "chai-spies": "^1.0.0", 18 | "color-convert": "^2.0.1", 19 | "css-loader": "^3.6.0", 20 | "d3": "^6.2.0", 21 | "d3-simple-slider": "latest", 22 | "dropzone": "^5.7.2", 23 | "expose-loader": "^1.0.0", 24 | "file-loader": "^6.0.0", 25 | "install": "^0.13.0", 26 | "jquery": "^3.5.1", 27 | "jquery-form": "^4.3.0", 28 | "karma-sourcemap-loader": "^0.3.8", 29 | "lensing": "^1.0.17", 30 | "lodash": "^4.17.19", 31 | "mark.js": "^8.11.1", 32 | "node-fetch": "^2.6.1", 33 | "pngjs": "^5.0.0", 34 | "popper.js": "^1.16.1", 35 | "regenerator-runtime": "^0.13.7", 36 | "sortablejs": "^1.10.2", 37 | "style-loader": "^1.2.1", 38 | "viawebgl": "github:thejohnhoffer/viaWebGL#webgl2" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "^7.17.12", 42 | "@babel/plugin-transform-runtime": "^7.12.1", 43 | "@babel/preset-env": "^7.17.12", 44 | "@babel/preset-typescript": "^7.17.12", 45 | "@babel/runtime": "^7.17.9", 46 | "@babel/runtime-corejs2": "^7.17.11", 47 | "@babel/plugin-proposal-class-properties": "latest", 48 | "@httptoolkit/proxy-agent": "^5.0.1-socks-lookup-fix.0", 49 | "@types/chai": "^4.3.1", 50 | "@types/jquery": "^3.5.14", 51 | "@types/karma-fixture": "^0.2.5", 52 | "@types/mocha": "^9.1.1", 53 | "@types/openseadragon": "^3.0.3", 54 | "babel-loader": "^8.1.0", 55 | "chai": "^4.3.6", 56 | "karma": "^6.3.19", 57 | "karma-chai": "^0.1.0", 58 | "karma-chrome-launcher": "^3.1.1", 59 | "karma-fixture": "^0.2.6", 60 | "karma-html2js-preprocessor": "^1.1.0", 61 | "karma-jquery": "^0.2.4", 62 | "karma-json-fixtures-preprocessor": "0.0.6", 63 | "karma-mocha": "^2.0.1", 64 | "karma-sinon-chai": "^2.0.2", 65 | "karma-webpack": "^4.0.0", 66 | "mocha": "^10.0.0", 67 | "mockttp": "^3.0.0", 68 | "sinon": "^14.0.0", 69 | "sinon-chai": "^3.7.0", 70 | "typescript": "^4.6.4", 71 | "webpack": "^4.44.2", 72 | "webpack-cli": "^3.3.12" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/services/numericData.js: -------------------------------------------------------------------------------- 1 | class NumericData { 2 | /** 3 | * Constructor for NumericDataLayer. 4 | * 5 | * @param config - the cinfiguration file (json) 6 | * @param dataLayer - the data layer (stub) that executes server requests and holds client side data 7 | */ 8 | constructor(config, dataLayer) { 9 | this.features = config.featureData[0]; 10 | this.dataLayer = dataLayer; 11 | } 12 | 13 | /* 14 | * Load cell segmentation data 15 | */ 16 | async loadCells() { 17 | const { idField, xCoordinate, yCoordinate } = this.features; 18 | const fields = [ idField, xCoordinate, yCoordinate ]; 19 | const idsCenters = await this.getAllUInt32Entries(fields); 20 | const isCenter = (_, i) => !!(i % fields.length); 21 | const centers = idsCenters.filter(isCenter); 22 | const isId = (_, i) => !(i % fields.length); 23 | const ids = idsCenters.filter(isId); 24 | return { ids, centers }; 25 | } 26 | 27 | /* 28 | * Access DataLayer bitrange as floating point. 29 | */ 30 | get floatRange() { 31 | return this.dataLayer.getImageBitRange(true); 32 | } 33 | 34 | /* 35 | * Access DataLayer bitrange as an integer. 36 | */ 37 | get intRange() { 38 | return this.dataLayer.getImageBitRange(false); 39 | } 40 | 41 | /* 42 | * @function getNearestCell - return nearest cell to point 43 | * 44 | * @param x - cell x position in image coordinates 45 | * @param y - cell y position in image coordinates 46 | */ 47 | getNearestCell(x, y) { 48 | return this.dataLayer.getNearestCell(x, y); 49 | } 50 | 51 | /* 52 | * @function getAllFloat32Ids - all integer entries 53 | * @param keys - list of keys to access 54 | */ 55 | async getAllFloat32Entries(keys) { 56 | return this.getAllEntries(keys, false); 57 | } 58 | 59 | /* 60 | * @function getAllUInt32Ids - all integer entries 61 | * @param keys - list of keys to access 62 | */ 63 | async getAllUInt32Entries(keys) { 64 | return this.getAllEntries(keys, true); 65 | } 66 | 67 | /* 68 | * @function getAllEntries - all cell entries by keys 69 | * @param keys - list of keys to access 70 | * @param useInt - whether requesting integers 71 | */ 72 | async getAllEntries(keys, useInt) { 73 | if (!keys.length) { 74 | return []; 75 | } 76 | const { dataLayer } = this; 77 | const arr = await dataLayer.getAllCells(keys, useInt); 78 | if (useInt) { 79 | return new Uint32Array(arr); 80 | } 81 | return new Float32Array(arr); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /minerva_analysis/server/utils/pre_normalization.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def preNormalize(input_csvPath, output_csvPath, skip_columns=[]): 5 | RAW_DATA = np.genfromtxt(input_csvPath, names=True, dtype=float, delimiter=',') 6 | marker_list = RAW_DATA.dtype.names 7 | norm_data = RAW_DATA.view((np.float, len(marker_list))) 8 | 9 | # A list of markers to skip normalization 10 | # markers_notToNorm = ['Field_Row', 'Field_Col', 'CellID', 'X_position','Y_position','Percent_Touching','Number_Neighbors','Neighbor_1','Neighbor_2','Neighbor_3','Neighbor_4','Neighbor_5', 'Eccentricity', 'Solidity', 'Extent', 'EulerNumber', 'Perimeter', 'MajorAxisLength', 'MinorAxisLength', 'Orientation', 'X_position', 'Y_position'] 11 | # A list of markers to skip log10 transform 12 | # markers_notToLog = ['DAPI1', 'A488b1', 'A555b1'] # skip 'DAPI1', 'A488b1', 'A555b1' 13 | markers_notToLog = [] # nothing to skip 14 | 15 | for marker_id in range(norm_data.shape[1]): 16 | if marker_list[marker_id] in skip_columns: 17 | continue 18 | if marker_list[marker_id] in markers_notToLog: 19 | # Log10 transform 20 | norm_data[:, marker_id] = np.log10(norm_data[:, marker_id] + 1) 21 | print(marker_list[marker_id], 'with log10 transform') 22 | else: 23 | print(marker_list[marker_id], 'without log10 transform') 24 | # Percentile normalization by mapping [0.1%, 99.9%] into [0, 1] 25 | min_tile, max_tile = np.percentile(norm_data[:, marker_id], [0.1, 99.9]) 26 | 27 | # norm_data[:, marker_id] =(norm_data[:, marker_id] - min_tile) * (max_tile - min_tile) / (max_tile - min_tile) + min_tile 28 | norm_data[:, marker_id] = (norm_data[:, marker_id] - min_tile) / (max_tile - min_tile) 29 | norm_data[:, marker_id] = np.minimum(norm_data[:, marker_id], 1) 30 | norm_data[:, marker_id] = np.maximum(norm_data[:, marker_id], 0) 31 | 32 | with open(output_csvPath, 'w') as f: 33 | for marker_id, marker_name in enumerate(marker_list): 34 | f.write(marker_name) 35 | if marker_id != (len(marker_list) - 1): 36 | f.write(',') 37 | f.write('\n') 38 | for norm_row in norm_data: 39 | for elem_id, norm_elem in enumerate(norm_row): 40 | f.write(str(norm_elem)) 41 | if elem_id != (norm_row.shape[0] - 1): 42 | f.write(',') 43 | f.write('\n') 44 | 45 | # input_csvPath = 'Sample_23.csv' 46 | # output_csvPath = 'Sample_23_norm2.csv' 47 | # preNormalize(input_csvPath, output_csvPath) 48 | -------------------------------------------------------------------------------- /minerva_analysis/server/models/database_model.py: -------------------------------------------------------------------------------- 1 | from minerva_analysis import app, db 2 | from sqlalchemy.orm import relationship 3 | from sqlalchemy import func 4 | 5 | import io 6 | import numpy as np 7 | 8 | 9 | # Via https://stackoverflow.com/questions/2546207/does-sqlalchemy-have-an-equivalent-of-djangos-get-or-create 10 | def create(model, **kwargs): 11 | instance = model(**kwargs) 12 | db.session.add(instance) 13 | db.session.commit() 14 | return instance 15 | 16 | 17 | def get(model, **kwargs): 18 | return db.session.query(model).filter_by(**kwargs).one_or_none() 19 | 20 | 21 | def edit(model, id, edit_field, edit_value): 22 | instance = get(model, id=id) 23 | instance.__setattr__(edit_field, edit_value) 24 | db.session.commit() 25 | 26 | 27 | def get_all(model, **kwargs): 28 | return db.session.query(model).filter_by(is_deleted=False, **kwargs).order_by(model.id).all() 29 | 30 | 31 | def get_or_create(model, **kwargs): 32 | if 'cells' in kwargs: 33 | cells = kwargs['cells'] 34 | del kwargs['cells'] 35 | instance = db.session.query(model).filter_by(**kwargs).one_or_none() 36 | if instance: 37 | return instance 38 | else: 39 | instance = model(cells=cells, **kwargs) 40 | db.session.add(instance) 41 | db.session.commit() 42 | return instance 43 | 44 | 45 | def save_list(model, **kwargs): 46 | if 'cells' in kwargs: 47 | cells = kwargs['cells'] 48 | del kwargs['cells'] 49 | 50 | instance = db.session.query(model).filter_by(**kwargs).one_or_none() 51 | if instance: 52 | instance.__setattr__('cells', cells) 53 | db.session.commit() 54 | return instance 55 | else: 56 | instance = model(cells=cells, **kwargs) 57 | db.session.add(instance) 58 | db.session.commit() 59 | return instance 60 | 61 | 62 | class ChannelList(db.Model): 63 | __tablename__ = 'channelList' 64 | id = db.Column(db.Integer, primary_key=True) 65 | datasource = db.Column(db.String(80), unique=False, nullable=False) 66 | cells = db.Column(db.LargeBinary, default={}, nullable=False) 67 | is_deleted = db.Column(db.Boolean, default=False, nullable=False) 68 | 69 | 70 | class GatingList(db.Model): 71 | __tablename__ = 'gatinglist' 72 | id = db.Column(db.Integer, primary_key=True) 73 | datasource = db.Column(db.String(80), unique=False, nullable=False) 74 | cells = db.Column(db.LargeBinary, default={}, nullable=False) 75 | is_deleted = db.Column(db.Boolean, default=False, nullable=False) 76 | 77 | 78 | with app.app_context(): 79 | db.create_all() 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minerva Analysis 2 | 3 | 4 | ## About 5 | This is an [openseadragon](https://openseadragon.github.io/) based **Cellular Image Viewing and Analysis Tool**. 6 | It is built with a python [Flask](http://flask.pocoo.org/) backend and a [Node.js](https://nodejs.org/en/) javascript frontend. 7 | 8 | ## Executables (for Users) 9 | Releases can be found here: 10 | https://github.com/labsyspharm/minerva_analysis/releases 11 | These are executables for Windows and MacOS that can be run locally without any installations. 12 | 13 | 14 | ## Running as a Docker container 15 | 16 | * Build image: `docker build -t gating .` 17 | * Run image with mounted path: `docker run --rm -dp 8000:8000 -v [source path]:/[target path] gating` 18 | 19 | where 20 | * `--rm` cleans up the container after it finishes executing 21 | * `-v` mounts the "present working directory" (containing your data) to be `/data` inside the container. This is necessary in order to import your data via the import page. 22 | * `-dp` forwards the port 8000 23 | 24 | Once the container is running, go to `http://localhost:8000/` in your web browser. 25 | To import your imaging files in the import gui type in the mounted `/data/..` 26 | 27 | ## Clone and Run Codebase (for Developers) 28 | #### 1. Checkout Project 29 | `git clone https://github.com/labsyspharm/minerva_analysis.git` 30 | #### 2. Checkout Necessary Branch 31 | * **For Gating, run** `git checkout gating` 32 | * Run `git pull` to make sure everything is up to date 33 | 34 | 35 | 36 | #### 3. Conda Install Instructions. 37 | ##### Install Conda 38 | * Install [miniconda](https://conda.io/miniconda.html) or [conda](https://docs.conda.io/projects/conda/en/latest/user-guide/install/download.html). 39 | * Create env: `conda env create -f requirements.yml` 40 | 41 | ##### Activate Environment 42 | * Active environment: `conda activate minerva_analysis` 43 | 44 | 45 | ##### Start the Server 46 | 47 | * `python run.py` - Runs the webserver 48 | ##### Start the Server 49 | 50 | * Access the tool via `http://localhost:8000/` 51 | 52 | 53 | #### (4. Node.js installation and packages) 54 | This step is only needed when you plan to edit js code. The codebase already included bundled js files. 55 | * Install [Node.js](https://nodejs.org/en/), then navigate to `/minerva_analysis/client` and run `npm install` to install all packages listed in package.json. 56 | * Run `npm run start` to package the Javascript, or run `npm run watch` if you plan on editing dependencies 57 | 58 | 59 | ## Packaging/Bundling Code as Executable (for Developers) 60 | Any tagged commit to a branch will trigger a build, where `tag == commit message`. This will appear under releases. Note building may take ~10 min. 61 | 62 | Tagging Conventions: All release tags should look like `v{version_number}_{branch_name}`. 63 | -------------------------------------------------------------------------------- /minerva_analysis/client/templates/channel_match.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | 4 | {% block content %} 5 | Minerva Analysis - Channel Match 6 |
7 |
8 |
9 |
10 |

{{ data.datasetName }}: Config

11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 | Destination 25 |
26 |
27 |
28 |
29 |
30 | CSV Header 31 |
32 |
33 | 34 | 37 |
38 | Drag Elements Into Order 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 | 59 | {% endblock %} 60 | 61 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | jobs: 7 | build: 8 | strategy: 9 | matrix: 10 | os: [ macos-latest, windows-latest ] 11 | node-version: [ 12.x ] 12 | runs-on: ${{ matrix.os }} 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - name: Get Branch 18 | shell: bash -l {0} 19 | run: | 20 | raw=$(git branch -r --contains ${{ github.ref }}) 21 | branch=${raw##*/} 22 | echo "BRANCH=$branch" >> $GITHUB_ENV 23 | - run: echo ${{ env.BRANCH }} 24 | - name: Creating Private Key 25 | uses: webfactory/ssh-agent@v0.5.3 26 | with: 27 | ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} 28 | - name: Create Repository Dispatch 29 | if: startsWith(matrix.os,'mac') 30 | uses: peter-evans/repository-dispatch@v2 31 | with: 32 | token: ${{ secrets.GATING_PAT }} 33 | repository: labsyspharm/gater 34 | event-type: merge 35 | client-payload: '{"ref": "${{ github.ref }}"}' 36 | - name: Use Node.js ${{ matrix.node-version }} 37 | uses: actions/setup-node@v2 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | - name: Node and Webpack 41 | run: | 42 | cd minerva_analysis 43 | cd client 44 | npm ci 45 | npm run start 46 | rm -r node_modules/ 47 | cd .. 48 | cd .. 49 | - uses: conda-incubator/setup-miniconda@v2 50 | with: 51 | python-version: 3.7 52 | environment-file: requirements.yml 53 | activate-environment: minerva_analysis 54 | - name: Package Windows 55 | if: startsWith(matrix.os,'windows') 56 | shell: cmd /C CALL {0} 57 | run: | 58 | package_win.bat 59 | - name: Rename Windows 60 | if: startsWith(matrix.os,'windows') 61 | shell: bash -l {0} 62 | run: | 63 | ls dist/ 64 | mv dist/minerva_analysis_windows.exe "dist/minerva_analysis_windows_${{ env.BRANCH }}.exe" 65 | ls dist/ 66 | - name: Package Mac 67 | if: startsWith(matrix.os,'mac') 68 | shell: bash -l {0} 69 | run: | 70 | ./package_mac.sh 71 | - name: Rename Mac 72 | if: startsWith(matrix.os,'mac') 73 | shell: bash -l {0} 74 | run: | 75 | mv dist/minerva_analysis_mac "dist/minerva_analysis_mac_${{ env.BRANCH }}" 76 | cd dist 77 | zip "minerva_analysis_mac_${{ env.BRANCH }}.zip" "minerva_analysis_mac_${{ env.BRANCH }}" 78 | rm "minerva_analysis_mac_${{ env.BRANCH }}" 79 | - name: Release 80 | uses: softprops/action-gh-release@v1 81 | if: startsWith(github.ref, 'refs/tags/') 82 | with: 83 | files: | 84 | dist/* 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/views/lensingFilters/lfSegmentationOutlines.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @class LfSegmentationOutlines 3 | * Note - useful template implementation for viewfinder functionality without viewerfinder visibility 4 | */ 5 | export class LfSegmentationOutlines { 6 | 7 | // Class vars (and chart 'vars') 8 | data = []; 9 | load = []; 10 | vars = {}; 11 | 12 | /** 13 | * @constructor 14 | */ 15 | constructor(_imageViewer) { 16 | this.image_viewer = _imageViewer; 17 | const { dataLayer, channelList } = __minervaAnalysis; 18 | 19 | // From global vars 20 | this.data_layer = dataLayer; 21 | this.channel_list = channelList; 22 | 23 | // Init 24 | this.init() 25 | } 26 | 27 | 28 | /** 29 | * @function init 30 | * 31 | * @return void 32 | */ 33 | init() { 34 | 35 | this.data = []; 36 | this.load = { 37 | data: [], 38 | config: { 39 | type: 'object-single', 40 | filter: 'fil_data_custom', 41 | vf_ref: 'vis_data_custom', 42 | filterCode: { 43 | data: [], 44 | name: 'fil_data_segmentation_outlines', 45 | vis_name: 'Data Segmentation Outlines', 46 | settings: { 47 | active: 1, 48 | async: false, 49 | default: 1, 50 | loading: false, 51 | max: 1, 52 | min: 0, 53 | step: 1, 54 | vf: true, 55 | vf_setup: 'vis_data_segmentation_outlines', 56 | iter: 'px' 57 | }, 58 | set_pixel: () => { 59 | 60 | }, 61 | update: (i, index) => { 62 | 63 | // Magnify (simply pass through after filter) 64 | this.image_viewer.viewer.lensing.lenses.selections.magnifier.update(i, index); 65 | }, 66 | fill: 'rgba(255, 255, 255, 0)', 67 | stroke: 'rgba(0, 0, 0, 1)' 68 | }, 69 | get_vf_setup: () => { 70 | return { 71 | name: 'vis_data_segmentation_outlines', 72 | init: () => { 73 | 74 | // Hide vf 75 | const vf = this.image_viewer.viewer.lensing.viewfinder; 76 | vf.els.svg.attr('opacity', 0); 77 | 78 | // Trigger channel list data request 79 | this.channel_list.triggerChannelSelect(); 80 | 81 | // Show outlines 82 | this.image_viewer.viewerManagerVAuxi.show_sel = true; 83 | this.image_viewer.viewerManagerVAuxi.force_repaint(); 84 | 85 | }, 86 | wrangle: () => { 87 | 88 | }, 89 | render: () => { 90 | 91 | }, 92 | destroy: () => { 93 | 94 | // Show vf 95 | const vf = this.image_viewer.viewer.lensing.viewfinder; 96 | vf.els.svg.attr('opacity', 1); 97 | 98 | // Show outlines 99 | this.image_viewer.viewerManagerVAuxi.show_sel = false; 100 | this.image_viewer.viewerManagerVAuxi.force_repaint(); 101 | 102 | } 103 | } 104 | }, 105 | }, 106 | }; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-svg-overlay.js: -------------------------------------------------------------------------------- 1 | // OpenSeadragon SVG Overlay plugin 0.0.5 2 | 3 | (function() { 4 | 5 | var $ = window.OpenSeadragon; 6 | 7 | if (!$) { 8 | $ = require('openseadragon'); 9 | if (!$) { 10 | throw new Error('OpenSeadragon is missing.'); 11 | } 12 | } 13 | 14 | var svgNS = 'http://www.w3.org/2000/svg'; 15 | 16 | // ---------- 17 | $.Viewer.prototype.svgOverlay = function() { 18 | if (this._svgOverlayInfo) { 19 | return this._svgOverlayInfo; 20 | } 21 | 22 | this._svgOverlayInfo = new Overlay(this); 23 | return this._svgOverlayInfo; 24 | }; 25 | 26 | // ---------- 27 | var Overlay = function(viewer) { 28 | var self = this; 29 | 30 | this._viewer = viewer; 31 | this._containerWidth = 0; 32 | this._containerHeight = 0; 33 | 34 | this._svg = document.createElementNS(svgNS, 'svg'); 35 | this._svg.id = "svgOverlay"; 36 | this._svg.style.position = 'absolute'; 37 | this._svg.style.left = 0; 38 | this._svg.style.top = 0; 39 | this._svg.style.width = '100%'; 40 | this._svg.style.height = '100%'; 41 | this._viewer.canvas.appendChild(this._svg); 42 | 43 | this._node = document.createElementNS(svgNS, 'g'); 44 | this._svg.appendChild(this._node); 45 | 46 | this._viewer.addHandler('animation', function() { 47 | self.resize(); 48 | }); 49 | 50 | this._viewer.addHandler('open', function() { 51 | self.resize(); 52 | }); 53 | 54 | this._viewer.addHandler('rotate', function(evt) { 55 | self.resize(); 56 | }); 57 | 58 | this._viewer.addHandler('resize', function() { 59 | self.resize(); 60 | }); 61 | 62 | this.resize(); 63 | }; 64 | 65 | // ---------- 66 | Overlay.prototype = { 67 | // ---------- 68 | node: function() { 69 | return this._node; 70 | }, 71 | 72 | // ---------- 73 | resize: function() { 74 | if (this._containerWidth !== this._viewer.container.clientWidth) { 75 | this._containerWidth = this._viewer.container.clientWidth; 76 | this._svg.setAttribute('width', this._containerWidth); 77 | } 78 | 79 | if (this._containerHeight !== this._viewer.container.clientHeight) { 80 | this._containerHeight = this._viewer.container.clientHeight; 81 | this._svg.setAttribute('height', this._containerHeight); 82 | } 83 | 84 | var p = this._viewer.viewport.pixelFromPoint(new $.Point(0, 0), true); 85 | var zoom = this._viewer.viewport.getZoom(true); 86 | var rotation = this._viewer.viewport.getRotation(); 87 | // TODO: Expose an accessor for _containerInnerSize in the OSD API so we don't have to use the private variable. 88 | var scale = this._viewer.viewport._containerInnerSize.x * zoom; 89 | this._node.setAttribute('transform', 90 | 'translate(' + p.x + ',' + p.y + ') scale(' + scale + ') rotate(' + rotation + ')'); 91 | }, 92 | 93 | // ---------- 94 | onClick: function(node, handler) { 95 | // TODO: Fast click for mobile browsers 96 | 97 | new $.MouseTracker({ 98 | element: node, 99 | clickHandler: handler 100 | }).setTracking(true); 101 | }, 102 | 103 | 104 | onDrag: function(node, handler){ 105 | new $.MouseTracker({ 106 | element: node, 107 | dragHandler: function(event) { 108 | // Move node by event.delta 109 | } 110 | }).setTracking(true); 111 | } 112 | 113 | 114 | 115 | }; 116 | 117 | })(); 118 | -------------------------------------------------------------------------------- /minerva_analysis/client/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}{% endblock %} 4 | 5 | 6 | 7 | {% block style %}{% endblock %} 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 84 | 85 | {% block content %}{% endblock %} 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openSeadragonGL.js: -------------------------------------------------------------------------------- 1 | /*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~ 2 | /* openSeadragonGL - Set Shaders in OpenSeaDragon with viaWebGL 3 | */ 4 | openSeadragonGL = function(openSD) { 5 | 6 | /* OpenSeaDragon API calls 7 | ~*~*~*~*~*~*~*~*~*~*~*~*/ 8 | this.interface = { 9 | 'tile-loaded': function(e) { 10 | // Set the imageSource as a data URL and then complete 11 | var output = this.viaGL.toCanvas(e.image); 12 | e.image.onload = e.getCompletionCallback(); 13 | e.image.src = output.toDataURL(); 14 | }, 15 | 'tile-drawing': function(e) { 16 | // Render a webGL canvas to an input canvas 17 | var input = e.rendered.canvas; 18 | e.rendered.drawImage(this.viaGL.toCanvas(input), 0, 0, input.width, input.height); 19 | } 20 | }; 21 | this.defaults = { 22 | 'tile-loaded': function(callback, e) { 23 | callback(e); 24 | }, 25 | 'tile-drawing': function(callback, e) { 26 | if (e.tile.loaded !==1) { 27 | e.tile.loaded = 1; 28 | callback(e); 29 | } 30 | } 31 | }; 32 | this.openSD = openSD; 33 | this.viaGL = new ViaWebGL(); 34 | }; 35 | 36 | openSeadragonGL.prototype = { 37 | // Map to viaWebGL and openSeadragon 38 | 39 | // jojo - forcing init manually, make sure data has been loaded 40 | forceinit: function() { 41 | var open = this.merger.bind(this); 42 | open(); 43 | return this; 44 | }, 45 | init: function() { 46 | var open = this.merger.bind(this); 47 | this.openSD.addHandler('open',open); 48 | 49 | //jojo - handles addTiledImage() 50 | this.openSD.world.addHandler('add-item',open); 51 | 52 | return this; 53 | }, 54 | // User adds events 55 | addHandler: function(key,custom) { 56 | if (key in this.defaults){ 57 | this[key] = this.defaults[key]; 58 | } 59 | if (typeof custom == 'function') { 60 | this[key] = custom; 61 | } 62 | }, 63 | // Merge with viaGL 64 | merger: function(e) { 65 | 66 | // jojo - check with multiple datasources being loaded 67 | //var tilesource = this.openSD.viewer.world.getItemAt(0); 68 | var tilesource = this.openSD.source; 69 | 70 | // Take GL height and width from OpenSeaDragon 71 | this.width = tilesource.getTileWidth(); 72 | this.height = tilesource.getTileHeight(); 73 | // Add all viaWebGL properties 74 | for (var key of this.and(this.viaGL)) { 75 | this.viaGL[key] = this[key]; 76 | } 77 | this.viaGL.init().then(this.adder.bind(this)); 78 | }, 79 | // Add all seadragon properties 80 | adder: function(e) { 81 | for (var key of this.and(this.defaults)) { 82 | var handler = this[key].bind(this); 83 | var _interface = this.interface[key].bind(this); 84 | // Add all openSeadragon event handlers 85 | this.openSD.addHandler(key, function(e) { 86 | handler.call(this, _interface, e); 87 | }); 88 | } 89 | }, 90 | // Joint keys 91 | and: function(obj) { 92 | return Object.keys(obj).filter(Object.hasOwnProperty,this); 93 | }, 94 | // Add your own button to OSD controls 95 | button: function(terms) { 96 | 97 | var name = terms.name || 'tool'; 98 | var prefix = terms.prefix || this.openSD.prefixUrl; 99 | if (!terms.hasOwnProperty('onClick')){ 100 | terms.onClick = this.shade; 101 | } 102 | terms.onClick = terms.onClick.bind(this); 103 | terms.srcRest = terms.srcRest || prefix+name+'_rest.png'; 104 | terms.srcHover = terms.srcHover || prefix+name+'_hover.png'; 105 | terms.srcDown = terms.srcDown || prefix+name+'_pressed.png'; 106 | terms.srcGroup = terms.srcGroup || prefix+name+'_grouphover.png'; 107 | // Replace the current controls with the same controls plus a new button 108 | this.openSD.clearControls().buttons.buttons.push(new OpenSeadragon.Button(terms)); 109 | var toolbar = new OpenSeadragon.ButtonGroup({buttons: this.openSD.buttons.buttons}); 110 | this.openSD.addControl(toolbar.element,{anchor: OpenSeadragon.ControlAnchor.TOP_LEFT}); 111 | }, 112 | // Switch Shaders on or off 113 | shade: function() { 114 | 115 | this.viaGL.on++; 116 | this.openSD.world.resetItems(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /minerva_analysis/client/karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('./webpack.config'); 2 | delete webpackConfig.entry; 3 | delete webpackConfig.output; 4 | 5 | // Moccked test server of Mockttp 6 | const SERVER = "http://localhost:8765"; 7 | 8 | module.exports = function(config) { 9 | config.set({ 10 | 11 | // base path that will be used to resolve all patterns (eg. files, exclude) 12 | basePath: '', 13 | 14 | // frameworks to use 15 | // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter 16 | frameworks: ['mocha', 'chai', 'sinon-chai', 'webpack', 'fixture', 'jquery-3.4.0'], 17 | 18 | chai: { 19 | includeStack: true 20 | }, 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | { 25 | pattern: 'src/css/**/*.css', 26 | }, 27 | { 28 | pattern: 'test/fixtures/**', 29 | }, 30 | { 31 | pattern: 'src/shaders/**/*.glsl', 32 | included: false, 33 | served: true, 34 | watch: false 35 | }, 36 | { 37 | pattern: 'src/js/**/*.js', 38 | included: false, 39 | served: true, 40 | watch: false 41 | }, 42 | { 43 | pattern: 'external/**/*', 44 | included: false, 45 | served: true, 46 | watch: false 47 | }, 48 | { 49 | pattern: 'dist/**/*.js', 50 | included: false, 51 | served: true, 52 | watch: false 53 | }, 54 | { 55 | pattern: 'test/data/*', 56 | included: false, 57 | served: true, 58 | watch: false 59 | }, 60 | { 61 | pattern: 'test/globals/*.js', 62 | included: false, 63 | served: true, 64 | watch: false 65 | }, 66 | { 67 | pattern: 'test/**/*.ts', 68 | watch: false, 69 | type: 'js' 70 | } 71 | ], 72 | 73 | customContextFile: 'test/fixtures/context.html', 74 | 75 | proxies: { 76 | "/dist/": "/base/dist/", 77 | "/js/": "/base/src/js/", 78 | "/client/src/": "/base/src/", 79 | "/data/": "/base/test/data/", 80 | "/globals/": "/base/test/globals/", 81 | "/fixtures/": "/base/test/fixtures/", 82 | "/client/external/": "/base/external/", 83 | "/config": `${SERVER}/config`, 84 | "/init_database": `${SERVER}/init_database`, 85 | "/get_all_cells/": `${SERVER}/get_all_cells/`, 86 | "/generated/data/": `${SERVER}/generated/data/`, 87 | "/get_gating_gmm": `${SERVER}/get_gating_gmm`, 88 | "/get_channel_gmm": `${SERVER}/get_channel_gmm`, 89 | "/get_ome_metadata": `${SERVER}/get_ome_metadata`, 90 | "/get_channel_names": `${SERVER}/get_channel_names`, 91 | "/download_gating_csv": `${SERVER}/download_gating_csv`, 92 | "/download_channels_csv": `${SERVER}/download_channels_csv`, 93 | "/get_database_description": `${SERVER}/get_database_description`, 94 | }, 95 | 96 | // list of files / patterns to exclude 97 | exclude: [ 98 | ], 99 | 100 | preprocessors: { 101 | 'test/fixtures/*.html': ['html2js'], 102 | 'test/**/*.ts': ['webpack'], 103 | 'src/*.js': ['webpack'] 104 | }, 105 | 106 | html2JsPreprocessor: { 107 | stripPrefix: 'test/fixtures/', 108 | prependPrefix: 'html/', 109 | }, 110 | 111 | webpack: webpackConfig, 112 | 113 | // test results reporter to use 114 | // possible values: 'dots', 'progress' 115 | // available reporters: https://www.npmjs.com/search?q=keywords:karma-$eporter 116 | reporters: ['progress'], 117 | 118 | 119 | // web server port 120 | port: 9876, 121 | 122 | 123 | // enable / disable colors in the output (reporters and logs) 124 | colors: true, 125 | 126 | 127 | // level of logging 128 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 129 | logLevel: config.LOG_INFO, 130 | 131 | 132 | // enable / disable watching file and executing tests whenever any file changes 133 | autoWatch: false, 134 | 135 | 136 | // start these browsers 137 | // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher 138 | browsers: ['Chrome'], 139 | 140 | client: { 141 | mocha: { 142 | reporter: 'html', 143 | timeout: 60000 144 | } 145 | }, 146 | 147 | // Continuous Integration mode 148 | // if true, Karma captures browsers, runs the tests and exits 149 | singleRun: true, 150 | 151 | // Concurrency level 152 | // how many browser instances should be started simultaneously 153 | concurrency: Infinity 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/get_gating_gmm/Hoechst0.json: -------------------------------------------------------------------------------- 1 | {"gate":9.229024200579097,"gmm_1":[{"x":7.941156196249974,"y":0.006610361078734373},{"x":7.996426554182134,"y":0.010210444137980217},{"x":8.051696912114293,"y":0.015393751261947444},{"x":8.106967270046454,"y":0.02265294529400751},{"x":8.162237627978612,"y":0.03253758141543446},{"x":8.217507985910771,"y":0.04561694949160224},{"x":8.272778343842932,"y":0.0624234247441211},{"x":8.32804870177509,"y":0.08337758734963924},{"x":8.38331905970725,"y":0.10870047367125932},{"x":8.43858941763941,"y":0.13832285024479704},{"x":8.493859775571568,"y":0.17180539696031583},{"x":8.549130133503729,"y":0.20828598839087667},{"x":8.60440049143589,"y":0.24646978652661866},{"x":8.659670849368048,"y":0.28467393369672667},{"x":8.714941207300207,"y":0.3209313156288721},{"x":8.770211565232366,"y":0.3531480972078388},{"x":8.825481923164526,"y":0.37929928576554334},{"x":8.880752281096687,"y":0.39763770582898356},{"x":8.936022639028845,"y":0.40688668131328437},{"x":8.991292996961004,"y":0.40638696491564547},{"x":9.046563354893163,"y":0.39617443250898465},{"x":9.101833712825323,"y":0.3769758246888223},{"x":9.157104070757484,"y":0.350123237523123},{"x":9.212374428689643,"y":0.3174013368179365},{"x":9.267644786621801,"y":0.2808516293134718},{"x":9.32291514455396,"y":0.24256354107771047},{"x":9.37818550248612,"y":0.20448171580767904},{"x":9.433455860418281,"y":0.16825338777237575},{"x":9.48872621835044,"y":0.13513054692394244},{"x":9.543996576282598,"y":0.10593113651957864},{"x":9.599266934214759,"y":0.08105393602516694},{"x":9.65453729214692,"y":0.0605347798045048},{"x":9.709807650079078,"y":0.04412819919905841},{"x":9.765078008011237,"y":0.03139842206992913},{"x":9.820348365943396,"y":0.02180619149072185},{"x":9.875618723875556,"y":0.014781966037997375},{"x":9.930889081807717,"y":0.009780587865516898},{"x":9.986159439739875,"y":0.0063165232357785695},{"x":10.041429797672034,"y":0.003981728258575618},{"x":10.096700155604193,"y":0.0024498839747510415},{"x":10.151970513536353,"y":0.0014712951272351023},{"x":10.207240871468514,"y":0.0008624510526922471},{"x":10.262511229400673,"y":0.000493457207615959},{"x":10.317781587332831,"y":0.0002755782368843593},{"x":10.37305194526499,"y":0.00015021756501289586},{"x":10.428322303197152,"y":0.0000799239425172939},{"x":10.483592661129311,"y":0.000041506246397467925},{"x":10.53886301906147,"y":0.000021039257123488818},{"x":10.594133376993629,"y":0.000010409448545267131},{"x":10.649403734925789,"y":5.026959942604477e-6}],"gmm_2":[{"x":7.941156196249974,"y":1.2461601834613228e-7},{"x":7.996426554182134,"y":3.7563807236822607e-7},{"x":8.051696912114293,"y":1.0880192646478337e-6},{"x":8.106967270046454,"y":3.028132096080135e-6},{"x":8.162237627978612,"y":8.098121664684989e-6},{"x":8.217507985910771,"y":0.000020809662058935164},{"x":8.272778343842932,"y":0.0000513827101574643},{"x":8.32804870177509,"y":0.00012191025406790867},{"x":8.38331905970725,"y":0.0002779295440725048},{"x":8.43858941763941,"y":0.0006088361669006198},{"x":8.493859775571568,"y":0.0012815555053214522},{"x":8.549130133503729,"y":0.002592063565649209},{"x":8.60440049143589,"y":0.00503761674712037},{"x":8.659670849368048,"y":0.00940753515670564},{"x":8.714941207300207,"y":0.01688098660891822},{"x":8.770211565232366,"y":0.029106571355540338},{"x":8.825481923164526,"y":0.048223144318408837},{"x":8.880752281096687,"y":0.07676994831880525},{"x":8.936022639028845,"y":0.11743518099003705},{"x":8.991292996961004,"y":0.17261416991446474},{"x":9.046563354893163,"y":0.2437956297519257},{"x":9.101833712825323,"y":0.33086180739605725},{"x":9.157104070757484,"y":0.43145808234186284},{"x":9.212374428689643,"y":0.5406321012955762},{"x":9.267644786621801,"y":0.650933015057991},{"x":9.32291514455396,"y":0.7530815689976493},{"x":9.37818550248612,"y":0.8371802757027196},{"x":9.433455860418281,"y":0.8942670023572666},{"x":9.48872621835044,"y":0.9178816206310842},{"x":9.543996576282598,"y":0.9052684629681734},{"x":9.599266934214759,"y":0.8579053136317547},{"x":9.65453729214692,"y":0.7812185983265675},{"x":9.709807650079078,"y":0.6835606271121271},{"x":9.765078008011237,"y":0.5747152941811681},{"x":9.820348365943396,"y":0.4643011070497866},{"x":9.875618723875556,"y":0.3604275113542587},{"x":9.930889081807717,"y":0.2688483631296608},{"x":9.986159439739875,"y":0.19269396340388434},{"x":10.041429797672034,"y":0.13270890688855036},{"x":10.096700155604193,"y":0.08782199202591492},{"x":10.151970513536353,"y":0.055844157730913474},{"x":10.207240871468514,"y":0.03412113396279151},{"x":10.262511229400673,"y":0.020032742841008304},{"x":10.317781587332831,"y":0.01130130577780043},{"x":10.37305194526499,"y":0.006126156467168205},{"x":10.428322303197152,"y":0.0031909411411949282},{"x":10.483592661129311,"y":0.0015970582312908026},{"x":10.53886301906147,"y":0.0007680578973024362},{"x":10.594133376993629,"y":0.00035492649144622273},{"x":10.649403734925789,"y":0.00015759924129203507}]} -------------------------------------------------------------------------------- /minerva_analysis/client/src/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 12 | 18 | 47 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/config.json: -------------------------------------------------------------------------------- 1 | {"karma-test":{"activeChannel":"","channelFile":"/Users/jth30/Desktop/crop-crc01/crop-crc01-097-096.ome.tif","featureData":[{"idField":"CellID","isTransformed":true,"normalization":"none","src":"/Users/jth30/projects/minerva_analysis/minerva_analysis/data/karma-test/crop-quantification-crc01-097-096.csv","xCoordinate":"X_centroid","yCoordinate":"Y_centroid"}],"height":2000,"imageData":[{"fullname":"Area","name":"Area","src":"/generated/data/karma-test/crop-mask-crc01-097-096/"},{"fullname":"Hoechst0","name":"Hoechst0","src":"/generated/data/karma-test/crop-crc01-097-096_0/"},{"fullname":"AF488","name":"AF488","src":"/generated/data/karma-test/crop-crc01-097-096_1/"},{"fullname":"AF555","name":"AF555","src":"/generated/data/karma-test/crop-crc01-097-096_2/"},{"fullname":"AF647","name":"AF647","src":"/generated/data/karma-test/crop-crc01-097-096_3/"},{"fullname":"Hoechst1","name":"Hoechst1","src":"/generated/data/karma-test/crop-crc01-097-096_4/"},{"fullname":"AF488","name":"AF488","src":"/generated/data/karma-test/crop-crc01-097-096_5/"},{"fullname":"A555","name":"A555","src":"/generated/data/karma-test/crop-crc01-097-096_6/"},{"fullname":"A647","name":"A647","src":"/generated/data/karma-test/crop-crc01-097-096_7/"},{"fullname":"Hoechst2","name":"Hoechst2","src":"/generated/data/karma-test/crop-crc01-097-096_8/"},{"fullname":"anti_CD3","name":"anti_CD3","src":"/generated/data/karma-test/crop-crc01-097-096_9/"},{"fullname":"anti_NaKATPase","name":"anti_NaKATPase","src":"/generated/data/karma-test/crop-crc01-097-096_10/"},{"fullname":"anti_CD45RO","name":"anti_CD45RO","src":"/generated/data/karma-test/crop-crc01-097-096_11/"},{"fullname":"Hoechst3","name":"Hoechst3","src":"/generated/data/karma-test/crop-crc01-097-096_12/"},{"fullname":"Ki67_488","name":"Ki67_488","src":"/generated/data/karma-test/crop-crc01-097-096_13/"},{"fullname":"Keratin_570","name":"Keratin_570","src":"/generated/data/karma-test/crop-crc01-097-096_14/"},{"fullname":"aSMA_660","name":"aSMA_660","src":"/generated/data/karma-test/crop-crc01-097-096_15/"},{"fullname":"Hoechst4","name":"Hoechst4","src":"/generated/data/karma-test/crop-crc01-097-096_16/"},{"fullname":"CD4_488","name":"CD4_488","src":"/generated/data/karma-test/crop-crc01-097-096_17/"},{"fullname":"CD45_PE","name":"CD45_PE","src":"/generated/data/karma-test/crop-crc01-097-096_18/"},{"fullname":"PD1_647","name":"PD1_647","src":"/generated/data/karma-test/crop-crc01-097-096_19/"},{"fullname":"Hoechst5","name":"Hoechst5","src":"/generated/data/karma-test/crop-crc01-097-096_20/"},{"fullname":"CD20_488","name":"CD20_488","src":"/generated/data/karma-test/crop-crc01-097-096_21/"},{"fullname":"CD68_555","name":"CD68_555","src":"/generated/data/karma-test/crop-crc01-097-096_22/"},{"fullname":"CD8a_660","name":"CD8a_660","src":"/generated/data/karma-test/crop-crc01-097-096_23/"},{"fullname":"Hoechst6","name":"Hoechst6","src":"/generated/data/karma-test/crop-crc01-097-096_24/"},{"fullname":"CD163_488","name":"CD163_488","src":"/generated/data/karma-test/crop-crc01-097-096_25/"},{"fullname":"FOXP3_570","name":"FOXP3_570","src":"/generated/data/karma-test/crop-crc01-097-096_26/"},{"fullname":"PDL1_647","name":"PDL1_647","src":"/generated/data/karma-test/crop-crc01-097-096_27/"},{"fullname":"Hoechst7","name":"Hoechst7","src":"/generated/data/karma-test/crop-crc01-097-096_28/"},{"fullname":"Ecad_488","name":"Ecad_488","src":"/generated/data/karma-test/crop-crc01-097-096_29/"},{"fullname":"Vimentin_555","name":"Vimentin_555","src":"/generated/data/karma-test/crop-crc01-097-096_30/"},{"fullname":"CDX2_647","name":"CDX2_647","src":"/generated/data/karma-test/crop-crc01-097-096_31/"},{"fullname":"Hoechst8","name":"Hoechst8","src":"/generated/data/karma-test/crop-crc01-097-096_32/"},{"fullname":"LaminABC_488","name":"LaminABC_488","src":"/generated/data/karma-test/crop-crc01-097-096_33/"},{"fullname":"Desmin_555","name":"Desmin_555","src":"/generated/data/karma-test/crop-crc01-097-096_34/"},{"fullname":"CD31_647","name":"CD31_647","src":"/generated/data/karma-test/crop-crc01-097-096_35/"},{"fullname":"Hoechst9","name":"Hoechst9","src":"/generated/data/karma-test/crop-crc01-097-096_36/"},{"fullname":"PCNA_488","name":"PCNA_488","src":"/generated/data/karma-test/crop-crc01-097-096_37/"},{"fullname":"Ki67_570","name":"Ki67_570","src":"/generated/data/karma-test/crop-crc01-097-096_38/"},{"fullname":"CollagenIV_647","name":"CollagenIV_647","src":"/generated/data/karma-test/crop-crc01-097-096_39/"},{"fullname":"HE_r","name":"HE_r","src":"/generated/data/karma-test/crop-crc01-097-096_40/"},{"fullname":"HE_g","name":"HE_g","src":"/generated/data/karma-test/crop-crc01-097-096_41/"},{"fullname":"HE_b","name":"HE_b","src":"/generated/data/karma-test/crop-crc01-097-096_42/"}],"maxLevel":2,"num_channels":43,"segmentation":"/Users/jth30/projects/minerva_analysis/minerva_analysis/data/karma-test/crop-mask-crc01-097-096.tif","shapes":"","tileHeight":1024,"tileWidth":1024,"width":2000}} 2 | -------------------------------------------------------------------------------- /minerva_analysis/server/utils/smallestenclosingcircle.py: -------------------------------------------------------------------------------- 1 | # 2 | # Smallest enclosing circle - Library (Python) 3 | # 4 | # Copyright (c) 2020 Project Nayuki 5 | # https://www.nayuki.io/page/smallest-enclosing-circle 6 | # 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU Lesser General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with this program (see COPYING.txt and COPYING.LESSER.txt). 19 | # If not, see . 20 | # 21 | 22 | import math, random 23 | 24 | 25 | # Data conventions: A point is a pair of floats (x, y). A circle is a triple of floats (center x, center y, radius). 26 | 27 | # Returns the smallest circle that encloses all the given points. Runs in expected O(n) time, randomized. 28 | # Input: A sequence of pairs of floats or ints, e.g. [(0,5), (3.1,-2.7)]. 29 | # Output: A triple of floats representing a circle. 30 | # Note: If 0 points are given, None is returned. If 1 point is given, a circle of radius 0 is returned. 31 | # 32 | # Initially: No boundary points known 33 | def make_circle(points): 34 | # Convert to float and randomize order 35 | shuffled = [(float(x), float(y)) for (x, y) in points] 36 | random.shuffle(shuffled) 37 | 38 | # Progressively add points to circle or recompute circle 39 | c = None 40 | for (i, p) in enumerate(shuffled): 41 | if c is None or not is_in_circle(c, p): 42 | c = _make_circle_one_point(shuffled[ : i + 1], p) 43 | return c 44 | 45 | 46 | # One boundary point known 47 | def _make_circle_one_point(points, p): 48 | c = (p[0], p[1], 0.0) 49 | for (i, q) in enumerate(points): 50 | if not is_in_circle(c, q): 51 | if c[2] == 0.0: 52 | c = make_diameter(p, q) 53 | else: 54 | c = _make_circle_two_points(points[ : i + 1], p, q) 55 | return c 56 | 57 | 58 | # Two boundary points known 59 | def _make_circle_two_points(points, p, q): 60 | circ = make_diameter(p, q) 61 | left = None 62 | right = None 63 | px, py = p 64 | qx, qy = q 65 | 66 | # For each point not in the two-point circle 67 | for r in points: 68 | if is_in_circle(circ, r): 69 | continue 70 | 71 | # Form a circumcircle and classify it on left or right side 72 | cross = _cross_product(px, py, qx, qy, r[0], r[1]) 73 | c = make_circumcircle(p, q, r) 74 | if c is None: 75 | continue 76 | elif cross > 0.0 and (left is None or _cross_product(px, py, qx, qy, c[0], c[1]) > _cross_product(px, py, qx, qy, left[0], left[1])): 77 | left = c 78 | elif cross < 0.0 and (right is None or _cross_product(px, py, qx, qy, c[0], c[1]) < _cross_product(px, py, qx, qy, right[0], right[1])): 79 | right = c 80 | 81 | # Select which circle to return 82 | if left is None and right is None: 83 | return circ 84 | elif left is None: 85 | return right 86 | elif right is None: 87 | return left 88 | else: 89 | return left if (left[2] <= right[2]) else right 90 | 91 | 92 | def make_diameter(a, b): 93 | cx = (a[0] + b[0]) / 2 94 | cy = (a[1] + b[1]) / 2 95 | r0 = math.hypot(cx - a[0], cy - a[1]) 96 | r1 = math.hypot(cx - b[0], cy - b[1]) 97 | return (cx, cy, max(r0, r1)) 98 | 99 | 100 | def make_circumcircle(a, b, c): 101 | # Mathematical algorithm from Wikipedia: Circumscribed circle 102 | ox = (min(a[0], b[0], c[0]) + max(a[0], b[0], c[0])) / 2 103 | oy = (min(a[1], b[1], c[1]) + max(a[1], b[1], c[1])) / 2 104 | ax = a[0] - ox; ay = a[1] - oy 105 | bx = b[0] - ox; by = b[1] - oy 106 | cx = c[0] - ox; cy = c[1] - oy 107 | d = (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by)) * 2.0 108 | if d == 0.0: 109 | return None 110 | x = ox + ((ax*ax + ay*ay) * (by - cy) + (bx*bx + by*by) * (cy - ay) + (cx*cx + cy*cy) * (ay - by)) / d 111 | y = oy + ((ax*ax + ay*ay) * (cx - bx) + (bx*bx + by*by) * (ax - cx) + (cx*cx + cy*cy) * (bx - ax)) / d 112 | ra = math.hypot(x - a[0], y - a[1]) 113 | rb = math.hypot(x - b[0], y - b[1]) 114 | rc = math.hypot(x - c[0], y - c[1]) 115 | return (x, y, max(ra, rb, rc)) 116 | 117 | 118 | _MULTIPLICATIVE_EPSILON = 1 + 1e-14 119 | 120 | def is_in_circle(c, p): 121 | return c is not None and math.hypot(p[0] - c[0], p[1] - c[1]) <= c[2] * _MULTIPLICATIVE_EPSILON 122 | 123 | 124 | # Returns twice the signed area of the triangle defined by (x0, y0), (x1, y1), (x2, y2). 125 | def _cross_product(x0, y0, x1, y1, x2, y2): 126 | return (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0) 127 | -------------------------------------------------------------------------------- /minerva_analysis/client/src/css/viewer.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap Overrides */ 2 | #bodyDiv { 3 | background-color: rgba(0, 0, 0, 1) !important; 4 | height: 100% 5 | } 6 | 7 | #container, #row { 8 | border: 0; 9 | box-sizing: border-box; 10 | margin: 0; 11 | padding: 0; 12 | } 13 | 14 | #container { 15 | height: 95vh; 16 | max-height: 95vh; 17 | margin: 0; 18 | overflow: hidden; 19 | position: relative; 20 | width: 100vw; 21 | max-width: 100vw; 22 | z-index: 100; 23 | } 24 | 25 | #row { 26 | padding: 0 16px; 27 | width: 100%; 28 | } 29 | 30 | #channel_list_wrapper { 31 | background-color: rgba(0, 0, 0, 0); 32 | left: 1.25vw; 33 | position: absolute; 34 | top: 2.5vh; 35 | width: 17.5%; 36 | z-index: 101; 37 | } 38 | 39 | #csv_channel_list_wrapper { 40 | background-color: rgba(0, 0, 0, 0); 41 | left: 80.75vw; 42 | position: absolute; 43 | top: 2.5vh; 44 | width: 17.5%; 45 | z-index: 101; 46 | } 47 | 48 | #openseadragon_wrapper { 49 | display: flex; 50 | height: 100%; 51 | padding: 0 0 0 2.5vw; 52 | position: relative; 53 | width: 100%; 54 | z-index: 100; 55 | } 56 | 57 | #lowRow1, #lowRow2, #lowRow3 { 58 | position: relative; 59 | } 60 | 61 | #boxHeading1, #boxHeading2 , #boxHeading3 { 62 | color: rgba(255, 255, 255, 1); 63 | font-size: 16px; 64 | position: relative; 65 | } 66 | 67 | #selected-channels-header-div { 68 | position: absolute; 69 | right: 0; 70 | top: 0px; 71 | } 72 | 73 | #scalebar-header-div { 74 | position: absolute; 75 | right: 0; 76 | top: 0px; 77 | } 78 | 79 | #scalebar-header-div input { 80 | height: 11px; 81 | margin: 1px 10px 0 5px; 82 | } 83 | 84 | #download-channels-header-div { 85 | color: rgba(255, 255, 255, 1); 86 | /*position: absolute;*/ 87 | /*right: 0;*/ 88 | /*top: -3px;*/ 89 | } 90 | 91 | #download-channels-header-div span { 92 | cursor: pointer; 93 | font-size: 15px 94 | } 95 | 96 | #download-gating-header-div { 97 | color: rgba(255, 255, 255, 1); 98 | /*position: absolute;*/ 99 | /*right: 0;*/ 100 | /*top: -3px;*/ 101 | } 102 | 103 | #download-gating-header-div span { 104 | cursor: pointer; 105 | font-size: 15px 106 | } 107 | 108 | #channels_upload_icon_db, #channels_download_icon_db, #channels_upload_icon, #channels_download_icon, 109 | #gating_upload_icon_db, #gating_download_icon_db, #gating_upload_icon, #gating_download_icon{ 110 | cursor: pointer 111 | } 112 | 113 | #channels_download_icon_db, #gating_download_icon_db { 114 | margin-right: 10px; 115 | } 116 | 117 | #openseadragon { 118 | position: relative; 119 | z-index: 100; 120 | height: 100%; 121 | transform: translate3d(0, 0, 0); 122 | width: 100%; 123 | will-change: width; 124 | } 125 | 126 | .list { 127 | border: 1px solid rgba(255, 255, 255, 1); 128 | overflow-y: scroll; 129 | position: relative; 130 | width: 100%; 131 | } 132 | 133 | .list .list-group-item { 134 | background-color: rgba(0, 0, 0, 0.75); 135 | border: 0; 136 | color: rgba(255, 255, 255, 1); 137 | padding: 5px 2px 3px 2px; 138 | } 139 | 140 | #boxChannels1A, #boxChannels1B { 141 | font-size: 10px; 142 | } 143 | 144 | #gating_download_panel { 145 | background-color: rgba(255, 255, 255, 1); 146 | padding: 35px 10px 30px 10px; 147 | position: absolute; 148 | top: 19px; 149 | width: 98%; 150 | visibility: hidden; 151 | z-index: 100; 152 | } 153 | 154 | #gating_download_panel #gating_exit { 155 | cursor: pointer; 156 | font-size: 16px; 157 | font-weight: lighter; 158 | position: absolute; 159 | right: 5px; 160 | top: 5px; 161 | } 162 | 163 | #gating_download_panel label { 164 | font-size: 12px; 165 | width: 100%; 166 | } 167 | 168 | #gating_download_panel .download_flex { 169 | display: flex; 170 | flex-flow: row wrap; 171 | margin: 10px 0 5px 0; 172 | width: 100%; 173 | } 174 | 175 | #gating_download_panel .download_flex input[type=text] { 176 | background-color: rgba(255, 255, 255, 1); 177 | border: 1px solid rgba(0, 0, 0, 1); 178 | border-radius: 1px; 179 | color: darkblue; 180 | font-size: 10px; 181 | padding: 5px; 182 | width: 85%; 183 | } 184 | 185 | #gating_download_panel .download_button { 186 | color: rgba(0, 0, 0, 1); 187 | cursor: pointer; 188 | font-size: 18px; 189 | margin: 0 0 0 7px; 190 | transform: rotate(180deg); 191 | } 192 | 193 | #gating_controls_panel { 194 | background-color: rgba(0, 0, 0, 0.75); 195 | border: 1px solid rgba(255, 255, 255, 0.75); 196 | color: rgba(255, 255, 255, 1); 197 | bottom: -49px; 198 | display: flex; 199 | flex-flow: row wrap; 200 | padding: 5px 10px 0 10px; 201 | position: absolute; 202 | width: 98%; 203 | z-index: 100; 204 | } 205 | 206 | #gating_controls_panel label { 207 | align-items: center; 208 | display: flex; 209 | font-size: 11px; 210 | } 211 | 212 | #gating_controls_panel input { 213 | height: 11px; 214 | margin: 1px 10px 0 5px; 215 | } 216 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js: -------------------------------------------------------------------------------- 1 | // https://github.com/joshua-gould/OpenSeadragonCanvasOverlayHd v1.0.0 Copyright 2018 Joshua Gould 2 | (function (global, factory) { 3 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 4 | typeof define === 'function' && define.amd ? define(factory) : 5 | (global.OpenSeadragon = global.OpenSeadragon || {}, global.OpenSeadragon.CanvasOverlayHd = factory()); 6 | }(this, (function () { 'use strict'; 7 | 8 | class CanvasOverlayHd { 9 | constructor (viewer, options) { 10 | this._viewer = viewer; 11 | this.backingScale = 1; 12 | this._containerWidth = 0; 13 | this._containerHeight = 0; 14 | this._canvasdiv = document.createElement('div'); 15 | this._canvasdiv.style.position = 'absolute'; 16 | this._canvasdiv.style.left = 0; 17 | this._canvasdiv.style.top = 0; 18 | this._canvasdiv.style.width = '100%'; 19 | this._canvasdiv.style.height = '100%'; 20 | this._viewer.canvas.appendChild(this._canvasdiv); 21 | this._canvas = document.createElement('canvas'); 22 | this._canvasdiv.appendChild(this._canvas); 23 | this.onRedraw = options.onRedraw || function () {}; 24 | this.clearBeforeRedraw = (typeof (options.clearBeforeRedraw) !== 'undefined') ? 25 | options.clearBeforeRedraw : true; 26 | 27 | 28 | // 29 | // var that = this; 30 | // this._viewer.world.addHandler('add-item', function(event) { 31 | // var tiledImage = event.item; 32 | // tiledImage.addHandler('fully-loaded-change', function(e) { 33 | // that.resize(); 34 | // that._updateCanvas(); 35 | // }); 36 | // }); 37 | 38 | 39 | this._viewer.addHandler('update-viewport', () => { 40 | this.resize(); 41 | this._updateCanvas(); 42 | }); 43 | this._viewer.addHandler('open', () => { 44 | this.resize(); 45 | this._updateCanvas(); 46 | }); 47 | } 48 | 49 | static getTileIndexFromPixel (viewer, webPoint) { 50 | let viewportPos = viewer.viewport.pointFromPixel(webPoint); 51 | for (let i = 0, count = viewer.world.getItemCount(); i < count; i++) { 52 | let tiledImage = viewer.world.getItemAt(i); 53 | // box = viewer.world.getItemAt(i).getBounds(); 54 | // if (viewportPos.x > box.x && 55 | // viewportPos.y > box.y && 56 | // viewportPos.x < box.x + box.width && 57 | // viewportPos.y < box.y + box.height) { 58 | // 59 | // } 60 | // tiledImage.lastDrawn.forEach(function (tile) { 61 | // if (tile.bounds.containsPoint(viewportPos)) { 62 | // console.log('lastDrawn', tile); 63 | // } 64 | // }); 65 | 66 | let viewportPosRect = new OpenSeadragon.Rect(viewportPos.x, viewportPos.y, 0, 0); 67 | let tileSourcePosRect = tiledImage._viewportToTiledImageRectangle(viewportPosRect); 68 | let tileSourcePos = tileSourcePosRect.getTopLeft(); 69 | let source = tiledImage.source; 70 | if (tileSourcePos.x >= 0 && tileSourcePos.x <= 1 && tileSourcePos.y >= 0 && 71 | tileSourcePos.y <= 1 / source.aspectRatio) { 72 | return i; 73 | // for (let level = source.minLevel; level <= source.maxLevel; level++) { 74 | // let tilePoint = source.getTileAtPoint(level, tileSourcePos); 75 | // return i; 76 | // } 77 | } 78 | } 79 | return -1; 80 | } 81 | 82 | canvas () { 83 | return this._canvas; 84 | } 85 | 86 | context2d () { 87 | return this._canvas.getContext('2d'); 88 | } 89 | 90 | clear () { 91 | this._canvas.getContext('2d'). 92 | clearRect(0, 0, this._containerWidth * this.backingScale, this._containerHeight * this.backingScale); 93 | } 94 | 95 | resize () { 96 | let backingScale = 1; 97 | if (typeof window !== 'undefined' && 'devicePixelRatio' in window) { 98 | backingScale = window.devicePixelRatio; 99 | } 100 | let backingScaleUpdated = this.backingScale !== backingScale; 101 | this.backingScale = backingScale; 102 | if (this._containerWidth !== this._viewer.container.clientWidth || backingScaleUpdated) { 103 | this._containerWidth = this._viewer.container.clientWidth; 104 | this._canvasdiv.setAttribute('width', backingScale * this._containerWidth); 105 | this._canvas.setAttribute('width', backingScale * this._containerWidth); 106 | this._canvas.style.width = this._containerWidth + 'px'; 107 | } 108 | 109 | if (this._containerHeight !== this._viewer.container.clientHeight || backingScaleUpdated) { 110 | this._containerHeight = this._viewer.container.clientHeight; 111 | this._canvasdiv.setAttribute('height', backingScale * this._containerHeight); 112 | this._canvas.setAttribute('height', backingScale * this._containerHeight); 113 | this._canvas.style.height = this._containerHeight + 'px'; 114 | } 115 | } 116 | 117 | _updateCanvas () { 118 | let viewportZoom = this._viewer.viewport.getZoom(true); 119 | if (this.clearBeforeRedraw) { 120 | this.clear(); 121 | } 122 | let context = this._canvas.getContext('2d'); 123 | for (let i = 0, count = this._viewer.world.getItemCount(); i < count; i++) { 124 | let image = this._viewer.world.getItemAt(i); 125 | if (image) { 126 | let zoom = image.viewportToImageZoom(viewportZoom); 127 | let vp = image.imageToViewportCoordinates(0, 0, true); 128 | let p = this._viewer.viewport.pixelFromPoint(vp, true); 129 | context.scale(this.backingScale, this.backingScale); 130 | context.translate(p.x, p.y); 131 | context.scale(zoom, zoom); 132 | this.onRedraw({ index: i, context: context, x: p.x, y: p.y, zoom: zoom }); 133 | context.setTransform(1, 0, 0, 1, 0, 0); 134 | } 135 | } 136 | } 137 | } 138 | 139 | return CanvasOverlayHd; 140 | }))); -------------------------------------------------------------------------------- /minerva_analysis/client/src/img/logo_with_text.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/fixtures/main.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | Channels 7 |
8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 |
16 | 17 |
18 |
19 | 0 20 | of 4 (Max) Selected 21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 |
36 | Viewer 37 |
38 |
39 | Scalebar 40 | 41 | 42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 | 57 | 64 | 71 | 77 |
78 |
79 |
80 | CSV Gating 81 |
82 | 83 |
84 | 85 | 86 | 87 |
88 | 89 | 90 |
91 | 92 |
93 |
94 | 95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | Drag and Drop A Gating CSV File 109 |
110 |
111 |
112 |
113 | 116 | 119 |
120 |
121 |
122 |
123 | -------------------------------------------------------------------------------- /minerva_analysis/client/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | 3 | {% block style %} 4 | 5 | 6 | {% endblock %} 7 | 8 | {% block content %} 9 | 10 | {% if data.datasource %} 11 |
12 |
13 |
14 |
15 |
16 | Channels 17 |
18 |
19 | 20 | 21 | 22 |
23 | 24 | 25 |
26 | 27 |
28 |
29 | 0 30 | of 4 Selected 31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 | 61 | 68 | 75 | 81 |
82 |
83 |
84 | CSV Gating 85 |
86 | 87 |
88 | 89 | 90 | 91 |
92 | 93 | 94 |
95 | 96 |
97 |
98 | 99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | Drag and Drop A Gating CSV File 113 |
114 |
115 |
116 |
117 | 120 | 123 | 126 |
127 |
128 |
129 |
130 | 131 | {% endif %} 132 | {% endblock %} 133 | -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /minerva_analysis/client/test/data/get_channel_gmm/Hoechst0.json: -------------------------------------------------------------------------------- 1 | {"vmin":9795.0,"vmax":25553.0,"image_gmm_1":[{"x":6.679025760635471,"y":0.009050729430264264},{"x":6.7586855536315245,"y":0.012142899645098626},{"x":6.838345346627577,"y":0.016019823375663936},{"x":6.918005139623629,"y":0.020782105016742766},{"x":6.997664932619682,"y":0.026510495219650876},{"x":7.077324725615735,"y":0.033253904080230476},{"x":7.156984518611788,"y":0.04101700378968621},{"x":7.23664431160784,"y":0.049748698082369626},{"x":7.316304104603892,"y":0.05933295803947063},{"x":7.3959638975999455,"y":0.06958358100400472},{"x":7.475623690595998,"y":0.08024427243772846},{"x":7.555283483592051,"y":0.09099505798197316},{"x":7.634943276588103,"y":0.1014654211323705},{"x":7.714603069584156,"y":0.11125378938258657},{"x":7.794262862580209,"y":0.11995215540641478},{"x":7.873922655576261,"y":0.12717384261460535},{"x":7.953582448572314,"y":0.13258183496434056},{"x":8.033242241568367,"y":0.1359148001539766},{"x":8.11290203456442,"y":0.13700801371529672},{"x":8.192561827560471,"y":0.135806852273498},{"x":8.272221620556525,"y":0.1323713171316587},{"x":8.351881413552576,"y":0.1268710666188978},{"x":8.43154120654863,"y":0.11957152981387731},{"x":8.511200999544684,"y":0.11081268403360697},{"x":8.590860792540735,"y":0.10098285769648824},{"x":8.670520585536789,"y":0.0904903634956157},{"x":8.75018037853284,"y":0.07973582696764661},{"x":8.829840171528893,"y":0.06908776870364423},{"x":8.909499964524947,"y":0.05886339738861326},{"x":8.989159757520998,"y":0.0493157878945017},{"x":9.068819550517052,"y":0.04062778274923091},{"x":9.148479343513102,"y":0.03291218842629883},{"x":9.228139136509157,"y":0.026217235420389458},{"x":9.30779892950521,"y":0.02053588959678036},{"x":9.387458722501261,"y":0.015817456241804875},{"x":9.467118515497315,"y":0.011979984505469235},{"x":9.546778308493366,"y":0.008922208437723875},{"x":9.62643810148942,"y":0.0065340878202481655},{"x":9.706097894485474,"y":0.004705373079413734},{"x":9.785757687481524,"y":0.0033319590365882027},{"x":9.865417480477578,"y":0.0023200734671342945},{"x":9.945077273473629,"y":0.0015885479014327863},{"x":10.024737066469683,"y":0.001069535949533644},{"x":10.104396859465737,"y":0.0007080875398703338},{"x":10.184056652461788,"y":0.0004609724934669239},{"x":10.263716445457842,"y":0.00029509344647873697},{"x":10.343376238453892,"y":0.00018575504238217192},{"x":10.423036031449946,"y":0.00011497890080819768},{"x":10.502695824446,"y":0.00006998294061882393},{"x":10.582355617442051,"y":0.00004188540467426009}],"image_gmm_2":[{"x":6.679025760635471,"y":9.066296610132594e-8},{"x":6.7586855536315245,"y":2.4924701181941924e-7},{"x":6.838345346627577,"y":6.61484776923135e-7},{"x":6.918005139623629,"y":1.6947264366193838e-6},{"x":6.997664932619682,"y":4.191497495257678e-6},{"x":7.077324725615735,"y":0.000010007570869046336},{"x":7.156984518611788,"y":0.000023066303077242698},{"x":7.23664431160784,"y":0.00005132360696125278},{"x":7.316304104603892,"y":0.00011024177657291966},{"x":7.3959638975999455,"y":0.00022859413986813496},{"x":7.475623690595998,"y":0.0004575871697683731},{"x":7.555283483592051,"y":0.0008842446510197065},{"x":7.634943276588103,"y":0.00164953259021957},{"x":7.714603069584156,"y":0.0029705660688835855},{"x":7.794262862580209,"y":0.005164251545911771},{"x":7.873922655576261,"y":0.008666932553646268},{"x":7.953582448572314,"y":0.014041493463032908},{"x":8.033242241568367,"y":0.021960945991492734},{"x":8.11290203456442,"y":0.03315726056591225},{"x":8.192561827560471,"y":0.048327696689729596},{"x":8.272221620556525,"y":0.06799913817028662},{"x":8.351881413552576,"y":0.09236354142093596},{"x":8.43154120654863,"y":0.12111211848287085},{"x":8.511200999544684,"y":0.15330788132560463},{"x":8.590860792540735,"y":0.18734030346849137},{"x":8.670520585536789,"y":0.22099773034570472},{"x":8.75018037853284,"y":0.25167163127544395},{"x":8.829840171528893,"y":0.2766754157950902},{"x":8.909499964524947,"y":0.29362750509172647},{"x":8.989159757520998,"y":0.3008241894836459},{"x":9.068819550517052,"y":0.2975216898448589},{"x":9.148479343513102,"y":0.28406280117906746},{"x":9.228139136509157,"y":0.26181827383445266},{"x":9.30779892950521,"y":0.23295680516801662},{"x":9.387458722501261,"y":0.20009705859047197},{"x":9.467118515497315,"y":0.16591890163793735},{"x":9.546778308493366,"y":0.1328130897226346},{"x":9.62643810148942,"y":0.1026303364582472},{"x":9.706097894485474,"y":0.0765597456226703},{"x":9.785757687481524,"y":0.05513343704277886},{"x":9.865417480477578,"y":0.03832829580655106},{"x":9.945077273473629,"y":0.02572253745914669},{"x":10.024737066469683,"y":0.0166647177958167},{"x":10.104396859465737,"y":0.010422501718899236},{"x":10.184056652461788,"y":0.006292682847525124},{"x":10.263716445457842,"y":0.003667664221111506},{"x":10.343376238453892,"y":0.0020636362593336077},{"x":10.423036031449946,"y":0.0011208992744551516},{"x":10.502695824446,"y":0.0005877462712242194},{"x":10.582355617442051,"y":0.0002975110040344205}],"image_gmm_3":[{"x":6.679025760635471,"y":7.87133319981338e-23},{"x":6.7586855536315245,"y":1.219552983881034e-21},{"x":6.838345346627577,"y":1.7503696244186915e-20},{"x":6.918005139623629,"y":2.327210038584052e-19},{"x":6.997664932619682,"y":2.8662767030639225e-18},{"x":7.077324725615735,"y":3.270222968990085e-17},{"x":7.156984518611788,"y":3.456315141875119e-16},{"x":7.23664431160784,"y":3.3839662424938042e-15},{"x":7.316304104603892,"y":3.069131019600064e-14},{"x":7.3959638975999455,"y":2.5785856051064254e-13},{"x":7.475623690595998,"y":2.0068938300580874e-12},{"x":7.555283483592051,"y":1.4469181362017424e-11},{"x":7.634943276588103,"y":9.6636288435415e-11},{"x":7.714603069584156,"y":5.978789197228122e-10},{"x":7.794262862580209,"y":3.426596441254396e-9},{"x":7.873922655576261,"y":1.819237487112067e-8},{"x":7.953582448572314,"y":8.947311387657094e-8},{"x":8.033242241568367,"y":4.076359014033377e-7},{"x":8.11290203456442,"y":1.7203983570579054e-6},{"x":8.192561827560471,"y":6.72608467857657e-6},{"x":8.272221620556525,"y":0.000024359723849638437},{"x":8.351881413552576,"y":0.00008172578661319966},{"x":8.43154120654863,"y":0.0002539934848191699},{"x":8.511200999544684,"y":0.0007312447526140889},{"x":8.590860792540735,"y":0.0019502023129308796},{"x":8.670520585536789,"y":0.004818072051627818},{"x":8.75018037853284,"y":0.011026651076772895},{"x":8.829840171528893,"y":0.023377103118176118},{"x":8.909499964524947,"y":0.04591075889102707},{"x":8.989159757520998,"y":0.08352470861567128},{"x":9.068819550517052,"y":0.14076418996667303},{"x":9.148479343513102,"y":0.2197587217966316},{"x":9.228139136509157,"y":0.3178167438981949},{"x":9.30779892950521,"y":0.42577891018909825},{"x":9.387458722501261,"y":0.5284066040093863},{"x":9.467118515497315,"y":0.6074758902295783},{"x":9.546778308493366,"y":0.646943828268618},{"x":9.62643810148942,"y":0.6382352998881451},{"x":9.706097894485474,"y":0.583272882849234},{"x":9.785757687481524,"y":0.4937867928261337},{"x":9.865417480477578,"y":0.3872432641886635},{"x":9.945077273473629,"y":0.281322840200563},{"x":10.024737066469683,"y":0.18932276258962552},{"x":10.104396859465737,"y":0.11802591917713139},{"x":10.184056652461788,"y":0.06815985663972503},{"x":10.263716445457842,"y":0.036463358207061265},{"x":10.343376238453892,"y":0.018070134205688072},{"x":10.423036031449946,"y":0.00829550464516075},{"x":10.502695824446,"y":0.003527775682666191},{"x":10.582355617442051,"y":0.0013897472786821928}]} -------------------------------------------------------------------------------- /minerva_analysis/client/external/openseadragon-bin-2.4.0/viaWebGL.js: -------------------------------------------------------------------------------- 1 | /*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~ 2 | /* viaWebGL 3 | /* Set shaders on Image or Canvas with WebGL 4 | /* Built on 2016-9-9 5 | /* http://via.hoff.in 6 | */ 7 | ViaWebGL = function(incoming) { 8 | 9 | /* Custom WebGL API calls 10 | ~*~*~*~*~*~*~*~*~*~*~*~*/ 11 | this['gl-drawing'] = function(e) { return e; }; 12 | this['gl-loaded'] = function(e) { return e; }; 13 | this.ready = function(e) { return e; }; 14 | 15 | var gl = this.maker(); 16 | this.flat = document.createElement('canvas').getContext('2d'); 17 | this.tile_size = 'u_tile_size'; 18 | this.vShader = 'vShader.glsl'; 19 | this.fShader = 'fShader.glsl'; 20 | this.wrap = gl.CLAMP_TO_EDGE; 21 | this.tile_pos = 'a_tile_pos'; 22 | this.filter = gl.NEAREST; 23 | this.pos = 'a_pos'; 24 | this.height = 128; 25 | this.width = 128; 26 | this.on = 0; 27 | this.gl = gl; 28 | // Assign from incoming terms 29 | for (var key in incoming) { 30 | this[key] = incoming[key]; 31 | } 32 | }; 33 | 34 | ViaWebGL.prototype = { 35 | 36 | init: function(source) { 37 | var ready = this.ready; 38 | // Allow for mouse actions on click 39 | if (this.hasOwnProperty('container') && this.hasOwnProperty('onclick')) { 40 | this.container.onclick = this[this.onclick].bind(this); 41 | } 42 | if (source && source.height && source.width) { 43 | this.ready = this.toCanvas.bind(this,source); 44 | this.height = source.height; 45 | this.width = source.width; 46 | } 47 | this.source = source; 48 | this.gl.canvas.width = this.width; 49 | this.gl.canvas.height = this.height; 50 | this.gl.viewport(0, 0, this.width, this.height); 51 | // Load the shaders when ready and return the promise 52 | var step = [[this.vShader, this.fShader].map(this.getter)]; 53 | step.push(this.toProgram.bind(this), this.toBuffers.bind(this)); 54 | return Promise.all(step[0]).then(step[1]).then(step[2]).then(this.ready); 55 | 56 | }, 57 | // Make a canvas 58 | maker: function(options){ 59 | return this.context(document.createElement('canvas')); 60 | }, 61 | context: function(a){ 62 | return a.getContext('experimental-webgl') || a.getContext('webgl'); 63 | }, 64 | // Get a file as a promise 65 | getter: function(where) { 66 | return new Promise(function(done){ 67 | // Return if not a valid filename 68 | if (where.slice(-4) != 'glsl') { 69 | return done(where); 70 | } 71 | var bid = new XMLHttpRequest(); 72 | var win = function(){ 73 | if (bid.status == 200) { 74 | return done(bid.response); 75 | } 76 | return done(where); 77 | }; 78 | bid.open('GET', where, true); 79 | bid.onerror = bid.onload = win; 80 | bid.send(); 81 | }); 82 | }, 83 | // Link shaders from strings 84 | toProgram: function(files) { 85 | var gl = this.gl; 86 | var program = gl.createProgram(); 87 | var ok = function(kind,status,value,sh) { 88 | if (!gl['get'+kind+'Parameter'](value, gl[status+'_STATUS'])){ 89 | console.log((sh||'LINK')+':\n'+gl['get'+kind+'InfoLog'](value)); 90 | } 91 | return value; 92 | } 93 | // 1st is vertex; 2nd is fragment 94 | files.map(function(given,i) { 95 | var sh = ['VERTEX_SHADER', 'FRAGMENT_SHADER'][i]; 96 | var shader = gl.createShader(gl[sh]); 97 | gl.shaderSource(shader, given); 98 | gl.compileShader(shader); 99 | gl.attachShader(program, shader); 100 | ok('Shader','COMPILE',shader,sh); 101 | }); 102 | gl.linkProgram(program); 103 | return ok('Program','LINK',program); 104 | }, 105 | // Load data to the buffers 106 | toBuffers: function(program) { 107 | 108 | // Allow for custom loading 109 | this.gl.useProgram(program); 110 | this['gl-loaded'].call(this, program); 111 | 112 | // Unchangeable square array buffer fills viewport with texture 113 | var boxes = [[-1, 1,-1,-1, 1, 1, 1,-1], [0, 1, 0, 0, 1, 1, 1, 0]]; 114 | var buffer = new Float32Array([].concat.apply([], boxes)); 115 | var bytes = buffer.BYTES_PER_ELEMENT; 116 | var gl = this.gl; 117 | var count = 4; 118 | 119 | // Get uniform term 120 | var tile_size = gl.getUniformLocation(program, this.tile_size); 121 | gl.uniform2f(tile_size, gl.canvas.height, gl.canvas.width); 122 | 123 | // Get attribute terms 124 | this.att = [this.pos, this.tile_pos].map(function(name, number) { 125 | 126 | var index = Math.min(number, boxes.length-1); 127 | var vec = Math.floor(boxes[index].length/count); 128 | var vertex = gl.getAttribLocation(program, name); 129 | 130 | return [vertex, vec, gl.FLOAT, 0, vec*bytes, count*index*vec*bytes]; 131 | }); 132 | // Get texture 133 | this.tex = { 134 | texParameteri: [ 135 | [gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, this.wrap], 136 | [gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, this.wrap], 137 | [gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.filter], 138 | [gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.filter] 139 | ], 140 | texImage2D: [gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE], 141 | bindTexture: [gl.TEXTURE_2D, gl.createTexture()], 142 | drawArrays: [gl.TRIANGLE_STRIP, 0, count], 143 | pixelStorei: [gl.UNPACK_FLIP_Y_WEBGL, 1] 144 | }; 145 | // Build the position and texture buffer 146 | gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 147 | gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW); 148 | }, 149 | // Turns image or canvas into a rendered canvas 150 | toCanvas: function(tile) { 151 | // Stop Rendering 152 | if (this.on%2 !== 0) { 153 | if(tile.nodeName == 'IMG') { 154 | this.flat.canvas.width = tile.width; 155 | this.flat.canvas.height = tile.height; 156 | this.flat.drawImage(tile,0,0,tile.width,tile.height); 157 | return this.flat.canvas; 158 | } 159 | return tile; 160 | } 161 | 162 | // Allow for custom drawing in webGL 163 | this['gl-drawing'].call(this,tile); 164 | var gl = this.gl; 165 | 166 | // Set Attributes for GLSL 167 | this.att.map(function(x){ 168 | 169 | gl.enableVertexAttribArray(x.slice(0,1)); 170 | gl.vertexAttribPointer.apply(gl, x); 171 | }); 172 | 173 | // Set Texture for GLSL 174 | gl.activeTexture(gl.TEXTURE0); 175 | gl.bindTexture.apply(gl, this.tex.bindTexture); 176 | gl.pixelStorei.apply(gl, this.tex.pixelStorei); 177 | 178 | // Apply texture parameters 179 | this.tex.texParameteri.map(function(x){ 180 | gl.texParameteri.apply(gl, x); 181 | }); 182 | // Send the tile into the texture. 183 | var output = this.tex.texImage2D.concat([tile]); 184 | gl.texImage2D.apply(gl, output); 185 | 186 | // Draw everything needed to canvas 187 | gl.drawArrays.apply(gl, this.tex.drawArrays); 188 | 189 | // Apply to container if needed 190 | if (this.container) { 191 | this.container.appendChild(this.gl.canvas); 192 | } 193 | return this.gl.canvas; 194 | }, 195 | toggle: function() { 196 | this.on ++; 197 | this.container.innerHTML = ''; 198 | this.container.appendChild(this.toCanvas(this.source)); 199 | 200 | } 201 | } -------------------------------------------------------------------------------- /minerva_analysis/client/src/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * main.js Initializes main client/interface setup, and distributes events to respective views 3 | */ 4 | 5 | //EVENTHANDLER 6 | const eventHandler = new SimpleEventHandler(d3.select("body").node()); 7 | const datasource = flaskVariables.datasource; 8 | 9 | //VIEWS 10 | const __minervaAnalysis = { 11 | dataLayer: null, 12 | channelList: null, 13 | csv_gatingList: null 14 | } 15 | 16 | //DATA MANAGEMENT 17 | const dataSrcIndex = 0; 18 | 19 | //CHANNELS 20 | const imageChannels = {}; // lookup table between channel id and channel name (for image viewer) 21 | const imageChannelsIdx = {}; 22 | 23 | //OTHER SETTINGS 24 | document.getElementById("openseadragon").addEventListener("contextmenu", (event) => event.preventDefault()); //Disable right clicking on element 25 | 26 | //LOAD DATA 27 | // Data prevent caching on the config file, as it may have been modified 28 | d3.json(`/config?t=${Date.now()}`).then(function (config) { 29 | return init(config[datasource]); 30 | }); 31 | 32 | //INITS 33 | 34 | /** 35 | * Init all views. 36 | * 37 | * @param conf - The configuration json file 38 | */ 39 | async function init(config) { 40 | //maximum selections 41 | config.maxSelections = 4; 42 | config.extraZoomLevels = 3; 43 | //channel information 44 | for (let idx = 0; idx < config["imageData"].length; idx++) { 45 | imageChannels[config["imageData"][idx].fullname] = idx; 46 | let name = config["imageData"][idx].name; 47 | if (name !== "Area") { 48 | imageChannelsIdx[idx] = name; 49 | } 50 | } 51 | 52 | //initialize metadata 53 | const dataLayer = new DataLayer(config, imageChannels); 54 | const numericData = new NumericData(config, dataLayer); 55 | const columns = await dataLayer.getChannelNames(true); 56 | const imgMetadata = await dataLayer.getMetadata(); 57 | 58 | //Create channel panels 59 | channelList = new ChannelList(config, columns, dataLayer, eventHandler); 60 | csv_gatingList = new CSVGatingList(config, columns, dataLayer, eventHandler); 61 | __minervaAnalysis.csv_gatingList = csv_gatingList; 62 | __minervaAnalysis.channelList = channelList; 63 | __minervaAnalysis.dataLayer = dataLayer; 64 | 65 | 66 | //Create image viewer 67 | const imageArgs = [imgMetadata, numericData, eventHandler]; 68 | const seaDragonViewer = new ImageViewer(config, dataLayer, ...imageArgs); 69 | const viewerManager = new ViewerManager(seaDragonViewer, channelList); 70 | 71 | //Initialize with database description 72 | const [dd, { ids, centers }] = await Promise.all([dataLayer.getDatabaseDescription(), numericData.loadCells()]); 73 | channelList.init(dd); 74 | csv_gatingList.init(dd, seaDragonViewer); 75 | const imageInit = [viewerManager, channelList, csv_gatingList, centers, ids]; 76 | await Promise.all([dataLayer.init(), seaDragonViewer.init(...imageInit)]); 77 | 78 | //EVENT HANDLING 79 | 80 | /** 81 | * Listen to Color Transfer Change Events and forwards it to respective views. 82 | * 83 | * @param d - The color map object 84 | */ 85 | const actionColorTransferChange = (d) => { 86 | //map to full name 87 | d.name = dataLayer.getFullChannelName(d.name); 88 | // d3.select('body').style('cursor', 'progress'); 89 | seaDragonViewer.updateChannelColors(d.name, d.color, d.type); 90 | // d3.select('body').style('cursor', 'default'); 91 | }; 92 | eventHandler.bind(ChannelList.events.COLOR_TRANSFER_CHANGE, actionColorTransferChange); 93 | 94 | /** 95 | * Listen to Render Mode Events and forwards it to respective views. 96 | * 97 | * @param d - The render mode object 98 | */ 99 | const actionRenderingModeChange = (d) => { 100 | seaDragonViewer.updateRenderingMode(d); 101 | }; 102 | eventHandler.bind(ImageViewer.events.renderingMode, actionRenderingModeChange); 103 | 104 | /** 105 | * Listen to Channels set for Rendering and forwards it to respective views. 106 | * 107 | * @param d - The channel package object 108 | */ 109 | const actionChannelsToRenderChange = (d) => { 110 | d3.select("body").style("cursor", "progress"); 111 | 112 | //map to full name 113 | d.name = dataLayer.getFullChannelName(d.name); 114 | 115 | //send to image viewer 116 | const action = ["remove", "add"][+d.status]; 117 | seaDragonViewer.updateActiveChannels(d.name, action); 118 | 119 | d3.select("body").style("cursor", "default"); 120 | }; 121 | eventHandler.bind(ChannelList.events.CHANNELS_CHANGE, actionChannelsToRenderChange); 122 | 123 | /** 124 | * Listen to regional or single cell selection. 125 | * 126 | * @param d - The selections 127 | */ 128 | const actionImageClickedMultiSel = (d) => { 129 | d3.select("body").style("cursor", "progress"); 130 | const { idField } = config.featureData[0]; 131 | // add newly clicked item to selection 132 | if (!Array.isArray(d.item)) { 133 | dataLayer.addToCurrentSelection(d.item, true, d.clearPriors); 134 | const picked = [d.item[idField]]; 135 | updateSeaDragonSelection({ picked }); 136 | } else { 137 | dataLayer.addAllToCurrentSelection(d.item); 138 | const picked = d.item.map(i => i[idField]); 139 | updateSeaDragonSelection({ picked }); 140 | } 141 | d3.select("body").style("cursor", "default"); 142 | }; 143 | eventHandler.bind(ImageViewer.events.imageClickedMultiSel, actionImageClickedMultiSel); 144 | 145 | /** 146 | * Listen to Channel Select Click Events. 147 | * 148 | * @param sels - The selected/deselected channels 149 | */ 150 | const channelSelect = async (sels) => { 151 | updateSeaDragonSelection(); 152 | let channelCells = await dataLayer.getChannelCellIds(sels); 153 | dataLayer.addAllToCurrentSelection(channelCells); 154 | }; 155 | eventHandler.bind(ChannelList.events.CHANNEL_SELECT, channelSelect); 156 | 157 | /** 158 | * Listens to and updates based on selection changes (specific for seadragon). 159 | * 160 | * @param props - may contain cell id 161 | */ 162 | function updateSeaDragonSelection(props = {}) { 163 | if ("picked" in props) { 164 | seaDragonViewer.pickedIds = props.picked; 165 | } 166 | seaDragonViewer.forceRepaint(); 167 | } 168 | 169 | /** 170 | * Add picked cell ids from lasso selection 171 | */ 172 | const imageLassoSel = (d) => { 173 | updateSeaDragonSelection(d); 174 | }; 175 | eventHandler.bind(ImageViewer.events.imageLassoSel, imageLassoSel); 176 | 177 | /** 178 | * Remove currently selected picked cell ids 179 | */ 180 | function clearSeaDragonSelection() { 181 | updateSeaDragonSelection({ picked: [] }); 182 | } 183 | eventHandler.bind(ImageViewer.events.clearImageLasso, clearSeaDragonSelection); 184 | 185 | const handler = () => updateSeaDragonSelection(); 186 | eventHandler.bind(CSVGatingList.events.GATING_BRUSH_END, handler); 187 | eventHandler.bind(CSVGatingList.events.GATING_BRUSH_MOVE, handler); 188 | 189 | eventHandler.bind(ChannelList.events.BRUSH_MOVE, (d) => { 190 | const fullName = dataLayer.getFullChannelName(d.name); 191 | seaDragonViewer.updateChannelRange(fullName, d.dataRange[0], d.dataRange[1]); 192 | }); 193 | 194 | /** 195 | * Reset the gating list to inital values. 196 | */ 197 | const reset_lists = () => { 198 | csv_gatingList.resetGatingList(); 199 | channelList.resetChannelList(); 200 | seaDragonViewer.forceRepaint(); 201 | }; 202 | eventHandler.bind(ChannelList.events.RESET_LISTS, reset_lists); 203 | 204 | const reset_gatinglist = () => { 205 | csv_gatingList.resetGatingList(); 206 | seaDragonViewer.forceRepaint(); 207 | }; 208 | eventHandler.bind(CSVGatingList.events.RESET_GATINGLIST, reset_gatinglist); 209 | 210 | const add_scalebar = () => { 211 | seaDragonViewer.addScaleBar(); 212 | seaDragonViewer.forceRepaint(); 213 | }; 214 | eventHandler.bind(ImageViewer.events.addScaleBar, add_scalebar); 215 | } 216 | -------------------------------------------------------------------------------- /minerva_analysis/client/templates/upload.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% block content %} 3 | Minerva Analysis - Data Upload 4 |
5 | Upload Form 6 |
7 |
8 |
9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 |
Valid.
25 |
26 | Please name the dataset. 27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | 38 |
Valid.
39 |
40 | Please provide a valid path to the channel image file. 41 |
42 |
43 |
44 |
45 |
46 |
47 | 48 |
49 |
50 | 51 |
Valid.
52 |
53 | Please provide a valid path to the segmentation mask. 54 |
55 |
56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 | {# #} 64 | 65 |
Valid.
66 |
67 | Please provide a valid single cell csv file. 68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 |
79 | 127 | 128 |
129 |
130 |
131 |
132 | 0% 133 | 0% 134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | 143 | {% endblock %} -------------------------------------------------------------------------------- /minerva_analysis/server/utils/pyramid_upgrade.py: -------------------------------------------------------------------------------- 1 | # Via https://raw.githubusercontent.com/labsyspharm/ome-tiff-pyramid-tools/master/pyramid_upgrade.py 2 | import argparse 3 | import io 4 | import re 5 | import sys 6 | from minerva_analysis.server.utils import tiffsurgeon 7 | import xml.etree.ElementTree 8 | 9 | 10 | def fix_attrib_namespace(elt): 11 | """Prefix un-namespaced XML attributes with the tag's namespace.""" 12 | # This fixes ElementTree's inability to round-trip XML with a default 13 | # namespace ("cannot use non-qualified names with default_namespace option" 14 | # error). 7-year-old BPO issue here: https://bugs.python.org/issue17088 15 | # Code inspired by https://gist.github.com/provegard/1381912 . 16 | if elt.tag[0] == "{": 17 | uri, _ = elt.tag[1:].rsplit("}", 1) 18 | new_attrib = {} 19 | for name, value in elt.attrib.items(): 20 | if name[0] != "{": 21 | # For un-namespaced attributes, copy namespace from element. 22 | name = f"{{{uri}}}{name}" 23 | new_attrib[name] = value 24 | elt.attrib = new_attrib 25 | for child in elt: 26 | fix_attrib_namespace(child) 27 | 28 | 29 | def parse_args(): 30 | parser = argparse.ArgumentParser( 31 | description="Convert an OME-TIFF legacy pyramid to the BioFormats 6" 32 | " OME-TIFF pyramid format in-place.", 33 | ) 34 | parser.add_argument("image", help="OME-TIFF file to convert") 35 | parser.add_argument( 36 | "-n", 37 | dest="channel_names", 38 | nargs="+", 39 | default=[], 40 | metavar="NAME", 41 | help="Channel names to be inserted into OME metadata. Number of names" 42 | " must match number of channels in image. Be sure to put quotes" 43 | " around names containing spaces or other special shell characters." 44 | ) 45 | args = parser.parse_args() 46 | return args 47 | 48 | 49 | def main(py_args=None): 50 | image_path = py_args['out_path'] 51 | 52 | try: 53 | tiff = tiffsurgeon.TiffSurgeon( 54 | image_path, encoding="utf-8", writeable=True 55 | ) 56 | except tiffsurgeon.FormatError as e: 57 | print(f"TIFF format error: {e}") 58 | sys.exit(1) 59 | 60 | tiff.read_ifds() 61 | 62 | # ElementTree doesn't parse xml declarations so we'll just run some sanity 63 | # checks that we do have UTF-8 and give it a decoded string instead of raw 64 | # bytes. We need to both ensure that the raw tag bytes decode properly and 65 | # that the declaration encoding is UTF-8 if present. 66 | try: 67 | omexml = tiff.ifds[0].tags.get_value(270, "") 68 | except FormatError: 69 | print("ImageDescription tag is not a valid UTF-8 string (not an OME-TIFF?)") 70 | sys.exit(1) 71 | if re.match(r'<\?xml [^>]*encoding="(?!UTF-8)[^"]*"', omexml): 72 | print("OME-XML is encoded with something other than UTF-8.") 73 | sys.exit(1) 74 | 75 | xml_ns = {"ome": "http://www.openmicroscopy.org/Schemas/OME/2016-06"} 76 | 77 | if xml_ns["ome"] not in omexml: 78 | print("Not an OME-TIFF.") 79 | sys.exit(1) 80 | if ( 81 | "Faas" not in tiff.ifds[0].tags.get_value(305, "") 82 | or 330 in tiff.ifds[0].tags 83 | ): 84 | print("Not a legacy OME-TIFF pyramid.") 85 | sys.exit(1) 86 | 87 | # All XML manipulation assumes the document is valid OME-XML! 88 | root = xml.etree.ElementTree.fromstring(omexml) 89 | image = root.find("ome:Image", xml_ns) 90 | pixels = image.find("ome:Pixels", xml_ns) 91 | size_x = int(pixels.get("SizeX")) 92 | size_y = int(pixels.get("SizeY")) 93 | size_c = int(pixels.get("SizeC")) 94 | size_z = int(pixels.get("SizeZ")) 95 | size_t = int(pixels.get("SizeT")) 96 | num_levels = len(root.findall("ome:Image", xml_ns)) 97 | page_dims = [(ifd.tags[256].value, ifd.tags[257].value) for ifd in tiff.ifds] 98 | 99 | if len(root) != num_levels: 100 | print("Top-level OME-XML elements other than Image are not supported.") 101 | if size_z != 1 or size_t != 1: 102 | print("Z-stacks and multiple timepoints are not supported.") 103 | sys.exit(1) 104 | if size_c * num_levels != len(tiff.ifds): 105 | print("TIFF page count does not match OME-XML Image elements.") 106 | sys.exit(1) 107 | if any(dims != (size_x, size_y) for dims in page_dims[:size_c]): 108 | print(f"TIFF does not begin with SizeC={size_c} full-size pages.") 109 | sys.exit(1) 110 | for level in range(1, num_levels): 111 | level_dims = page_dims[level * size_c : (level + 1) * size_c] 112 | if len(set(level_dims)) != 1: 113 | print( 114 | f"Pyramid level {level + 1} out of {num_levels} has inconsistent" 115 | f" sizes:\n{level_dims}" 116 | ) 117 | sys.exit(1) 118 | 119 | print("Input image summary") 120 | print("===================") 121 | print(f"Dimensions: {size_x} x {size_y}") 122 | print(f"Number of channels: {size_c}") 123 | print(f"Pyramid sub-resolutions ({num_levels - 1} total):") 124 | for dim_x, dim_y in page_dims[size_c::size_c]: 125 | print(f" {dim_x} x {dim_y}") 126 | software = tiff.ifds[0].tags.get_value(305, "") 127 | print(f"Software: {software}") 128 | print() 129 | 130 | print("Updating OME-XML metadata...") 131 | # We already verified there is nothing but Image elements under the root. 132 | for other_image in root[1:]: 133 | root.remove(other_image) 134 | for tiffdata in pixels.findall("ome:TiffData", xml_ns): 135 | pixels.remove(tiffdata) 136 | new_tiffdata = xml.etree.ElementTree.Element( 137 | f"{{{xml_ns['ome']}}}TiffData", 138 | attrib={"IFD": "0", "PlaneCount": str(size_c)}, 139 | ) 140 | # A valid OME-XML Pixels begins with size_c Channels; then comes TiffData. 141 | pixels.insert(size_c, new_tiffdata) 142 | 143 | fix_attrib_namespace(root) 144 | # ElementTree.tostring would have been simpler but it only supports 145 | # xml_declaration and default_namespace starting with Python 3.8. 146 | xml_file = io.BytesIO() 147 | tree = xml.etree.ElementTree.ElementTree(root) 148 | tree.write( 149 | xml_file, 150 | encoding="utf-8", 151 | xml_declaration=True, 152 | default_namespace=xml_ns["ome"], 153 | ) 154 | new_omexml = xml_file.getvalue() 155 | 156 | print("Writing new TIFF headers...") 157 | stale_ranges = [ifd.offset_range for ifd in tiff.ifds] 158 | main_ifds = tiff.ifds[:size_c] 159 | channel_sub_ifds = [tiff.ifds[c + size_c : : size_c] for c in range(size_c)] 160 | for i, (main_ifd, sub_ifds) in enumerate(zip(main_ifds, channel_sub_ifds)): 161 | for ifd in sub_ifds: 162 | if 305 in ifd.tags: 163 | stale_ranges.append(ifd.tags[305].offset_range) 164 | del ifd.tags[305] 165 | ifd.tags.insert(tiff.append_tag_data(254, 3, 1)) 166 | if i == 0: 167 | stale_ranges.append(main_ifd.tags[305].offset_range) 168 | stale_ranges.append(main_ifd.tags[270].offset_range) 169 | old_software = main_ifd.tags[305].value.replace("Faas", "F*a*a*s") 170 | new_software = f"pyramid_upgrade.py (was {old_software})" 171 | main_ifd.tags.insert(tiff.append_tag_data(305, 2, new_software)) 172 | main_ifd.tags.insert(tiff.append_tag_data(270, 2, new_omexml)) 173 | else: 174 | if 305 in main_ifd.tags: 175 | stale_ranges.append(main_ifd.tags[305].offset_range) 176 | del main_ifd.tags[305] 177 | sub_ifds[:] = tiff.append_ifd_sequence(sub_ifds) 178 | offsets = [ifd.offset for ifd in sub_ifds] 179 | main_ifd.tags.insert(tiff.append_tag_data(330, 16, offsets)) 180 | main_ifds = tiff.append_ifd_sequence(main_ifds) 181 | tiff.write_first_ifd_offset(main_ifds[0].offset) 182 | 183 | print("Clearing old headers and tag values...") 184 | # We overwrite all the old IFDs and referenced data values with obvious 185 | # "filler" as a courtesy to anyone who might need to poke around in the TIFF 186 | # structure down the road. A real TIFF parser wouldn't see the stale data, 187 | # but a human might just scan for the first thing that looks like a run of 188 | # OME-XML and not realize it's been replaced with something else. The filler 189 | # content is the repeated string "unused " with square brackets at the 190 | # beginning and end of each filled IFD or data value. 191 | filler = b"unused " 192 | f_len = len(filler) 193 | for r in stale_ranges: 194 | tiff.file.seek(r.start) 195 | tiff.file.write(b"[") 196 | f_total = len(r) - 2 197 | for i in range(f_total // f_len): 198 | tiff.file.write(filler) 199 | tiff.file.write(b" " * (f_total % f_len)) 200 | tiff.file.write(b"]") 201 | 202 | tiff.close() 203 | 204 | print() 205 | print("Success!") 206 | 207 | 208 | if __name__ == "__main__": 209 | main() 210 | --------------------------------------------------------------------------------