mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-14 04:10:04 +01:00
Opencv.js via npm for blob-detection (#1185)
This commit is contained in:
@@ -9,6 +9,7 @@ List of Module Documentations
|
|||||||
4. [Add QR](#Add-QR-module)
|
4. [Add QR](#Add-QR-module)
|
||||||
5. [Average](#average-module)
|
5. [Average](#average-module)
|
||||||
6. [Blend](#blend-module)
|
6. [Blend](#blend-module)
|
||||||
|
7. [Blob-Analysis](#blob-analysis)
|
||||||
7. [Blur](#blur-module)
|
7. [Blur](#blur-module)
|
||||||
8. [Brightness](#brightness-module)
|
8. [Brightness](#brightness-module)
|
||||||
9. [Channel](#channel-module)
|
9. [Channel](#channel-module)
|
||||||
@@ -155,6 +156,20 @@ where `options` is an object with the following properties:
|
|||||||
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
* offset: step of image with which current image is to be blended(Two steps back is -2, three steps back is -3 etc; default -2)
|
||||||
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
* func: function used to blend two images (default : function(r1, g1, b1, a1, r2, g2, b2, a2) { return [ r1, g2, b2, a2 ] })
|
||||||
|
|
||||||
|
## Blob Analysis
|
||||||
|
|
||||||
|
This module uses Opencv.js for detecting and marking blob/region in microscopic images. It requires an opencv.js file to
|
||||||
|
be loaded before using the functionalities which is currently being loaded to the webpage via script.It supports both environments, Node.js and browser for processing.
|
||||||
|
|
||||||
|
As the size of opencv.js file is quite large, the future versions will focus on loading it asynchronously, on demand of the the module to optimise performance.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
```js
|
||||||
|
sequencer.loadImage('PATH')
|
||||||
|
.addSteps('blob-analysis')
|
||||||
|
.run()
|
||||||
|
```
|
||||||
|
|
||||||
## blur-module
|
## blur-module
|
||||||
|
|
||||||
This module is used for applying a Gaussian blur effect.
|
This module is used for applying a Gaussian blur effect.
|
||||||
|
|||||||
@@ -232,6 +232,7 @@
|
|||||||
var sequencer;
|
var sequencer;
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
<script async src="../node_modules/opencv.js/opencv.js" type="text/javascript"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
"jsqr": "^1.1.1",
|
"jsqr": "^1.1.1",
|
||||||
"lodash": "^4.17.11",
|
"lodash": "^4.17.11",
|
||||||
"ndarray": "^1.0.18",
|
"ndarray": "^1.0.18",
|
||||||
|
"opencv.js": "^1.2.1",
|
||||||
"ora": "^3.0.0",
|
"ora": "^3.0.0",
|
||||||
"pace": "0.0.4",
|
"pace": "0.0.4",
|
||||||
"puppeteer": "^1.14.0",
|
"puppeteer": "^1.14.0",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ module.exports = {
|
|||||||
'add-qr': require('./modules/AddQR'),
|
'add-qr': require('./modules/AddQR'),
|
||||||
'average': require('./modules/Average'),
|
'average': require('./modules/Average'),
|
||||||
'blend': require('./modules/Blend'),
|
'blend': require('./modules/Blend'),
|
||||||
|
'blob-analysis': require('./modules/BlobAnalysis'),
|
||||||
'blur': require('./modules/Blur'),
|
'blur': require('./modules/Blur'),
|
||||||
'brightness': require('./modules/Brightness'),
|
'brightness': require('./modules/Brightness'),
|
||||||
'canvas-resize': require('./modules/CanvasResize'),
|
'canvas-resize': require('./modules/CanvasResize'),
|
||||||
|
|||||||
81
src/modules/BlobAnalysis/BlobAnalysis.js
Normal file
81
src/modules/BlobAnalysis/BlobAnalysis.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
module.exports = function(pixels, options, priorStep){
|
||||||
|
|
||||||
|
var $ = require('jquery'); // to make Blob-analysis work for node.js
|
||||||
|
|
||||||
|
var img = $(priorStep.imgElement);
|
||||||
|
if(Object.keys(img).length === 0){
|
||||||
|
img = $(priorStep.options.step.imgElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.width = pixels.shape[0];
|
||||||
|
canvas.height = pixels.shape[1];
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img[0], 0, 0);
|
||||||
|
let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
|
||||||
|
let src = cv.matFromImageData(imgData);
|
||||||
|
let dst = new cv.Mat();
|
||||||
|
let gray = new cv.Mat();
|
||||||
|
let opening = new cv.Mat();
|
||||||
|
let imageBg = new cv.Mat();
|
||||||
|
let imageFg = 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, imageBg, 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, imageFg, 0.7 * 1, 255, cv.THRESH_BINARY);
|
||||||
|
imageFg.convertTo(imageFg, cv.CV_8U, 1, 0);
|
||||||
|
cv.subtract(imageBg, imageFg, unknown);
|
||||||
|
|
||||||
|
// get connected components markers
|
||||||
|
cv.connectedComponents(imageFg, 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(canvas, src);
|
||||||
|
|
||||||
|
src.delete(); dst.delete(); gray.delete(); opening.delete(); imageBg.delete();
|
||||||
|
imageFg.delete(); distTrans.delete(); unknown.delete(); markers.delete(); M.delete();
|
||||||
|
|
||||||
|
var myImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
pixels.data = myImageData.data;
|
||||||
|
|
||||||
|
return pixels;
|
||||||
|
};
|
||||||
43
src/modules/BlobAnalysis/Module.js
Normal file
43
src/modules/BlobAnalysis/Module.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
module.exports = function BlobAnalysis(options, UI){
|
||||||
|
|
||||||
|
var output;
|
||||||
|
|
||||||
|
function draw(input, callback, progressObj) {
|
||||||
|
|
||||||
|
progressObj.stop(true);
|
||||||
|
progressObj.overrideFlag = true;
|
||||||
|
|
||||||
|
var step = this;
|
||||||
|
|
||||||
|
var priorStep = this.getStep(-1); // get the previous step to process it
|
||||||
|
|
||||||
|
function extraManipulation(pixels){
|
||||||
|
|
||||||
|
pixels = require('./BlobAnalysis')(pixels, options, priorStep);
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
function output(image, datauri, mimetype){
|
||||||
|
|
||||||
|
step.output = { src: datauri, format: mimetype};
|
||||||
|
}
|
||||||
|
|
||||||
|
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||||
|
output: output,
|
||||||
|
extraManipulation: extraManipulation,
|
||||||
|
format: input.format,
|
||||||
|
image: options.image,
|
||||||
|
inBrowser: options.inBrowser,
|
||||||
|
callback: callback
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
options: options,
|
||||||
|
draw: draw,
|
||||||
|
output: output,
|
||||||
|
UI: UI
|
||||||
|
};
|
||||||
|
};
|
||||||
4
src/modules/BlobAnalysis/index.js
Normal file
4
src/modules/BlobAnalysis/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module.exports = [
|
||||||
|
require('./Module'),
|
||||||
|
require('./info.json')
|
||||||
|
];
|
||||||
6
src/modules/BlobAnalysis/info.json
Normal file
6
src/modules/BlobAnalysis/info.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "Blob Analysis",
|
||||||
|
"description": "Blob/Region identification for microscopic images.",
|
||||||
|
"inputs": {},
|
||||||
|
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md#blob-analysis"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user