src_image_labelingThread.js
import {ThreadPool, WorkerTask} from '../utils/thread.js';
// doc imports
/* eslint-disable no-unused-vars */
import {Size} from './size.js';
/* eslint-enable no-unused-vars */
/**
* List of compatible typed arrays.
*
* @typedef {(
* Uint8Array | Int8Array |
* Uint16Array | Int16Array |
* Uint32Array | Int32Array
* )} TypedArray
*/
/**
* Generate a worker message to send to the labeling worker.
*
* @param {TypedArray} imageBuffer The buffer to label.
* @param {Size} imageSize The image size.
*
* @returns {object} The message to send to the worker.
*/
export function generateWorkerMessage(imageBuffer, imageSize) {
// We can't pass these metadata objects directly, so we will just
// pull out what we need and pass that.
const ndims = imageSize.length();
// Cache the unit vector offsets to make a couple calculations faster.
const unitVectors = Array(ndims).fill(0);
for (let d = 0; d < ndims; d++) {
unitVectors[d] = imageSize.getDimSize(d);
}
const sizes = Array(ndims).fill(0);
for (let d = 0; d < ndims; d++) {
sizes[d] = imageSize.get(d);
}
const totalSize = imageSize.getTotalSize();
return {
imageBuffer: imageBuffer,
unitVectors: unitVectors,
sizes: sizes,
totalSize: totalSize
};
}
/**
* Labeling worker task.
*/
class LabelingWorkerTask extends WorkerTask {
constructor(message, info) {
super(message, info);
}
getWorker() {
return new Worker(
new URL('./labeling.worker.js', import.meta.url),
{
name: 'labeling.worker'
}
);
}
}
/**
* Labeling thread.
*/
export class LabelingThread {
/**
* The thread pool.
*
* @type {ThreadPool}
*/
#threadPool = new ThreadPool(1);
constructor() {
this.#threadPool.onerror = ((e) => {
console.error('Labeling failed!', e.error);
});
}
/**
* Trigger a labels recalculation.
*
* @param {TypedArray} imageBuffer The buffer to label.
* @param {Size} size The image size.
*/
run(imageBuffer, size) {
// We can't just pass in an Image or we would get a circular dependency
this.#threadPool.onworkitem = this.ondone;
const workerTask = new LabelingWorkerTask(
generateWorkerMessage(imageBuffer, size),
{}
);
// add it the queue and run it
this.#threadPool.addWorkerTask(workerTask);
}
/**
* Handle a completed labeling. Default behavior is do nothing,
* this is meant to be overridden.
*
* @param {object} _event The work item event fired when a labeling
* calculation is completed. Event.data should contain a 'lebels' item.
*/
ondone(_event) {}
}