You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
3.7 KiB
TypeScript
136 lines
3.7 KiB
TypeScript
import { ImageWrapper } from '../common/image-wrapper';
|
|
import { Point } from '../common/point';
|
|
|
|
/**
|
|
* Invert matrix
|
|
* @param matrix the matrix to invert
|
|
* @returns the inverted matrix
|
|
*/
|
|
export function invert(matrix: Float32Array): Float32Array {
|
|
const a0 = matrix[0];
|
|
const a1 = matrix[1];
|
|
const a2 = matrix[2];
|
|
const a3 = matrix[3];
|
|
const determinant = a0 * a3 - a2 * a1;
|
|
|
|
if (!determinant) {
|
|
return null;
|
|
}
|
|
|
|
return new Float32Array([a3 / determinant, -a1 / determinant, -a2 / determinant, a0 / determinant]);
|
|
}
|
|
|
|
/**
|
|
* Transforms the vector with a matrix
|
|
* @param { x, y } vector to transform
|
|
* @param matrix matrix to transform with
|
|
* @returns the transformed vector
|
|
*/
|
|
export function transformWithMatrix({ x, y }: Point, matrix: Float32Array): Point {
|
|
return {
|
|
x: matrix[0] * x + matrix[2] * y,
|
|
y: matrix[1] * x + matrix[3] * y
|
|
};
|
|
}
|
|
|
|
function _computeHistogram(imageWrapper: ImageWrapper, bitsPerPixel: number): Int32Array {
|
|
if (!bitsPerPixel) {
|
|
bitsPerPixel = 8;
|
|
}
|
|
|
|
const imageData = imageWrapper.data;
|
|
const bitShift = 8 - bitsPerPixel;
|
|
const bucketCount = 1 << bitsPerPixel;
|
|
const histogram = new Int32Array(bucketCount);
|
|
|
|
for (let i = imageData.length; i--;) {
|
|
histogram[imageData[i] >> bitShift]++;
|
|
}
|
|
|
|
return histogram;
|
|
}
|
|
|
|
function _determineOtsuThreshold(imageWrapper: ImageWrapper, bitsPerPixel?: number): number {
|
|
if (!bitsPerPixel) {
|
|
bitsPerPixel = 8;
|
|
}
|
|
|
|
const bitShift = 8 - bitsPerPixel;
|
|
const hist = _computeHistogram(imageWrapper, bitsPerPixel);
|
|
const vet = [0];
|
|
const max = (1 << bitsPerPixel) - 1;
|
|
|
|
function px(init: number, end: number): number {
|
|
let sum = 0;
|
|
|
|
for (let i = init; i <= end; i++) {
|
|
sum += hist[i];
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
function mx(init: number, end: number): number {
|
|
let sum = 0;
|
|
|
|
for (let i = init; i <= end; i++) {
|
|
sum += i * hist[i];
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
for (let k = 1; k < max; k++) {
|
|
const p1 = px(0, k);
|
|
const p2 = px(k + 1, max);
|
|
const p12 = p1 * p2 || 1;
|
|
const m1 = mx(0, k) * p2;
|
|
const m2 = mx(k + 1, max) * p1;
|
|
const m12 = m1 - m2;
|
|
vet[k] = m12 * m12 / p12;
|
|
}
|
|
|
|
// index of max element
|
|
const threshold = vet.reduce((maxIndex, item, index, array) => item > array[maxIndex] ? index : maxIndex, 0);
|
|
|
|
return threshold << bitShift;
|
|
}
|
|
|
|
export function otsuThreshold(imageWrapper: ImageWrapper, targetWrapper: ImageWrapper): number {
|
|
const threshold = _determineOtsuThreshold(imageWrapper);
|
|
const targetData = targetWrapper.data;
|
|
|
|
imageWrapper.data.forEach((value, index) => {
|
|
targetData[index] = value < threshold ? 1 : 0;
|
|
});
|
|
|
|
return threshold;
|
|
}
|
|
|
|
/**
|
|
* @param imageWrapper input image to be sampled
|
|
* @param outImageWrapper {ImageWrapper} to be stored in
|
|
*/
|
|
export function halfSample(imageWrapper: ImageWrapper, outImageWrapper: ImageWrapper): void {
|
|
const image = imageWrapper.data;
|
|
const width = imageWrapper.size.x;
|
|
const outImage = outImageWrapper.data;
|
|
const endIndex = image.length;
|
|
const outWidth = width >> 1;
|
|
let topRowIndex = 0;
|
|
let bottomRowIndex = width;
|
|
let outImgIndex = 0;
|
|
|
|
while (bottomRowIndex < endIndex) {
|
|
for (let i = 0; i < outWidth; i++) {
|
|
outImage[outImgIndex] =
|
|
(image[topRowIndex] + image[topRowIndex + 1] + image[bottomRowIndex] + image[bottomRowIndex + 1]) >> 2;
|
|
outImgIndex++;
|
|
topRowIndex += 2;
|
|
bottomRowIndex += 2;
|
|
}
|
|
topRowIndex += width;
|
|
bottomRowIndex += width;
|
|
}
|
|
}
|