react-native-rectangle-doc-scanner 0.36.0 → 0.38.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.
@@ -91,12 +91,52 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
91
91
  (0, react_1.useEffect)(() => {
92
92
  requestPermission();
93
93
  }, [requestPermission]);
94
+ const lastQuadRef = (0, react_1.useRef)(null);
95
+ const smoothingBufferRef = (0, react_1.useRef)([]);
94
96
  const updateQuad = (0, react_native_worklets_core_1.useRunOnJS)((value) => {
95
97
  if (__DEV__) {
96
98
  console.log('[DocScanner] quad', value);
97
99
  }
98
- // Always update immediately for real-time tracking
99
- setQuad(value);
100
+ // Add to smoothing buffer
101
+ smoothingBufferRef.current.push(value);
102
+ // Keep only last 3 frames for smoothing
103
+ if (smoothingBufferRef.current.length > 3) {
104
+ smoothingBufferRef.current.shift();
105
+ }
106
+ // If we have a valid quad, smooth it
107
+ if (value && value.length === 4) {
108
+ // Average with previous frames if available
109
+ if (smoothingBufferRef.current.length >= 2) {
110
+ const validQuads = smoothingBufferRef.current.filter(q => q !== null && q.length === 4);
111
+ if (validQuads.length >= 2) {
112
+ // Average the positions
113
+ const smoothed = value.map((_, idx) => {
114
+ let sumX = 0;
115
+ let sumY = 0;
116
+ validQuads.forEach(quad => {
117
+ sumX += quad[idx].x;
118
+ sumY += quad[idx].y;
119
+ });
120
+ return {
121
+ x: sumX / validQuads.length,
122
+ y: sumY / validQuads.length,
123
+ };
124
+ });
125
+ lastQuadRef.current = smoothed;
126
+ setQuad(smoothed);
127
+ return;
128
+ }
129
+ }
130
+ lastQuadRef.current = value;
131
+ setQuad(value);
132
+ }
133
+ else if (lastQuadRef.current) {
134
+ // Keep showing last quad for 1 frame to reduce flickering
135
+ setQuad(lastQuadRef.current);
136
+ }
137
+ else {
138
+ setQuad(null);
139
+ }
100
140
  }, []);
101
141
  const reportError = (0, react_native_worklets_core_1.useRunOnJS)((step, error) => {
102
142
  const message = error instanceof Error ? error.message : `${error}`;
@@ -115,8 +155,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
115
155
  try {
116
156
  // Report frame size for coordinate transformation
117
157
  updateFrameSize(frame.width, frame.height);
118
- // Use higher resolution for better accuracy - 720p instead of 480p
119
- const ratio = 720 / frame.width;
158
+ // Use higher resolution for better accuracy - 960p
159
+ const ratio = 960 / frame.width;
120
160
  const width = Math.floor(frame.width * ratio);
121
161
  const height = Math.floor(frame.height * ratio);
122
162
  step = 'resize';
@@ -145,7 +185,7 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
145
185
  react_native_fast_opencv_1.OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
146
186
  step = 'Canny';
147
187
  reportStage(step);
148
- react_native_fast_opencv_1.OpenCV.invoke('Canny', mat, mat, 50, 150);
188
+ react_native_fast_opencv_1.OpenCV.invoke('Canny', mat, mat, 30, 100);
149
189
  step = 'createContours';
150
190
  reportStage(step);
151
191
  const contours = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVectorOfVectors);
@@ -179,7 +219,7 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
179
219
  console.log('[DocScanner] area', area, 'ratio', areaRatio);
180
220
  }
181
221
  // Skip if area ratio is too small or too large
182
- if (areaRatio < 0.02 || areaRatio > 0.95) {
222
+ if (areaRatio < 0.005 || areaRatio > 0.95) {
183
223
  continue;
184
224
  }
185
225
  step = `contour_${i}_arcLength`;
@@ -187,8 +227,11 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
187
227
  const { value: perimeter } = react_native_fast_opencv_1.OpenCV.invoke('arcLength', contour, true);
188
228
  const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
189
229
  let approxArray = [];
190
- // Try epsilon values from 0.2% to 8% of perimeter
191
- const epsilonValues = [0.002, 0.004, 0.006, 0.008, 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08];
230
+ // Try more epsilon values from 0.1% to 10% for difficult shapes
231
+ const epsilonValues = [
232
+ 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009,
233
+ 0.01, 0.012, 0.015, 0.018, 0.02, 0.025, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1
234
+ ];
192
235
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
193
236
  const epsilon = epsilonValues[attempt] * perimeter;
194
237
  step = `contour_${i}_approxPolyDP_attempt_${attempt}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -96,13 +96,57 @@ export const DocScanner: React.FC<Props> = ({
96
96
  requestPermission();
97
97
  }, [requestPermission]);
98
98
 
99
+ const lastQuadRef = useRef<Point[] | null>(null);
100
+ const smoothingBufferRef = useRef<(Point[] | null)[]>([]);
101
+
99
102
  const updateQuad = useRunOnJS((value: Point[] | null) => {
100
103
  if (__DEV__) {
101
104
  console.log('[DocScanner] quad', value);
102
105
  }
103
106
 
104
- // Always update immediately for real-time tracking
105
- setQuad(value);
107
+ // Add to smoothing buffer
108
+ smoothingBufferRef.current.push(value);
109
+
110
+ // Keep only last 3 frames for smoothing
111
+ if (smoothingBufferRef.current.length > 3) {
112
+ smoothingBufferRef.current.shift();
113
+ }
114
+
115
+ // If we have a valid quad, smooth it
116
+ if (value && value.length === 4) {
117
+ // Average with previous frames if available
118
+ if (smoothingBufferRef.current.length >= 2) {
119
+ const validQuads = smoothingBufferRef.current.filter(q => q !== null && q.length === 4) as Point[][];
120
+
121
+ if (validQuads.length >= 2) {
122
+ // Average the positions
123
+ const smoothed: Point[] = value.map((_, idx) => {
124
+ let sumX = 0;
125
+ let sumY = 0;
126
+ validQuads.forEach(quad => {
127
+ sumX += quad[idx].x;
128
+ sumY += quad[idx].y;
129
+ });
130
+ return {
131
+ x: sumX / validQuads.length,
132
+ y: sumY / validQuads.length,
133
+ };
134
+ });
135
+
136
+ lastQuadRef.current = smoothed;
137
+ setQuad(smoothed);
138
+ return;
139
+ }
140
+ }
141
+
142
+ lastQuadRef.current = value;
143
+ setQuad(value);
144
+ } else if (lastQuadRef.current) {
145
+ // Keep showing last quad for 1 frame to reduce flickering
146
+ setQuad(lastQuadRef.current);
147
+ } else {
148
+ setQuad(null);
149
+ }
106
150
  }, []);
107
151
 
108
152
  const reportError = useRunOnJS((step: string, error: unknown) => {
@@ -128,8 +172,8 @@ export const DocScanner: React.FC<Props> = ({
128
172
  // Report frame size for coordinate transformation
129
173
  updateFrameSize(frame.width, frame.height);
130
174
 
131
- // Use higher resolution for better accuracy - 720p instead of 480p
132
- const ratio = 720 / frame.width;
175
+ // Use higher resolution for better accuracy - 960p
176
+ const ratio = 960 / frame.width;
133
177
  const width = Math.floor(frame.width * ratio);
134
178
  const height = Math.floor(frame.height * ratio);
135
179
  step = 'resize';
@@ -162,7 +206,7 @@ export const DocScanner: React.FC<Props> = ({
162
206
  OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
163
207
  step = 'Canny';
164
208
  reportStage(step);
165
- OpenCV.invoke('Canny', mat, mat, 50, 150);
209
+ OpenCV.invoke('Canny', mat, mat, 30, 100);
166
210
 
167
211
  step = 'createContours';
168
212
  reportStage(step);
@@ -206,7 +250,7 @@ export const DocScanner: React.FC<Props> = ({
206
250
  }
207
251
 
208
252
  // Skip if area ratio is too small or too large
209
- if (areaRatio < 0.02 || areaRatio > 0.95) {
253
+ if (areaRatio < 0.005 || areaRatio > 0.95) {
210
254
  continue;
211
255
  }
212
256
 
@@ -217,8 +261,11 @@ export const DocScanner: React.FC<Props> = ({
217
261
 
218
262
  let approxArray: Array<{ x: number; y: number }> = [];
219
263
 
220
- // Try epsilon values from 0.2% to 8% of perimeter
221
- const epsilonValues = [0.002, 0.004, 0.006, 0.008, 0.01, 0.015, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08];
264
+ // Try more epsilon values from 0.1% to 10% for difficult shapes
265
+ const epsilonValues = [
266
+ 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009,
267
+ 0.01, 0.012, 0.015, 0.018, 0.02, 0.025, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1
268
+ ];
222
269
 
223
270
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
224
271
  const epsilon = epsilonValues[attempt] * perimeter;