Skip to content

Commit a32a3db

Browse files
authored
Merge pull request #22 from Udayraj123/master
Patch for improved edge detection
2 parents 9aefe41 + ffb0727 commit a32a3db

File tree

3 files changed

+84
-25
lines changed

3 files changed

+84
-25
lines changed

liveedgedetection/src/main/java/com/adityaarora/liveedgedetection/constants/ScanConstants.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,10 @@ public class ScanConstants {
1010
public static final String IMAGE_DIR = "imageDir";
1111
public static final int HIGHER_SAMPLING_THRESHOLD = 2200;
1212

13+
public static int KSIZE_BLUR = 3;
14+
public static int KSIZE_CLOSE = 10;
15+
public static final int CANNY_THRESH_L = 85;
16+
public static final int CANNY_THRESH_U = 185;
17+
public static final int TRUNC_THRESH = 150;
18+
public static final int CUTOFF_THRESH = 155;
1319
}

liveedgedetection/src/main/java/com/adityaarora/liveedgedetection/util/ScanUtils.java

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,18 @@
1414
import android.view.Display;
1515
import android.view.Surface;
1616

17+
import com.adityaarora.liveedgedetection.constants.ScanConstants;
1718
import com.adityaarora.liveedgedetection.view.Quadrilateral;
1819

1920
import org.opencv.android.Utils;
21+
import org.opencv.core.Core;
2022
import org.opencv.core.CvType;
2123
import org.opencv.core.Mat;
24+
import org.opencv.core.MatOfInt;
2225
import org.opencv.core.MatOfPoint;
2326
import org.opencv.core.MatOfPoint2f;
2427
import org.opencv.core.Point;
28+
import org.opencv.core.Scalar;
2529
import org.opencv.core.Size;
2630
import org.opencv.imgproc.Imgproc;
2731
import org.opencv.utils.Converters;
@@ -276,19 +280,7 @@ public static int configureCameraAngle(Activity activity) {
276280
return angle;
277281
}
278282

279-
public static Quadrilateral detectLargestQuadrilateral(Mat mat) {
280-
Mat mGrayMat = new Mat(mat.rows(), mat.cols(), CV_8UC1);
281-
Imgproc.cvtColor(mat, mGrayMat, Imgproc.COLOR_BGR2GRAY, 4);
282-
Imgproc.threshold(mGrayMat, mGrayMat, 150, 255, THRESH_BINARY + THRESH_OTSU);
283283

284-
List<MatOfPoint> largestContour = findLargestContour(mGrayMat);
285-
if (null != largestContour) {
286-
Quadrilateral mLargestRect = findQuadrilateral(largestContour);
287-
if (mLargestRect != null)
288-
return mLargestRect;
289-
}
290-
return null;
291-
}
292284

293285
public static double getMaxCosine(double maxCosine, Point[] approxPoints) {
294286
Log.i(TAG, "ANGLES ARE:");
@@ -339,24 +331,86 @@ public int compare(Point lhs, Point rhs) {
339331
return result;
340332
}
341333

342-
private static List<MatOfPoint> findLargestContour(Mat inputMat) {
334+
private static Mat morph_kernel = new Mat(new Size(ScanConstants.KSIZE_CLOSE, ScanConstants.KSIZE_CLOSE), CvType.CV_8UC1, new Scalar(255));
335+
336+
public static Quadrilateral detectLargestQuadrilateral(Mat originalMat) {
337+
Imgproc.cvtColor(originalMat, originalMat, Imgproc.COLOR_BGR2GRAY, 4);
338+
339+
// Just OTSU/Binary thresholding is not enough.
340+
//Imgproc.threshold(mGrayMat, mGrayMat, 150, 255, THRESH_BINARY + THRESH_OTSU);
341+
342+
/*
343+
* 1. We shall first blur and normalize the image for uniformity,
344+
* 2. Truncate light-gray to white and normalize,
345+
* 3. Apply canny edge detection,
346+
* 4. Cutoff weak edges,
347+
* 5. Apply closing(morphology), then proceed to finding contours.
348+
*/
349+
350+
// step 1.
351+
Imgproc.blur(originalMat, originalMat, new Size(ScanConstants.KSIZE_BLUR, ScanConstants.KSIZE_BLUR));
352+
Core.normalize(originalMat, originalMat, 0, 255, Core.NORM_MINMAX);
353+
// step 2.
354+
// As most papers are bright in color, we can use truncation to make it uniformly bright.
355+
Imgproc.threshold(originalMat,originalMat, ScanConstants.TRUNC_THRESH,255,Imgproc.THRESH_TRUNC);
356+
Core.normalize(originalMat, originalMat, 0, 255, Core.NORM_MINMAX);
357+
// step 3.
358+
// After above preprocessing, canny edge detection can now work much better.
359+
Imgproc.Canny(originalMat, originalMat, ScanConstants.CANNY_THRESH_U, ScanConstants.CANNY_THRESH_L);
360+
// step 4.
361+
// Cutoff the remaining weak edges
362+
Imgproc.threshold(originalMat,originalMat,ScanConstants.CUTOFF_THRESH,255,Imgproc.THRESH_TOZERO);
363+
// step 5.
364+
// Closing - closes small gaps. Completes the edges on canny image; AND also reduces stringy lines near edge of paper.
365+
Imgproc.morphologyEx(originalMat, originalMat, Imgproc.MORPH_CLOSE, morph_kernel, new Point(-1,-1),1);
366+
367+
// Get only the 10 largest contours (each approximated to their convex hulls)
368+
List<MatOfPoint> largestContour = findLargestContours(originalMat, 10);
369+
if (null != largestContour) {
370+
Quadrilateral mLargestRect = findQuadrilateral(largestContour);
371+
if (mLargestRect != null)
372+
return mLargestRect;
373+
}
374+
return null;
375+
}
376+
private static MatOfPoint hull2Points(MatOfInt hull, MatOfPoint contour) {
377+
List<Integer> indexes = hull.toList();
378+
List<Point> points = new ArrayList<>();
379+
List<Point> ctrList = contour.toList();
380+
for(Integer index:indexes) {
381+
points.add(ctrList.get(index));
382+
}
383+
MatOfPoint point= new MatOfPoint();
384+
point.fromList(points);
385+
return point;
386+
}
387+
private static List<MatOfPoint> findLargestContours(Mat inputMat, int NUM_TOP_CONTOURS) {
343388
Mat mHierarchy = new Mat();
344389
List<MatOfPoint> mContourList = new ArrayList<>();
345-
//finding contours
346-
Imgproc.findContours(inputMat, mContourList, mHierarchy, Imgproc.RETR_EXTERNAL,
347-
Imgproc.CHAIN_APPROX_SIMPLE);
348-
349-
Mat mContoursMat = new Mat();
350-
mContoursMat.create(inputMat.rows(), inputMat.cols(), CvType.CV_8U);
351-
352-
if (mContourList.size() != 0) {
353-
Collections.sort(mContourList, new Comparator<MatOfPoint>() {
390+
//finding contours - as we are sorting by area anyway, we can use RETR_LIST - faster than RETR_EXTERNAL.
391+
Imgproc.findContours(inputMat, mContourList, mHierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
392+
393+
// Convert the contours to their Convex Hulls i.e. removes minor nuances in the contour
394+
List<MatOfPoint> mHullList = new ArrayList<>();
395+
MatOfInt tempHullIndices = new MatOfInt();
396+
for (int i = 0; i < mContourList.size(); i++) {
397+
Imgproc.convexHull(mContourList.get(i), tempHullIndices);
398+
mHullList.add(hull2Points(tempHullIndices, mContourList.get(i)));
399+
}
400+
// Release mContourList as its job is done
401+
for (MatOfPoint c : mContourList)
402+
c.release();
403+
tempHullIndices.release();
404+
mHierarchy.release();
405+
406+
if (mHullList.size() != 0) {
407+
Collections.sort(mHullList, new Comparator<MatOfPoint>() {
354408
@Override
355409
public int compare(MatOfPoint lhs, MatOfPoint rhs) {
356-
return Double.valueOf(Imgproc.contourArea(rhs)).compareTo(Imgproc.contourArea(lhs));
410+
return Double.compare(Imgproc.contourArea(rhs),Imgproc.contourArea(lhs));
357411
}
358412
});
359-
return mContourList;
413+
return mHullList.subList(0, Math.min(mHullList.size(), NUM_TOP_CONTOURS));
360414
}
361415
return null;
362416
}

liveedgedetection/src/main/java/com/adityaarora/liveedgedetection/view/ScanSurfaceView.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ public void surfaceCreated(SurfaceHolder holder) {
7474
requestLayout();
7575
openCamera();
7676
this.camera.setPreviewDisplay(holder);
77-
setPreviewCallback();
7877
} catch (IOException e) {
7978
Log.e(TAG, e.getMessage(), e);
8079
}

0 commit comments

Comments
 (0)