react-native-rectangle-doc-scanner 0.35.0 → 0.37.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.
@@ -92,36 +92,58 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
92
92
  requestPermission();
93
93
  }, [requestPermission]);
94
94
  const lastQuadRef = (0, react_1.useRef)(null);
95
- const noQuadFramesRef = (0, react_1.useRef)(0);
95
+ const smoothingBufferRef = (0, react_1.useRef)([]);
96
96
  const updateQuad = (0, react_native_worklets_core_1.useRunOnJS)((value) => {
97
97
  if (__DEV__) {
98
98
  console.log('[DocScanner] quad', value);
99
99
  }
100
- if (value) {
101
- // Found a quad, reset counter and update
102
- noQuadFramesRef.current = 0;
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
+ }
103
130
  lastQuadRef.current = value;
104
131
  setQuad(value);
105
132
  }
133
+ else if (lastQuadRef.current) {
134
+ // Keep showing last quad for 1 frame to reduce flickering
135
+ setQuad(lastQuadRef.current);
136
+ }
106
137
  else {
107
- // No quad found, keep the last one for a few frames
108
- noQuadFramesRef.current += 1;
109
- // Keep the last quad for up to 3 frames
110
- if (noQuadFramesRef.current > 3) {
111
- lastQuadRef.current = null;
112
- setQuad(null);
113
- }
114
- // Otherwise, keep showing the last quad
138
+ setQuad(null);
115
139
  }
116
140
  }, []);
117
141
  const reportError = (0, react_native_worklets_core_1.useRunOnJS)((step, error) => {
118
142
  const message = error instanceof Error ? error.message : `${error}`;
119
143
  console.warn(`[DocScanner] frame error at ${step}: ${message}`);
120
144
  }, []);
121
- const reportStage = (0, react_native_worklets_core_1.useRunOnJS)((stage) => {
122
- if (__DEV__) {
123
- console.log('[DocScanner] stage', stage);
124
- }
145
+ const reportStage = (0, react_native_worklets_core_1.useRunOnJS)((_stage) => {
146
+ // Disabled for performance
125
147
  }, []);
126
148
  const [frameSize, setFrameSize] = (0, react_1.useState)(null);
127
149
  const updateFrameSize = (0, react_native_worklets_core_1.useRunOnJS)((width, height) => {
@@ -133,8 +155,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
133
155
  try {
134
156
  // Report frame size for coordinate transformation
135
157
  updateFrameSize(frame.width, frame.height);
136
- // Use higher resolution for better accuracy - 720p instead of 480p
137
- const ratio = 720 / frame.width;
158
+ // Use higher resolution for better accuracy - 960p
159
+ const ratio = 960 / frame.width;
138
160
  const width = Math.floor(frame.width * ratio);
139
161
  const height = Math.floor(frame.height * ratio);
140
162
  step = 'resize';
@@ -163,7 +185,7 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
163
185
  react_native_fast_opencv_1.OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
164
186
  step = 'Canny';
165
187
  reportStage(step);
166
- 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);
167
189
  step = 'createContours';
168
190
  reportStage(step);
169
191
  const contours = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVectorOfVectors);
@@ -205,8 +227,8 @@ const DocScanner = ({ onCapture, overlayColor = '#e7a649', autoCapture = true, m
205
227
  const { value: perimeter } = react_native_fast_opencv_1.OpenCV.invoke('arcLength', contour, true);
206
228
  const approx = react_native_fast_opencv_1.OpenCV.createObject(react_native_fast_opencv_1.ObjectType.PointVector);
207
229
  let approxArray = [];
208
- // Try epsilon values from 0.2% to 8% of perimeter
209
- 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 epsilon values from 0.3% to 6% of perimeter for better corner detection
231
+ const epsilonValues = [0.003, 0.005, 0.007, 0.009, 0.011, 0.013, 0.015, 0.018, 0.02, 0.025, 0.03, 0.04, 0.05, 0.06];
210
232
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
211
233
  const epsilon = epsilonValues[attempt] * perimeter;
212
234
  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.35.0",
3
+ "version": "0.37.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -97,28 +97,55 @@ export const DocScanner: React.FC<Props> = ({
97
97
  }, [requestPermission]);
98
98
 
99
99
  const lastQuadRef = useRef<Point[] | null>(null);
100
- const noQuadFramesRef = useRef(0);
100
+ const smoothingBufferRef = useRef<(Point[] | null)[]>([]);
101
101
 
102
102
  const updateQuad = useRunOnJS((value: Point[] | null) => {
103
103
  if (__DEV__) {
104
104
  console.log('[DocScanner] quad', value);
105
105
  }
106
106
 
107
- if (value) {
108
- // Found a quad, reset counter and update
109
- noQuadFramesRef.current = 0;
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
+
110
142
  lastQuadRef.current = value;
111
143
  setQuad(value);
144
+ } else if (lastQuadRef.current) {
145
+ // Keep showing last quad for 1 frame to reduce flickering
146
+ setQuad(lastQuadRef.current);
112
147
  } else {
113
- // No quad found, keep the last one for a few frames
114
- noQuadFramesRef.current += 1;
115
-
116
- // Keep the last quad for up to 3 frames
117
- if (noQuadFramesRef.current > 3) {
118
- lastQuadRef.current = null;
119
- setQuad(null);
120
- }
121
- // Otherwise, keep showing the last quad
148
+ setQuad(null);
122
149
  }
123
150
  }, []);
124
151
 
@@ -127,10 +154,8 @@ export const DocScanner: React.FC<Props> = ({
127
154
  console.warn(`[DocScanner] frame error at ${step}: ${message}`);
128
155
  }, []);
129
156
 
130
- const reportStage = useRunOnJS((stage: string) => {
131
- if (__DEV__) {
132
- console.log('[DocScanner] stage', stage);
133
- }
157
+ const reportStage = useRunOnJS((_stage: string) => {
158
+ // Disabled for performance
134
159
  }, []);
135
160
 
136
161
  const [frameSize, setFrameSize] = useState<{ width: number; height: number } | null>(null);
@@ -147,8 +172,8 @@ export const DocScanner: React.FC<Props> = ({
147
172
  // Report frame size for coordinate transformation
148
173
  updateFrameSize(frame.width, frame.height);
149
174
 
150
- // Use higher resolution for better accuracy - 720p instead of 480p
151
- const ratio = 720 / frame.width;
175
+ // Use higher resolution for better accuracy - 960p
176
+ const ratio = 960 / frame.width;
152
177
  const width = Math.floor(frame.width * ratio);
153
178
  const height = Math.floor(frame.height * ratio);
154
179
  step = 'resize';
@@ -181,7 +206,7 @@ export const DocScanner: React.FC<Props> = ({
181
206
  OpenCV.invoke('GaussianBlur', mat, mat, gaussianKernel, 0);
182
207
  step = 'Canny';
183
208
  reportStage(step);
184
- OpenCV.invoke('Canny', mat, mat, 50, 150);
209
+ OpenCV.invoke('Canny', mat, mat, 30, 100);
185
210
 
186
211
  step = 'createContours';
187
212
  reportStage(step);
@@ -236,8 +261,8 @@ export const DocScanner: React.FC<Props> = ({
236
261
 
237
262
  let approxArray: Array<{ x: number; y: number }> = [];
238
263
 
239
- // Try epsilon values from 0.2% to 8% of perimeter
240
- 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 epsilon values from 0.3% to 6% of perimeter for better corner detection
265
+ const epsilonValues = [0.003, 0.005, 0.007, 0.009, 0.011, 0.013, 0.015, 0.018, 0.02, 0.025, 0.03, 0.04, 0.05, 0.06];
241
266
 
242
267
  for (let attempt = 0; attempt < epsilonValues.length; attempt += 1) {
243
268
  const epsilon = epsilonValues[attempt] * perimeter;