Skip to content

Instantly share code, notes, and snippets.

@oeway
Created November 30, 2020 15:55
Show Gist options
  • Save oeway/02a5736d552383df9b43930cbc75b168 to your computer and use it in GitHub Desktop.
Save oeway/02a5736d552383df9b43930cbc75b168 to your computer and use it in GitHub Desktop.
<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