react-native-rectangle-doc-scanner 0.28.0 → 0.30.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.
@@ -150,29 +150,20 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
150
150
  step = 'cvtColor';
151
151
  reportStage(step);
152
152
  react_native_fast_opencv_1.OpenCV.invoke('cvtColor', mat, mat, react_native_fast_opencv_1.ColorConversionCodes.COLOR_BGR2GRAY);
153
- // Apply Gaussian blur to reduce noise
154
- const gaussianKernel = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 5, 5);
155
- step = 'GaussianBlur';
156
- reportStage(step);
157
- react_native_fast_opencv_1.OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
158
- // Morphological operations to enhance edges
159
- const morphologyKernel = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 3, 3);
153
+ const morphologyKernel = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 5, 5);
160
154
  step = 'getStructuringElement';
161
155
  reportStage(step);
162
156
  const element = react_native_fast_opencv_1.OpenCV.invoke('getStructuringElement', react_native_fast_opencv_1.MorphShapes.MORPH_RECT, morphologyKernel);
163
157
  step = 'morphologyEx';
164
158
  reportStage(step);
165
- react_native_fast_opencv_1.OpenCV.invoke('morphologyEx', mat, mat, react_native_fast_opencv_1.MorphTypes.MORPH_CLOSE, element);
166
- // Canny edge detection with optimized thresholds
159
+ react_native_fast_opencv_1.OpenCV.invoke('morphologyEx', mat, mat, react_native_fast_opencv_1.MorphTypes.MORPH_OPEN, element);
160
+ const gaussianKernel = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 5, 5);
161
+ step = 'GaussianBlur';
162
+ reportStage(step);
163
+ react_native_fast_opencv_1.OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
167
164
  step = 'Canny';
168
165
  reportStage(step);
169
166
  react_native_fast_opencv_1.OpenCV.invoke('Canny', mat, mat, 50, 150);
170
- // Dilate edges slightly to connect nearby contours
171
- step = 'dilate';
172
- reportStage(step);
173
- const dilateKernel = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.Size, 2, 2);
174
- const dilateElement = react_native_fast_opencv_1.OpenCV.invoke('getStructuringElement', react_native_fast_opencv_1.MorphShapes.MORPH_RECT, dilateKernel);
175
- react_native_fast_opencv_1.OpenCV.invoke('dilate', mat, mat, dilateElement);
176
167
  step = 'createContours';
177
168
  reportStage(step);
178
169
  const contours = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVectorOfVectors);
@@ -188,16 +179,23 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
188
179
  step = `contour_${i}_copy`;
189
180
  reportStage(step);
190
181
  const contour = react_native_fast_opencv_1.OpenCV.copyObjectFromVector(contours, i);
191
- step = `contour_${i}_area`;
182
+ // Compute absolute area first
183
+ step = `contour_${i}_area_abs`;
192
184
  reportStage(step);
193
185
  const { value: area } = react_native_fast_opencv_1.OpenCV.invoke('contourArea', contour, false);
186
+ // Skip extremely small contours below an absolute pixel threshold
187
+ if (area < 500) {
188
+ continue;
189
+ }
190
+ step = `contour_${i}_area`; // ratio stage
191
+ reportStage(step);
194
192
  const areaRatio = area / frameArea;
195
193
  if (__DEV__) {
196
- console.log('[DocScanner] area ratio', areaRatio);
194
+ console.log('[DocScanner] area', area, 'ratio', areaRatio);
197
195
  }
198
- // Filter by area: document should be at least 5% and at most 95% of frame
196
+ // Filter by area: document should be at least 0.01% and at most 99% of frame
199
197
  // This prevents detecting tiny noise or the entire frame
200
- if (areaRatio < 0.05 || areaRatio > 0.95) {
198
+ if (areaRatio < 0.0001 || areaRatio > 0.99) {
201
199
  continue;
202
200
  }
203
201
  step = `contour_${i}_arcLength`;
@@ -206,8 +204,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
206
204
  const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
207
205
  let approxArray = [];
208
206
  // Start with smaller epsilon for more accurate corner detection
209
- // Try epsilon values from 0.5% to 5% of perimeter
210
- const epsilonValues = [0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05];
207
+ // Try epsilon values from 0.4% up to 6% of perimeter
208
+ const epsilonValues = [0.004, 0.006, 0.008, 0.01, 0.012, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06];
211
209
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
212
210
  const epsilon = epsilonValues[attempt] * perimeter;
213
211
  step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
@@ -225,7 +223,32 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
225
223
  break;
226
224
  }
227
225
  }
228
- // Only proceed if we found exactly 4 corners
226
+ if (approxArray.length !== 4) {
227
+ // fallback: boundingRect (axis-aligned) so we always have 4 points
228
+ try {
229
+ const rect = react_native_fast_opencv_1.OpenCV.invoke('boundingRect', contour);
230
+ const rectValue = rect?.value ?? rect;
231
+ const rectX = rectValue.x ?? rectValue?.topLeft?.x ?? 0;
232
+ const rectY = rectValue.y ?? rectValue?.topLeft?.y ?? 0;
233
+ const rectW = rectValue.width ?? rectValue?.size?.width ?? 0;
234
+ const rectH = rectValue.height ?? rectValue?.size?.height ?? 0;
235
+ approxArray = [
236
+ { x: rectX, y: rectY },
237
+ { x: rectX + rectW, y: rectY },
238
+ { x: rectX + rectW, y: rectY + rectH },
239
+ { x: rectX, y: rectY + rectH },
240
+ ];
241
+ if (__DEV__) {
242
+ console.log('[DocScanner] boundingRect fallback', approxArray);
243
+ }
244
+ }
245
+ catch (err) {
246
+ if (__DEV__) {
247
+ console.warn('[DocScanner] boundingRect fallback failed', err);
248
+ }
249
+ }
250
+ }
251
+ // Only proceed if we found exactly 4 corners after fallback
229
252
  if (approxArray.length !== 4) {
230
253
  continue;
231
254
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -167,33 +167,22 @@ export const DocScanner: React.FC<Props> = ({
167
167
  reportStage(step);
168
168
  OpenCV.invoke('cvtColor', mat, mat, ColorConversionCodes.COLOR_BGR2GRAY);
169
169
 
170
- // Apply Gaussian blur to reduce noise
171
- const gaussianKernel = OpenCV.createObject(ObjectType.Size, 5, 5);
172
- step = 'GaussianBlur';
173
- reportStage(step);
174
- OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
175
-
176
- // Morphological operations to enhance edges
177
- const morphologyKernel = OpenCV.createObject(ObjectType.Size, 3, 3);
170
+ const morphologyKernel = OpenCV.createObject(ObjectType.Size, 5, 5);
178
171
  step = 'getStructuringElement';
179
172
  reportStage(step);
180
173
  const element = OpenCV.invoke('getStructuringElement', MorphShapes.MORPH_RECT, morphologyKernel);
181
174
  step = 'morphologyEx';
182
175
  reportStage(step);
183
- OpenCV.invoke('morphologyEx', mat, mat, MorphTypes.MORPH_CLOSE, element);
176
+ OpenCV.invoke('morphologyEx', mat, mat, MorphTypes.MORPH_OPEN, element);
184
177
 
185
- // Canny edge detection with optimized thresholds
178
+ const gaussianKernel = OpenCV.createObject(ObjectType.Size, 5, 5);
179
+ step = 'GaussianBlur';
180
+ reportStage(step);
181
+ OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
186
182
  step = 'Canny';
187
183
  reportStage(step);
188
184
  OpenCV.invoke('Canny', mat, mat, 50, 150);
189
185
 
190
- // Dilate edges slightly to connect nearby contours
191
- step = 'dilate';
192
- reportStage(step);
193
- const dilateKernel = OpenCV.createObject(ObjectType.Size, 2, 2);
194
- const dilateElement = OpenCV.invoke('getStructuringElement', MorphShapes.MORPH_RECT, dilateKernel);
195
- OpenCV.invoke('dilate', mat, mat, dilateElement);
196
-
197
186
  step = 'createContours';
198
187
  reportStage(step);
199
188
  const contours = OpenCV.createObject(ObjectType.PointVectorOfVectors);
@@ -213,18 +202,27 @@ export const DocScanner: React.FC<Props> = ({
213
202
  reportStage(step);
214
203
  const contour = OpenCV.copyObjectFromVector(contours, i);
215
204
 
216
- step = `contour_${i}_area`;
205
+ // Compute absolute area first
206
+ step = `contour_${i}_area_abs`;
217
207
  reportStage(step);
218
208
  const { value: area } = OpenCV.invoke('contourArea', contour, false);
209
+
210
+ // Skip extremely small contours below an absolute pixel threshold
211
+ if (area < 500) {
212
+ continue;
213
+ }
214
+
215
+ step = `contour_${i}_area`; // ratio stage
216
+ reportStage(step);
219
217
  const areaRatio = area / frameArea;
220
218
 
221
219
  if (__DEV__) {
222
- console.log('[DocScanner] area ratio', areaRatio);
220
+ console.log('[DocScanner] area', area, 'ratio', areaRatio);
223
221
  }
224
222
 
225
- // Filter by area: document should be at least 5% and at most 95% of frame
223
+ // Filter by area: document should be at least 0.01% and at most 99% of frame
226
224
  // This prevents detecting tiny noise or the entire frame
227
- if (areaRatio < 0.05 || areaRatio > 0.95) {
225
+ if (areaRatio < 0.0001 || areaRatio > 0.99) {
228
226
  continue;
229
227
  }
230
228
 
@@ -236,8 +234,8 @@ export const DocScanner: React.FC<Props> = ({
236
234
  let approxArray: Array<{ x: number; y: number }> = [];
237
235
 
238
236
  // Start with smaller epsilon for more accurate corner detection
239
- // Try epsilon values from 0.5% to 5% of perimeter
240
- const epsilonValues = [0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05];
237
+ // Try epsilon values from 0.4% up to 6% of perimeter
238
+ const epsilonValues = [0.004, 0.006, 0.008, 0.01, 0.012, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06];
241
239
 
242
240
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
243
241
  const epsilon = epsilonValues[attempt] * perimeter;
@@ -260,7 +258,34 @@ export const DocScanner: React.FC<Props> = ({
260
258
  }
261
259
  }
262
260
 
263
- // Only proceed if we found exactly 4 corners
261
+ if (approxArray.length !== 4) {
262
+ // fallback: boundingRect (axis-aligned) so we always have 4 points
263
+ try {
264
+ const rect = OpenCV.invoke('boundingRect', contour);
265
+ 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
+
278
+ if (__DEV__) {
279
+ console.log('[DocScanner] boundingRect fallback', approxArray);
280
+ }
281
+ } catch (err) {
282
+ if (__DEV__) {
283
+ console.warn('[DocScanner] boundingRect fallback failed', err);
284
+ }
285
+ }
286
+ }
287
+
288
+ // Only proceed if we found exactly 4 corners after fallback
264
289
  if (approxArray.length !== 4) {
265
290
  continue;
266
291
  }