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.
- package/dist/DocScanner.js +43 -21
- package/package.json +1 -1
- package/src/DocScanner.tsx +47 -22
package/dist/DocScanner.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
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)((
|
|
122
|
-
|
|
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 -
|
|
137
|
-
const ratio =
|
|
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,
|
|
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.
|
|
209
|
-
const epsilonValues = [0.
|
|
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
package/src/DocScanner.tsx
CHANGED
|
@@ -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
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
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((
|
|
131
|
-
|
|
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 -
|
|
151
|
-
const ratio =
|
|
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,
|
|
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.
|
|
240
|
-
const epsilonValues = [0.
|
|
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;
|