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 = 1280 // Max dimension for analysis
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(1280, 960)) // Limit resolution for analysis
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
- // Apply Canny edge detection with lower thresholds for better corner detection.
177
- Imgproc.Canny(blurredMat, cannyMat, 30.0, 90.0)
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 largestArea = 0.0
222
- val minArea = max(300.0, (srcMat.rows() * srcMat.cols()) * 0.0005)
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
- if (contourArea > largestArea) {
247
- largestArea = contourArea
248
- largestRectangle = refineRectangle(grayMat, orderPoints(points))
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 && contourArea > largestArea) {
302
+ if (rectangularity >= 0.6) {
259
303
  val boxPoints = Array(4) { Point() }
260
304
  rotated.points(boxPoints)
261
- largestArea = contourArea
262
- largestRectangle = refineRectangle(grayMat, orderPoints(boxPoints))
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-rectangle-doc-scanner",
3
- "version": "10.24.0",
3
+ "version": "10.26.0",
4
4
  "description": "Native-backed document scanner for React Native with customizable overlays.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",