a page to setup the microscope and calibrate the camera FoV Size
This commit is contained in:
parent
71710d0c42
commit
7e7cb6770e
474
setup_microscope.html
Normal file
474
setup_microscope.html
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
<!--- 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>
|
||||
|
||||
<style>
|
||||
#output{
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
#video_container{
|
||||
position:relative;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#video {
|
||||
position:relative;
|
||||
z-index: 15;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#canvas-draw{
|
||||
position:absolute;
|
||||
z-index:10;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#photo {
|
||||
position:absolute;
|
||||
z-index:5;
|
||||
height:100%
|
||||
}
|
||||
|
||||
#canvas-temp{
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
<div class = "container">
|
||||
<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 ">
|
||||
<div class = "row">
|
||||
<button class="btn btn-primary float-end" onclick="takePicture();" id="start-button">
|
||||
Take photo
|
||||
</button>
|
||||
<button class="btn btn-primary float-end" onclick="toggle_cam();">
|
||||
Toggle camera
|
||||
</button>
|
||||
</div>
|
||||
<div id="real_dimensions" class="row align-items-end">
|
||||
<p>
|
||||
put an object of known length and width under the microscope.
|
||||
enter width and height of measured object
|
||||
</p>
|
||||
<div class="col" name="width" >
|
||||
<label class = "form-label" for="real_width">
|
||||
Width
|
||||
</label>
|
||||
<input class="form-control" id = "real_width" type="text"
|
||||
name="real_width" value=0>
|
||||
</input>
|
||||
</div>
|
||||
<div class="col" name="height" >
|
||||
<label class = "form-label" for="real_height">
|
||||
Height
|
||||
</label>
|
||||
<input class="form-control" id = "real_height" type="text"
|
||||
name="real_height" value=0>
|
||||
</input>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<form class="needs-validation" name = "microscope_setup" onsubmit="return false;">
|
||||
<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" 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" name="Slip_Width">
|
||||
</input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class = "form-label" for="eyepiece_number">
|
||||
Eyepiece Number
|
||||
</label>
|
||||
</div>
|
||||
<div class="col">
|
||||
<input class="form-control" type="number" name="eyepiece_number">
|
||||
</input>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary float-end" onclick="add();">submit</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
<script>
|
||||
|
||||
var db;
|
||||
let openRequest = indexedDB.open("my_db");
|
||||
|
||||
openRequest.onupgradeneeded = function() {
|
||||
db = openRequest.result;
|
||||
db.createObjectStore("microscope_setup", {autoIncrement : true});
|
||||
db.createObjectStore("prep_protocol", {autoIncrement : true});
|
||||
|
||||
};
|
||||
|
||||
openRequest.onerror = function() {
|
||||
console.error("Error", openRequest.error);
|
||||
};
|
||||
|
||||
openRequest.onsuccess = function() {
|
||||
db = openRequest.result;
|
||||
// continue working with database using db object
|
||||
};
|
||||
function add(e){
|
||||
e.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
|
||||
}
|
||||
let transaction = db.transaction(["microscope_setup"], "readwrite");
|
||||
let objectStore = transaction.objectStore("microscope_setup");
|
||||
let add_request = objectStore.add(microscope_setup); // (3)
|
||||
|
||||
transaction.oncomplete = (event) => {
|
||||
console.log("All done!");
|
||||
};
|
||||
transaction.onerror = (event) => {
|
||||
console.log("something went wrong");
|
||||
// Don't forget to handle errors!
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var video = document.querySelector("#video");
|
||||
var out = document.querySelector("#output");
|
||||
// width of FoV -- will be supplied by FLASK microscope setup.
|
||||
var real_width_FoV = 255;
|
||||
// init video switching get IDs of video inputs
|
||||
|
||||
navigator.mediaDevices
|
||||
.enumerateDevices()
|
||||
.then((devices) => {
|
||||
devices.forEach((device) => {
|
||||
if (device.kind=="videoinput"){
|
||||
deviceIDs.push(device.deviceId);
|
||||
}
|
||||
console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`${err.name}: ${err.message}`);
|
||||
});
|
||||
|
||||
|
||||
// toggle between cameras
|
||||
|
||||
var deviceIDs = [];
|
||||
var device_index = 0;
|
||||
var real_dimensions = document.querySelector("#real_dimensions");
|
||||
var fov_width = document.querySelector("#FoV_width");
|
||||
var fov_height = document.querySelector("#FoV_height");
|
||||
var real_width = document.querySelector("#real_width");
|
||||
var real_height = document.querySelector("#real_height");
|
||||
|
||||
function toggle_cam(){
|
||||
device_index += 1
|
||||
if (device_index>=deviceIDs.length){
|
||||
device_index=0
|
||||
};
|
||||
|
||||
if (navigator.mediaDevices.getUserMedia) {
|
||||
navigator.mediaDevices.getUserMedia({ video: {
|
||||
deviceId: deviceIDs[device_index],
|
||||
}, })
|
||||
.then(function (stream) {
|
||||
video.srcObject = stream;
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log("Something went wrong!");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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_path(path){
|
||||
// draw on canvas.
|
||||
ctx.beginPath(); // begin
|
||||
ctx.lineWidth = 0.5;
|
||||
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 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');
|
||||
if (lines.length == 0){
|
||||
line.push({x: start_pos.x, y: pos.y});
|
||||
lines.push(line);
|
||||
let measured_height = get_length(line);
|
||||
fov_height.value = ((real_height.value/(measured_height * canvas_draw.width))*canvas_draw.height).toFixed(0);
|
||||
line = [];
|
||||
|
||||
return;
|
||||
|
||||
} else if ((lines.length == 1)){
|
||||
line.push({x: pos.x, y: start_pos.y});
|
||||
lines.push(line);
|
||||
let measured_width = get_length(line);
|
||||
fov_width.value = ((real_width.value/(measured_width * canvas_draw.width)) * canvas_draw.width).toFixed(0);
|
||||
line = [];
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// draw on canvas.
|
||||
if (e.buttons == 1){
|
||||
if (lines.length == 2){
|
||||
lines=[];
|
||||
}
|
||||
|
||||
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 = 0.5;
|
||||
ctx.lineCap = 'round';
|
||||
ctx.strokeStyle = 'black';
|
||||
// update line
|
||||
// first time push the first position
|
||||
if (line.length == 0){
|
||||
console.log('yay');
|
||||
start_pos = {x:pos.x, y:pos.y};
|
||||
line.push(start_pos);
|
||||
}
|
||||
console.log(start_pos.x)
|
||||
|
||||
ctx.moveTo(start_pos.x, start_pos.y); // from
|
||||
setPosition(e);
|
||||
if (lines.length==0){
|
||||
ctx.lineTo(start_pos.x, pos.y); // to
|
||||
} else if (lines.length==1){
|
||||
ctx.lineTo(pos.x, start_pos.y); // to
|
||||
}
|
||||
ctx.stroke(); // draw it!
|
||||
}
|
||||
}
|
||||
|
||||
let streaming = false;
|
||||
|
||||
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: true, audio: false })
|
||||
.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;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Set up our event listener to run the startup process
|
||||
// once loading is complete.
|
||||
window.addEventListener("load", startup, false);
|
||||
//})();
|
||||
|
||||
</script>
|
||||
</html>
|
||||
Loading…
Reference in a new issue