open-organism-observer/setup_microscope.html

480 lines
15 KiB
HTML

<!--- this is the main file --->
<!DOCTYPE html>
<html>
<header>
<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: 9;
width: 90%;
}
#canvas-draw{
position:absolute;
z-index:10;
width: 90%;
}
#photo {
position:absolute;
z-index:5;
width: 100%;
}
#canvas-temp{
display: none;
}
#control-elements {
height:30em;
}
</style>
</header>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarTogglerDemo03" aria-controls="navbarTogglerDemo03" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="#">Open Microscopy App</a>
<div class="collapse navbar-collapse" id="navbarTogglerDemo03">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Settings
</a>
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
<li><a class="dropdown-item" href="/setup_microscope.html">Setup Camera</a></li>
<li><a class="dropdown-item" href="/sample_prep_protocol.html">Setup Preparation Protocol</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">Defaults</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/sample_intake.html">add sample</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarScrollingDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Observe
</a>
<ul class="dropdown-menu" aria-labelledby="navbarScrollingDropdown">
<li><a class="dropdown-item" href="/observe_nema.html">Nematodes</a></li>
<li><a class="dropdown-item" href="/observe_main.html">Filamentous and Protozoa</a></li>
<li><a class="dropdown-item" href="/observe_bact.html">Bacteria</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link" href="/create_report.html">generate report</a>
</li>
</ul>
</div>
</div>
</nav>
<div class = "container">
<h1> Setup Microscope & Camera </h1>
<canvas id="canvas-temp"></canvas>
<!-- 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-8">
<canvas id="canvas-draw"></canvas>
<img id="photo" alt="The screen capture will appear in this box."></img>
<video autoplay="true" id="video"></video>
</div>
<div class="col-4 ">
<label class = "form-label" for="camera_chooser">
Select a Camera
</label>
<select class="form-select" aria-label="Default select example" name="camera_chooser" id = "camera_chooser">
</select>
<div id="control_elements" class="row align-items-end">
<div class="col" name="length" >
<label class = "form-label" for="real_length">
Please enter the length of the object in um:
</label>
<input class="form-control" id = "real_length" type="text"
name="real_length" value=0>
</input>
</div>
</div>
<br>
<form class="needs-validation" id = "microscope_setup">
<div class="row">
<div class="col">
<label class = "form-label" for="name">
Name of Setup
</label>
</div>
<div class="col">
<input class="form-control" type="text" name="name" id = "name"
placeholder="e.g. camera">
</input>
</div>
</div>
<div class="row">
<div class="col">
<label class = "form-label" for="FoV_Width">
Width of Field of View (Camera) [um]
</label>
</div>
<div class="col">
<input class="form-control" type="number" id="FoV_width"
name="FoV_Width">
</input>
</div>
</div>
<div class="row">
<div class="col">
<label class = "form-label" for="FoV_Height">
Height of Field of View (Camera) [um]
</label>
</div>
<div class="col">
<input class="form-control" type="number"
id="FoV_height" name="FoV_Height">
</input>
</div>
</div>
<div class="row">
<div class="col">
<label class = "form-label" for="Slip_Height">
Height of Coverslip [mm]
</label>
</div>
<div class="col">
<input class="form-control" type="number"
id="Slip_height" step="0.1" name="Slip_Height">
</input>
</div>
</div>
<div class="row">
<div class="col">
<label class = "form-label" for="Slip_Width">
Width of Coverslip [mm]
</label>
</div>
<div class="col">
<input class="form-control" type="number"
id="Slip_width" step="0.1" name="Slip_Width">
</input>
</div>
</div>
<div class="row">
<div class="col">
<label class = "form-check-label" for="default">
This should be the default Camera setup
</label>
</div>
<div class="col">
<input class="form-check-input" type="checkbox" id = "Default" name="default">
</input>
</div>
</div>
<button class="btn btn-primary float-end" type="submit">submit</button>
</form>
</div>
</div>
</div>
</body>
<script>
var db;
let openRequest = indexedDB.open("my_db");
openRequest.onerror = function() {
console.error("Error", openRequest.error);
};
openRequest.onsuccess = function() {
db = openRequest.result;
// continue working with database using db object
};
const form = document.getElementById("microscope_setup");
form.addEventListener("submit", add);
function add(event){
event.preventDefault();
let microscope_setup = {
"name" : document.getElementById("name").value,
"FoV_Height" : document.getElementById("FoV_height").value,
"FoV_Width" : document.getElementById("FoV_width").value,
"Slip_Height" : document.getElementById("Slip_height").value,
"Slip_Width" : document.getElementById("Slip_width").value,
"video_prefs" : video_prefs,
}
let transaction = db.transaction(["microscope_setup"], "readwrite");
let objectStore = transaction.objectStore("microscope_setup");
let add_request = objectStore.add(microscope_setup); // (3)
add_request.onsuccess = (event) => {
console.log("All done!");
let id_of_setup = event.target.result;
if (document.getElementById("Default").checked){
let my_default = {"id" : 0, "microscope_setup" : id_of_setup};
let next_transaction = db.transaction(["defaults"], "readwrite");
let next_objectStore = next_transaction.objectStore("defaults");
let add_next_request = next_objectStore.put(my_default);
transaction.oncomplete = (event) => {
console.log("All done!");
};
transaction.onerror = (event) => {
console.log("something went wrong");
// Don't forget to handle errors!
};
}
};
add_request.onerror = (event) => {
console.log("something went wrong");
// Don't forget to handle errors!
};
}
var video = document.querySelector("#video");
var out = document.querySelector("#output");
var camera_chooser = document.querySelector("#camera_chooser")
// init video switching get IDs of video inputs
let video_prefs={ video: {width: { ideal: 99999} , height: { ideal: 99999 }}, audio: false };
let streaming = false;
let index = 1;
navigator.mediaDevices
.enumerateDevices()
.then((devices) => {
devices.forEach((device) => {
if (device.kind=="videoinput"){
let cameraID = document.createElement("option");
let text = document.createTextNode("camera " + index);
if (index == 1){
video_prefs.video.deviceId = device.deviceId;
cameraID.defaultSelected = true;
}
++index;
cameraID.value = device.deviceId;
cameraID.appendChild(text);
camera_chooser.appendChild(cameraID);
}
});
})
.catch((err) => {
console.error(`${err.name}: ${err.message}`);
});
camera_chooser.addEventListener("change", change_cam);
function change_cam(){
video_prefs.video.deviceId = this.value;
startup();
};
var real_dimensions = document.querySelector("#real_dimensions");
var fov_width = document.querySelector("#FoV_width");
var fov_height = document.querySelector("#FoV_height");
var real_length = document.querySelector("#real_length");
// init drawing
var canvas_draw = document.querySelector("#canvas-draw");
var ctx = canvas_draw.getContext('2d');
var pos = { x: 0, y: 0 };
var start_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 distance in units
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;
console.log(pos);
}
// 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 && line.length !== 0) {
// push the line to lines array
console.log('Yay');
line.push({x: pos.x, y: pos.y});
let measured_length = get_length(line);
fov_height.value = ((real_length.value/(measured_length * canvas_draw.width))* canvas_draw.height).toFixed(0);
fov_width.value = ((real_length.value/(measured_length * canvas_draw.width))* canvas_draw.width).toFixed(0);
line = [];
return;
}
// draw on canvas.
if (e.buttons == 1){
canvas_draw.getContext('2d').clearRect(0, 0, canvas_draw.width, canvas_draw.height);
if (lines.length == 1){
draw_path(lines[0]);
}
ctx.beginPath(); // begin
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
// update line
// first time push the first position
if (line.length == 0){
start_pos = {x:pos.x, y:pos.y};
line.push(start_pos);
}
ctx.moveTo(start_pos.x, start_pos.y); // from
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to
ctx.stroke(); // draw it!
}
}
startup();
function startup() {
output = document.getElementById("output");
video = document.getElementById("video");
canvas = document.getElementById("canvas-temp");
photo = document.getElementById("photo");
startButton = document.getElementById("start-button");
navigator.mediaDevices
.getUserMedia(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>
</html>