react-native-rectangle-doc-scanner 0.30.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 +31 -22
- package/package.json +1 -1
- package/src/DocScanner.tsx +35 -23
package/dist/DocScanner.js
CHANGED
|
@@ -183,8 +183,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
183
183
|
step = `contour_${i}_area_abs`;
|
|
184
184
|
reportStage(step);
|
|
185
185
|
const { value: area } = react_native_fast_opencv_1.OpenCV.invoke('contourArea', contour, false);
|
|
186
|
-
// Skip extremely small contours
|
|
187
|
-
if (area <
|
|
186
|
+
// Skip extremely small contours, but keep threshold very low to allow distant documents
|
|
187
|
+
if (area < 200) {
|
|
188
188
|
continue;
|
|
189
189
|
}
|
|
190
190
|
step = `contour_${i}_area`; // ratio stage
|
|
@@ -193,14 +193,15 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
193
193
|
if (__DEV__) {
|
|
194
194
|
console.log('[DocScanner] area', area, 'ratio', areaRatio);
|
|
195
195
|
}
|
|
196
|
-
|
|
197
|
-
// This prevents detecting tiny noise or the entire frame
|
|
198
|
-
if (areaRatio < 0.0001 || areaRatio > 0.99) {
|
|
196
|
+
if (areaRatio < 0.00002 || areaRatio > 0.99) {
|
|
199
197
|
continue;
|
|
200
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);
|
|
201
202
|
step = `contour_${i}_arcLength`;
|
|
202
203
|
reportStage(step);
|
|
203
|
-
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);
|
|
204
205
|
const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
|
|
205
206
|
let approxArray = [];
|
|
206
207
|
// Start with smaller epsilon for more accurate corner detection
|
|
@@ -210,7 +211,7 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
210
211
|
const epsilon = epsilonValues[attempt] * perimeter;
|
|
211
212
|
step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
|
|
212
213
|
reportStage(step);
|
|
213
|
-
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP',
|
|
214
|
+
react_native_fast_opencv_1.OpenCV.invoke('approxPolyDP', hull, approx, epsilon, true);
|
|
214
215
|
step = `contour_${i}_toJS_attempt_${attempt}`;
|
|
215
216
|
reportStage(step);
|
|
216
217
|
const approxValue = react_native_fast_opencv_1.OpenCV.toJSValue(approx);
|
|
@@ -224,27 +225,35 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
|
|
|
224
225
|
}
|
|
225
226
|
}
|
|
226
227
|
if (approxArray.length !== 4) {
|
|
227
|
-
// fallback:
|
|
228
|
+
// fallback: rotated rectangle using minAreaRect
|
|
228
229
|
try {
|
|
229
|
-
const rect = react_native_fast_opencv_1.OpenCV.invoke('
|
|
230
|
+
const rect = react_native_fast_opencv_1.OpenCV.invoke('minAreaRect', contour);
|
|
230
231
|
const rectValue = rect?.value ?? rect;
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const rectW = rectValue.width ?? rectValue
|
|
234
|
-
const rectH = rectValue.height ?? rectValue
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
+
}
|
|
243
252
|
}
|
|
244
253
|
}
|
|
245
254
|
catch (err) {
|
|
246
255
|
if (__DEV__) {
|
|
247
|
-
console.warn('[DocScanner]
|
|
256
|
+
console.warn('[DocScanner] minAreaRect fallback failed', err);
|
|
248
257
|
}
|
|
249
258
|
}
|
|
250
259
|
}
|
package/package.json
CHANGED
package/src/DocScanner.tsx
CHANGED
|
@@ -207,8 +207,8 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
207
207
|
reportStage(step);
|
|
208
208
|
const { value: area } = OpenCV.invoke('contourArea', contour, false);
|
|
209
209
|
|
|
210
|
-
// Skip extremely small contours
|
|
211
|
-
if (area <
|
|
210
|
+
// Skip extremely small contours, but keep threshold very low to allow distant documents
|
|
211
|
+
if (area < 200) {
|
|
212
212
|
continue;
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -220,15 +220,17 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
220
220
|
console.log('[DocScanner] area', area, 'ratio', areaRatio);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
// This prevents detecting tiny noise or the entire frame
|
|
225
|
-
if (areaRatio < 0.0001 || areaRatio > 0.99) {
|
|
223
|
+
if (areaRatio < 0.00002 || areaRatio > 0.99) {
|
|
226
224
|
continue;
|
|
227
225
|
}
|
|
228
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
|
+
|
|
229
231
|
step = `contour_${i}_arcLength`;
|
|
230
232
|
reportStage(step);
|
|
231
|
-
const { value: perimeter } = OpenCV.invoke('arcLength',
|
|
233
|
+
const { value: perimeter } = OpenCV.invoke('arcLength', hull, true);
|
|
232
234
|
const approx = OpenCV.createObject(ObjectType.PointVector);
|
|
233
235
|
|
|
234
236
|
let approxArray: Array<{ x: number; y: number }> = [];
|
|
@@ -241,7 +243,7 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
241
243
|
const epsilon = epsilonValues[attempt] * perimeter;
|
|
242
244
|
step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
|
|
243
245
|
reportStage(step);
|
|
244
|
-
OpenCV.invoke('approxPolyDP',
|
|
246
|
+
OpenCV.invoke('approxPolyDP', hull, approx, epsilon, true);
|
|
245
247
|
|
|
246
248
|
step = `contour_${i}_toJS_attempt_${attempt}`;
|
|
247
249
|
reportStage(step);
|
|
@@ -259,28 +261,38 @@ export const DocScanner: React.FC<Props> = ({
|
|
|
259
261
|
}
|
|
260
262
|
|
|
261
263
|
if (approxArray.length !== 4) {
|
|
262
|
-
// fallback:
|
|
264
|
+
// fallback: rotated rectangle using minAreaRect
|
|
263
265
|
try {
|
|
264
|
-
const rect = OpenCV.invoke('
|
|
266
|
+
const rect = OpenCV.invoke('minAreaRect', contour);
|
|
265
267
|
const rectValue = rect?.value ?? rect;
|
|
266
|
-
const rectX = rectValue.x ?? rectValue?.topLeft?.x ?? 0;
|
|
267
|
-
const rectY = rectValue.y ?? rectValue?.topLeft?.y ?? 0;
|
|
268
|
-
const rectW = rectValue.width ?? rectValue?.size?.width ?? 0;
|
|
269
|
-
const rectH = rectValue.height ?? rectValue?.size?.height ?? 0;
|
|
270
|
-
|
|
271
|
-
approxArray = [
|
|
272
|
-
{ x: rectX, y: rectY },
|
|
273
|
-
{ x: rectX + rectW, y: rectY },
|
|
274
|
-
{ x: rectX + rectW, y: rectY + rectH },
|
|
275
|
-
{ x: rectX, y: rectY + rectH },
|
|
276
|
-
];
|
|
277
268
|
|
|
278
|
-
|
|
279
|
-
|
|
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
|
+
}
|
|
280
292
|
}
|
|
281
293
|
} catch (err) {
|
|
282
294
|
if (__DEV__) {
|
|
283
|
-
console.warn('[DocScanner]
|
|
295
|
+
console.warn('[DocScanner] minAreaRect fallback failed', err);
|
|
284
296
|
}
|
|
285
297
|
}
|
|
286
298
|
}
|