mindee 4.2.0 → 4.3.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v4.3.1 - 2023-09-20
4
+ ### Fixes
5
+ * :bug: take line height tolerance into account when evaluating fields
6
+
7
+
8
+ ## v4.3.0 - 2023-09-19
9
+ ### Changes
10
+ * :sparkles: add line items reconstruction for API builder
11
+
12
+
3
13
  ## v4.2.0 - 2023-09-15
4
14
  ### Changes
5
15
  * :sparkles: add support for W9 V1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mindee",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "description": "Mindee Client Library for Node.js",
5
5
  "main": "src/index.js",
6
6
  "bin": "bin/mindee.js",
@@ -1,5 +1,11 @@
1
1
  import { Point } from "./point";
2
2
  /** A simple bounding box defined by 4 coordinates: xMin, yMin, xMax, yMax */
3
- export type BBox = [number, number, number, number];
3
+ export declare class BBox {
4
+ xMin: number;
5
+ yMin: number;
6
+ xMax: number;
7
+ yMax: number;
8
+ constructor(xMin: number, yMin: number, xMax: number, yMax: number);
9
+ }
4
10
  /** A bounding box defined by 4 points. */
5
11
  export type BoundingBox = [Point, Point, Point, Point];
@@ -1,2 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BBox = void 0;
4
+ /** A simple bounding box defined by 4 coordinates: xMin, yMin, xMax, yMax */
5
+ class BBox {
6
+ constructor(xMin, yMin, xMax, yMax) {
7
+ this.xMin = xMin;
8
+ this.yMin = yMin;
9
+ this.xMax = xMax;
10
+ this.yMax = yMax;
11
+ }
12
+ }
13
+ exports.BBox = BBox;
@@ -13,10 +13,10 @@ export declare function getBoundingBoxFromBBox(bbox: BBox): BoundingBox;
13
13
  */
14
14
  export declare function mergeBbox(bbox1: BBox, bbox2: BBox): BBox;
15
15
  /**
16
- * Given a Polygon, calculate a bounding box that encompasses all points.
16
+ * Given a Polygon, calculate a BBox that encompasses all points.
17
17
  */
18
18
  export declare function getBbox(polygon: Polygon): BBox;
19
19
  /**
20
- * Given polygons, calculate a bounding box that encompasses all points.
20
+ * Given polygons, calculate a BBox that encompasses all points.
21
21
  */
22
22
  export declare function getBBoxForPolygons(polygons: Polygon[]): BBox;
@@ -1,17 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getBBoxForPolygons = exports.getBbox = exports.mergeBbox = exports.getBoundingBoxFromBBox = exports.getBoundingBox = void 0;
4
+ const boundingBox_1 = require("./boundingBox");
4
5
  /**
5
6
  * Given a Polygon, calculate a polygon that encompasses all points.
6
7
  */
7
8
  function getBoundingBox(polygon) {
8
9
  const bbox = getBbox(polygon);
9
- return [
10
- [bbox[0], bbox[1]],
11
- [bbox[2], bbox[1]],
12
- [bbox[2], bbox[3]],
13
- [bbox[0], bbox[3]],
14
- ];
10
+ return getBoundingBoxFromBBox(bbox);
15
11
  }
16
12
  exports.getBoundingBox = getBoundingBox;
17
13
  /**
@@ -19,10 +15,10 @@ exports.getBoundingBox = getBoundingBox;
19
15
  */
20
16
  function getBoundingBoxFromBBox(bbox) {
21
17
  return [
22
- [bbox[0], bbox[1]],
23
- [bbox[2], bbox[1]],
24
- [bbox[2], bbox[3]],
25
- [bbox[0], bbox[3]],
18
+ [bbox.xMin, bbox.yMin],
19
+ [bbox.xMax, bbox.yMin],
20
+ [bbox.xMax, bbox.yMax],
21
+ [bbox.xMin, bbox.yMax],
26
22
  ];
27
23
  }
28
24
  exports.getBoundingBoxFromBBox = getBoundingBoxFromBBox;
@@ -30,37 +26,24 @@ exports.getBoundingBoxFromBBox = getBoundingBoxFromBBox;
30
26
  * Given 2 bbox, merge them.
31
27
  */
32
28
  function mergeBbox(bbox1, bbox2) {
33
- return [
34
- Math.min(bbox1[0], bbox2[0]),
35
- Math.min(bbox1[1], bbox2[1]),
36
- Math.max(bbox1[2], bbox2[2]),
37
- Math.max(bbox1[3], bbox2[3]),
38
- ];
29
+ return new boundingBox_1.BBox(Math.min(bbox1.xMin, bbox2.xMin), Math.min(bbox1.yMin, bbox2.yMin), Math.max(bbox1.xMax, bbox2.xMax), Math.max(bbox1.yMax, bbox2.yMax));
39
30
  }
40
31
  exports.mergeBbox = mergeBbox;
41
32
  /**
42
- * Given a Polygon, calculate a bounding box that encompasses all points.
33
+ * Given a Polygon, calculate a BBox that encompasses all points.
43
34
  */
44
35
  function getBbox(polygon) {
45
36
  const allY = polygon.map((point) => point[1]);
46
37
  const allX = polygon.map((point) => point[0]);
47
- const yMin = Math.min(...allY);
48
- const yMax = Math.max(...allY);
49
- const xMin = Math.min(...allX);
50
- const xMax = Math.max(...allX);
51
- return [xMin, yMin, xMax, yMax];
38
+ return new boundingBox_1.BBox(Math.min(...allX), Math.min(...allY), Math.max(...allX), Math.max(...allY));
52
39
  }
53
40
  exports.getBbox = getBbox;
54
41
  /**
55
- * Given polygons, calculate a bounding box that encompasses all points.
42
+ * Given polygons, calculate a BBox that encompasses all points.
56
43
  */
57
44
  function getBBoxForPolygons(polygons) {
58
45
  const allY = polygons.flatMap((polygon) => polygon.map((point) => point[1]));
59
46
  const allX = polygons.flatMap((polygon) => polygon.map((point) => point[0]));
60
- const yMin = Math.min(...allY);
61
- const yMax = Math.max(...allY);
62
- const xMin = Math.min(...allX);
63
- const xMax = Math.max(...allX);
64
- return [xMin, yMin, xMax, yMax];
47
+ return new boundingBox_1.BBox(Math.min(...allX), Math.min(...allY), Math.max(...allX), Math.max(...allY));
65
48
  }
66
49
  exports.getBBoxForPolygons = getBBoxForPolygons;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.mergeBbox = exports.getBoundingBoxFromBBox = exports.getBoundingBox = exports.getBBoxForPolygons = exports.getBbox = exports.getMinMaxY = exports.getMinMaxX = exports.relativeY = exports.relativeX = exports.isPointInPolygonY = exports.isPointInPolygonX = exports.getCentroid = exports.compareOnY = exports.compareOnX = exports.Polygon = void 0;
3
+ exports.mergeBbox = exports.getBoundingBoxFromBBox = exports.getBoundingBox = exports.getBBoxForPolygons = exports.getBbox = exports.BBox = exports.getMinMaxY = exports.getMinMaxX = exports.relativeY = exports.relativeX = exports.isPointInPolygonY = exports.isPointInPolygonX = exports.getCentroid = exports.compareOnY = exports.compareOnX = exports.Polygon = void 0;
4
4
  var polygon_1 = require("./polygon");
5
5
  Object.defineProperty(exports, "Polygon", { enumerable: true, get: function () { return polygon_1.Polygon; } });
6
6
  var polygonUtils_1 = require("./polygonUtils");
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "relativeX", { enumerable: true, get: function ()
13
13
  Object.defineProperty(exports, "relativeY", { enumerable: true, get: function () { return polygonUtils_1.relativeY; } });
14
14
  Object.defineProperty(exports, "getMinMaxX", { enumerable: true, get: function () { return polygonUtils_1.getMinMaxX; } });
15
15
  Object.defineProperty(exports, "getMinMaxY", { enumerable: true, get: function () { return polygonUtils_1.getMinMaxY; } });
16
+ var boundingBox_1 = require("./boundingBox");
17
+ Object.defineProperty(exports, "BBox", { enumerable: true, get: function () { return boundingBox_1.BBox; } });
16
18
  var boundingBoxUtils_1 = require("./boundingBoxUtils");
17
19
  Object.defineProperty(exports, "getBbox", { enumerable: true, get: function () { return boundingBoxUtils_1.getBbox; } });
18
20
  Object.defineProperty(exports, "getBBoxForPolygons", { enumerable: true, get: function () { return boundingBoxUtils_1.getBBoxForPolygons; } });
@@ -1,3 +1,3 @@
1
- export { Line, LineItems, getLineItems } from "./lineItems";
1
+ export { CustomLine, CustomLines, getLineItems } from "./lineItems";
2
2
  export { ClassificationField } from "./classificationField";
3
3
  export { ListField, ListFieldValue } from "./listField";
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ListFieldValue = exports.ListField = exports.ClassificationField = exports.getLineItems = exports.LineItems = exports.Line = void 0;
3
+ exports.ListFieldValue = exports.ListField = exports.ClassificationField = exports.getLineItems = exports.CustomLines = exports.CustomLine = void 0;
4
4
  var lineItems_1 = require("./lineItems");
5
- Object.defineProperty(exports, "Line", { enumerable: true, get: function () { return lineItems_1.Line; } });
6
- Object.defineProperty(exports, "LineItems", { enumerable: true, get: function () { return lineItems_1.LineItems; } });
5
+ Object.defineProperty(exports, "CustomLine", { enumerable: true, get: function () { return lineItems_1.CustomLine; } });
6
+ Object.defineProperty(exports, "CustomLines", { enumerable: true, get: function () { return lineItems_1.CustomLines; } });
7
7
  Object.defineProperty(exports, "getLineItems", { enumerable: true, get: function () { return lineItems_1.getLineItems; } });
8
8
  var classificationField_1 = require("./classificationField");
9
9
  Object.defineProperty(exports, "ClassificationField", { enumerable: true, get: function () { return classificationField_1.ClassificationField; } });
@@ -1,6 +1,6 @@
1
1
  import { BBox, Polygon } from "../../geometry";
2
2
  import { ListField, ListFieldValue } from "./listField";
3
- export declare class Line {
3
+ export declare class CustomLine {
4
4
  /**
5
5
  * Number of the current line.
6
6
  * Starts at 1.
@@ -11,15 +11,10 @@ export declare class Line {
11
11
  */
12
12
  fields: Map<string, ListFieldValue>;
13
13
  /**
14
- * The BBox of the current line.
14
+ * The BBox of the entire line, all fields included.
15
15
  */
16
16
  bbox: BBox;
17
- /**
18
- * The height tolerance used to build the line.
19
- * It helps when the height of a line can vary unexpectedly.
20
- */
21
- heightTolerance: number;
22
- constructor(rowNumber: number, heightTolerance: number);
17
+ constructor(rowNumber: number);
23
18
  /**
24
19
  * Extends the current bbox of the line with the bbox.
25
20
  */
@@ -28,14 +23,11 @@ export declare class Line {
28
23
  * Extends the current bbox of the line with the polygon.
29
24
  */
30
25
  extendWith(polygon: Polygon): void;
31
- /**
32
- * Check if the bbox fits the current line.
33
- */
34
- contains(bbox: BBox): boolean;
35
26
  updateField(name: string, fieldValue: ListFieldValue): void;
36
27
  }
37
- export declare class LineItems {
38
- rows: Line[];
39
- constructor(lines: Line[]);
28
+ export declare class CustomLines extends Array<CustomLine> {
40
29
  }
41
- export declare function getLineItems(anchorNames: string[], heightLineTolerance: number, fieldNamesTargeted: string[], fields: Map<string, ListField>): LineItems;
30
+ /**
31
+ * Get line items from fields.
32
+ */
33
+ export declare function getLineItems(anchorNames: string[], fieldNames: string[], fields: Map<string, ListField>, heightLineTolerance: number): CustomLines;
@@ -1,16 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLineItems = exports.LineItems = exports.Line = void 0;
3
+ exports.getLineItems = exports.CustomLines = exports.CustomLine = void 0;
4
4
  const handler_1 = require("../../errors/handler");
5
5
  const errors_1 = require("../../errors");
6
6
  const geometry_1 = require("../../geometry");
7
7
  const listField_1 = require("./listField");
8
- class Line {
9
- constructor(rowNumber, heightTolerance) {
8
+ class CustomLine {
9
+ constructor(rowNumber) {
10
10
  this.rowNumber = rowNumber;
11
- this.bbox = [1, 1, 0, 0];
11
+ this.bbox = new geometry_1.BBox(1, 1, 0, 0);
12
12
  this.fields = new Map();
13
- this.heightTolerance = heightTolerance;
14
13
  }
15
14
  /**
16
15
  * Extends the current bbox of the line with the bbox.
@@ -24,15 +23,6 @@ class Line {
24
23
  extendWith(polygon) {
25
24
  this.bbox = (0, geometry_1.mergeBbox)(this.bbox, (0, geometry_1.getBbox)(polygon));
26
25
  }
27
- /**
28
- * Check if the bbox fits the current line.
29
- */
30
- contains(bbox) {
31
- if (Math.abs(bbox[1] - this.bbox[1]) <= this.heightTolerance) {
32
- return true;
33
- }
34
- return Math.abs(this.bbox[1] - bbox[1]) <= this.heightTolerance;
35
- }
36
26
  updateField(name, fieldValue) {
37
27
  if (!this.fields.has(name)) {
38
28
  this.fields.set(name, fieldValue);
@@ -58,32 +48,34 @@ class Line {
58
48
  }
59
49
  }
60
50
  }
61
- exports.Line = Line;
62
- class LineItems {
63
- constructor(lines) {
64
- this.rows = [];
65
- this.rows = lines;
66
- }
51
+ exports.CustomLine = CustomLine;
52
+ class CustomLines extends Array {
67
53
  }
68
- exports.LineItems = LineItems;
69
- function getLineItems(anchorNames, heightLineTolerance, fieldNamesTargeted, fields) {
70
- const fieldsToTransformIntoLines = new Map([...fields].filter(([k]) => fieldNamesTargeted.includes(k)));
54
+ exports.CustomLines = CustomLines;
55
+ /**
56
+ * Get line items from fields.
57
+ */
58
+ function getLineItems(anchorNames, fieldNames, fields, heightLineTolerance) {
59
+ const fieldsToTransformIntoLines = new Map([...fields].filter(([k]) => fieldNames.includes(k)));
71
60
  const anchorName = findBestAnchor(anchorNames, fieldsToTransformIntoLines);
72
- const lineItemsPrepared = prepare(anchorName, fieldsToTransformIntoLines, heightLineTolerance);
73
- lineItemsPrepared.rows.forEach((currentLine) => {
61
+ const linesPrepared = prepare(anchorName, fieldsToTransformIntoLines, heightLineTolerance);
62
+ linesPrepared.forEach((currentLine) => {
74
63
  fieldsToTransformIntoLines.forEach((field, fieldName) => {
75
64
  field.values.forEach((listFieldValue) => {
76
- const minYCurrentValue = (0, geometry_1.getMinMaxY)(listFieldValue.polygon).min;
77
- if (minYCurrentValue < currentLine.bbox[3] &&
78
- minYCurrentValue >= currentLine.bbox[1]) {
65
+ const minMaxY = (0, geometry_1.getMinMaxY)(listFieldValue.polygon);
66
+ if (Math.abs(minMaxY.max - currentLine.bbox.yMax) <= heightLineTolerance &&
67
+ Math.abs(minMaxY.min - currentLine.bbox.yMin) <= heightLineTolerance) {
79
68
  currentLine.updateField(fieldName, listFieldValue);
80
69
  }
81
70
  });
82
71
  });
83
72
  });
84
- return lineItemsPrepared;
73
+ return linesPrepared;
85
74
  }
86
75
  exports.getLineItems = getLineItems;
76
+ /**
77
+ * Loop through the possible anchor fields and find the one with the most values.
78
+ */
87
79
  function findBestAnchor(possibleAnchorNames, fields) {
88
80
  let anchorName = "";
89
81
  let anchorRows = 0;
@@ -99,31 +91,40 @@ function findBestAnchor(possibleAnchorNames, fields) {
99
91
  }
100
92
  return anchorName;
101
93
  }
94
+ /**
95
+ * Check if the bbox fits inside the line.
96
+ */
97
+ function isBboxInLine(line, bbox, heightTolerance) {
98
+ if (Math.abs(bbox.yMin - line.bbox.yMin) <= heightTolerance) {
99
+ return true;
100
+ }
101
+ return Math.abs(line.bbox.yMin - bbox.yMin) <= heightTolerance;
102
+ }
102
103
  function prepare(anchorName, fields, heightLineTolerance) {
103
- const lineItemsPrepared = [];
104
+ const linesPrepared = [];
104
105
  const anchorField = fields.get(anchorName);
105
106
  if (anchorField === undefined || anchorField.values.length === 0) {
106
107
  handler_1.errorHandler.throw(new errors_1.MindeeError("No lines have been detected."));
107
108
  }
108
109
  let currentLineNumber = 1;
109
- let currentLine = new Line(currentLineNumber, heightLineTolerance);
110
+ let currentLine = new CustomLine(currentLineNumber);
110
111
  if (anchorField !== undefined) {
111
112
  let currentValue = anchorField.values[0];
112
113
  currentLine.extendWith(currentValue.polygon);
113
114
  for (let index = 1; index < anchorField.values.length; index++) {
114
115
  currentValue = anchorField.values[index];
115
116
  const currentFieldBbox = (0, geometry_1.getBbox)(currentValue.polygon);
116
- if (!currentLine.contains(currentFieldBbox)) {
117
- lineItemsPrepared.push(currentLine);
117
+ if (!isBboxInLine(currentLine, currentFieldBbox, heightLineTolerance)) {
118
+ linesPrepared.push(currentLine);
118
119
  currentLineNumber++;
119
- currentLine = new Line(currentLineNumber, heightLineTolerance);
120
+ currentLine = new CustomLine(currentLineNumber);
120
121
  }
121
122
  currentLine.extendWithBbox(currentFieldBbox);
122
123
  }
123
- if (lineItemsPrepared.filter((line) => line.rowNumber === currentLineNumber)
124
+ if (linesPrepared.filter((line) => line.rowNumber === currentLineNumber)
124
125
  .length === 0) {
125
- lineItemsPrepared.push(currentLine);
126
+ linesPrepared.push(currentLine);
126
127
  }
127
128
  }
128
- return new LineItems(lineItemsPrepared);
129
+ return linesPrepared;
129
130
  }
@@ -1,5 +1,5 @@
1
1
  import { StringDict, Prediction } from "../../parsing/common";
2
- import { ClassificationField, ListField } from "../../parsing/custom";
2
+ import { ClassificationField, ListField, CustomLines } from "../../parsing/custom";
3
3
  /**
4
4
  * Document data for Custom builds.
5
5
  */
@@ -18,6 +18,13 @@ export declare class CustomV1Document implements Prediction {
18
18
  * @param pageId page the field was found on.
19
19
  */
20
20
  protected setField(fieldName: string, fieldValue: any, pageId?: number): void;
21
+ /**
22
+ * Order column fields into line items.
23
+ * @param anchorNames list of possible anchor fields.
24
+ * @param fieldNames list of all column fields.
25
+ * @param heightTolerance height tolerance to apply to lines.
26
+ */
27
+ columnsToLineItems(anchorNames: string[], fieldNames: string[], heightTolerance?: number): CustomLines;
21
28
  /**
22
29
  * Default string representation.
23
30
  */
@@ -40,6 +40,15 @@ class CustomV1Document {
40
40
  throw new Error(`Unknown API field type for field ${fieldName} : ${fieldValue}`);
41
41
  }
42
42
  }
43
+ /**
44
+ * Order column fields into line items.
45
+ * @param anchorNames list of possible anchor fields.
46
+ * @param fieldNames list of all column fields.
47
+ * @param heightTolerance height tolerance to apply to lines.
48
+ */
49
+ columnsToLineItems(anchorNames, fieldNames, heightTolerance = 0.01) {
50
+ return (0, custom_1.getLineItems)(anchorNames, fieldNames, this.fields, heightTolerance);
51
+ }
43
52
  /**
44
53
  * Default string representation.
45
54
  */
@@ -1,5 +1,5 @@
1
1
  import { StringDict, Prediction } from "../../parsing/common";
2
- import { ListField } from "../../parsing/custom";
2
+ import { ListField, CustomLines } from "../../parsing/custom";
3
3
  /**
4
4
  * Page data for Custom builds.
5
5
  */
@@ -7,6 +7,13 @@ export declare class CustomV1Page implements Prediction {
7
7
  /** List of page-specific fields for a Custom build. Cannot include Classification fields. */
8
8
  fields: Map<string, ListField>;
9
9
  constructor(rawPrediction: StringDict, pageId?: number);
10
+ /**
11
+ * Order column fields into line items.
12
+ * @param anchorNames list of possible anchor fields.
13
+ * @param fieldNames list of all column fields.
14
+ * @param heightTolerance height tolerance to apply to lines.
15
+ */
16
+ columnsToLineItems(anchorNames: string[], fieldNames: string[], heightTolerance?: number): CustomLines;
10
17
  /**
11
18
  * Default string representation.
12
19
  */
@@ -17,6 +17,15 @@ class CustomV1Page {
17
17
  }));
18
18
  });
19
19
  }
20
+ /**
21
+ * Order column fields into line items.
22
+ * @param anchorNames list of possible anchor fields.
23
+ * @param fieldNames list of all column fields.
24
+ * @param heightTolerance height tolerance to apply to lines.
25
+ */
26
+ columnsToLineItems(anchorNames, fieldNames, heightTolerance = 0.01) {
27
+ return (0, custom_1.getLineItems)(anchorNames, fieldNames, this.fields, heightTolerance);
28
+ }
20
29
  /**
21
30
  * Default string representation.
22
31
  */