Created
November 30, 2020 15:55
-
-
Save oeway/02a5736d552383df9b43930cbc75b168 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<config lang="json"> | |
{ | |
"name": "OpenCVSegmentation", | |
"type": "window", | |
"tags": [], | |
"ui": "", | |
"version": "0.1.0", | |
"cover": "", | |
"description": "This is a demo plugin for segmenting images with OpenCV.js", | |
"icon": "extension", | |
"inputs": null, | |
"outputs": null, | |
"api_version": "0.1.8", | |
"env": "", | |
"permissions": [], | |
"requirements": ["https://docs.opencv.org/master/opencv.js", "https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css", "https://use.fontawesome.com/releases/v5.14.0/js/all.js"], | |
"dependencies": ["https://github.com/imjoy-team/imjoy-plugins/blob/master/repository/tifFileImporter.imjoy.html"] | |
} | |
</config> | |
<script lang="javascript"> | |
// draw a base64 encoded image to the canvas | |
const drawImage = (canvas, base64Image)=>{ | |
return new Promise((resolve, reject)=>{ | |
var img = new Image() | |
img.crossOrigin = "anonymous" | |
img.onload = function(){ | |
const ctx = canvas.getContext("2d"); | |
canvas.width = Math.min(this.width, 512); | |
canvas.height= Math.min(this.height, parseInt(512*this.height/this.width), 1024); | |
// draw the img into canvas | |
ctx.drawImage(this, 0, 0, canvas.width, canvas.height); | |
resolve(canvas); | |
} | |
img.onerror = reject; | |
img.src = base64Image; | |
}) | |
} | |
// read the file and return as a base64 string | |
const readImageFile = (file)=>{ | |
return new Promise((resolve, reject)=>{ | |
const U = window.URL || window.webkitURL; | |
// this works for safari | |
if(U.createObjectURL){ | |
resolve(U.createObjectURL(file)) | |
} | |
// fallback | |
else{ | |
var fr = new FileReader(); | |
// when image is loaded, set the src of the image where you want to display it | |
fr.onload = function(e) { | |
resolve(e.target.result) | |
}; | |
fr.onerror = reject | |
// fill fr with image data | |
fr.readAsDataURL(file); | |
} | |
}) | |
} | |
function segmentImage(inputCanvasId, outputCanvasId){ | |
let src = cv.imread(inputCanvasId); | |
let dst = new cv.Mat(); | |
let gray = new cv.Mat(); | |
let opening = new cv.Mat(); | |
let coinsBg = new cv.Mat(); | |
let coinsFg = new cv.Mat(); | |
let distTrans = new cv.Mat(); | |
let unknown = new cv.Mat(); | |
let markers = new cv.Mat(); | |
// gray and threshold image | |
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0); | |
cv.threshold(gray, gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU); | |
// get background | |
let M = cv.Mat.ones(3, 3, cv.CV_8U); | |
cv.erode(gray, gray, M); | |
cv.dilate(gray, opening, M); | |
cv.dilate(opening, coinsBg, M, new cv.Point(-1, -1), 3); | |
// distance transform | |
cv.distanceTransform(opening, distTrans, cv.DIST_L2, 5); | |
cv.normalize(distTrans, distTrans, 1, 0, cv.NORM_INF); | |
// get foreground | |
cv.threshold(distTrans, coinsFg, 0.7 * 1, 255, cv.THRESH_BINARY); | |
coinsFg.convertTo(coinsFg, cv.CV_8U, 1, 0); | |
cv.subtract(coinsBg, coinsFg, unknown); | |
// get connected components markers | |
cv.connectedComponents(coinsFg, markers); | |
for (let i = 0; i < markers.rows; i++) { | |
for (let j = 0; j < markers.cols; j++) { | |
markers.intPtr(i, j)[0] = markers.ucharPtr(i, j)[0] + 1; | |
if (unknown.ucharPtr(i, j)[0] == 255) { | |
markers.intPtr(i, j)[0] = 0; | |
} | |
} | |
} | |
cv.cvtColor(src, src, cv.COLOR_RGBA2RGB, 0); | |
cv.watershed(src, markers); | |
// draw barriers | |
for (let i = 0; i < markers.rows; i++) { | |
for (let j = 0; j < markers.cols; j++) { | |
if (markers.intPtr(i, j)[0] == -1) { | |
src.ucharPtr(i, j)[0] = 255; // R | |
src.ucharPtr(i, j)[1] = 0; // G | |
src.ucharPtr(i, j)[2] = 0; // B | |
} | |
} | |
} | |
cv.imshow(outputCanvasId, src); | |
src.delete(); dst.delete(); gray.delete(); opening.delete(); coinsBg.delete(); | |
coinsFg.delete(); distTrans.delete(); unknown.delete(); markers.delete(); M.delete(); | |
} | |
class ImJoyPlugin{ | |
async setup(){ | |
// Display image when a file is selected. | |
const fileInput = document.getElementById("file-input"); | |
const canvas = document.getElementById("input-canvas"); | |
fileInput.addEventListener("change", async ()=>{ | |
const file = fileInput.files[0] | |
let img; | |
if(file.name.endsWith('.tiff') || file.name.endsWith('.tif')){ | |
const p = await api.getPlugin('Tif File Importer') | |
const tiffObj = await p.open(file) | |
// locate the first frame | |
tiffObj.seek(0) | |
img = await tiffObj.readAsURL() | |
} | |
else{ | |
img = await readImageFile(file); | |
} | |
await drawImage(canvas, img); | |
// display the segment butotn | |
segmentButton.style.display = 'inline-block'; | |
}, true); | |
// trigger the file dialog when the button is clicked | |
const selectButton = document.getElementById("select-button"); | |
selectButton.addEventListener("click", async ()=>{ | |
// simulate a click on the <input> tag | |
fileInput.click() | |
}, true); | |
const segmentButton = document.getElementById("segment-button"); | |
// hide the button after initialization | |
segmentButton.style.display = 'none'; | |
// trigger segmentation when clicked | |
segmentButton.addEventListener("click", async ()=>{ | |
segmentImage(canvas, canvas); | |
}, true); | |
await api.log("plugin initialized") | |
} | |
async run(ctx){ | |
} | |
} | |
api.export(new ImJoyPlugin()) | |
</script> | |
<window> | |
<div> | |
<input id="file-input" accept="image/*" capture="camera" type="file"/> | |
<nav class="panel"> | |
<p class="panel-heading"> | |
<i class="fas fa-eye" aria-hidden="true"></i> Segmentation with OpenCV.js | |
</p> | |
<div class="panel-block"> | |
<button id="select-button" class="button is-link is-outlined is-fullwidth"> | |
Open an image | |
</button> | |
<button id="segment-button" class="button is-link is-outlined is-fullwidth"> | |
Segment the image | |
</button> | |
</div> | |
<div class="panel-block"> | |
<canvas id="input-canvas" style="width: 100%; object-fit: cover;"></canvas> | |
</div> | |
</div> | |
</window> | |
<style> | |
#file-input{ | |
display: none; | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment