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