label-studio-converter 1.0.0 → 1.1.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.
@@ -19,7 +19,7 @@ var init_esm_shims = __esm({
19
19
  });
20
20
 
21
21
  // src/constants.ts
22
- var OUTPUT_BASE_DIR, DEFAULT_LABEL_NAME, DEFAULT_LABEL_STUDIO_FULL_JSON, DEFAULT_CREATE_FILE_PER_IMAGE, DEFAULT_CREATE_FILE_LIST_FOR_SERVING, DEFAULT_FILE_LIST_NAME, DEFAULT_BASE_SERVER_URL, DEFAULT_PPOCR_FILE_NAME, SORT_VERTICAL_NONE, SORT_VERTICAL_TOP_BOTTOM, SORT_VERTICAL_BOTTOM_TOP, DEFAULT_SORT_VERTICAL, SORT_HORIZONTAL_NONE, SORT_HORIZONTAL_LTR, SORT_HORIZONTAL_RTL, DEFAULT_SORT_HORIZONTAL;
22
+ var OUTPUT_BASE_DIR, DEFAULT_LABEL_NAME, DEFAULT_LABEL_STUDIO_FULL_JSON, DEFAULT_CREATE_FILE_PER_IMAGE, DEFAULT_CREATE_FILE_LIST_FOR_SERVING, DEFAULT_FILE_LIST_NAME, DEFAULT_BASE_SERVER_URL, DEFAULT_PPOCR_FILE_NAME, SORT_VERTICAL_NONE, SORT_VERTICAL_TOP_BOTTOM, SORT_VERTICAL_BOTTOM_TOP, DEFAULT_SORT_VERTICAL, SORT_HORIZONTAL_NONE, SORT_HORIZONTAL_LTR, SORT_HORIZONTAL_RTL, DEFAULT_SORT_HORIZONTAL, SHAPE_NORMALIZE_NONE, SHAPE_NORMALIZE_RECTANGLE, DEFAULT_SHAPE_NORMALIZE, DEFAULT_WIDTH_INCREMENT, DEFAULT_HEIGHT_INCREMENT, DEFAULT_LABEL_STUDIO_PRECISION, DEFAULT_PPOCR_PRECISION;
23
23
  var init_constants = __esm({
24
24
  "src/constants.ts"() {
25
25
  "use strict";
@@ -40,6 +40,99 @@ var init_constants = __esm({
40
40
  SORT_HORIZONTAL_LTR = "ltr";
41
41
  SORT_HORIZONTAL_RTL = "rtl";
42
42
  DEFAULT_SORT_HORIZONTAL = SORT_HORIZONTAL_NONE;
43
+ SHAPE_NORMALIZE_NONE = "none";
44
+ SHAPE_NORMALIZE_RECTANGLE = "rectangle";
45
+ DEFAULT_SHAPE_NORMALIZE = SHAPE_NORMALIZE_NONE;
46
+ DEFAULT_WIDTH_INCREMENT = 0;
47
+ DEFAULT_HEIGHT_INCREMENT = 0;
48
+ DEFAULT_LABEL_STUDIO_PRECISION = -1;
49
+ DEFAULT_PPOCR_PRECISION = 0;
50
+ }
51
+ });
52
+
53
+ // src/lib/geometry.ts
54
+ function roundToPrecision(value, precision) {
55
+ if (precision < 0) {
56
+ return value;
57
+ }
58
+ const multiplier = Math.pow(10, precision);
59
+ return Math.round(value * multiplier) / multiplier;
60
+ }
61
+ function roundPoints(points, precision) {
62
+ if (precision < 0) {
63
+ return points;
64
+ }
65
+ return points.map(
66
+ ([x, y]) => [roundToPrecision(x, precision), roundToPrecision(y, precision)]
67
+ );
68
+ }
69
+ function calculateCenter(points) {
70
+ const sum = points.reduce((acc, [x, y]) => [acc[0] + x, acc[1] + y], [
71
+ 0,
72
+ 0
73
+ ]);
74
+ return [sum[0] / points.length, sum[1] / points.length];
75
+ }
76
+ function getMinimumBoundingRect(points) {
77
+ const minX = Math.min(...points.map(([x]) => x));
78
+ const maxX = Math.max(...points.map(([x]) => x));
79
+ const minY = Math.min(...points.map(([, y]) => y));
80
+ const maxY = Math.max(...points.map(([, y]) => y));
81
+ return {
82
+ minX,
83
+ minY,
84
+ maxX,
85
+ maxY,
86
+ width: maxX - minX,
87
+ height: maxY - minY
88
+ };
89
+ }
90
+ function normalizeShape(points) {
91
+ if (points.length < 3) {
92
+ return points;
93
+ }
94
+ const { minX, minY, maxX, maxY } = getMinimumBoundingRect(points);
95
+ return [
96
+ [minX, minY],
97
+ [maxX, minY],
98
+ [maxX, maxY],
99
+ [minX, maxY]
100
+ ];
101
+ }
102
+ function resizeBoundingBox(points, widthIncrement, heightIncrement) {
103
+ if (points.length === 0) {
104
+ return points;
105
+ }
106
+ const center = calculateCenter(points);
107
+ const bbox = getMinimumBoundingRect(points);
108
+ const newWidth = Math.max(1, bbox.width + widthIncrement);
109
+ const newHeight = Math.max(1, bbox.height + heightIncrement);
110
+ const scaleX = newWidth / bbox.width;
111
+ const scaleY = newHeight / bbox.height;
112
+ return points.map(([x, y]) => {
113
+ const relX = x - center[0];
114
+ const relY = y - center[1];
115
+ return [center[0] + relX * scaleX, center[1] + relY * scaleY];
116
+ });
117
+ }
118
+ function transformPoints(points, options) {
119
+ let result = points;
120
+ if (options.normalizeShape && options.normalizeShape === "rectangle") {
121
+ result = normalizeShape(result);
122
+ }
123
+ if (options.widthIncrement !== void 0 || options.heightIncrement !== void 0) {
124
+ result = resizeBoundingBox(
125
+ result,
126
+ options.widthIncrement ?? 0,
127
+ options.heightIncrement ?? 0
128
+ );
129
+ }
130
+ return result;
131
+ }
132
+ var init_geometry = __esm({
133
+ "src/lib/geometry.ts"() {
134
+ "use strict";
135
+ init_esm_shims();
43
136
  }
44
137
  });
45
138
 
@@ -54,6 +147,7 @@ var init_ppocr_label = __esm({
54
147
  "use strict";
55
148
  init_esm_shims();
56
149
  init_constants();
150
+ init_geometry();
57
151
  ppocrToLabelStudio = async (data, options) => {
58
152
  const {
59
153
  imagePath,
@@ -61,7 +155,11 @@ var init_ppocr_label = __esm({
61
155
  inputDir,
62
156
  toFullJson = true,
63
157
  taskId = 1,
64
- labelName = DEFAULT_LABEL_NAME
158
+ labelName = DEFAULT_LABEL_NAME,
159
+ normalizeShape: normalizeShape2,
160
+ widthIncrement = 0,
161
+ heightIncrement = 0,
162
+ precision = DEFAULT_LABEL_STUDIO_PRECISION
65
163
  } = options || {};
66
164
  if (toFullJson) {
67
165
  return ppocrToFullLabelStudio(
@@ -70,7 +168,11 @@ var init_ppocr_label = __esm({
70
168
  baseServerUrl,
71
169
  inputDir,
72
170
  taskId,
73
- labelName
171
+ labelName,
172
+ normalizeShape2,
173
+ widthIncrement,
174
+ heightIncrement,
175
+ precision
74
176
  );
75
177
  } else {
76
178
  return ppocrToMinLabelStudio(
@@ -78,11 +180,15 @@ var init_ppocr_label = __esm({
78
180
  imagePath,
79
181
  baseServerUrl,
80
182
  inputDir,
81
- labelName
183
+ labelName,
184
+ normalizeShape2,
185
+ widthIncrement,
186
+ heightIncrement,
187
+ precision
82
188
  );
83
189
  }
84
190
  };
85
- ppocrToFullLabelStudio = (data, imagePath, baseServerUrl, inputDir, taskId = 1, labelName = DEFAULT_LABEL_NAME) => {
191
+ ppocrToFullLabelStudio = (data, imagePath, baseServerUrl, inputDir, taskId = 1, labelName = DEFAULT_LABEL_NAME, normalizeShape2, widthIncrement = 0, heightIncrement = 0, precision = DEFAULT_LABEL_STUDIO_PRECISION) => {
86
192
  const newBaseServerUrl = baseServerUrl.replace(/\/+$/, "") + (baseServerUrl === "" ? "" : "/");
87
193
  const now = (/* @__PURE__ */ new Date()).toISOString();
88
194
  let original_width = 1920;
@@ -109,11 +215,16 @@ var init_ppocr_label = __esm({
109
215
  id: taskId,
110
216
  completed_by: 1,
111
217
  result: data.map((item) => {
112
- const { points } = item;
218
+ let { points } = item;
219
+ points = transformPoints(points, {
220
+ normalizeShape: normalizeShape2,
221
+ widthIncrement,
222
+ heightIncrement
223
+ });
113
224
  const annotationId = randomUUID().slice(0, 10);
114
225
  const polygonPoints = points.map(([x, y]) => [
115
- (x ?? 0) / original_width * 100,
116
- (y ?? 0) / original_height * 100
226
+ roundToPrecision((x ?? 0) / original_width * 100, precision),
227
+ roundToPrecision((y ?? 0) / original_height * 100, precision)
117
228
  ]);
118
229
  return [
119
230
  // 1. Polygon geometry only
@@ -207,7 +318,7 @@ var init_ppocr_label = __esm({
207
318
  ];
208
319
  return result;
209
320
  };
210
- ppocrToMinLabelStudio = (data, imagePath, baseServerUrl, inputDir, labelName = "text") => {
321
+ ppocrToMinLabelStudio = (data, imagePath, baseServerUrl, inputDir, labelName = "text", normalizeShape2, widthIncrement = 0, heightIncrement = 0, precision = DEFAULT_LABEL_STUDIO_PRECISION) => {
211
322
  const newBaseServerUrl = baseServerUrl.replace(/\/+$/, "") + (baseServerUrl === "" ? "" : "/");
212
323
  const now = (/* @__PURE__ */ new Date()).toISOString();
213
324
  let original_width = 1920;
@@ -226,12 +337,23 @@ var init_ppocr_label = __esm({
226
337
  original_width = dimensions.width;
227
338
  original_height = dimensions.height;
228
339
  return data.map((item, index) => {
229
- const { points } = item;
340
+ let { points } = item;
341
+ points = transformPoints(points, {
342
+ normalizeShape: normalizeShape2,
343
+ widthIncrement,
344
+ heightIncrement
345
+ });
346
+ const roundedPoints = points.map(
347
+ ([x, y]) => [
348
+ roundToPrecision(x ?? 0, precision),
349
+ roundToPrecision(y ?? 0, precision)
350
+ ]
351
+ );
230
352
  let minX = Infinity;
231
353
  let minY = Infinity;
232
354
  let maxX = -Infinity;
233
355
  let maxY = -Infinity;
234
- for (const point of points) {
356
+ for (const point of roundedPoints) {
235
357
  const [x, y] = point;
236
358
  if (x !== void 0 && y !== void 0) {
237
359
  minX = Math.min(minX, x);
@@ -258,7 +380,7 @@ var init_ppocr_label = __esm({
258
380
  ],
259
381
  label: [
260
382
  {
261
- points,
383
+ points: roundedPoints,
262
384
  closed: true,
263
385
  labels: [labelName],
264
386
  original_width,
@@ -268,7 +390,7 @@ var init_ppocr_label = __esm({
268
390
  transcription: [item.transcription],
269
391
  poly: [
270
392
  {
271
- points,
393
+ points: roundedPoints,
272
394
  closed: true,
273
395
  original_width,
274
396
  original_height
@@ -301,6 +423,7 @@ var init_schema = __esm({
301
423
  completed_by: z.number(),
302
424
  result: z.array(
303
425
  z.union([
426
+ // Most specific rectangle variants first (with text or labels)
304
427
  z.object({
305
428
  original_width: z.number(),
306
429
  original_height: z.number(),
@@ -310,7 +433,8 @@ var init_schema = __esm({
310
433
  y: z.number(),
311
434
  width: z.number(),
312
435
  height: z.number(),
313
- rotation: z.number()
436
+ rotation: z.number(),
437
+ text: z.array(z.string())
314
438
  }),
315
439
  id: z.string(),
316
440
  from_name: z.string(),
@@ -336,6 +460,7 @@ var init_schema = __esm({
336
460
  type: z.string(),
337
461
  origin: z.string()
338
462
  }),
463
+ // Base rectangle without text or labels
339
464
  z.object({
340
465
  original_width: z.number(),
341
466
  original_height: z.number(),
@@ -345,8 +470,7 @@ var init_schema = __esm({
345
470
  y: z.number(),
346
471
  width: z.number(),
347
472
  height: z.number(),
348
- rotation: z.number(),
349
- text: z.array(z.string())
473
+ rotation: z.number()
350
474
  }),
351
475
  id: z.string(),
352
476
  from_name: z.string(),
@@ -354,13 +478,15 @@ var init_schema = __esm({
354
478
  type: z.string(),
355
479
  origin: z.string()
356
480
  }),
481
+ // Most specific polygon variants first (with text or labels)
357
482
  z.object({
358
483
  original_width: z.number(),
359
484
  original_height: z.number(),
360
485
  image_rotation: z.number(),
361
486
  value: z.object({
362
487
  points: z.array(z.array(z.number())),
363
- closed: z.boolean()
488
+ closed: z.boolean(),
489
+ text: z.array(z.string())
364
490
  }),
365
491
  id: z.string(),
366
492
  from_name: z.string(),
@@ -383,14 +509,14 @@ var init_schema = __esm({
383
509
  type: z.string(),
384
510
  origin: z.string()
385
511
  }),
512
+ // Base polygon without text or labels
386
513
  z.object({
387
514
  original_width: z.number(),
388
515
  original_height: z.number(),
389
516
  image_rotation: z.number(),
390
517
  value: z.object({
391
518
  points: z.array(z.array(z.number())),
392
- closed: z.boolean(),
393
- text: z.array(z.string())
519
+ closed: z.boolean()
394
520
  }),
395
521
  id: z.string(),
396
522
  from_name: z.string(),
@@ -429,6 +555,7 @@ var init_schema = __esm({
429
555
  created_ago: z.string(),
430
556
  result: z.array(
431
557
  z.union([
558
+ // Most specific rectangle variants first (with text or labels)
432
559
  z.object({
433
560
  original_width: z.number(),
434
561
  original_height: z.number(),
@@ -438,7 +565,8 @@ var init_schema = __esm({
438
565
  y: z.number(),
439
566
  width: z.number(),
440
567
  height: z.number(),
441
- rotation: z.number()
568
+ rotation: z.number(),
569
+ text: z.array(z.string())
442
570
  }),
443
571
  id: z.string(),
444
572
  from_name: z.string(),
@@ -464,6 +592,7 @@ var init_schema = __esm({
464
592
  type: z.string(),
465
593
  origin: z.string()
466
594
  }),
595
+ // Base rectangle without text or labels
467
596
  z.object({
468
597
  original_width: z.number(),
469
598
  original_height: z.number(),
@@ -473,8 +602,7 @@ var init_schema = __esm({
473
602
  y: z.number(),
474
603
  width: z.number(),
475
604
  height: z.number(),
476
- rotation: z.number(),
477
- text: z.array(z.string())
605
+ rotation: z.number()
478
606
  }),
479
607
  id: z.string(),
480
608
  from_name: z.string(),
@@ -482,13 +610,15 @@ var init_schema = __esm({
482
610
  type: z.string(),
483
611
  origin: z.string()
484
612
  }),
613
+ // Most specific polygon variants first (with text or labels)
485
614
  z.object({
486
615
  original_width: z.number(),
487
616
  original_height: z.number(),
488
617
  image_rotation: z.number(),
489
618
  value: z.object({
490
619
  points: z.array(z.array(z.number())),
491
- closed: z.boolean()
620
+ closed: z.boolean(),
621
+ text: z.array(z.string())
492
622
  }),
493
623
  id: z.string(),
494
624
  from_name: z.string(),
@@ -511,14 +641,14 @@ var init_schema = __esm({
511
641
  type: z.string(),
512
642
  origin: z.string()
513
643
  }),
644
+ // Base polygon without text or labels
514
645
  z.object({
515
646
  original_width: z.number(),
516
647
  original_height: z.number(),
517
648
  image_rotation: z.number(),
518
649
  value: z.object({
519
650
  points: z.array(z.array(z.number())),
520
- closed: z.boolean(),
521
- text: z.array(z.string())
651
+ closed: z.boolean()
522
652
  }),
523
653
  id: z.string(),
524
654
  from_name: z.string(),
@@ -569,7 +699,7 @@ var init_schema = __esm({
569
699
  original_width: z.number(),
570
700
  original_height: z.number()
571
701
  })
572
- ),
702
+ ).optional().default([]),
573
703
  label: z.array(
574
704
  z.union([
575
705
  z.object({
@@ -590,8 +720,11 @@ var init_schema = __esm({
590
720
  original_height: z.number()
591
721
  })
592
722
  ])
593
- ),
594
- transcription: z.array(z.string()),
723
+ ).optional().default([]),
724
+ transcription: z.union([z.string(), z.array(z.string())]).optional().transform((val) => {
725
+ if (!val) return [];
726
+ return Array.isArray(val) ? val : [val];
727
+ }),
595
728
  poly: z.array(
596
729
  z.object({
597
730
  points: z.array(z.array(z.number())),
@@ -599,7 +732,7 @@ var init_schema = __esm({
599
732
  original_width: z.number(),
600
733
  original_height: z.number()
601
734
  })
602
- ),
735
+ ).optional().default([]),
603
736
  annotator: z.number(),
604
737
  annotation_id: z.number(),
605
738
  created_at: z.string(),
@@ -726,7 +859,11 @@ async function convertToLabelStudio(flags, ...inputDirs) {
726
859
  fileListName = DEFAULT_FILE_LIST_NAME,
727
860
  baseServerUrl = DEFAULT_BASE_SERVER_URL,
728
861
  sortVertical = DEFAULT_SORT_VERTICAL,
729
- sortHorizontal = DEFAULT_SORT_HORIZONTAL
862
+ sortHorizontal = DEFAULT_SORT_HORIZONTAL,
863
+ normalizeShape: normalizeShape2 = DEFAULT_SHAPE_NORMALIZE,
864
+ widthIncrement = DEFAULT_WIDTH_INCREMENT,
865
+ heightIncrement = DEFAULT_HEIGHT_INCREMENT,
866
+ precision = DEFAULT_LABEL_STUDIO_PRECISION
730
867
  } = flags;
731
868
  const newBaseServerUrl = baseServerUrl.replace(/\/+$/, "") + (baseServerUrl === "" ? "" : "/");
732
869
  await mkdir(outDir, { recursive: true });
@@ -769,7 +906,11 @@ async function convertToLabelStudio(flags, ...inputDirs) {
769
906
  baseServerUrl: newBaseServerUrl,
770
907
  inputDir,
771
908
  taskId,
772
- labelName: defaultLabelName
909
+ labelName: defaultLabelName,
910
+ normalizeShape: normalizeShape2 !== SHAPE_NORMALIZE_NONE ? normalizeShape2 : void 0,
911
+ widthIncrement,
912
+ heightIncrement,
913
+ precision
773
914
  });
774
915
  if (toFullJson) {
775
916
  allLabelStudioData.push(labelStudioData[0]);
@@ -850,7 +991,15 @@ var init_label_studio = __esm({
850
991
  "src/lib/label-studio.ts"() {
851
992
  "use strict";
852
993
  init_esm_shims();
853
- labelStudioToPPOCR = async (data, baseImageDir) => {
994
+ init_geometry();
995
+ labelStudioToPPOCR = async (data, options) => {
996
+ const {
997
+ baseImageDir,
998
+ normalizeShape: normalizeShape2,
999
+ widthIncrement = 0,
1000
+ heightIncrement = 0,
1001
+ precision = 0
1002
+ } = options || {};
854
1003
  const resultMap = /* @__PURE__ */ new Map();
855
1004
  for (const task of data) {
856
1005
  let imagePath = task.file_upload || "";
@@ -901,6 +1050,12 @@ var init_label_studio = __esm({
901
1050
  }
902
1051
  }
903
1052
  if (points && points.length > 0) {
1053
+ points = transformPoints(points, {
1054
+ normalizeShape: normalizeShape2,
1055
+ widthIncrement,
1056
+ heightIncrement
1057
+ });
1058
+ points = roundPoints(points, precision);
904
1059
  let dt_score = 1;
905
1060
  try {
906
1061
  const firstPoint = points[0];
@@ -926,7 +1081,14 @@ var init_label_studio = __esm({
926
1081
  }
927
1082
  return resultMap;
928
1083
  };
929
- minLabelStudioToPPOCR = async (data, baseImageDir) => {
1084
+ minLabelStudioToPPOCR = async (data, options) => {
1085
+ const {
1086
+ baseImageDir,
1087
+ normalizeShape: normalizeShape2,
1088
+ widthIncrement = 0,
1089
+ heightIncrement = 0,
1090
+ precision = 0
1091
+ } = options || {};
930
1092
  const resultMap = /* @__PURE__ */ new Map();
931
1093
  for (const item of data) {
932
1094
  let imagePath = item.ocr || "";
@@ -938,43 +1100,62 @@ var init_label_studio = __esm({
938
1100
  if (baseImageDir) {
939
1101
  imagePath = `${baseImageDir}/${imagePath.split("/").pop() || imagePath}`;
940
1102
  }
941
- let points;
942
- if (item.poly.length > 0 && item.poly[0]) {
943
- const { points: polyPoints } = item.poly[0];
944
- points = polyPoints;
945
- } else if (item.bbox.length > 0 && item.bbox[0]) {
946
- const bbox = item.bbox[0];
947
- const { x, y, width, height } = bbox;
948
- points = [
949
- [x, y],
950
- [x + width, y],
951
- [x + width, y + height],
952
- [x, y + height]
953
- ];
954
- } else {
955
- continue;
956
- }
957
- const transcription = item.transcription.length > 0 ? item.transcription[0] : "";
958
- let dt_score = 1;
959
- try {
960
- const firstPoint = points[0];
961
- if (firstPoint) {
962
- const polygon2 = turf.polygon([points.concat([firstPoint])]);
963
- const area2 = turf.area(polygon2);
964
- dt_score = Math.min(1, Math.max(0.5, area2 / 1e4));
1103
+ const numAnnotations = Math.max(
1104
+ item.poly?.length || 0,
1105
+ item.bbox?.length || 0,
1106
+ item.transcription?.length || 0
1107
+ );
1108
+ for (let i = 0; i < numAnnotations; i++) {
1109
+ let points;
1110
+ if (item.poly && item.poly.length > i && item.poly[i]) {
1111
+ const poly = item.poly[i];
1112
+ if (poly) {
1113
+ const { points: polyPoints } = poly;
1114
+ points = polyPoints;
1115
+ }
1116
+ } else if (item.bbox && item.bbox.length > i && item.bbox[i]) {
1117
+ const bbox = item.bbox[i];
1118
+ if (bbox) {
1119
+ const { x, y, width, height } = bbox;
1120
+ points = [
1121
+ [x, y],
1122
+ [x + width, y],
1123
+ [x + width, y + height],
1124
+ [x, y + height]
1125
+ ];
1126
+ }
965
1127
  }
966
- } catch {
967
- dt_score = 0.8;
968
- }
969
- const annotation = {
970
- transcription: transcription ?? "",
971
- points,
972
- dt_score
973
- };
974
- if (!resultMap.has(imagePath)) {
975
- resultMap.set(imagePath, []);
1128
+ if (!points) {
1129
+ continue;
1130
+ }
1131
+ points = transformPoints(points, {
1132
+ normalizeShape: normalizeShape2,
1133
+ widthIncrement,
1134
+ heightIncrement
1135
+ });
1136
+ points = roundPoints(points, precision);
1137
+ const transcription = item.transcription && item.transcription.length > i ? item.transcription[i] : "";
1138
+ let dt_score = 1;
1139
+ try {
1140
+ const firstPoint = points[0];
1141
+ if (firstPoint) {
1142
+ const polygon2 = turf.polygon([points.concat([firstPoint])]);
1143
+ const area2 = turf.area(polygon2);
1144
+ dt_score = Math.min(1, Math.max(0.5, area2 / 1e4));
1145
+ }
1146
+ } catch {
1147
+ dt_score = 0.8;
1148
+ }
1149
+ const annotation = {
1150
+ transcription: transcription ?? "",
1151
+ points,
1152
+ dt_score
1153
+ };
1154
+ if (!resultMap.has(imagePath)) {
1155
+ resultMap.set(imagePath, []);
1156
+ }
1157
+ resultMap.get(imagePath).push(annotation);
976
1158
  }
977
- resultMap.get(imagePath).push(annotation);
978
1159
  }
979
1160
  return resultMap;
980
1161
  };
@@ -995,7 +1176,11 @@ async function convertToPPOCR(flags, ...inputDirs) {
995
1176
  fileName = DEFAULT_PPOCR_FILE_NAME,
996
1177
  baseImageDir,
997
1178
  sortVertical = DEFAULT_SORT_VERTICAL,
998
- sortHorizontal = DEFAULT_SORT_HORIZONTAL
1179
+ sortHorizontal = DEFAULT_SORT_HORIZONTAL,
1180
+ normalizeShape: normalizeShape2 = DEFAULT_SHAPE_NORMALIZE,
1181
+ widthIncrement = DEFAULT_WIDTH_INCREMENT,
1182
+ heightIncrement = DEFAULT_HEIGHT_INCREMENT,
1183
+ precision = DEFAULT_PPOCR_PRECISION
999
1184
  } = flags;
1000
1185
  await mkdir2(outDir, { recursive: true });
1001
1186
  for (const inputDir of inputDirs) {
@@ -1011,10 +1196,19 @@ async function convertToPPOCR(flags, ...inputDirs) {
1011
1196
  const fileData = await readFile2(filePath, "utf-8");
1012
1197
  const labelStudioData = JSON.parse(fileData);
1013
1198
  const { data, isFull } = isLabelStudioFullJSON(labelStudioData);
1014
- const ppocrDataMap = isFull ? await labelStudioToPPOCR(data, baseImageDir) : await minLabelStudioToPPOCR(
1015
- data,
1016
- baseImageDir
1017
- );
1199
+ const ppocrDataMap = isFull ? await labelStudioToPPOCR(data, {
1200
+ baseImageDir,
1201
+ normalizeShape: normalizeShape2 !== SHAPE_NORMALIZE_NONE ? normalizeShape2 : void 0,
1202
+ widthIncrement,
1203
+ heightIncrement,
1204
+ precision
1205
+ }) : await minLabelStudioToPPOCR(data, {
1206
+ baseImageDir,
1207
+ normalizeShape: normalizeShape2 !== SHAPE_NORMALIZE_NONE ? normalizeShape2 : void 0,
1208
+ widthIncrement,
1209
+ heightIncrement,
1210
+ precision
1211
+ });
1018
1212
  const outputLines = [];
1019
1213
  for (const [imagePath, annotations] of ppocrDataMap.entries()) {
1020
1214
  const sortedAnnotations = sortBoundingBoxes(
@@ -1082,7 +1276,7 @@ import {
1082
1276
  import { buildApplication, buildRouteMap } from "@stricli/core";
1083
1277
 
1084
1278
  // package.json
1085
- var version = "1.0.0";
1279
+ var version = "1.1.0";
1086
1280
  var description = "Convert between Label Studio OCR format and PPOCRLabelv2 format";
1087
1281
 
1088
1282
  // src/commands/toLabelStudio/command.ts
@@ -1154,6 +1348,30 @@ var toLabelStudioCommand = buildCommand({
1154
1348
  brief: `Sort bounding boxes horizontally. Options: "${SORT_HORIZONTAL_NONE}" (default), "${SORT_HORIZONTAL_LTR}", "${SORT_HORIZONTAL_RTL}"`,
1155
1349
  parse: String,
1156
1350
  optional: true
1351
+ },
1352
+ normalizeShape: {
1353
+ kind: "parsed",
1354
+ brief: `Normalize diamond-like shapes to axis-aligned rectangles. Options: "${SHAPE_NORMALIZE_NONE}" (default), "${SHAPE_NORMALIZE_RECTANGLE}"`,
1355
+ parse: String,
1356
+ optional: true
1357
+ },
1358
+ widthIncrement: {
1359
+ kind: "parsed",
1360
+ brief: `Increase bounding box width by this amount (in pixels). Can be negative to decrease. Default: ${DEFAULT_WIDTH_INCREMENT}`,
1361
+ parse: Number,
1362
+ optional: true
1363
+ },
1364
+ heightIncrement: {
1365
+ kind: "parsed",
1366
+ brief: `Increase bounding box height by this amount (in pixels). Can be negative to decrease. Default: ${DEFAULT_HEIGHT_INCREMENT}`,
1367
+ parse: Number,
1368
+ optional: true
1369
+ },
1370
+ precision: {
1371
+ kind: "parsed",
1372
+ brief: `Number of decimal places for coordinates. Use -1 for full precision (no rounding). Default: ${DEFAULT_LABEL_STUDIO_PRECISION}`,
1373
+ parse: Number,
1374
+ optional: true
1157
1375
  }
1158
1376
  }
1159
1377
  },
@@ -1210,6 +1428,30 @@ var toPPOCRCommand = buildCommand2({
1210
1428
  brief: `Sort bounding boxes horizontally. Options: "${SORT_HORIZONTAL_NONE}" (default), "${SORT_HORIZONTAL_LTR}", "${SORT_HORIZONTAL_RTL}"`,
1211
1429
  parse: String,
1212
1430
  optional: true
1431
+ },
1432
+ normalizeShape: {
1433
+ kind: "parsed",
1434
+ brief: `Normalize diamond-like shapes to axis-aligned rectangles. Options: "${SHAPE_NORMALIZE_NONE}" (default), "${SHAPE_NORMALIZE_RECTANGLE}"`,
1435
+ parse: String,
1436
+ optional: true
1437
+ },
1438
+ widthIncrement: {
1439
+ kind: "parsed",
1440
+ brief: `Increase bounding box width by this amount (in pixels). Can be negative to decrease. Default: ${DEFAULT_WIDTH_INCREMENT}`,
1441
+ parse: Number,
1442
+ optional: true
1443
+ },
1444
+ heightIncrement: {
1445
+ kind: "parsed",
1446
+ brief: `Increase bounding box height by this amount (in pixels). Can be negative to decrease. Default: ${DEFAULT_HEIGHT_INCREMENT}`,
1447
+ parse: Number,
1448
+ optional: true
1449
+ },
1450
+ precision: {
1451
+ kind: "parsed",
1452
+ brief: `Number of decimal places for coordinates. Use -1 for full precision (no rounding). Default: ${DEFAULT_PPOCR_PRECISION} (integers)`,
1453
+ parse: Number,
1454
+ optional: true
1213
1455
  }
1214
1456
  }
1215
1457
  },