1237 lines
41 KiB
HTML
1237 lines
41 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<link href="bootstrap-5.0.2-dist/css/bootstrap.min.css" rel="stylesheet" ></link>
|
|
<script src="bootstrap-5.0.2-dist/js/bootstrap.bundle.min.js" ></script>
|
|
<meta charset='utf-8'>
|
|
<style>
|
|
|
|
#output{
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
#video_container{
|
|
position: relative;
|
|
}
|
|
|
|
#video {
|
|
position:absolute;
|
|
z-index: 15;
|
|
width: 100%;
|
|
}
|
|
|
|
#canvas-draw{
|
|
position:absolute;
|
|
z-index:10;
|
|
width: 100%;
|
|
}
|
|
|
|
#photo {
|
|
position:absolute;
|
|
z-index:5;
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
#canvas-temp{
|
|
display: none;
|
|
}
|
|
|
|
#tables {
|
|
width: 100%;
|
|
}
|
|
|
|
#control-elements {
|
|
height:30em;
|
|
}
|
|
|
|
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- this canvas is not displayed and only used to generate the photo -->
|
|
<canvas id="canvas-temp"></canvas>
|
|
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-3">
|
|
<label class = "form-label" for="camera_chooser">choose the camera setup</label>
|
|
<select class="form-select" name="camera_chooser" id = "camera_chooser"></select>
|
|
</div>
|
|
<div class="col-3">
|
|
<label class = "form-label" for="protocol_chooser">choose the preparation protocol</label>
|
|
<select class="form-select" name="protocol_chooser" id = "protocol_chooser"></select>
|
|
</div>
|
|
<div class="col-3">
|
|
<label class = "form-label" for="dilution">change dilution</label>
|
|
<input class="form-control" name="dilution" id = "dilution"></input>
|
|
</div>
|
|
<div class="col-3">
|
|
<label class = "form-label" for="sample_chooser">choose the sample</label>
|
|
<select class="form-select" name="sample_chooser" id = "sample_chooser"></select>
|
|
</div>
|
|
</div>
|
|
<!-- the output div is used to display the video, the photo and the canvas where you can draw in (and measure) your organisms. -->
|
|
<div id="output" class="row">
|
|
<div id = "video_container" class="col-9">
|
|
<canvas id="canvas-draw"></canvas>
|
|
<img id="photo" alt="The screen capture will appear in this box." />
|
|
<video autoplay="true" id="video"> </video>
|
|
</div>
|
|
|
|
<div id = "control-elements" class="col-3 ">
|
|
<button class="btn btn-primary float-end" onclick="takePicture();" id="start-button">Take photo</button>
|
|
|
|
<form id="organism_type" class="form-check" onsubmit="return false;">
|
|
<input type="radio" class="btn-check" name="choice" value="Fungi" id="org-type-Fungi" checked></input>
|
|
<label class="btn btn-outline-success" for="org-type-Fungi">Fungi</label>
|
|
<input type="radio" class="btn-check" name="choice" value="Oomycete" id="org-type-Oomycete"></input>
|
|
<label class="btn btn-outline-success" for="org-type-Oomycete">Oomycete</label>
|
|
<input type="radio" class="btn-check" name="choice" value="Actinobacteria" id="org-type-Actinobacteria"></input>
|
|
<label class="btn btn-outline-success" for="org-type-Actinobacteria">Actinobacteria</label>
|
|
<input type="radio" class="btn-check" name="choice" value="Flagelate" id="org-type-Flagelate"></input>
|
|
<label class="btn btn-outline-success" for="org-type-Flagelate">Flagelate</label>
|
|
<input type="radio" class="btn-check" name="choice" value="Ciliate" id="org-type-Ciliate"></input>
|
|
<label class="btn btn-outline-success" for="org-type-Ciliate">Ciliate</label>
|
|
<input type="radio" class="btn-check" name="choice" value="Amoeba" id="org-type-Amoeba"></input>
|
|
<label class="btn btn-outline-success" for="org-type-Amoeba">Amoeba</label>
|
|
</form>
|
|
|
|
<div id="organism" class="row align-items-end">
|
|
<div class="col-5" name="Length" >
|
|
<label class = "form-label" for="org-length">Length [-]</label>
|
|
<input class="form-control" type="text" name="Length" id="org-length" value=0> </input>
|
|
</div>
|
|
<div class="col-4" name="Width">
|
|
<label class = "form-label" for="org-width">Width [-]</label>
|
|
<input class="form-control" type="text" name="Width" value=0 id="org-width"> </input>
|
|
</div>
|
|
<div class="col-3" name="Colour">
|
|
<label class = "form-label" for="org-colour">Colour</label>
|
|
<input class="form-control" type="text" name="Colour" value="" id="org-colour"> </input>
|
|
</div>
|
|
<div class="col" name="Notes" >
|
|
<label class = "form-label" for="org-notes">Notes</label>
|
|
<input class="form-control" type="textfield" name="Notes" value="" id="org-notes"> </input>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="btn btn-primary float-end" id="organism_submit">submit organism</button>
|
|
<button class="btn btn-primary float-end" onclick="push_to_FoV();">submit picture</button>
|
|
|
|
</div>
|
|
<div class="col-9">
|
|
<div class = "row">
|
|
<button class="btn btn-primary float-end" onclick="toggleFullscreen();" id="fullscreen-button">
|
|
toggle Fullscreen video
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div id="tables">
|
|
<div>
|
|
<h2> organisms </h2>
|
|
<table id= "org-table" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
type
|
|
</th>
|
|
<th>
|
|
length
|
|
</th>
|
|
<th>
|
|
width
|
|
</th>
|
|
<th>
|
|
colour
|
|
</th>
|
|
<th style="width:40%">
|
|
notes
|
|
</th>
|
|
<th style="width:15%">
|
|
<button class="btn btn-primary float-end" id="picture_submit">next picture</button>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<h2> Field of View </h2>
|
|
<table id= "FoV-table" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
picture
|
|
</th>
|
|
<th>
|
|
Fungi [μm³]
|
|
</th>
|
|
<th>
|
|
Oomycete [μm³]
|
|
</th>
|
|
<th>
|
|
Actinobacteria [μm³]
|
|
</th>
|
|
<th>
|
|
Flagelate [-]
|
|
</th>
|
|
<th>
|
|
Ciliate [-]
|
|
</th>
|
|
<th>
|
|
Amoeba [-]
|
|
</th>
|
|
<th style="width:15%">
|
|
<button class="btn btn-primary float-end" id="FoV_submit">next FoV</button>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<h2> Reading </h2>
|
|
<table id= "Reading-table" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
FoV #
|
|
</th>
|
|
<th>
|
|
Fungi [μm³]
|
|
</th>
|
|
<th>
|
|
Oomycete [μm³]
|
|
</th>
|
|
<th>
|
|
Actinobacteria [μm³]
|
|
</th>
|
|
<th>
|
|
Flagelate [-]
|
|
</th>
|
|
<th>
|
|
Ciliate [-]
|
|
</th>
|
|
<th>
|
|
Amoeba [-]
|
|
</th>
|
|
<th style="width:15%">
|
|
<button class="btn btn-primary float-end" id="Reading_submit">next Reading</button>
|
|
</th>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
|
|
<div>
|
|
<h2> Results </h2>
|
|
<table id= "Result-table" style="width:100%">
|
|
<thead>
|
|
<tr>
|
|
<th>
|
|
Reading #
|
|
</th>
|
|
<th>
|
|
Fungi [μm³]
|
|
</th>
|
|
<th>
|
|
Oomycete [μm³]
|
|
</th>
|
|
<th>
|
|
Actinobacteria [μm³]
|
|
</th>
|
|
<th>
|
|
Flagelate [-]
|
|
</th>
|
|
<th>
|
|
Ciliate [-]
|
|
</th>
|
|
<th>
|
|
Amoeba [-]
|
|
</th>
|
|
<th style="width:15%">
|
|
<button class="btn btn-primary float-end" id="Result_submit">submit Result</button>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
var video = document.querySelector("#video");
|
|
var out = document.querySelector("#output");
|
|
var microscope_setups = {};
|
|
var microscope_setup;
|
|
var default_setup_id;
|
|
var prep_protocols = {};
|
|
var prep_protocol;
|
|
var default_protocol_id;
|
|
var sample;
|
|
var samples = {};
|
|
var dilution_chooser = document.querySelector("#dilution")
|
|
|
|
// toggle fullscreen of output element (video + control elements)
|
|
function toggleFullscreen() {
|
|
if (!document.fullscreenElement) {
|
|
out.requestFullscreen().catch((err) => {
|
|
alert(
|
|
`Error attempting to enable fullscreen mode: ${err.message} (${err.name})`,
|
|
);
|
|
});
|
|
} else {
|
|
document.exitFullscreen();
|
|
}
|
|
|
|
}
|
|
// video shit.
|
|
// init video get video prefs from db
|
|
|
|
var db;
|
|
let openRequest = indexedDB.open("my_db");
|
|
|
|
openRequest.onerror = function() {
|
|
console.error("Error", openRequest.error);
|
|
};
|
|
|
|
openRequest.onsuccess = function() {
|
|
db = openRequest.result;
|
|
get_default_camera_setup();
|
|
get_default_prep_setup();
|
|
get_all_samples();
|
|
|
|
// continue working with database using db object
|
|
};
|
|
|
|
function get_default_camera_setup(){
|
|
const request = db.transaction('defaults')
|
|
.objectStore('defaults')
|
|
.get(0);
|
|
request.onsuccess = ()=> {
|
|
default_setup_id = request.result.microscope_setup;
|
|
get_all_video_prefs();
|
|
// width of FoV -- will be supplied by FLASK microscope setup.
|
|
|
|
}
|
|
|
|
request.onerror = (err)=> {
|
|
console.error(`Error to get all setups: ${err}`)
|
|
}
|
|
}
|
|
|
|
function get_all_video_prefs(){
|
|
const request = db.transaction('microscope_setup')
|
|
.objectStore('microscope_setup')
|
|
.openCursor();
|
|
request.onsuccess = ()=> {
|
|
let cursor = event.target.result;
|
|
if (cursor) {
|
|
// Access the current record
|
|
make_camera_chooser(cursor.value, cursor.key);
|
|
// Move to the next record
|
|
cursor.continue();
|
|
}
|
|
else{
|
|
startup();
|
|
}
|
|
}
|
|
|
|
request.onerror = (err)=> {
|
|
console.error(`Error to get all setups: ${err}`)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
var camera_chooser = document.querySelector("#camera_chooser");
|
|
|
|
function make_camera_chooser(setup, key){
|
|
let cameraID = document.createElement("option");
|
|
let text = document.createTextNode(setup.name);
|
|
cameraID.value = microscope_setups.length;
|
|
microscope_setups[key] = setup
|
|
|
|
|
|
if (key == default_setup_id){
|
|
cameraID.defaultSelected = true;
|
|
text.textContent += " (default)"
|
|
microscope_setup = setup;
|
|
}
|
|
cameraID.appendChild(text);
|
|
camera_chooser.appendChild(cameraID);
|
|
}
|
|
|
|
camera_chooser.addEventListener("change", change_cam);
|
|
function change_cam(){
|
|
microscope_setup = microscope_setups[this.value];
|
|
startup();
|
|
};
|
|
|
|
|
|
function get_default_prep_setup(){
|
|
const request = db.transaction('defaults')
|
|
.objectStore('defaults')
|
|
.get(1);
|
|
request.onsuccess = ()=> {
|
|
default_protocol_id = request.result.prep_setup;
|
|
get_all_prep_prefs();
|
|
|
|
// width of FoV -- will be supplied by FLASK microscope setup.
|
|
|
|
}
|
|
|
|
request.onerror = (err)=> {
|
|
console.error(`Error to get all setups: ${err}`)
|
|
}
|
|
}
|
|
|
|
function get_all_prep_prefs(){
|
|
const request = db.transaction('prep_protocol')
|
|
.objectStore('prep_protocol')
|
|
.openCursor();
|
|
request.onsuccess = ()=> {
|
|
let cursor = event.target.result;
|
|
if (cursor) {
|
|
// Access the current record
|
|
make_protocol_chooser(cursor.value, cursor.key);
|
|
// Move to the next record
|
|
cursor.continue();
|
|
}
|
|
|
|
}
|
|
|
|
request.onerror = (err)=> {
|
|
console.error(`Error to get all setups: ${err}`)
|
|
}
|
|
}
|
|
|
|
var protocol_chooser = document.querySelector("#protocol_chooser");
|
|
|
|
function make_protocol_chooser(protocol, key){
|
|
let protocolID = document.createElement("option");
|
|
let text = document.createTextNode(protocol.name);
|
|
protocolID.value = key;
|
|
prep_protocols[key] = protocol;
|
|
if (key == default_protocol_id){
|
|
protocolID.defaultSelected = true;
|
|
prep_protocol = protocol;
|
|
text.textContent += " (default)";
|
|
dilution_chooser.value = protocol.Main_Dilution;
|
|
|
|
|
|
}
|
|
protocolID.appendChild(text);
|
|
protocol_chooser.appendChild(protocolID);
|
|
}
|
|
|
|
protocol_chooser.addEventListener("change", change_protocol);
|
|
function change_protocol(){
|
|
prep_protocol = prep_protocols[this.value];
|
|
dilution_chooser.value = prep_protocol.Main_Dilution;
|
|
};
|
|
|
|
|
|
function get_all_samples(){
|
|
const request = db.transaction('sample')
|
|
.objectStore('sample')
|
|
.openCursor();
|
|
request.onsuccess = ()=> {
|
|
let cursor = event.target.result;
|
|
if (cursor) {
|
|
// Access the current record
|
|
make_sample_chooser(cursor.value, cursor.key);
|
|
sample = cursor.key;
|
|
// Move to the next record
|
|
cursor.continue();
|
|
}
|
|
|
|
}
|
|
|
|
request.onerror = (err)=> {
|
|
console.error(`Error to get all setups: ${err}`)
|
|
}
|
|
}
|
|
|
|
var sample_chooser = document.querySelector("#sample_chooser");
|
|
|
|
function make_sample_chooser(sample, key){
|
|
let sampleID = document.createElement("option");
|
|
let text = document.createTextNode(sample.Name);
|
|
sampleID.value = key;
|
|
samples[key] = sample;
|
|
sampleID.appendChild(text);
|
|
sample_chooser.appendChild(sampleID);
|
|
}
|
|
|
|
sample_chooser.addEventListener("change", change_sample);
|
|
function change_sample(){
|
|
sample = samples[this.value];
|
|
};
|
|
|
|
var canvas_draw = document.querySelector("#canvas-draw");
|
|
var ctx = canvas_draw.getContext('2d');
|
|
var pos = { x: 0, y: 0 };
|
|
|
|
// store lines for measurement
|
|
|
|
var lines = [];
|
|
var line = [];
|
|
|
|
// calculate length of line - helper function
|
|
|
|
function get_length(line){
|
|
var dist = 0.0;
|
|
if (line){
|
|
for (let j = 0; j < line.length-1; ++j) {
|
|
dist += Math.sqrt((line[j].x - line[j+1].x) ** 2 + (line[j].y - line[j+1].y) ** 2);
|
|
}
|
|
}
|
|
|
|
return dist/canvas_draw.width
|
|
}
|
|
|
|
// events to set the position of the drawing cursor
|
|
canvas_draw.addEventListener('mousedown', setPosition);
|
|
canvas_draw.addEventListener('mouseenter', setPosition);
|
|
|
|
// set new position from mouse event
|
|
// scale canvas coords
|
|
|
|
function setPosition(e) {
|
|
var rect = canvas_draw.getBoundingClientRect();
|
|
|
|
scaleX = canvas_draw.width / rect.width, // relationship bitmap vs. element for x
|
|
scaleY = canvas_draw.height / rect.height;
|
|
pos.x = (e.clientX - rect.x) * scaleX;
|
|
pos.y = (e.clientY - rect.y) * scaleY;
|
|
}
|
|
|
|
// the draw function to draw onto canvas
|
|
|
|
canvas_draw.addEventListener('mousemove', draw);
|
|
|
|
function draw(e) {
|
|
// mouse left button must be pressed and video must be hidden / a photo must have been taken.
|
|
if (e.buttons !== 1) {
|
|
// if the length of the line is 0 there is nothing to do and nothing to calculate.
|
|
if (line.length > 1) {
|
|
switch (organism_type.choice.value){
|
|
// calculate length of line(s) for filamentous organisms.
|
|
case "Actinobacteria":
|
|
if (lines.length == 1){
|
|
lines = [];
|
|
update_canvas_draw(organisms);
|
|
draw_path(line);
|
|
}
|
|
case "Oomycete":
|
|
case "Fungi":
|
|
if (lines.length == 2){
|
|
lines = [];
|
|
update_canvas_draw(organisms);
|
|
draw_path(line);
|
|
|
|
}
|
|
if (lines.length == 0){
|
|
organism.children.Length.children.Length.value = parseFloat(get_length(line).toFixed(3));
|
|
break
|
|
}
|
|
else if (lines.length == 1){
|
|
organism.children.Width.children.Width.value = parseFloat(get_length(line).toFixed(3));
|
|
break
|
|
}
|
|
else {
|
|
// reset lines if lines are longer then 2
|
|
organism.children.Length.children.Length.value = parseFloat(get_length(line).toFixed(3));
|
|
lines = [];
|
|
}
|
|
break
|
|
organism.children.Length.children.Length.value = parseFloat(get_length(line).toFixed(3));
|
|
break
|
|
case "Amoeba":
|
|
case "Ciliate":
|
|
case "Flagelate":
|
|
let xs=[];
|
|
let ys=[];
|
|
// create a bounding box line for Protozoa.
|
|
x_max = Math.max(... line.map(xs=>xs.x));
|
|
y_max = Math.max(... line.map(ys=>ys.y));
|
|
x_min = Math.min(... line.map(xs=>xs.x));
|
|
y_min = Math.min(... line.map(ys=>ys.y));
|
|
line = []
|
|
line.push({"x":x_max,"y":y_max});
|
|
line.push({"x":x_max,"y":y_min});
|
|
line.push({"x":x_min,"y":y_min});
|
|
line.push({"x":x_min,"y":y_max});
|
|
line.push({"x":x_max,"y":y_max});
|
|
|
|
}
|
|
// push the line to lines array
|
|
lines.push(line);
|
|
// clear line array
|
|
line = [];
|
|
}
|
|
return;
|
|
}
|
|
// draw on canvas.
|
|
if (e.buttons == 1){
|
|
ctx.beginPath(); // begin
|
|
ctx.lineWidth = 5;
|
|
ctx.lineCap = 'round';
|
|
ctx.strokeStyle = 'black';
|
|
// update line
|
|
// first time push the first position
|
|
if (line.length==0){
|
|
line.push({x:pos.x, y:pos.y});
|
|
}
|
|
ctx.moveTo(pos.x, pos.y); // from
|
|
setPosition(e);
|
|
// everytime push the last position
|
|
line.push({x:pos.x, y:pos.y});
|
|
ctx.lineTo(pos.x, pos.y); // to
|
|
ctx.stroke(); // draw it!
|
|
}
|
|
}
|
|
|
|
function draw_path(path){
|
|
// draw a path on the canvas.
|
|
ctx.beginPath(); // begin
|
|
ctx.lineWidth = 2;
|
|
ctx.lineCap = 'round';
|
|
ctx.strokeStyle = 'black';
|
|
for (var i = 0; i < path.length-1; i++) {
|
|
ctx.moveTo(path[i].x, path[i].y);
|
|
ctx.lineTo(path[i+1].x, path[i+1].y);
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
function update_canvas_draw(organisms){
|
|
lines=[];
|
|
canvas_draw.getContext('2d').clearRect(0, 0, canvas_draw.width, canvas_draw.height);
|
|
for (var i = 0; i < organisms.length; i++){
|
|
if (organisms[i].path_length){
|
|
draw_path(organisms[i].path_length);
|
|
}
|
|
if (organisms[i].path_width){
|
|
draw_path(organisms[i].path_width);
|
|
}
|
|
}
|
|
}
|
|
|
|
// this button sets the organism type and hides input elements
|
|
// according to the type of observed organism
|
|
|
|
organism_type.addEventListener('click', setOrganismType);
|
|
|
|
function setOrganismType(){
|
|
// first reset form when another organism type is selected.
|
|
var organismType = organism_type.choice.value;
|
|
organism.children.Length.children.Length.value = 0;
|
|
organism.children.Width.children.Width.value = 0;
|
|
organism.children.Colour.children.Colour.value = "";
|
|
organism.children.Notes.children.Notes.value = "";
|
|
// as well clear lines array to make space for new lines.
|
|
lines = [];
|
|
update_canvas_draw(organisms);
|
|
|
|
switch (organismType){
|
|
case "Fungi":
|
|
organism.children.Length.hidden = false;
|
|
organism.children.Width.hidden = false;
|
|
organism.children.Colour.hidden = false;
|
|
break
|
|
case "Oomycete":
|
|
organism.children.Length.hidden = false;
|
|
organism.children.Width.hidden = false;
|
|
organism.children.Colour.hidden = true;
|
|
break
|
|
case "Actinobacteria":
|
|
organism.children.Length.hidden = false;
|
|
organism.children.Width.hidden = true;
|
|
organism.children.Colour.hidden = true;
|
|
break
|
|
case "Flagelate":
|
|
case "Ciliate":
|
|
case "Amoeba":
|
|
organism.children.Length.hidden = true;
|
|
organism.children.Width.hidden = true;
|
|
organism.children.Colour.hidden = true;
|
|
break
|
|
}
|
|
return
|
|
}
|
|
|
|
// --------------------------------------------
|
|
// pushing organism to organisms array
|
|
// --------------------------------------------
|
|
|
|
// this button pushes the observed organism to organisms array.
|
|
organism_submit = document.querySelector("#organism_submit");
|
|
organism_submit.addEventListener('click', push_to_organisms);
|
|
|
|
// this is the place where data about the organism is entered.
|
|
var organism = document.querySelector("#organism");
|
|
|
|
// organsisms array contains a list of organisms visible in the picture together with their paths.
|
|
var organisms = [];
|
|
|
|
// this function pushes the observed organism into the organisms array.
|
|
function push_to_organisms(){
|
|
organisms.push({
|
|
"type":organism_type.choice.value,
|
|
"length":organism.children.Length.children.Length.value,
|
|
"width":organism.children.Width.children.Width.value,
|
|
"colour":organism.children.Colour.children.Colour.value,
|
|
"notes":organism.children.Notes.children.Notes.value,
|
|
"path_length":lines[0],
|
|
"path_width":lines[1],
|
|
}
|
|
);
|
|
|
|
organism.children.Length.children.Length.value = 0;
|
|
organism.children.Width.children.Width.value = 0;
|
|
organism.children.Colour.children.Colour.value = "";
|
|
organism.children.Notes.children.Notes.value = "";
|
|
update_organism_table();
|
|
update_canvas_draw(organisms);
|
|
lines=[];
|
|
}
|
|
|
|
// table where the organisms array is displayed
|
|
|
|
var organism_table = document.querySelector("#org-table");
|
|
|
|
// update organism table
|
|
|
|
function update_organism_table(){
|
|
while (organism_table.children[1].hasChildNodes()) {
|
|
organism_table.children[1].removeChild(organism_table.children[1].lastChild);
|
|
}
|
|
organisms.forEach((o, k) => render_organism_table(o,k));
|
|
}
|
|
|
|
// remove organism from organsisms and update table and canvas
|
|
|
|
function remove_organism(index){
|
|
organisms.splice(index, 1);
|
|
lines=[];
|
|
update_organism_table();
|
|
update_canvas_draw(organisms);
|
|
}
|
|
|
|
// make a new organism table
|
|
function render_organism_table(organism, index){
|
|
let newRow = organism_table.children[1].insertRow();
|
|
let type = newRow.insertCell(0);
|
|
let length = newRow.insertCell(1);
|
|
let width = newRow.insertCell(2);
|
|
let colour = newRow.insertCell(3);
|
|
let notes = newRow.insertCell(4);
|
|
let button = newRow.insertCell(5);
|
|
type.innerHTML = organism.type;
|
|
length.innerHTML = organism.length;
|
|
width.innerHTML = organism.width;
|
|
colour.innerHTML = organism.colour;
|
|
notes.innerHTML = organism.notes;
|
|
button.innerHTML = "<button class='btn btn-primary float-end' onclick=remove_organism(" + index + ")>remove</button>";
|
|
}
|
|
|
|
//----------------------------------------------
|
|
// push picture into FoV container
|
|
//----------------------------------------------
|
|
|
|
// this button pushes a picture with the associated organisms to the FoV array.
|
|
|
|
var picture_submit = document.querySelector("#picture_submit");
|
|
picture_submit.addEventListener('click', push_to_FoV);
|
|
|
|
var FoV_table = document.querySelector("#FoV-table");
|
|
|
|
// FoV array will contain a list of pictures with their asociated organisms array.
|
|
|
|
var FoV = [];
|
|
|
|
// this function gets called if the submit button is pressed.
|
|
|
|
function push_to_FoV(e){
|
|
FoV.push({
|
|
"picture":photo.src,
|
|
"organisms":organisms,
|
|
});
|
|
// reset organisms
|
|
organisms = [];
|
|
// and redraw the tables
|
|
update_organism_table();
|
|
update_FoV_table();
|
|
|
|
// reset video zIndex so you can see the video again to take the next picture.
|
|
video.style.zIndex = 15;
|
|
}
|
|
|
|
// get the total number of organisms from a picture
|
|
|
|
function total_organisms(organisms){
|
|
var total = {
|
|
"Fungi" : 0.0,
|
|
"Oomycete" : 0.0,
|
|
"Actinobacteria" : 0.0,
|
|
"Flagelate" : 0.0,
|
|
"Ciliate" : 0.0,
|
|
"Amoeba" : 0.0,
|
|
}
|
|
organisms.forEach((item, i) => {
|
|
switch (item.type){
|
|
case "Fungi":
|
|
// calculate the Volume of filamentous organisms
|
|
total.Fungi += item.length*real_width_FoV*
|
|
Math.pow((item.width*real_width_FoV),2.0)*Math.PI/4.0;
|
|
break
|
|
case "Oomycete":
|
|
total.Oomycete += item.length*real_width_FoV*
|
|
Math.pow((item.width*real_width_FoV),2.0)*Math.PI/4.0;
|
|
break
|
|
case "Actinobacteria":
|
|
total.Actinobacteria += item.length*real_width_FoV;
|
|
break
|
|
case "Flagelate":
|
|
total.Flagelate +=1;
|
|
break
|
|
case "Ciliate":
|
|
total.Ciliate +=1;
|
|
break
|
|
case "Amoeba":
|
|
total.Amoeba +=1;
|
|
break
|
|
}
|
|
});
|
|
return total;
|
|
}
|
|
|
|
// get total number of organisms in FoV
|
|
function total_organisms_in_FoV(FoV){
|
|
var total = {
|
|
"Fungi" : 0.0,
|
|
"Oomycete" : 0.0,
|
|
"Actinobacteria" : 0.0,
|
|
"Flagelate" : 0.0,
|
|
"Ciliate" : 0.0,
|
|
"Amoeba" : 0.0,
|
|
}
|
|
FoV.forEach((item, i) => {
|
|
var all_organisms_in_picture = total_organisms(item.organisms);
|
|
var keys = Object.keys(all_organisms_in_picture);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
total[keys[i]] += all_organisms_in_picture[keys[i]];
|
|
}
|
|
});
|
|
return total;
|
|
}
|
|
|
|
// update the FoV Table = delete all rows and recreate from FoV array
|
|
function update_FoV_table(){
|
|
//remove all rows
|
|
while (FoV_table.children[1].hasChildNodes()) {
|
|
FoV_table.children[1].removeChild(FoV_table.children[1].lastChild);
|
|
}
|
|
// recreate table
|
|
FoV.forEach((o, k) => render_FoV_table(o,k));
|
|
// check if FoV list is empty
|
|
if(FoV.length>0){
|
|
// add a row in the end with the summed up values for all organism types
|
|
let tot = total_organisms_in_FoV(FoV);
|
|
let newRow = FoV_table.children[1].insertRow();
|
|
newRow.style.borderTop = "solid #000000";
|
|
let image = newRow.insertCell(0);
|
|
let fungi = newRow.insertCell(1);
|
|
let oomycete = newRow.insertCell(2);
|
|
let actinobacteria = newRow.insertCell(3);
|
|
let flagelate = newRow.insertCell(4);
|
|
let ciliate = newRow.insertCell(5);
|
|
let amoeba = newRow.insertCell(6);
|
|
let button = newRow.insertCell(7);
|
|
image.innerHTML = "total"
|
|
fungi.innerHTML = tot.Fungi.toFixed(5).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = tot.Oomycete.toFixed(5).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = tot.Actinobacteria.toFixed(5).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = tot.Flagelate;
|
|
ciliate.innerHTML = tot.Ciliate;
|
|
amoeba.innerHTML = tot.Amoeba;
|
|
}
|
|
}
|
|
|
|
function render_FoV_table(element, i){
|
|
var img = new Image();
|
|
img.src = element.picture;
|
|
let tot = total_organisms(element.organisms);
|
|
let newRow = FoV_table.children[1].insertRow();
|
|
let image = newRow.insertCell(0);
|
|
let fungi = newRow.insertCell(1);
|
|
let oomycete = newRow.insertCell(2);
|
|
let actinobacteria = newRow.insertCell(3);
|
|
let flagelate = newRow.insertCell(4);
|
|
let ciliate = newRow.insertCell(5);
|
|
let amoeba = newRow.insertCell(6);
|
|
let button = newRow.insertCell(7);
|
|
image.appendChild(img).style.width = "50px";
|
|
fungi.innerHTML = tot.Fungi.toFixed(5).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = tot.Oomycete.toFixed(5).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = tot.Actinobacteria.toFixed(5).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = tot.Flagelate;
|
|
ciliate.innerHTML = tot.Ciliate;
|
|
amoeba.innerHTML = tot.Amoeba;
|
|
button.innerHTML = "<button class='btn btn-primary float-end' onclick=remove_picture("+i+")>remove</button>";
|
|
}
|
|
|
|
// remove the image at position index from table
|
|
|
|
function remove_picture(index){
|
|
FoV.splice(index, 1);
|
|
update_FoV_table();
|
|
}
|
|
|
|
//--------------------------------------
|
|
// push FoV into Reading container
|
|
//--------------------------------------
|
|
|
|
//button reference to submit FoV to reading array
|
|
var FoV_submit = document.querySelector("#FoV_submit");
|
|
|
|
// add event listener to button
|
|
FoV_submit.addEventListener('click', push_to_reading);
|
|
|
|
function get_standard_deviation(reading){
|
|
var total = {
|
|
"Fungi" : 0.0,
|
|
"Oomycete" : 0.0,
|
|
"Actinobacteria" : 0.0,
|
|
"Flagelate" : 0.0,
|
|
"Ciliate" : 0.0,
|
|
"Amoeba" : 0.0,
|
|
}
|
|
reading.forEach((item, index) => {
|
|
var all_organisms_in_FoV = total_organisms_in_FoV(item);
|
|
var keys = Object.keys(all_organisms_in_FoV);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
total[keys[i]] += all_organisms_in_FoV[keys[i]];
|
|
}
|
|
});
|
|
var mean = {};
|
|
var standard_deviation = {};
|
|
var keys = Object.keys(total);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
mean[keys[i]] = total[keys[i]]/reading.length;
|
|
standard_deviation[keys[i]] = 0;
|
|
}
|
|
reading.forEach((item, index) => {
|
|
var all_organisms_in_FoV = total_organisms_in_FoV(item);
|
|
var keys = Object.keys(all_organisms_in_FoV);
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
standard_deviation[keys[i]] += Math.pow(all_organisms_in_FoV[keys[i]]-mean[keys[i]], 2);
|
|
}
|
|
});
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
standard_deviation[keys[i]] = Math.sqrt(standard_deviation[keys[i]]/reading.length);
|
|
}
|
|
return {"mean" : mean, "standard_deviation" : standard_deviation};
|
|
}
|
|
|
|
// reading array contains a list of n FoV arrays where n is the number of FoVs per reading.
|
|
|
|
var reading = [];
|
|
|
|
// push FoV to reading array and update FoV and Reading Tables as well as FoV array
|
|
|
|
function push_to_reading(e){
|
|
reading.push(FoV);
|
|
update_Reading_table();
|
|
FoV = [];
|
|
update_FoV_table();
|
|
}
|
|
|
|
// this is the reference to the table
|
|
|
|
var Reading_table = document.querySelector("#Reading-table");
|
|
|
|
// update function for Reading table
|
|
|
|
function update_Reading_table(){
|
|
//remove all rows
|
|
while (Reading_table.children[1].hasChildNodes()) {
|
|
Reading_table.children[1].removeChild(Reading_table.children[1].lastChild);
|
|
}
|
|
reading.forEach((item, i) => render_Reading_table(item,i));
|
|
// add last row with mean and standard deviation for FoVs in Readings if reading is not empty
|
|
|
|
if (reading.length>0){
|
|
let result = get_standard_deviation(reading);
|
|
let mean = result.mean;
|
|
let standard_deviation = result.standard_deviation;
|
|
let newRow = Reading_table.children[1].insertRow();
|
|
newRow.style.borderTop = "solid #000000";
|
|
let number = newRow.insertCell(0);
|
|
let fungi = newRow.insertCell(1);
|
|
let oomycete = newRow.insertCell(2);
|
|
let actinobacteria = newRow.insertCell(3);
|
|
let flagelate = newRow.insertCell(4);
|
|
let ciliate = newRow.insertCell(5);
|
|
let amoeba = newRow.insertCell(6);
|
|
let button = newRow.insertCell(7);
|
|
number.innerHTML = "mean";
|
|
fungi.innerHTML = mean.Fungi.toFixed(2).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = mean.Oomycete.toFixed(2).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = mean.Actinobacteria.toFixed(2).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = mean.Flagelate.toFixed(2).replace(/\.?0*$/,'');
|
|
ciliate.innerHTML = mean.Ciliate.toFixed(2).replace(/\.?0*$/,'');
|
|
amoeba.innerHTML = mean.Amoeba.toFixed(2).replace(/\.?0*$/,'');
|
|
button.innerHTML = "";
|
|
|
|
newRow = Reading_table.children[1].insertRow();
|
|
number = newRow.insertCell(0);
|
|
fungi = newRow.insertCell(1);
|
|
oomycete = newRow.insertCell(2);
|
|
actinobacteria = newRow.insertCell(3);
|
|
flagelate = newRow.insertCell(4);
|
|
ciliate = newRow.insertCell(5);
|
|
amoeba = newRow.insertCell(6);
|
|
button = newRow.insertCell(7);
|
|
number.innerHTML = "standard deviation";
|
|
fungi.innerHTML = standard_deviation.Fungi.toFixed(2).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = standard_deviation.Oomycete.toFixed(2).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = standard_deviation.Actinobacteria.toFixed(2).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = standard_deviation.Flagelate.toFixed(2).replace(/\.?0*$/,'');
|
|
ciliate.innerHTML = standard_deviation.Ciliate.toFixed(2).replace(/\.?0*$/,'');
|
|
amoeba.innerHTML = standard_deviation.Amoeba.toFixed(2).replace(/\.?0*$/,'');
|
|
button.innerHTML = "";
|
|
}
|
|
|
|
}
|
|
|
|
// remove a FoV at position index from reading
|
|
|
|
function remove_FoV(index){
|
|
reading.splice(index, 1);
|
|
update_Reading_table();
|
|
}
|
|
|
|
// render a line of the Reading table
|
|
|
|
function render_Reading_table(item, i){
|
|
tot = total_organisms_in_FoV(item);
|
|
let newRow = Reading_table.children[1].insertRow();
|
|
let number = newRow.insertCell(0);
|
|
let fungi = newRow.insertCell(1);
|
|
let oomycete = newRow.insertCell(2);
|
|
let actinobacteria = newRow.insertCell(3);
|
|
let flagelate = newRow.insertCell(4);
|
|
let ciliate = newRow.insertCell(5);
|
|
let amoeba = newRow.insertCell(6);
|
|
let button = newRow.insertCell(7);
|
|
number.innerHTML = i+1;
|
|
fungi.innerHTML = tot.Fungi.toFixed(5).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = tot.Oomycete.toFixed(5).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = tot.Actinobacteria.toFixed(5).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = tot.Flagelate.toFixed(5).replace(/\.?0*$/,'');
|
|
ciliate.innerHTML = tot.Ciliate.toFixed(5).replace(/\.?0*$/,'');
|
|
amoeba.innerHTML = tot.Amoeba.toFixed(5).replace(/\.?0*$/,'');
|
|
button.innerHTML = "<button class='btn btn-primary float-end' onclick=remove_FoV("+i+")>remove</button>";
|
|
}
|
|
|
|
//--------------------------------------
|
|
// push Reading into Result container
|
|
//--------------------------------------
|
|
|
|
var Reading_submit = document.querySelector("#Reading_submit");
|
|
Reading_submit.addEventListener('click', push_to_result);
|
|
// result array contains a list of n reading arrays where n is the number of Readings.
|
|
var result = [];
|
|
|
|
// this function gets called if the button is pressed.
|
|
function push_to_result(e){
|
|
result.push(reading);
|
|
reading = [];
|
|
update_Reading_table();
|
|
update_Result_table();
|
|
}
|
|
var Result_table = document.querySelector("#Result-table");
|
|
|
|
function update_Result_table(){
|
|
//remove all rows
|
|
while (Result_table.children[1].hasChildNodes()) {
|
|
Result_table.children[1].removeChild(Result_table.children[1].lastChild);
|
|
}
|
|
result.forEach((item, i) => render_Result_table(item,i));
|
|
}
|
|
|
|
function remove_reading(index){
|
|
result.splice(index, 1);
|
|
update_Result_table();
|
|
}
|
|
|
|
function render_Result_table(reading, i){
|
|
let result = get_standard_deviation(reading);
|
|
let newRow = Result_table.children[1].insertRow();
|
|
let number = newRow.insertCell(0);
|
|
let fungi = newRow.insertCell(1);
|
|
let oomycete = newRow.insertCell(2);
|
|
let actinobacteria = newRow.insertCell(3);
|
|
let flagelate = newRow.insertCell(4);
|
|
let ciliate = newRow.insertCell(5);
|
|
let amoeba = newRow.insertCell(6);
|
|
let button = newRow.insertCell(7);
|
|
number.innerHTML = i+1;
|
|
fungi.innerHTML = result["mean"].Fungi.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Fungi.toFixed(2).replace(/\.?0*$/,'');
|
|
oomycete.innerHTML = result["mean"].Oomycete.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Oomycete.toFixed(2).replace(/\.?0*$/,'');
|
|
actinobacteria.innerHTML = result["mean"].Actinobacteria.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Actinobacteria.toFixed(2).replace(/\.?0*$/,'');
|
|
flagelate.innerHTML = result["mean"].Flagelate.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Flagelate.toFixed(2).replace(/\.?0*$/,'');
|
|
ciliate.innerHTML = result["mean"].Ciliate.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Ciliate.toFixed(2).replace(/\.?0*$/,'');
|
|
amoeba.innerHTML = result["mean"].Amoeba.toFixed(2).replace(/\.?0*$/,'') + " +/- "
|
|
+ result["standard_deviation"].Amoeba.toFixed(2).replace(/\.?0*$/,'');
|
|
button.innerHTML = "<button class='btn btn-primary float-end' onclick=remove_reading("+i+")>remove</button>";
|
|
}
|
|
|
|
//--------------------------------------
|
|
//in the end everything should get send to the Flask Backend
|
|
//--------------------------------------
|
|
|
|
var result_submit = document.querySelector("#Result_submit");
|
|
result_submit.addEventListener('click', send_result_to_backend);
|
|
|
|
function send_result_to_backend(){
|
|
let to_send = {
|
|
"result" : result,
|
|
"sampleID" : sample_chooser.value,
|
|
"setupID" : camera_chooser.value,
|
|
"prepID" : protocol_chooser.value,
|
|
"dilution" : dilution_chooser.value,
|
|
}
|
|
|
|
let transaction = db.transaction(["main_scan"], "readwrite");
|
|
let objectStore = transaction.objectStore("main_scan");
|
|
let add_request = objectStore.add(to_send); // (3)
|
|
add_request.onsuccess = (event) => {
|
|
console.log("All done!");
|
|
};
|
|
add_request.onerror = (event) => {
|
|
console.log("something went wrong");
|
|
// Don't forget to handle errors!
|
|
};
|
|
}
|
|
|
|
|
|
let streaming = false;
|
|
|
|
function startup() {
|
|
setOrganismType()
|
|
real_width_FoV = microscope_setup.FoV_Width;
|
|
video = document.getElementById("video");
|
|
canvas = document.getElementById("canvas-temp");
|
|
photo = document.getElementById("photo");
|
|
startButton = document.getElementById("start-button");
|
|
|
|
navigator.mediaDevices
|
|
.getUserMedia(microscope_setup.video_prefs)
|
|
.then((stream) => {
|
|
video.srcObject = stream;
|
|
video.play();
|
|
})
|
|
.catch((err) => {
|
|
console.error(`An error occurred: ${err}`);
|
|
});
|
|
|
|
video.addEventListener(
|
|
"canplay",
|
|
(ev) => {
|
|
if (!streaming) {
|
|
video_aspect = video.videoWidth / video.videoHeight;
|
|
|
|
video.style.aspectRatio = video_aspect;
|
|
|
|
photo.style.aspectRatio = video_aspect;
|
|
|
|
canvas.style.aspectRatio = video_aspect;
|
|
|
|
canvas_draw.style.aspectRatio = video_aspect;
|
|
canvas_draw.height = canvas_draw.height*5;
|
|
canvas_draw.width = canvas_draw.height*video_aspect;
|
|
streaming = true;
|
|
}
|
|
},
|
|
false,
|
|
);
|
|
|
|
startButton.addEventListener(
|
|
"click",
|
|
(ev) => {
|
|
takePicture();
|
|
ev.preventDefault();
|
|
},
|
|
false,
|
|
);
|
|
|
|
clearPhoto();
|
|
}
|
|
|
|
// Fill the photo with an indication that none has been
|
|
// captured.
|
|
|
|
function clearPhoto() {
|
|
const context = canvas.getContext("2d");
|
|
|
|
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
const data = canvas.toDataURL("image/png");
|
|
photo.setAttribute("src", data);
|
|
}
|
|
|
|
// Capture a photo by fetching the current contents of the video
|
|
// and drawing it into a canvas, then converting that to a PNG
|
|
// format data URL. By drawing it on an offscreen canvas and then
|
|
// drawing that to the screen, we can change its size and/or apply
|
|
// other changes before drawing it.
|
|
|
|
function takePicture() {
|
|
canvas_draw.getContext('2d').clearRect(0, 0, canvas_draw.width, canvas_draw.height);
|
|
const context = canvas.getContext("2d");
|
|
if (video.videoWidth && video.videoHeight) {
|
|
canvas.width = video.videoWidth;
|
|
canvas.height = video.videoHeight;
|
|
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
|
video.style.zIndex = 1;
|
|
photo.style.zIndex = 5;
|
|
canvas.style.zIndex= 10;
|
|
|
|
const data = canvas.toDataURL("image/png");
|
|
photo.setAttribute("src", data);
|
|
} else {
|
|
clearPhoto();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|