react-native-rectangle-doc-scanner 10.24.0 → 10.26.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.
|
@@ -52,7 +52,7 @@ class CameraController(
|
|
|
52
52
|
|
|
53
53
|
companion object {
|
|
54
54
|
private const val TAG = "CameraController"
|
|
55
|
-
private const val ANALYSIS_TARGET_RESOLUTION =
|
|
55
|
+
private const val ANALYSIS_TARGET_RESOLUTION = 1920 // Max dimension for analysis
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
private fun getCameraSensorOrientation(): Int {
|
|
@@ -184,7 +184,7 @@ class CameraController(
|
|
|
184
184
|
// ImageAnalysis UseCase for document detection
|
|
185
185
|
imageAnalyzer = ImageAnalysis.Builder()
|
|
186
186
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
187
|
-
.setTargetResolution(android.util.Size(
|
|
187
|
+
.setTargetResolution(android.util.Size(1920, 1440)) // Higher resolution for better small-edge detection
|
|
188
188
|
.setTargetRotation(targetRotation) // Match preview rotation
|
|
189
189
|
.build()
|
|
190
190
|
.also {
|
|
@@ -170,11 +170,49 @@ class DocumentDetector {
|
|
|
170
170
|
srcMat.copyTo(grayMat)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
// Boost local contrast to improve low-contrast edges (e.g., business cards).
|
|
174
|
+
val clahe = Imgproc.createCLAHE()
|
|
175
|
+
clahe.clipLimit = 2.5
|
|
176
|
+
clahe.apply(grayMat, grayMat)
|
|
177
|
+
clahe.release()
|
|
178
|
+
|
|
173
179
|
// Apply a light blur to reduce noise without killing small edges.
|
|
174
180
|
Imgproc.GaussianBlur(grayMat, blurredMat, Size(5.0, 5.0), 0.0)
|
|
175
181
|
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
fun computeMedian(mat: Mat): Double {
|
|
183
|
+
val hist = Mat()
|
|
184
|
+
return try {
|
|
185
|
+
Imgproc.calcHist(
|
|
186
|
+
listOf(mat),
|
|
187
|
+
MatOfInt(0),
|
|
188
|
+
Mat(),
|
|
189
|
+
hist,
|
|
190
|
+
MatOfInt(256),
|
|
191
|
+
MatOfFloat(0f, 256f)
|
|
192
|
+
)
|
|
193
|
+
val total = mat.total().toDouble()
|
|
194
|
+
var cumulative = 0.0
|
|
195
|
+
var median = 0.0
|
|
196
|
+
for (i in 0 until 256) {
|
|
197
|
+
cumulative += hist.get(i, 0)[0]
|
|
198
|
+
if (cumulative >= total * 0.5) {
|
|
199
|
+
median = i.toDouble()
|
|
200
|
+
break
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
median
|
|
204
|
+
} finally {
|
|
205
|
+
hist.release()
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
val median = computeMedian(blurredMat)
|
|
210
|
+
val sigma = 0.33
|
|
211
|
+
val cannyLow = max(40.0, (1.0 - sigma) * median)
|
|
212
|
+
val cannyHigh = max(120.0, (1.0 + sigma) * median)
|
|
213
|
+
|
|
214
|
+
// Apply Canny edge detection with adaptive thresholds for better corner detection.
|
|
215
|
+
Imgproc.Canny(blurredMat, cannyMat, cannyLow, cannyHigh)
|
|
178
216
|
val kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, Size(3.0, 3.0))
|
|
179
217
|
Imgproc.morphologyEx(cannyMat, morphMat, Imgproc.MORPH_CLOSE, kernel)
|
|
180
218
|
kernel.release()
|
|
@@ -218,8 +256,8 @@ class DocumentDetector {
|
|
|
218
256
|
)
|
|
219
257
|
|
|
220
258
|
var largestRectangle: Rectangle? = null
|
|
221
|
-
var
|
|
222
|
-
val minArea = max(
|
|
259
|
+
var bestScore = 0.0
|
|
260
|
+
val minArea = max(800.0, (srcMat.rows() * srcMat.cols()) * 0.001)
|
|
223
261
|
|
|
224
262
|
for (contour in contours) {
|
|
225
263
|
val contourArea = Imgproc.contourArea(contour)
|
|
@@ -243,9 +281,15 @@ class DocumentDetector {
|
|
|
243
281
|
|
|
244
282
|
if (quad.total() == 4L && Imgproc.isContourConvex(MatOfPoint(*quad.toArray()))) {
|
|
245
283
|
val points = quad.toArray()
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
284
|
+
val rect = Imgproc.minAreaRect(MatOfPoint2f(*points))
|
|
285
|
+
val rectArea = rect.size.area()
|
|
286
|
+
val rectangularity = if (rectArea > 1.0) contourArea / rectArea else 0.0
|
|
287
|
+
if (rectangularity >= 0.6) {
|
|
288
|
+
val score = contourArea * rectangularity
|
|
289
|
+
if (score > bestScore) {
|
|
290
|
+
bestScore = score
|
|
291
|
+
largestRectangle = refineRectangle(grayMat, orderPoints(points))
|
|
292
|
+
}
|
|
249
293
|
}
|
|
250
294
|
} else {
|
|
251
295
|
// Fallback: use rotated bounding box when contour is near-rectangular.
|
|
@@ -255,11 +299,14 @@ class DocumentDetector {
|
|
|
255
299
|
val rectArea = rotated.size.area()
|
|
256
300
|
if (rectArea > 1.0) {
|
|
257
301
|
val rectangularity = contourArea / rectArea
|
|
258
|
-
if (rectangularity >= 0.6
|
|
302
|
+
if (rectangularity >= 0.6) {
|
|
259
303
|
val boxPoints = Array(4) { Point() }
|
|
260
304
|
rotated.points(boxPoints)
|
|
261
|
-
|
|
262
|
-
|
|
305
|
+
val score = contourArea * rectangularity
|
|
306
|
+
if (score > bestScore) {
|
|
307
|
+
bestScore = score
|
|
308
|
+
largestRectangle = refineRectangle(grayMat, orderPoints(boxPoints))
|
|
309
|
+
}
|
|
263
310
|
}
|
|
264
311
|
}
|
|
265
312
|
}
|
package/package.json
CHANGED