react-native-rectangle-doc-scanner 3.237.0 → 3.239.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.
|
@@ -106,6 +106,7 @@ class DocumentDetector {
|
|
|
106
106
|
val blurredMat = Mat()
|
|
107
107
|
val cannyMat = Mat()
|
|
108
108
|
val morphMat = Mat()
|
|
109
|
+
val threshMat = Mat()
|
|
109
110
|
|
|
110
111
|
try {
|
|
111
112
|
// Convert to grayscale
|
|
@@ -124,57 +125,74 @@ class DocumentDetector {
|
|
|
124
125
|
Imgproc.morphologyEx(cannyMat, morphMat, Imgproc.MORPH_CLOSE, kernel)
|
|
125
126
|
kernel.release()
|
|
126
127
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
val points = approx.toArray()
|
|
158
|
-
|
|
159
|
-
if (contourArea > largestArea) {
|
|
160
|
-
largestArea = contourArea
|
|
161
|
-
largestRectangle = orderPoints(points)
|
|
128
|
+
fun findLargestRectangle(source: Mat): Rectangle? {
|
|
129
|
+
val contours = mutableListOf<MatOfPoint>()
|
|
130
|
+
val hierarchy = Mat()
|
|
131
|
+
Imgproc.findContours(
|
|
132
|
+
source,
|
|
133
|
+
contours,
|
|
134
|
+
hierarchy,
|
|
135
|
+
Imgproc.RETR_EXTERNAL,
|
|
136
|
+
Imgproc.CHAIN_APPROX_SIMPLE
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
var largestRectangle: Rectangle? = null
|
|
140
|
+
var largestArea = 0.0
|
|
141
|
+
val minArea = max(500.0, (srcMat.rows() * srcMat.cols()) * 0.0008)
|
|
142
|
+
|
|
143
|
+
for (contour in contours) {
|
|
144
|
+
val contourArea = Imgproc.contourArea(contour)
|
|
145
|
+
if (contourArea < minArea) continue
|
|
146
|
+
|
|
147
|
+
val approx = MatOfPoint2f()
|
|
148
|
+
val contour2f = MatOfPoint2f(*contour.toArray())
|
|
149
|
+
val epsilon = 0.018 * Imgproc.arcLength(contour2f, true)
|
|
150
|
+
Imgproc.approxPolyDP(contour2f, approx, epsilon, true)
|
|
151
|
+
|
|
152
|
+
if (approx.total() == 4L && Imgproc.isContourConvex(MatOfPoint(*approx.toArray()))) {
|
|
153
|
+
val points = approx.toArray()
|
|
154
|
+
if (contourArea > largestArea) {
|
|
155
|
+
largestArea = contourArea
|
|
156
|
+
largestRectangle = orderPoints(points)
|
|
157
|
+
}
|
|
162
158
|
}
|
|
159
|
+
|
|
160
|
+
approx.release()
|
|
161
|
+
contour2f.release()
|
|
163
162
|
}
|
|
164
163
|
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
hierarchy.release()
|
|
165
|
+
contours.forEach { it.release() }
|
|
166
|
+
return largestRectangle
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
// First pass: Canny-based edges (good for strong edges).
|
|
170
|
+
var rectangle = findLargestRectangle(morphMat)
|
|
171
|
+
|
|
172
|
+
// Fallback: adaptive threshold (better for low-contrast cards).
|
|
173
|
+
if (rectangle == null) {
|
|
174
|
+
Imgproc.adaptiveThreshold(
|
|
175
|
+
blurredMat,
|
|
176
|
+
threshMat,
|
|
177
|
+
255.0,
|
|
178
|
+
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,
|
|
179
|
+
Imgproc.THRESH_BINARY,
|
|
180
|
+
15,
|
|
181
|
+
2.0
|
|
182
|
+
)
|
|
183
|
+
val kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, Size(3.0, 3.0))
|
|
184
|
+
Imgproc.morphologyEx(threshMat, morphMat, Imgproc.MORPH_CLOSE, kernel)
|
|
185
|
+
kernel.release()
|
|
186
|
+
rectangle = findLargestRectangle(morphMat)
|
|
187
|
+
}
|
|
171
188
|
|
|
172
|
-
return
|
|
189
|
+
return rectangle
|
|
173
190
|
} finally {
|
|
174
191
|
grayMat.release()
|
|
175
192
|
blurredMat.release()
|
|
176
193
|
cannyMat.release()
|
|
177
194
|
morphMat.release()
|
|
195
|
+
threshMat.release()
|
|
178
196
|
}
|
|
179
197
|
}
|
|
180
198
|
|
|
@@ -242,7 +260,7 @@ class DocumentDetector {
|
|
|
242
260
|
}
|
|
243
261
|
|
|
244
262
|
val minDim = kotlin.math.min(viewWidth.toDouble(), viewHeight.toDouble())
|
|
245
|
-
val angleThreshold = max(
|
|
263
|
+
val angleThreshold = max(60.0, minDim * 0.08)
|
|
246
264
|
|
|
247
265
|
val topYDiff = abs(rectangle.topRight.y - rectangle.topLeft.y)
|
|
248
266
|
val bottomYDiff = abs(rectangle.bottomLeft.y - rectangle.bottomRight.y)
|
|
@@ -253,7 +271,7 @@ class DocumentDetector {
|
|
|
253
271
|
return RectangleQuality.BAD_ANGLE
|
|
254
272
|
}
|
|
255
273
|
|
|
256
|
-
val margin = max(
|
|
274
|
+
val margin = max(120.0, minDim * 0.12)
|
|
257
275
|
if (rectangle.topLeft.y > margin ||
|
|
258
276
|
rectangle.topRight.y > margin ||
|
|
259
277
|
rectangle.bottomLeft.y < (viewHeight - margin) ||
|
package/dist/DocScanner.js
CHANGED
|
@@ -291,10 +291,11 @@ exports.DocScanner = (0, react_1.forwardRef)(({ onCapture, overlayColor = DEFAUL
|
|
|
291
291
|
if (payload.rectangleCoordinates || payload.rectangleOnScreen) {
|
|
292
292
|
lastRectangleRef.current = payload.rectangleCoordinates ?? payload.rectangleOnScreen ?? null;
|
|
293
293
|
}
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
294
|
+
const hasAnyRectangle = react_native_1.Platform.OS === 'android'
|
|
295
|
+
? Boolean(rectangleOnScreen || payload.rectangleCoordinates)
|
|
296
|
+
: payload.lastDetectionType === 0 && Boolean(rectangleOnScreen);
|
|
297
|
+
// 그리드를 표시할 조건: 사각형이 잡히면 품질과 무관하게 표시
|
|
298
|
+
if (hasAnyRectangle) {
|
|
298
299
|
// 기존 타임아웃 클리어
|
|
299
300
|
if (rectangleClearTimeoutRef.current) {
|
|
300
301
|
clearTimeout(rectangleClearTimeoutRef.current);
|
package/package.json
CHANGED
package/src/DocScanner.tsx
CHANGED
|
@@ -397,11 +397,13 @@ export const DocScanner = forwardRef<DocScannerHandle, Props>(
|
|
|
397
397
|
lastRectangleRef.current = payload.rectangleCoordinates ?? payload.rectangleOnScreen ?? null;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
const
|
|
401
|
-
|
|
400
|
+
const hasAnyRectangle =
|
|
401
|
+
Platform.OS === 'android'
|
|
402
|
+
? Boolean(rectangleOnScreen || payload.rectangleCoordinates)
|
|
403
|
+
: payload.lastDetectionType === 0 && Boolean(rectangleOnScreen);
|
|
402
404
|
|
|
403
|
-
// 그리드를 표시할 조건:
|
|
404
|
-
if (
|
|
405
|
+
// 그리드를 표시할 조건: 사각형이 잡히면 품질과 무관하게 표시
|
|
406
|
+
if (hasAnyRectangle) {
|
|
405
407
|
// 기존 타임아웃 클리어
|
|
406
408
|
if (rectangleClearTimeoutRef.current) {
|
|
407
409
|
clearTimeout(rectangleClearTimeoutRef.current);
|