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.
Files changed (112) hide show
  1. package/dist/image-js.esm.js +1134 -6568
  2. package/dist/image-js.esm.js.map +1 -1
  3. package/dist/image-js.esm.min.js +2 -2
  4. package/dist/image-js.esm.min.js.map +1 -1
  5. package/dist/image-js.umd.js +1135 -6567
  6. package/dist/image-js.umd.js.map +1 -1
  7. package/dist/image-js.umd.min.js +2 -2
  8. package/dist/image-js.umd.min.js.map +1 -1
  9. package/dist-types/image-js.d.ts +77 -13
  10. package/lib/Image.d.ts +18 -2
  11. package/lib/Image.d.ts.map +1 -1
  12. package/lib/Image.js +38 -1
  13. package/lib/Image.js.map +1 -1
  14. package/lib/featureMatching/index.d.ts +2 -0
  15. package/lib/featureMatching/index.d.ts.map +1 -1
  16. package/lib/featureMatching/index.js +2 -0
  17. package/lib/featureMatching/index.js.map +1 -1
  18. package/lib/featureMatching/keypoints/getEigenvaluesForScore.d.ts +11 -0
  19. package/lib/featureMatching/keypoints/getEigenvaluesForScore.d.ts.map +1 -0
  20. package/lib/featureMatching/keypoints/getEigenvaluesForScore.js +43 -0
  21. package/lib/featureMatching/keypoints/getEigenvaluesForScore.js.map +1 -0
  22. package/lib/featureMatching/keypoints/getFastKeypoints.d.ts +3 -2
  23. package/lib/featureMatching/keypoints/getFastKeypoints.d.ts.map +1 -1
  24. package/lib/featureMatching/keypoints/getFastKeypoints.js +14 -7
  25. package/lib/featureMatching/keypoints/getFastKeypoints.js.map +1 -1
  26. package/lib/featureMatching/keypoints/getHarrisScore.d.ts +2 -2
  27. package/lib/featureMatching/keypoints/getHarrisScore.d.ts.map +1 -1
  28. package/lib/featureMatching/keypoints/getHarrisScore.js +5 -37
  29. package/lib/featureMatching/keypoints/getHarrisScore.js.map +1 -1
  30. package/lib/featureMatching/keypoints/getOrientedFastKeypoints.d.ts +1 -1
  31. package/lib/featureMatching/keypoints/getOrientedFastKeypoints.d.ts.map +1 -1
  32. package/lib/featureMatching/keypoints/getOrientedFastKeypoints.js.map +1 -1
  33. package/lib/featureMatching/keypoints/getShiTomasiScore.d.ts +24 -0
  34. package/lib/featureMatching/keypoints/getShiTomasiScore.d.ts.map +1 -0
  35. package/lib/featureMatching/keypoints/getShiTomasiScore.js +18 -0
  36. package/lib/featureMatching/keypoints/getShiTomasiScore.js.map +1 -0
  37. package/lib/featureMatching/visualize/Montage.d.ts.map +1 -1
  38. package/lib/featureMatching/visualize/Montage.js.map +1 -1
  39. package/lib/filters/convolution.d.ts +1 -1
  40. package/lib/filters/convolution.d.ts.map +1 -1
  41. package/lib/filters/convolution.js +6 -1
  42. package/lib/filters/convolution.js.map +1 -1
  43. package/lib/filters/derivativeFilter.d.ts.map +1 -1
  44. package/lib/filters/derivativeFilter.js.map +1 -1
  45. package/lib/filters/medianFilter.d.ts +5 -5
  46. package/lib/filters/medianFilter.d.ts.map +1 -1
  47. package/lib/filters/medianFilter.js +3 -2
  48. package/lib/filters/medianFilter.js.map +1 -1
  49. package/lib/geometry/resize.d.ts +1 -1
  50. package/lib/geometry/resize.d.ts.map +1 -1
  51. package/lib/geometry/transform.d.ts +1 -1
  52. package/lib/geometry/transform.d.ts.map +1 -1
  53. package/lib/geometry/transform.js +6 -1
  54. package/lib/geometry/transform.js.map +1 -1
  55. package/lib/load/decodePng.d.ts.map +1 -1
  56. package/lib/load/decodePng.js +16 -0
  57. package/lib/load/decodePng.js.map +1 -1
  58. package/lib/load/decodeTiff.d.ts.map +1 -1
  59. package/lib/load/decodeTiff.js +35 -0
  60. package/lib/load/decodeTiff.js.map +1 -1
  61. package/lib/load/load.types.d.ts +6 -0
  62. package/lib/load/load.types.d.ts.map +1 -1
  63. package/lib/operations/extendBorders.d.ts +1 -1
  64. package/lib/operations/extendBorders.d.ts.map +1 -1
  65. package/lib/operations/extendBorders.js +6 -1
  66. package/lib/operations/extendBorders.js.map +1 -1
  67. package/lib/operations/grey.d.ts.map +1 -1
  68. package/lib/operations/grey.js.map +1 -1
  69. package/lib/operations/threshold.d.ts.map +1 -1
  70. package/lib/operations/threshold.js.map +1 -1
  71. package/lib/roi/colorRois.d.ts.map +1 -1
  72. package/lib/roi/colorRois.js.map +1 -1
  73. package/lib/roi/getRois.d.ts.map +1 -1
  74. package/lib/roi/getRois.js.map +1 -1
  75. package/lib/save/encode.d.ts.map +1 -1
  76. package/lib/save/encode.js.map +1 -1
  77. package/lib/utils/constants/colorModels.d.ts.map +1 -1
  78. package/lib/utils/constants/colorModels.js.map +1 -1
  79. package/lib/utils/cross_platform.d.ts +1 -1
  80. package/lib/utils/interpolateBorder.d.ts +1 -1
  81. package/lib/utils/interpolateBorder.d.ts.map +1 -1
  82. package/lib/utils/interpolateBorder.js +4 -1
  83. package/lib/utils/interpolateBorder.js.map +1 -1
  84. package/lib/utils/interpolatePixel.d.ts +1 -1
  85. package/lib/utils/interpolatePixel.d.ts.map +1 -1
  86. package/lib/utils/interpolatePixel.js.map +1 -1
  87. package/package.json +30 -29
  88. package/src/Image.ts +46 -6
  89. package/src/featureMatching/index.ts +2 -0
  90. package/src/featureMatching/keypoints/getEigenvaluesForScore.ts +56 -0
  91. package/src/featureMatching/keypoints/getFastKeypoints.ts +22 -15
  92. package/src/featureMatching/keypoints/getHarrisScore.ts +6 -45
  93. package/src/featureMatching/keypoints/getOrientedFastKeypoints.ts +2 -3
  94. package/src/featureMatching/keypoints/getShiTomasiScore.ts +36 -0
  95. package/src/featureMatching/visualize/Montage.ts +0 -1
  96. package/src/filters/convolution.ts +8 -2
  97. package/src/filters/derivativeFilter.ts +0 -1
  98. package/src/filters/medianFilter.ts +12 -10
  99. package/src/geometry/resize.ts +1 -1
  100. package/src/geometry/transform.ts +8 -2
  101. package/src/load/decodePng.ts +18 -0
  102. package/src/load/decodeTiff.ts +38 -1
  103. package/src/load/load.types.ts +8 -0
  104. package/src/operations/extendBorders.ts +7 -2
  105. package/src/operations/grey.ts +0 -1
  106. package/src/operations/threshold.ts +0 -1
  107. package/src/roi/colorRois.ts +0 -1
  108. package/src/roi/getRois.ts +0 -1
  109. package/src/save/encode.ts +0 -1
  110. package/src/utils/constants/colorModels.ts +0 -1
  111. package/src/utils/interpolateBorder.ts +6 -4
  112. 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
- harrisScoreOptions?: GetHarrisScoreOptions;
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
- return (image: Image, corner: Point) =>
96
- getHarrisScore(image, corner, harrisScoreOptions);
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 shoud result in
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 abolute value of the score is small: the region is flat
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 { windowSize = 7, harrisConstant = 0.04 } = options;
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 compotuation of the intensity centroid.
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 = 0 } = options;
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: 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 `1`
21
+ * @default `3`
21
22
  */
22
- cellSize: number;
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 { cellSize = 3, borderType = 'reflect101', borderValue } = options;
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;
@@ -41,7 +41,7 @@ export interface ResizeOptions {
41
41
  * Value of the border if BorderType is 'constant'.
42
42
  * @default `0`
43
43
  */
44
- borderValue?: number;
44
+ borderValue?: number | number[];
45
45
  }
46
46
 
47
47
  /**
@@ -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}`,
@@ -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
 
@@ -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
+ }
@@ -5,3 +5,11 @@ export interface ImageMetadata {
5
5
  };
6
6
  exif: Record<string, unknown>;
7
7
  }
8
+
9
+ export interface Resolution {
10
+ x: number;
11
+ y: number;
12
+ unit: ResolutionUnit;
13
+ }
14
+
15
+ export type ResolutionUnit = 'inch' | 'centimeter' | 'meter' | 'unknown';
@@ -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 = 0,
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, {
@@ -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
  {
@@ -37,7 +37,6 @@ export const ThresholdAlgorithm = {
37
37
  TRIANGLE: 'triangle',
38
38
  YEN: 'yen',
39
39
  } as const;
40
- // eslint-disable-next-line @typescript-eslint/no-redeclare
41
40
  export type ThresholdAlgorithm =
42
41
  (typeof ThresholdAlgorithm)[keyof typeof ThresholdAlgorithm];
43
42
 
@@ -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 {
@@ -9,7 +9,6 @@ export const RoiKind = {
9
9
  WHITE: 'white',
10
10
  BW: 'bw',
11
11
  } as const;
12
- // eslint-disable-next-line @typescript-eslint/no-redeclare
13
12
  export type RoiKind = (typeof RoiKind)[keyof typeof RoiKind];
14
13
 
15
14
  export interface GetRoisOptions {
@@ -15,7 +15,6 @@ export const ImageFormat = {
15
15
  JPEG: 'jpeg',
16
16
  BMP: 'bmp',
17
17
  } as const;
18
- // eslint-disable-next-line @typescript-eslint/no-redeclare
19
18
  export type ImageFormat = (typeof ImageFormat)[keyof typeof ImageFormat];
20
19
 
21
20
  export interface EncodeOptionsPng {
@@ -5,7 +5,6 @@ export const ImageColorModel = {
5
5
  RGBA: 'RGBA',
6
6
  BINARY: 'BINARY',
7
7
  } as const;
8
- // eslint-disable-next-line @typescript-eslint/no-redeclare
9
8
  export type ImageColorModel =
10
9
  (typeof ImageColorModel)[keyof typeof ImageColorModel];
11
10
 
@@ -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
- intepolateBorder: BorderInterpolationFunction,
24
+ interpolateBorder: BorderInterpolationFunction,
26
25
  clamp: ClampFunction,
27
26
  ) => number;
28
27