react-native-rectangle-doc-scanner 0.31.0 → 0.32.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/DocScanner.js +28 -17
- package/package.json +1 -1
- package/src/DocScanner.tsx +32 -18
package/dist/DocScanner.js
CHANGED
|
@@ -196,9 +196,12 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
196
196
|
if (areaRatio < 0.00002 || areaRatio > 0.99) {
|
|
197
197
|
continue;
|
|
198
198
|
}
|
|
199
|
+
// Use convex hull to simplify contour before polygon approximation
|
|
200
|
+
const hull = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
|
|
201
|
+
react_native_fast_opencv_1.OpenCV.invoke('convexHull', contour, hull);
|
|
199
202
|
step = `contour_${i}_arcLength`;
|
|
200
203
|
reportStage(step);
|
|
201
|
-
const { value: perimeter } = react_native_fast_opencv_1.OpenCV.invoke('arcLength',
|
|
204
|
+
const { value: perimeter } = react_native_fast_opencv_1.OpenCV.invoke('arcLength', hull, true);
|
|
202
205
|
const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
|
|
203
206
|
let approxArray = [];
|
|
204
207
|
// Start with smaller epsilon for more accurate corner detection
|
|
@@ -208,7 +211,7 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
208
211
|
const epsilon = epsilonValues[attempt] * perimeter;
|
|
209
212
|
step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
|
|
210
213
|
reportStage(step);
|
|
211
|
-
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP',
|
|
214
|
+
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP', hull, approx, epsilon, true);
|
|
212
215
|
step = `contour_${i}_toJS_attempt_${attempt}`;
|
|
213
216
|
reportStage(step);
|
|
214
217
|
const approxValue = react_native_fast_opencv_1.OpenCV.toJSValue(approx);
|
|
@@ -222,27 +225,35 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
222
225
|
}
|
|
223
226
|
}
|
|
224
227
|
if (approxArray.length !== 4) {
|
|
225
|
-
// fallback:
|
|
228
|
+
// fallback: rotated rectangle using minAreaRect
|
|
226
229
|
try {
|
|
227
|
-
const rect = react_native_fast_opencv_1.OpenCV.invoke('
|
|
230
|
+
const rect = react_native_fast_opencv_1.OpenCV.invoke('minAreaRect', contour);
|
|
228
231
|
const rectValue = rect?.value ?? rect;
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const rectW = rectValue.width ?? rectValue
|
|
232
|
-
const rectH = rectValue.height ?? rectValue
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
const centerX = rectValue.centerX ?? rectValue.center?.x ?? 0;
|
|
233
|
+
const centerY = rectValue.centerY ?? rectValue.center?.y ?? 0;
|
|
234
|
+
const rectW = rectValue.width ?? rectValue.size?.width ?? 0;
|
|
235
|
+
const rectH = rectValue.height ?? rectValue.size?.height ?? 0;
|
|
236
|
+
const angleDeg = rectValue.angle ?? rectValue.rotation ?? 0;
|
|
237
|
+
if (rectW > 0 && rectH > 0) {
|
|
238
|
+
const angleRad = (angleDeg * Math.PI) / 180;
|
|
239
|
+
const cosA = Math.cos(angleRad);
|
|
240
|
+
const sinA = Math.sin(angleRad);
|
|
241
|
+
const halfW = rectW / 2;
|
|
242
|
+
const halfH = rectH / 2;
|
|
243
|
+
approxArray = [
|
|
244
|
+
{ x: centerX - halfW * cosA + halfH * sinA, y: centerY - halfW * sinA - halfH * cosA },
|
|
245
|
+
{ x: centerX + halfW * cosA + halfH * sinA, y: centerY + halfW * sinA - halfH * cosA },
|
|
246
|
+
{ x: centerX + halfW * cosA - halfH * sinA, y: centerY + halfW * sinA + halfH * cosA },
|
|
247
|
+
{ x: centerX - halfW * cosA - halfH * sinA, y: centerY - halfW * sinA + halfH * cosA },
|
|
248
|
+
];
|
|
249
|
+
if (__DEV__) {
|
|
250
|
+
console.log('[DocScanner] minAreaRect fallback', rectValue, approxArray);
|
|
251
|
+
}
|
|
241
252
|
}
|
|
242
253
|
}
|
|
243
254
|
catch (err) {
|
|
244
255
|
if (__DEV__) {
|
|
245
|
-
console.warn('[DocScanner]
|
|
256
|
+
console.warn('[DocScanner] minAreaRect fallback failed', err);
|
|
246
257
|
}
|
|
247
258
|
}
|
|
248
259
|
}
|
package/package.json
CHANGED
package/src/DocScanner.tsx
CHANGED
|
@@ -224,9 +224,13 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
224
224
|
continue;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
+
// Use convex hull to simplify contour before polygon approximation
|
|
228
|
+
const hull = OpenCV.createObject(ObjectType.PointVector);
|
|
229
|
+
OpenCV.invoke('convexHull', contour, hull);
|
|
230
|
+
|
|
227
231
|
step = `contour_${i}_arcLength`;
|
|
228
232
|
reportStage(step);
|
|
229
|
-
const { value: perimeter } = OpenCV.invoke('arcLength',
|
|
233
|
+
const { value: perimeter } = OpenCV.invoke('arcLength', hull, true);
|
|
230
234
|
const approx = OpenCV.createObject(ObjectType.PointVector);
|
|
231
235
|
|
|
232
236
|
let approxArray: Array<{ x: number; y: number }> = [];
|
|
@@ -239,7 +243,7 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
239
243
|
const epsilon = epsilonValues[attempt] * perimeter;
|
|
240
244
|
step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
|
|
241
245
|
reportStage(step);
|
|
242
|
-
OpenCV.invoke('approxPolyDP',
|
|
246
|
+
OpenCV.invoke('approxPolyDP', hull, approx, epsilon, true);
|
|
243
247
|
|
|
244
248
|
step = `contour_${i}_toJS_attempt_${attempt}`;
|
|
245
249
|
reportStage(step);
|
|
@@ -257,28 +261,38 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
257
261
|
}
|
|
258
262
|
|
|
259
263
|
if (approxArray.length !== 4) {
|
|
260
|
-
// fallback:
|
|
264
|
+
// fallback: rotated rectangle using minAreaRect
|
|
261
265
|
try {
|
|
262
|
-
const rect = OpenCV.invoke('
|
|
266
|
+
const rect = OpenCV.invoke('minAreaRect', contour);
|
|
263
267
|
const rectValue = rect?.value ?? rect;
|
|
264
|
-
const rectX = rectValue.x ?? rectValue?.topLeft?.x ?? 0;
|
|
265
|
-
const rectY = rectValue.y ?? rectValue?.topLeft?.y ?? 0;
|
|
266
|
-
const rectW = rectValue.width ?? rectValue?.size?.width ?? 0;
|
|
267
|
-
const rectH = rectValue.height ?? rectValue?.size?.height ?? 0;
|
|
268
|
-
|
|
269
|
-
approxArray = [
|
|
270
|
-
{ x: rectX, y: rectY },
|
|
271
|
-
{ x: rectX + rectW, y: rectY },
|
|
272
|
-
{ x: rectX + rectW, y: rectY + rectH },
|
|
273
|
-
{ x: rectX, y: rectY + rectH },
|
|
274
|
-
];
|
|
275
268
|
|
|
276
|
-
|
|
277
|
-
|
|
269
|
+
const centerX = rectValue.centerX ?? rectValue.center?.x ?? 0;
|
|
270
|
+
const centerY = rectValue.centerY ?? rectValue.center?.y ?? 0;
|
|
271
|
+
const rectW = rectValue.width ?? rectValue.size?.width ?? 0;
|
|
272
|
+
const rectH = rectValue.height ?? rectValue.size?.height ?? 0;
|
|
273
|
+
const angleDeg = rectValue.angle ?? rectValue.rotation ?? 0;
|
|
274
|
+
|
|
275
|
+
if (rectW > 0 && rectH > 0) {
|
|
276
|
+
const angleRad = (angleDeg * Math.PI) / 180;
|
|
277
|
+
const cosA = Math.cos(angleRad);
|
|
278
|
+
const sinA = Math.sin(angleRad);
|
|
279
|
+
const halfW = rectW / 2;
|
|
280
|
+
const halfH = rectH / 2;
|
|
281
|
+
|
|
282
|
+
approxArray = [
|
|
283
|
+
{ x: centerX - halfW * cosA + halfH * sinA, y: centerY - halfW * sinA - halfH * cosA },
|
|
284
|
+
{ x: centerX + halfW * cosA + halfH * sinA, y: centerY + halfW * sinA - halfH * cosA },
|
|
285
|
+
{ x: centerX + halfW * cosA - halfH * sinA, y: centerY + halfW * sinA + halfH * cosA },
|
|
286
|
+
{ x: centerX - halfW * cosA - halfH * sinA, y: centerY - halfW * sinA + halfH * cosA },
|
|
287
|
+
];
|
|
288
|
+
|
|
289
|
+
if (__DEV__) {
|
|
290
|
+
console.log('[DocScanner] minAreaRect fallback', rectValue, approxArray);
|
|
291
|
+
}
|
|
278
292
|
}
|
|
279
293
|
} catch (err) {
|
|
280
294
|
if (__DEV__) {
|
|
281
|
-
console.warn('[DocScanner]
|
|
295
|
+
console.warn('[DocScanner] minAreaRect fallback failed', err);
|
|
282
296
|
}
|
|
283
297
|
}
|
|
284
298
|
}
|