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.
@@ -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', contour, true);
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', contour, approx, epsilon, true);
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: boundingRect (axis-aligned) so we always have 4 points
228
+ // fallback: rotated rectangle using minAreaRect
226
229
  try {
227
- const rect = react_native_fast_opencv_1.OpenCV.invoke('boundingRect', contour);
230
+ const rect = react_native_fast_opencv_1.OpenCV.invoke('minAreaRect', contour);
228
231
  const rectValue = rect?.value ?? rect;
229
- const rectX = rectValue.x ?? rectValue?.topLeft?.x ?? 0;
230
- const rectY = rectValue.y ?? rectValue?.topLeft?.y ?? 0;
231
- const rectW = rectValue.width ?? rectValue?.size?.width ?? 0;
232
- const rectH = rectValue.height ?? rectValue?.size?.height ?? 0;
233
- approxArray = [
234
- { x: rectX, y: rectY },
235
- { x: rectX + rectW, y: rectY },
236
- { x: rectX + rectW, y: rectY + rectH },
237
- { x: rectX, y: rectY + rectH },
238
- ];
239
- if (__DEV__) {
240
- console.log('[DocScanner] boundingRect fallback', approxArray);
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] boundingRect fallback failed', err);
256
+ console.warn('[DocScanner] minAreaRect fallback failed', err);
246
257
  }
247
258
  }
248
259
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -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', contour, true);
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', contour, approx, epsilon, true);
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: boundingRect (axis-aligned) so we always have 4 points
264
+ // fallback: rotated rectangle using minAreaRect
261
265
  try {
262
- const rect = OpenCV.invoke('boundingRect', contour);
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
- if (__DEV__) {
277
- console.log('[DocScanner] boundingRect fallback', approxArray);
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] boundingRect fallback failed', err);
295
+ console.warn('[DocScanner] minAreaRect fallback failed', err);
282
296
  }
283
297
  }
284
298
  }