image-js
Version:
Image processing and manipulation in JavaScript
191 lines • 7.25 kB
JavaScript
import { Image } from '../Image.js';
import { Mask } from '../Mask.js';
import { checkKernel } from '../utils/validators/checkKernel.js';
import checkProcessable from '../utils/validators/checkProcessable.js';
/**
* Dilatation is one of two fundamental operations (with erosion) in morphological
* image processing from which all other morphological operations are based (from Wikipedia).
* Replaces each value with it's local maximum among the pixels with a kernel value of 1.
* @see {@link http://docs.opencv.org/2.4/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html}
* @see {@link https://en.wikipedia.org/wiki/Dilation_(morphology)}
* @param image - Image to dilate.
* @param options - Dilate options.
* @returns Dilated image.
*/
export function dilate(image, options = {}) {
let defaultKernel = false;
if (options.kernel === undefined) {
defaultKernel = true;
}
const { kernel = [
[1, 1, 1],
[1, 1, 1],
[1, 1, 1],
], iterations = 1, } = options;
if (image instanceof Image) {
checkProcessable(image, {
bitDepth: [1, 8, 16],
components: 1,
alpha: false,
});
}
checkKernel(kernel);
let onlyOnes = true;
if (!defaultKernel) {
outer: for (const row of kernel) {
for (const value of row) {
if (value !== 1) {
onlyOnes = false;
break outer;
}
}
}
}
let result = image;
for (let i = 0; i < iterations; i++) {
if (result instanceof Mask) {
if (onlyOnes) {
const newMask = result.clone();
result = dilatMaskOnlyOnes(result, newMask, kernel[0].length, kernel.length);
}
else {
const newMask = Mask.createFrom(result);
result = dilateMask(result, newMask, kernel);
}
}
else if (onlyOnes) {
const newImage = Image.createFrom(result);
result = dilateGreyOnlyOnes(result, newImage, kernel[0].length, kernel.length);
}
else {
const newImage = Image.createFrom(result);
result = dilateGrey(result, newImage, kernel);
}
}
return result;
}
function dilateGrey(image, newImage, kernel) {
const kernelWidth = kernel[0].length;
const kernelHeight = kernel.length;
const radiusX = (kernelWidth - 1) / 2;
const radiusY = (kernelHeight - 1) / 2;
for (let row = 0; row < image.height; row++) {
for (let column = 0; column < image.width; column++) {
let max = 0;
for (let kernelRow = 0; kernelRow < kernelHeight; kernelRow++) {
for (let kernelColumn = 0; kernelColumn < kernelWidth; kernelColumn++) {
if (kernel[kernelRow][kernelColumn] !== 1)
continue;
const currentColumn = kernelColumn - radiusX + column;
const currentRow = kernelRow - radiusY + row;
if (currentColumn < 0 ||
currentRow < 0 ||
currentColumn >= image.width ||
currentRow >= image.height) {
continue;
}
const value = image.getValue(currentColumn, currentRow, 0);
if (value > max)
max = value;
}
}
newImage.setValue(column, row, 0, max);
}
}
return newImage;
}
function dilateGreyOnlyOnes(image, newImage, kernelWidth, kernelHeight) {
const radiusX = (kernelWidth - 1) / 2;
const radiusY = (kernelHeight - 1) / 2;
const maxList = [];
for (let column = 0; column < image.width; column++) {
maxList.push(0);
}
for (let row = 0; row < image.height; row++) {
for (let column = 0; column < image.width; column++) {
let max = 0;
for (let h = Math.max(0, row - radiusY); h < Math.min(image.height, row + radiusY + 1); h++) {
const value = image.getValue(column, h, 0);
if (value > max) {
max = value;
}
}
maxList[column] = max;
}
for (let column = 0; column < image.width; column++) {
let max = 0;
for (let i = Math.max(0, column - radiusX); i < Math.min(image.width, column + radiusX + 1); i++) {
if (maxList[i] > max) {
max = maxList[i];
}
}
newImage.setValue(column, row, 0, max);
}
}
return newImage;
}
function dilateMask(mask, newMask, kernel) {
const kernelWidth = kernel[0].length;
const kernelHeight = kernel.length;
const radiusX = (kernelWidth - 1) / 2;
const radiusY = (kernelHeight - 1) / 2;
for (let row = 0; row < mask.height; row++) {
for (let column = 0; column < mask.width; column++) {
let max = 0;
for (let kernelRow = 0; kernelRow < kernelHeight; kernelRow++) {
for (let kernelColumn = 0; kernelColumn < kernelWidth; kernelColumn++) {
if (kernel[kernelRow][kernelColumn] !== 1)
continue;
const currentColumn = kernelColumn - radiusX + column;
const currentRow = kernelRow - radiusY + row;
if (currentRow < 0 ||
currentColumn < 0 ||
currentColumn >= mask.width ||
currentRow >= mask.height) {
continue;
}
const value = mask.getBit(currentColumn, currentRow);
if (value === 1) {
max = 1;
break;
}
}
}
if (max === 1) {
newMask.setBit(column, row, 1);
}
}
}
return newMask;
}
function dilatMaskOnlyOnes(mask, newMask, kernelWidth, kernelHeight) {
const radiusX = (kernelWidth - 1) / 2;
const radiusY = (kernelHeight - 1) / 2;
const maxList = [];
for (let column = 0; column < mask.width; column++) {
maxList.push(1);
}
for (let row = 0; row < mask.height; row++) {
for (let column = 0; column < mask.width; column++) {
maxList[column] = 0;
for (let h = Math.max(0, row - radiusY); h < Math.min(mask.height, row + radiusY + 1); h++) {
if (mask.getBit(column, h) === 1) {
maxList[column] = 1;
break;
}
}
}
for (let column = 0; column < mask.width; column++) {
if (newMask.getBit(column, row) === 1)
continue;
for (let i = Math.max(0, column - radiusX); i < Math.min(mask.width, column + radiusX + 1); i++) {
if (maxList[i] === 1) {
newMask.setBit(column, row, 1);
break;
}
}
}
}
return newMask;
}
//# sourceMappingURL=dilate.js.map