image-js 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-js.esm.js +1134 -6568
- package/dist/image-js.esm.js.map +1 -1
- package/dist/image-js.esm.min.js +2 -2
- package/dist/image-js.esm.min.js.map +1 -1
- package/dist/image-js.umd.js +1135 -6567
- package/dist/image-js.umd.js.map +1 -1
- package/dist/image-js.umd.min.js +2 -2
- package/dist/image-js.umd.min.js.map +1 -1
- package/dist-types/image-js.d.ts +77 -13
- package/lib/Image.d.ts +18 -2
- package/lib/Image.d.ts.map +1 -1
- package/lib/Image.js +38 -1
- package/lib/Image.js.map +1 -1
- package/lib/featureMatching/index.d.ts +2 -0
- package/lib/featureMatching/index.d.ts.map +1 -1
- package/lib/featureMatching/index.js +2 -0
- package/lib/featureMatching/index.js.map +1 -1
- package/lib/featureMatching/keypoints/getEigenvaluesForScore.d.ts +11 -0
- package/lib/featureMatching/keypoints/getEigenvaluesForScore.d.ts.map +1 -0
- package/lib/featureMatching/keypoints/getEigenvaluesForScore.js +43 -0
- package/lib/featureMatching/keypoints/getEigenvaluesForScore.js.map +1 -0
- package/lib/featureMatching/keypoints/getFastKeypoints.d.ts +3 -2
- package/lib/featureMatching/keypoints/getFastKeypoints.d.ts.map +1 -1
- package/lib/featureMatching/keypoints/getFastKeypoints.js +14 -7
- package/lib/featureMatching/keypoints/getFastKeypoints.js.map +1 -1
- package/lib/featureMatching/keypoints/getHarrisScore.d.ts +2 -2
- package/lib/featureMatching/keypoints/getHarrisScore.d.ts.map +1 -1
- package/lib/featureMatching/keypoints/getHarrisScore.js +5 -37
- package/lib/featureMatching/keypoints/getHarrisScore.js.map +1 -1
- package/lib/featureMatching/keypoints/getOrientedFastKeypoints.d.ts +1 -1
- package/lib/featureMatching/keypoints/getOrientedFastKeypoints.d.ts.map +1 -1
- package/lib/featureMatching/keypoints/getOrientedFastKeypoints.js.map +1 -1
- package/lib/featureMatching/keypoints/getShiTomasiScore.d.ts +24 -0
- package/lib/featureMatching/keypoints/getShiTomasiScore.d.ts.map +1 -0
- package/lib/featureMatching/keypoints/getShiTomasiScore.js +18 -0
- package/lib/featureMatching/keypoints/getShiTomasiScore.js.map +1 -0
- package/lib/featureMatching/visualize/Montage.d.ts.map +1 -1
- package/lib/featureMatching/visualize/Montage.js.map +1 -1
- package/lib/filters/convolution.d.ts +1 -1
- package/lib/filters/convolution.d.ts.map +1 -1
- package/lib/filters/convolution.js +6 -1
- package/lib/filters/convolution.js.map +1 -1
- package/lib/filters/derivativeFilter.d.ts.map +1 -1
- package/lib/filters/derivativeFilter.js.map +1 -1
- package/lib/filters/medianFilter.d.ts +5 -5
- package/lib/filters/medianFilter.d.ts.map +1 -1
- package/lib/filters/medianFilter.js +3 -2
- package/lib/filters/medianFilter.js.map +1 -1
- package/lib/geometry/resize.d.ts +1 -1
- package/lib/geometry/resize.d.ts.map +1 -1
- package/lib/geometry/transform.d.ts +1 -1
- package/lib/geometry/transform.d.ts.map +1 -1
- package/lib/geometry/transform.js +6 -1
- package/lib/geometry/transform.js.map +1 -1
- package/lib/load/decodePng.d.ts.map +1 -1
- package/lib/load/decodePng.js +16 -0
- package/lib/load/decodePng.js.map +1 -1
- package/lib/load/decodeTiff.d.ts.map +1 -1
- package/lib/load/decodeTiff.js +35 -0
- package/lib/load/decodeTiff.js.map +1 -1
- package/lib/load/load.types.d.ts +6 -0
- package/lib/load/load.types.d.ts.map +1 -1
- package/lib/operations/extendBorders.d.ts +1 -1
- package/lib/operations/extendBorders.d.ts.map +1 -1
- package/lib/operations/extendBorders.js +6 -1
- package/lib/operations/extendBorders.js.map +1 -1
- package/lib/operations/grey.d.ts.map +1 -1
- package/lib/operations/grey.js.map +1 -1
- package/lib/operations/threshold.d.ts.map +1 -1
- package/lib/operations/threshold.js.map +1 -1
- package/lib/roi/colorRois.d.ts.map +1 -1
- package/lib/roi/colorRois.js.map +1 -1
- package/lib/roi/getRois.d.ts.map +1 -1
- package/lib/roi/getRois.js.map +1 -1
- package/lib/save/encode.d.ts.map +1 -1
- package/lib/save/encode.js.map +1 -1
- package/lib/utils/constants/colorModels.d.ts.map +1 -1
- package/lib/utils/constants/colorModels.js.map +1 -1
- package/lib/utils/cross_platform.d.ts +1 -1
- package/lib/utils/interpolateBorder.d.ts +1 -1
- package/lib/utils/interpolateBorder.d.ts.map +1 -1
- package/lib/utils/interpolateBorder.js +4 -1
- package/lib/utils/interpolateBorder.js.map +1 -1
- package/lib/utils/interpolatePixel.d.ts +1 -1
- package/lib/utils/interpolatePixel.d.ts.map +1 -1
- package/lib/utils/interpolatePixel.js.map +1 -1
- package/package.json +30 -29
- package/src/Image.ts +46 -6
- package/src/featureMatching/index.ts +2 -0
- package/src/featureMatching/keypoints/getEigenvaluesForScore.ts +56 -0
- package/src/featureMatching/keypoints/getFastKeypoints.ts +22 -15
- package/src/featureMatching/keypoints/getHarrisScore.ts +6 -45
- package/src/featureMatching/keypoints/getOrientedFastKeypoints.ts +2 -3
- package/src/featureMatching/keypoints/getShiTomasiScore.ts +36 -0
- package/src/featureMatching/visualize/Montage.ts +0 -1
- package/src/filters/convolution.ts +8 -2
- package/src/filters/derivativeFilter.ts +0 -1
- package/src/filters/medianFilter.ts +12 -10
- package/src/geometry/resize.ts +1 -1
- package/src/geometry/transform.ts +8 -2
- package/src/load/decodePng.ts +18 -0
- package/src/load/decodeTiff.ts +38 -1
- package/src/load/load.types.ts +8 -0
- package/src/operations/extendBorders.ts +7 -2
- package/src/operations/grey.ts +0 -1
- package/src/operations/threshold.ts +0 -1
- package/src/roi/colorRois.ts +0 -1
- package/src/roi/getRois.ts +0 -1
- package/src/save/encode.ts +0 -1
- package/src/utils/constants/colorModels.ts +0 -1
- package/src/utils/interpolateBorder.ts +6 -4
- package/src/utils/interpolatePixel.ts +1 -2
|
@@ -13,6 +13,8 @@ import type { GetHarrisScoreOptions } from '../featureMatching.types.js';
|
|
|
13
13
|
|
|
14
14
|
import { getFastScore } from './getFastScore.js';
|
|
15
15
|
import { getHarrisScore } from './getHarrisScore.js';
|
|
16
|
+
import type { GetShiTomasiScoreOptions } from './getShiTomasiScore.ts';
|
|
17
|
+
import { getShiTomasiScore } from './getShiTomasiScore.ts';
|
|
16
18
|
import type { IsFastKeypointOptions } from './isFastKeypoint.js';
|
|
17
19
|
import { isFastKeypoint } from './isFastKeypoint.js';
|
|
18
20
|
|
|
@@ -38,11 +40,11 @@ export interface GetFastKeypointsOptions extends IsFastKeypointOptions {
|
|
|
38
40
|
* Algorithm to use to compute corners score.
|
|
39
41
|
* @default `'FAST'`
|
|
40
42
|
*/
|
|
41
|
-
scoreAlgorithm?: 'HARRIS' | 'FAST';
|
|
43
|
+
scoreAlgorithm?: 'HARRIS' | 'FAST' | 'TOMASI';
|
|
42
44
|
/**
|
|
43
45
|
* Options for the Harris score computation.
|
|
44
46
|
*/
|
|
45
|
-
|
|
47
|
+
scoreOptions?: GetHarrisScoreOptions | GetShiTomasiScoreOptions;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
export interface FastKeypoint {
|
|
@@ -69,11 +71,7 @@ export function getFastKeypoints(
|
|
|
69
71
|
image: Image,
|
|
70
72
|
options: GetFastKeypointsOptions = {},
|
|
71
73
|
): FastKeypoint[] {
|
|
72
|
-
const {
|
|
73
|
-
fastRadius = 3,
|
|
74
|
-
scoreAlgorithm = 'FAST',
|
|
75
|
-
harrisScoreOptions,
|
|
76
|
-
} = options;
|
|
74
|
+
const { fastRadius = 3, scoreAlgorithm = 'FAST', scoreOptions } = options;
|
|
77
75
|
|
|
78
76
|
const circlePoints = getCirclePoints(fastRadius);
|
|
79
77
|
const compassPoints = getCompassPoints(fastRadius);
|
|
@@ -90,15 +88,24 @@ export function getFastKeypoints(
|
|
|
90
88
|
alpha: false,
|
|
91
89
|
});
|
|
92
90
|
|
|
91
|
+
function harrisScore(image: Image, corner: Point) {
|
|
92
|
+
return getHarrisScore(image, corner, scoreOptions as GetHarrisScoreOptions);
|
|
93
|
+
}
|
|
94
|
+
function fastScore(image: Image, corner: Point) {
|
|
95
|
+
return getFastScore(image, corner, threshold, circlePoints);
|
|
96
|
+
}
|
|
97
|
+
function tomasiScore(image: Image, corner: Point) {
|
|
98
|
+
return getShiTomasiScore(
|
|
99
|
+
image,
|
|
100
|
+
corner,
|
|
101
|
+
scoreOptions as GetShiTomasiScoreOptions,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
const getScore = match(scoreAlgorithm)
|
|
94
|
-
.with('HARRIS', () =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
})
|
|
98
|
-
.with('FAST', () => {
|
|
99
|
-
return (image: Image, corner: Point) =>
|
|
100
|
-
getFastScore(image, corner, threshold, circlePoints);
|
|
101
|
-
})
|
|
106
|
+
.with('HARRIS', () => harrisScore)
|
|
107
|
+
.with('FAST', () => fastScore)
|
|
108
|
+
.with('TOMASI', () => tomasiScore)
|
|
102
109
|
.exhaustive();
|
|
103
110
|
|
|
104
111
|
const allKeypoints: FastKeypoint[] = [];
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { EigenvalueDecomposition, Matrix, WrapperMatrix1D } from 'ml-matrix';
|
|
2
|
-
|
|
3
1
|
import type { Image } from '../../Image.js';
|
|
4
2
|
import type { Point } from '../../geometry/index.js';
|
|
5
|
-
import { SOBEL_X, SOBEL_Y } from '../../utils/constants/kernels.js';
|
|
6
3
|
import type { GetHarrisScoreOptions } from '../featureMatching.types.js';
|
|
7
4
|
|
|
5
|
+
import { getEigenvaluesForScore } from './getEigenvaluesForScore.js';
|
|
6
|
+
|
|
8
7
|
/**
|
|
9
8
|
* Get the Harris score of a corner. The idea behind the algorithm is that a
|
|
10
|
-
* slight shift of a window around a corner along x and y
|
|
9
|
+
* slight shift of a window around a corner along x and y should result in
|
|
11
10
|
* a very different image.
|
|
12
11
|
*
|
|
13
12
|
* We distinguish 3 cases:
|
|
14
13
|
* - the score is highly negative: you have an edge
|
|
15
|
-
* - the
|
|
14
|
+
* - the absolute value of the score is small: the region is flat
|
|
16
15
|
* - the score is highly positive: you have a corner.
|
|
17
16
|
* @see {@link https://en.wikipedia.org/wiki/Harris_corner_detector}
|
|
18
17
|
* @param image - Image to which the corner belongs. It must be a greyscale image with only one channel.
|
|
@@ -25,46 +24,8 @@ export function getHarrisScore(
|
|
|
25
24
|
origin: Point,
|
|
26
25
|
options: GetHarrisScoreOptions = {},
|
|
27
26
|
): number {
|
|
28
|
-
const {
|
|
29
|
-
|
|
30
|
-
if (!(windowSize % 2)) {
|
|
31
|
-
throw new TypeError('windowSize must be an odd integer');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const cropOrigin = {
|
|
35
|
-
row: origin.row - (windowSize - 1) / 2,
|
|
36
|
-
column: origin.column - (windowSize - 1) / 2,
|
|
37
|
-
};
|
|
38
|
-
const window = image.crop({
|
|
39
|
-
origin: cropOrigin,
|
|
40
|
-
width: windowSize,
|
|
41
|
-
height: windowSize,
|
|
42
|
-
});
|
|
43
|
-
const xDerivative = window.gradientFilter({ kernelX: SOBEL_X });
|
|
44
|
-
const yDerivative = window.gradientFilter({ kernelY: SOBEL_Y });
|
|
45
|
-
|
|
46
|
-
const xMatrix = new WrapperMatrix1D(xDerivative.getRawImage().data, {
|
|
47
|
-
rows: xDerivative.height,
|
|
48
|
-
});
|
|
49
|
-
const yMatrix = new WrapperMatrix1D(yDerivative.getRawImage().data, {
|
|
50
|
-
rows: yDerivative.height,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const xx = xMatrix.mmul(xMatrix);
|
|
54
|
-
const xy = yMatrix.mmul(xMatrix);
|
|
55
|
-
const yy = yMatrix.mmul(yMatrix);
|
|
56
|
-
|
|
57
|
-
const xxSum = xx.sum();
|
|
58
|
-
const xySum = xy.sum();
|
|
59
|
-
const yySum = yy.sum();
|
|
60
|
-
|
|
61
|
-
const structureTensor = new Matrix([
|
|
62
|
-
[xxSum, xySum],
|
|
63
|
-
[xySum, yySum],
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
const eigenValues = new EigenvalueDecomposition(structureTensor)
|
|
67
|
-
.realEigenvalues;
|
|
27
|
+
const { harrisConstant = 0.04, windowSize } = options;
|
|
28
|
+
const eigenValues = getEigenvaluesForScore(image, origin, windowSize);
|
|
68
29
|
|
|
69
30
|
return (
|
|
70
31
|
eigenValues[0] * eigenValues[1] -
|
|
@@ -11,10 +11,9 @@ import type {
|
|
|
11
11
|
import { getFastKeypoints } from './getFastKeypoints.js';
|
|
12
12
|
import { getPatchIntensityCentroid } from './getPatchIntensityCentroid.js';
|
|
13
13
|
|
|
14
|
-
export interface GetOrientedFastKeypointsOptions
|
|
15
|
-
extends GetFastKeypointsOptions {
|
|
14
|
+
export interface GetOrientedFastKeypointsOptions extends GetFastKeypointsOptions {
|
|
16
15
|
/**
|
|
17
|
-
* Diameter of the circle used for
|
|
16
|
+
* Diameter of the circle used for computation of the intensity centroid.
|
|
18
17
|
* @default `7`
|
|
19
18
|
*/
|
|
20
19
|
centroidPatchDiameter?: number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Image } from '../../Image.ts';
|
|
2
|
+
import type { Point } from '../../index_full.ts';
|
|
3
|
+
|
|
4
|
+
import { getEigenvaluesForScore } from './getEigenvaluesForScore.js';
|
|
5
|
+
|
|
6
|
+
export interface GetShiTomasiScoreOptions {
|
|
7
|
+
qualityLevel?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Size of the window to compute the Harris score.
|
|
10
|
+
* Should be an odd number so that the window can be centered on the corner.
|
|
11
|
+
* @default `7`
|
|
12
|
+
*/
|
|
13
|
+
windowSize?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the Shi-Tomasi score of a corner. The idea is similar to Harris score, but it removes constant to calculate the score and just takes the minimum from two eigenvalues.
|
|
18
|
+
* We distinguish 3 cases:
|
|
19
|
+
* - the score is highly negative: you have an edge
|
|
20
|
+
* - the absolute value of the score is small: the region is flat
|
|
21
|
+
* - the score is highly positive: you have a corner.
|
|
22
|
+
* @param image - Image to which the corner belongs. It must be a greyscale image with only one channel.
|
|
23
|
+
* @param origin - Center of the window, where the corner should be.
|
|
24
|
+
* @param options - Get Shi-Tomasi score options.
|
|
25
|
+
* @returns The Shi-Tomasi score.
|
|
26
|
+
*/
|
|
27
|
+
export function getShiTomasiScore(
|
|
28
|
+
image: Image,
|
|
29
|
+
origin: Point,
|
|
30
|
+
options: GetShiTomasiScoreOptions = {},
|
|
31
|
+
): number {
|
|
32
|
+
const { windowSize = 7 } = options;
|
|
33
|
+
|
|
34
|
+
const eigenValues = getEigenvaluesForScore(image, origin, windowSize);
|
|
35
|
+
return Math.min(eigenValues[0], eigenValues[1]);
|
|
36
|
+
}
|
|
@@ -13,7 +13,6 @@ export const MontageDisposition = {
|
|
|
13
13
|
HORIZONTAL: 'horizontal',
|
|
14
14
|
VERTICAL: 'vertical',
|
|
15
15
|
} as const;
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
17
16
|
export type MontageDisposition =
|
|
18
17
|
(typeof MontageDisposition)[keyof typeof MontageDisposition];
|
|
19
18
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { Image } from '../Image.js';
|
|
7
7
|
import { extendBorders } from '../operations/extendBorders.js';
|
|
8
8
|
import { getClamp } from '../utils/clamp.js';
|
|
9
|
+
import { getDefaultColor } from '../utils/getDefaultColor.ts';
|
|
9
10
|
import { getIndex } from '../utils/getIndex.js';
|
|
10
11
|
import { getOutputImage } from '../utils/getOutputImage.js';
|
|
11
12
|
import type { BorderType } from '../utils/interpolateBorder.js';
|
|
@@ -15,6 +16,7 @@ import type {
|
|
|
15
16
|
BorderInterpolationFunction,
|
|
16
17
|
ClampFunction,
|
|
17
18
|
} from '../utils/utils.types.js';
|
|
19
|
+
import { validateColor } from '../utils/validators/validators.ts';
|
|
18
20
|
|
|
19
21
|
export interface ConvolutionOptions {
|
|
20
22
|
/**
|
|
@@ -26,7 +28,7 @@ export interface ConvolutionOptions {
|
|
|
26
28
|
* Value of the border if BorderType is 'constant'.
|
|
27
29
|
* @default `0`
|
|
28
30
|
*/
|
|
29
|
-
borderValue?: number;
|
|
31
|
+
borderValue?: number | number[];
|
|
30
32
|
/**
|
|
31
33
|
* Whether the kernel should be normalized.
|
|
32
34
|
* @default `false`
|
|
@@ -83,7 +85,11 @@ export function rawDirectConvolution(
|
|
|
83
85
|
kernel: number[][],
|
|
84
86
|
options: ConvolutionOptions = {},
|
|
85
87
|
): Float64Array {
|
|
86
|
-
const { borderType = 'reflect101', borderValue =
|
|
88
|
+
const { borderType = 'reflect101', borderValue = getDefaultColor(image) } =
|
|
89
|
+
options;
|
|
90
|
+
if (Array.isArray(borderValue)) {
|
|
91
|
+
validateColor(borderValue, image);
|
|
92
|
+
}
|
|
87
93
|
const interpolateBorder = getBorderInterpolation(borderType, borderValue);
|
|
88
94
|
|
|
89
95
|
const result = new Float64Array(image.size * image.channels);
|
|
@@ -19,7 +19,6 @@ export const DerivativeFilter = {
|
|
|
19
19
|
// for 2x2 matrices, the current pixel corresponds to the top-left
|
|
20
20
|
// ROBERTS = 'roberts',
|
|
21
21
|
} as const;
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
23
22
|
export type DerivativeFilter =
|
|
24
23
|
(typeof DerivativeFilter)[keyof typeof DerivativeFilter];
|
|
25
24
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { xMedian } from 'ml-spectra-processing';
|
|
2
2
|
|
|
3
3
|
import { Image } from '../Image.js';
|
|
4
|
+
import { getDefaultColor } from '../utils/getDefaultColor.ts';
|
|
4
5
|
import type { BorderType } from '../utils/interpolateBorder.js';
|
|
5
6
|
import { getBorderInterpolation } from '../utils/interpolateBorder.js';
|
|
6
7
|
import checkProcessable from '../utils/validators/checkProcessable.js';
|
|
@@ -10,16 +11,16 @@ export interface MedianFilterOptions {
|
|
|
10
11
|
* Type of border algorithm to interpolate from.
|
|
11
12
|
* @default `'reflect101'`
|
|
12
13
|
*/
|
|
13
|
-
borderType
|
|
14
|
+
borderType?: BorderType;
|
|
14
15
|
/**
|
|
15
16
|
* Value of border.
|
|
16
17
|
*/
|
|
17
|
-
borderValue?: number;
|
|
18
|
+
borderValue?: number | number[];
|
|
18
19
|
/**
|
|
19
20
|
* The radius of the cell to extract median value from. Must be odd.
|
|
20
|
-
* @default `
|
|
21
|
+
* @default `3`
|
|
21
22
|
*/
|
|
22
|
-
cellSize
|
|
23
|
+
cellSize?: number;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Calculate a new image that replaces all pixel values by the median of neighbouring pixels.
|
|
@@ -27,8 +28,12 @@ export interface MedianFilterOptions {
|
|
|
27
28
|
* @param options - MedianFilterOptions
|
|
28
29
|
* @returns Image after median filter.
|
|
29
30
|
*/
|
|
30
|
-
export function medianFilter(image: Image, options: MedianFilterOptions) {
|
|
31
|
-
const {
|
|
31
|
+
export function medianFilter(image: Image, options: MedianFilterOptions = {}) {
|
|
32
|
+
const {
|
|
33
|
+
cellSize = 3,
|
|
34
|
+
borderType = 'reflect101',
|
|
35
|
+
borderValue = getDefaultColor(image),
|
|
36
|
+
} = options;
|
|
32
37
|
|
|
33
38
|
checkProcessable(image, {
|
|
34
39
|
bitDepth: [8, 16],
|
|
@@ -46,10 +51,7 @@ export function medianFilter(image: Image, options: MedianFilterOptions) {
|
|
|
46
51
|
);
|
|
47
52
|
}
|
|
48
53
|
|
|
49
|
-
const interpolateBorder = getBorderInterpolation(
|
|
50
|
-
borderType,
|
|
51
|
-
borderValue as number,
|
|
52
|
-
);
|
|
54
|
+
const interpolateBorder = getBorderInterpolation(borderType, borderValue);
|
|
53
55
|
|
|
54
56
|
const newImage = Image.createFrom(image);
|
|
55
57
|
const size = cellSize ** 2;
|
package/src/geometry/resize.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { Matrix, inverse } from 'ml-matrix';
|
|
|
2
2
|
|
|
3
3
|
import { Image } from '../Image.js';
|
|
4
4
|
import { getClamp } from '../utils/clamp.js';
|
|
5
|
+
import { getDefaultColor } from '../utils/getDefaultColor.ts';
|
|
5
6
|
import type { BorderType } from '../utils/interpolateBorder.js';
|
|
6
7
|
import { getBorderInterpolation } from '../utils/interpolateBorder.js';
|
|
7
8
|
import type { InterpolationType } from '../utils/interpolatePixel.js';
|
|
8
9
|
import { getInterpolationFunction } from '../utils/interpolatePixel.js';
|
|
10
|
+
import { validateColor } from '../utils/validators/validators.ts';
|
|
9
11
|
|
|
10
12
|
export interface TransformOptions {
|
|
11
13
|
/**
|
|
@@ -31,7 +33,7 @@ export interface TransformOptions {
|
|
|
31
33
|
* Value of the border if BorderType is 'constant'.
|
|
32
34
|
* @default `0`
|
|
33
35
|
*/
|
|
34
|
-
borderValue?: number;
|
|
36
|
+
borderValue?: number | number[];
|
|
35
37
|
/**
|
|
36
38
|
* Whether the transform matrix should be inverted.
|
|
37
39
|
*/
|
|
@@ -56,13 +58,17 @@ export function transform(
|
|
|
56
58
|
options: TransformOptions = {},
|
|
57
59
|
): Image {
|
|
58
60
|
const {
|
|
61
|
+
borderValue = getDefaultColor(image),
|
|
59
62
|
borderType = 'constant',
|
|
60
|
-
borderValue = 0,
|
|
61
63
|
interpolationType = 'bilinear',
|
|
62
64
|
fullImage,
|
|
63
65
|
} = options;
|
|
64
66
|
let { width = image.width, height = image.height } = options;
|
|
65
67
|
|
|
68
|
+
if (Array.isArray(borderValue)) {
|
|
69
|
+
validateColor(borderValue, image);
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
if (!isValidMatrix(transformMatrix)) {
|
|
67
73
|
throw new TypeError(
|
|
68
74
|
`transformation matrix must be 2x3 or 3x3. Received ${transformMatrix.length}x${transformMatrix[1].length}`,
|
package/src/load/decodePng.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { Image } from '../Image.js';
|
|
|
6
6
|
import type { ImageColorModel } from '../utils/constants/colorModels.js';
|
|
7
7
|
import { assert } from '../utils/validators/assert.js';
|
|
8
8
|
|
|
9
|
+
import type { Resolution } from './load.types.ts';
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* Decode a PNG. See the fast-png npm module.
|
|
11
13
|
* @param buffer - The data to decode.
|
|
@@ -41,11 +43,27 @@ export function decodePng(buffer: Uint8Array): Image {
|
|
|
41
43
|
default:
|
|
42
44
|
throw new RangeError(`invalid number of channels: ${png.channels}`);
|
|
43
45
|
}
|
|
46
|
+
let resolution: Resolution | undefined;
|
|
47
|
+
if (png.resolution) {
|
|
48
|
+
resolution =
|
|
49
|
+
png.resolution.unit === 1
|
|
50
|
+
? /*If the resolution unit is meters*/ {
|
|
51
|
+
x: png.resolution.x,
|
|
52
|
+
y: png.resolution.y,
|
|
53
|
+
unit: 'meter' as const,
|
|
54
|
+
}
|
|
55
|
+
: /*If resolution unit is unknown */ {
|
|
56
|
+
x: png.resolution.x,
|
|
57
|
+
y: png.resolution.y,
|
|
58
|
+
unit: 'unknown' as const,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
44
61
|
|
|
45
62
|
return new Image(png.width, png.height, {
|
|
46
63
|
colorModel,
|
|
47
64
|
bitDepth,
|
|
48
65
|
data: png.data,
|
|
66
|
+
resolution,
|
|
49
67
|
});
|
|
50
68
|
}
|
|
51
69
|
|
package/src/load/decodeTiff.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { BitDepth } from '../Image.js';
|
|
|
4
4
|
import { Image } from '../Image.js';
|
|
5
5
|
|
|
6
6
|
import { getMetadata } from './getMetadata.js';
|
|
7
|
+
import type { Resolution } from './load.types.ts';
|
|
7
8
|
|
|
8
9
|
type TiffIfd = ReturnType<typeof decode>[number];
|
|
9
10
|
|
|
@@ -26,7 +27,7 @@ export function getImageFromIFD(ifd: TiffIfd): Image {
|
|
|
26
27
|
if (ifd.data instanceof Float32Array || ifd.data instanceof Float64Array) {
|
|
27
28
|
throw new Error('Float TIFF data is not supported.');
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
const resolution = getTiffResolution(ifd);
|
|
30
31
|
if (ifd.type === 3) {
|
|
31
32
|
const hasAlpha = ifd.samplesPerPixel === 2;
|
|
32
33
|
const pixelLength = hasAlpha ? 4 : 3;
|
|
@@ -66,6 +67,7 @@ export function getImageFromIFD(ifd: TiffIfd): Image {
|
|
|
66
67
|
colorModel: hasAlpha ? 'RGBA' : 'RGB',
|
|
67
68
|
bitDepth: 16,
|
|
68
69
|
meta: getMetadata(ifd),
|
|
70
|
+
resolution,
|
|
69
71
|
});
|
|
70
72
|
} else if (ifd.type === 1 || ifd.type === 0) {
|
|
71
73
|
if (ifd.bitsPerSample !== 1) {
|
|
@@ -74,6 +76,7 @@ export function getImageFromIFD(ifd: TiffIfd): Image {
|
|
|
74
76
|
bitDepth: ifd.bitsPerSample as BitDepth,
|
|
75
77
|
colorModel: ifd.alpha ? 'GREYA' : 'GREY',
|
|
76
78
|
meta: getMetadata(ifd),
|
|
79
|
+
resolution,
|
|
77
80
|
});
|
|
78
81
|
} else {
|
|
79
82
|
return new Image(ifd.width, ifd.height, {
|
|
@@ -81,6 +84,7 @@ export function getImageFromIFD(ifd: TiffIfd): Image {
|
|
|
81
84
|
bitDepth: 8 as BitDepth,
|
|
82
85
|
colorModel: 'GREY',
|
|
83
86
|
meta: getMetadata(ifd),
|
|
87
|
+
resolution,
|
|
84
88
|
});
|
|
85
89
|
}
|
|
86
90
|
} else {
|
|
@@ -89,6 +93,39 @@ export function getImageFromIFD(ifd: TiffIfd): Image {
|
|
|
89
93
|
bitDepth: ifd.bitsPerSample as BitDepth,
|
|
90
94
|
colorModel: ifd.alpha ? 'RGBA' : 'RGB',
|
|
91
95
|
meta: getMetadata(ifd),
|
|
96
|
+
resolution,
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Gets image resolution from its metadata and converts it into Pixels per meter, when it's possible. Also keeps original resolution values and units.
|
|
103
|
+
* @param ifd - Tiff metadata.
|
|
104
|
+
* @returns Resolution object.
|
|
105
|
+
*/
|
|
106
|
+
function getTiffResolution(ifd: TiffIfd): Resolution | undefined {
|
|
107
|
+
if (!ifd.xResolution || !ifd.yResolution) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
switch (ifd.resolutionUnit) {
|
|
112
|
+
case 1:
|
|
113
|
+
return {
|
|
114
|
+
x: ifd.xResolution,
|
|
115
|
+
y: ifd.yResolution,
|
|
116
|
+
unit: 'unknown',
|
|
117
|
+
};
|
|
118
|
+
case 3:
|
|
119
|
+
return {
|
|
120
|
+
x: ifd.xResolution,
|
|
121
|
+
y: ifd.yResolution,
|
|
122
|
+
unit: 'centimeter',
|
|
123
|
+
};
|
|
124
|
+
default:
|
|
125
|
+
return {
|
|
126
|
+
x: ifd.xResolution,
|
|
127
|
+
y: ifd.yResolution,
|
|
128
|
+
unit: 'inch',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
package/src/load/load.types.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Image } from '../Image.js';
|
|
2
|
+
import { getDefaultColor } from '../utils/getDefaultColor.ts';
|
|
2
3
|
import type { BorderType } from '../utils/interpolateBorder.js';
|
|
3
4
|
import { getBorderInterpolation } from '../utils/interpolateBorder.js';
|
|
5
|
+
import { validateColor } from '../utils/validators/validators.ts';
|
|
4
6
|
|
|
5
7
|
export interface ExtendBordersOptions {
|
|
6
8
|
/**
|
|
@@ -20,7 +22,7 @@ export interface ExtendBordersOptions {
|
|
|
20
22
|
* Value of the border if BorderType is 'constant'.
|
|
21
23
|
* @default `0`
|
|
22
24
|
*/
|
|
23
|
-
borderValue?: number;
|
|
25
|
+
borderValue?: number | number[];
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
/**
|
|
@@ -37,9 +39,12 @@ export function extendBorders(
|
|
|
37
39
|
horizontal,
|
|
38
40
|
vertical,
|
|
39
41
|
borderType = 'reflect101',
|
|
40
|
-
borderValue =
|
|
42
|
+
borderValue = getDefaultColor(image),
|
|
41
43
|
} = options;
|
|
42
44
|
|
|
45
|
+
if (Array.isArray(borderValue)) {
|
|
46
|
+
validateColor(borderValue, image);
|
|
47
|
+
}
|
|
43
48
|
const interpolateBorder = getBorderInterpolation(borderType, borderValue);
|
|
44
49
|
|
|
45
50
|
const newImage = Image.createFrom(image, {
|
package/src/operations/grey.ts
CHANGED
|
@@ -25,7 +25,6 @@ export const GreyAlgorithm = {
|
|
|
25
25
|
SATURATION: 'saturation',
|
|
26
26
|
LIGHTNESS: 'lightness',
|
|
27
27
|
} as const satisfies Record<string, keyof typeof greyAlgorithms>;
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
29
28
|
export type GreyAlgorithm = (typeof GreyAlgorithm)[keyof typeof GreyAlgorithm];
|
|
30
29
|
|
|
31
30
|
{
|
package/src/roi/colorRois.ts
CHANGED
|
@@ -18,7 +18,6 @@ export const RoisColorMode = {
|
|
|
18
18
|
*/
|
|
19
19
|
RAINBOW: 'rainbow',
|
|
20
20
|
} as const;
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
22
21
|
export type RoisColorMode = (typeof RoisColorMode)[keyof typeof RoisColorMode];
|
|
23
22
|
|
|
24
23
|
export interface ColorRoisOptions {
|
package/src/roi/getRois.ts
CHANGED
package/src/save/encode.ts
CHANGED
|
@@ -11,7 +11,6 @@ export const BorderType = {
|
|
|
11
11
|
WRAP: 'wrap',
|
|
12
12
|
REFLECT_101: 'reflect101',
|
|
13
13
|
} as const;
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
15
14
|
export type BorderType = (typeof BorderType)[keyof typeof BorderType];
|
|
16
15
|
|
|
17
16
|
/**
|
|
@@ -24,8 +23,11 @@ export type BorderType = (typeof BorderType)[keyof typeof BorderType];
|
|
|
24
23
|
*/
|
|
25
24
|
export function getBorderInterpolation(
|
|
26
25
|
type: BorderType,
|
|
27
|
-
value: number,
|
|
26
|
+
value: number | number[],
|
|
28
27
|
): BorderInterpolationFunction {
|
|
28
|
+
if (typeof value === 'number') {
|
|
29
|
+
value = new Array(4).fill(value);
|
|
30
|
+
}
|
|
29
31
|
return match(type)
|
|
30
32
|
.with('constant', () => getInterpolateConstant(value))
|
|
31
33
|
.with('replicate', () => interpolateReplicate)
|
|
@@ -41,7 +43,7 @@ function checkRange(point: number, length: number): void {
|
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
function getInterpolateConstant(value: number): BorderInterpolationFunction {
|
|
46
|
+
function getInterpolateConstant(value: number[]): BorderInterpolationFunction {
|
|
45
47
|
return function interpolateConstant(
|
|
46
48
|
column: number,
|
|
47
49
|
row: number,
|
|
@@ -51,7 +53,7 @@ function getInterpolateConstant(value: number): BorderInterpolationFunction {
|
|
|
51
53
|
const newColumn = interpolateConstantPoint(column, image.width);
|
|
52
54
|
const newRow = interpolateConstantPoint(row, image.height);
|
|
53
55
|
if (newColumn === -1 || newRow === -1) {
|
|
54
|
-
return value;
|
|
56
|
+
return value[channel];
|
|
55
57
|
}
|
|
56
58
|
return image.getValue(newColumn, newRow, channel);
|
|
57
59
|
};
|
|
@@ -13,7 +13,6 @@ export const InterpolationType = {
|
|
|
13
13
|
BILINEAR: 'bilinear',
|
|
14
14
|
BICUBIC: 'bicubic',
|
|
15
15
|
} as const;
|
|
16
|
-
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
|
17
16
|
export type InterpolationType =
|
|
18
17
|
(typeof InterpolationType)[keyof typeof InterpolationType];
|
|
19
18
|
|
|
@@ -22,7 +21,7 @@ type InterpolationFunction = (
|
|
|
22
21
|
column: number,
|
|
23
22
|
row: number,
|
|
24
23
|
channel: number,
|
|
25
|
-
|
|
24
|
+
interpolateBorder: BorderInterpolationFunction,
|
|
26
25
|
clamp: ClampFunction,
|
|
27
26
|
) => number;
|
|
28
27
|
|