I am making Document scanner. It have both option for Capture and Import from gallery.I am using AsynkTask for processing image. Image process flow- Image>Resize>Apply k-means with 2 cluster center>Identify cluster and binarize image>find contours and pick largest one>detect lines and corners>Transform> Result image.
It is detecting edges and displaying only white edges with black background. I want cropped color image. Edge is detecting and largest contour is also detected , but cannot sort corners properly.
getPage Fucntion
private void getPage() {
new AsyncTask<Void, Void, Bitmap>() {
ProgressDialog dialog;
@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(MainActivity.this, "Processing Image", "please wait...");
}
@Override
protected Bitmap doInBackground(Void... params) {
Mat srcRes = new Mat(src.size(), src.type());
Mat srcGray = new Mat();
//Preparing image to perform k-means algo
Mat samples = new Mat(src.rows() * src.cols(), 3, CvType.CV_32F);
for (int y = 0; y < src.rows(); y++) {
for (int x = 0; x < src.cols(); x++) {
for (int z = 0; z < 3; z++) {
samples.put(x + y * src.cols(), z, src.get(y, x)[z]);
}
}
}
//k-means algorithm
int clusterCount = 2;
Mat labels = new Mat();
int attempts = 5;
Mat centers = new Mat();
Core.kmeans(samples, clusterCount, labels, new TermCriteria(TermCriteria.MAX_ITER | TermCriteria.EPS, 10000, 0.0001), attempts, Core.KMEANS_PP_CENTERS, centers);
double dstCenter0 = calcWhiteDist(centers.get(0, 0)[0], centers.get(0, 1)[0], centers.get(0, 2)[0]);
double dstCenter1 = calcWhiteDist(centers.get(1, 0)[0], centers.get(1, 1)[0], centers.get(1, 2)[0]);
int paperCluster = (dstCenter0 < dstCenter1) ? 0 : 1;
for (int y = 0; y < src.rows(); y++) {
for (int x = 0; x < src.cols(); x++) {
int cluster_idx = (int) labels.get(x + y * src.cols(), 0)[0];
if (cluster_idx != paperCluster) {
srcRes.put(y, x, 0, 0, 0, 255);
} else {
srcRes.put(y, x, 255, 255, 255, 255);
}
}
}
Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
Imgproc.Canny(srcGray, srcGray, 50, 150);
List<MatOfPoint> contours = new ArrayList<>();
Mat hierarchy = new Mat();
//Find ontours
Imgproc.findContours(srcGray, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
int index = 0;
double maxim = Imgproc.contourArea(contours.get(0));
for (int contourIdx = 1; contourIdx < contours.size(); contourIdx++) {
double temp;
temp = Imgproc.contourArea(contours.get(contourIdx));
if (maxim < temp) {
maxim = temp;
index = contourIdx;
}
}
Mat drawing = Mat.zeros(srcRes.size(), CvType.CV_8UC1);
Imgproc.drawContours(drawing, contours, index, new Scalar(255), 1);
Mat lines = new Mat();
Imgproc.HoughLinesP(drawing, lines, 1, Math.PI / 180, 70, 30, 10);
ArrayList<Point> corners = new ArrayList<>();
for (int i = 0; i < lines.cols(); i++) {
for (int j = i + 1; j < lines.cols(); j++) {
double[] line1 = lines.get(0, i);
double[] line2 = lines.get(0, j);
Point pt = findIntersection(line1, line2);
if (pt.x >= 0 && pt.y >= 0 && pt.x <= drawing.cols() && pt.y <= drawing.rows()) {
if (!exists(corners, pt)) {
corners.add(pt);
}
}
}
}
if (corners.size() != 4) {
errorMessage = "Cannot detect perfect corners";
Bitmap bitmap = Bitmap.createBitmap(drawing.cols(), drawing.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(drawing, bitmap);
return bitmap;
}
sortCorners(corners);
if (corners.size() == 0) {
errorMessage = "Cannot sort corners";
return null;
}
double top = Math.sqrt(Math.pow(corners.get(0).x - corners.get(1).x, 2) + Math.pow(corners.get(0).y - corners.get(1).y, 2));
double right = Math.sqrt(Math.pow(corners.get(1).x - corners.get(2).x, 2) + Math.pow(corners.get(1).y - corners.get(2).y, 2));
double bottom = Math.sqrt(Math.pow(corners.get(2).x - corners.get(3).x, 2) + Math.pow(corners.get(2).y - corners.get(3).y, 2));
double left = Math.sqrt(Math.pow(corners.get(3).x - corners.get(1).x, 2) + Math.pow(corners.get(3).y - corners.get(1).y, 2));
Mat quad = Mat.zeros(new Size(Math.max(top, bottom), Math.max(left, right)), CvType.CV_8UC3);
ArrayList<Point> result_pts = new ArrayList<Point>();
result_pts.add(new Point(0, 0));
result_pts.add(new Point(quad.cols(), 0));
result_pts.add(new Point(quad.cols(), quad.rows()));
result_pts.add(new Point(0, quad.rows()));
Mat cornerPts = Converters.vector_Point2f_to_Mat(corners);
Mat resultPts = Converters.vector_Point2f_to_Mat(result_pts);
//Mat to Bitmap transform
Mat transformation = Imgproc.getPerspectiveTransform(cornerPts, resultPts);
Imgproc.warpPerspective(srcOrig, quad, transformation, quad.size());
Imgproc.cvtColor(quad, quad, Imgproc.COLOR_BGR2RGBA);
Bitmap bitmap = Bitmap.createBitmap(quad.cols(), quad.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(quad, bitmap);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
dialog.dismiss();
if (bitmap != null) {
imgview.setImageBitmap(bitmap);
} else if (errorMessage != null) {
Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show();
}
}
}.execute();
}
CalcWhiteDist
static double calcWhiteDist(double r, double g, double b) {
return Math.sqrt(Math.pow(255 - r, 2) + Math.pow(255 - g, 2) + Math.pow(255 - b, 2));
}
FindIntersection
static Point findIntersection(double[] line1, double[] line2) {
double start_x1 = line1[0], start_y1 = line1[1], end_x1 = line1[2], end_y1 = line1[3], start_x2 = line2[0], start_y2 = line2[1], end_x2 = line2[2], end_y2 = line2[3];
double denominator = ((start_x1 - end_x1) * (start_y2 - end_y2)) - ((start_y1 - end_y1) * (start_x2 - end_x2));
if (denominator != 0) {
Point pt = new Point();
pt.x = ((start_x1 * end_y1 - start_y1 * end_x1) * (start_x2 - end_x2) - (start_x1 - end_x1) * (start_x2 * end_y2 - start_y2 * end_x2)) / denominator;
pt.y = ((start_x1 * end_y1 - start_y1 * end_x1) * (start_y2 - end_y2) - (start_y1 - end_y1) * (start_x2 * end_y2 - start_y2 * end_x2)) / denominator;
return pt;
} else return new Point(-1, -1);
}
exists
static boolean exists(ArrayList<Point> corners, Point pt) {
for (int i = 0; i < corners.size(); i++) {
if (Math.sqrt(Math.pow(corners.get(i).x - pt.x, 2) + Math.pow(corners.get(i).y - pt.y, 2)) < 10) {
return true;
}
}
return false;
}
Sort Corners
static void sortCorners(ArrayList<Point> corners) {
ArrayList<Point> top, bottom;
top = new ArrayList<>();
bottom = new ArrayList<>();
Point center = new Point();
for (int i = 0; i < corners.size(); i++) {
center.x += corners.get(i).x / corners.size();
center.y += corners.get(i).y / corners.size();
}
for (int i = 0; i < corners.size(); i++) {
if (corners.get(i).y < center.y) top.add(corners.get(i));
else bottom.add(corners.get(i));
}
corners.clear();
if (top.size() == 2 && bottom.size() == 2) {
Point top_left = top.get(0).x > top.get(1).x ? top.get(1) : top.get(0);
Point top_right = top.get(0).x > top.get(1).x ? top.get(0) : top.get(1);
Point bottom_left = bottom.get(0).x > bottom.get(1).x ? bottom.get(1) : bottom.get(0);
Point bottom_right = bottom.get(0).x > bottom.get(1).x ? bottom.get(0) : bottom.get(1);
top_left.x *= scaleFactor;
top_left.y *= scaleFactor;
top_right.x *= scaleFactor;
top_right.y *= scaleFactor;
bottom_left.x *= scaleFactor;
bottom_left.y *= scaleFactor;
bottom_right.x *= scaleFactor;
bottom_right.y *= scaleFactor;
corners.add(top_left);
corners.add(top_right);
corners.add(bottom_right);
corners.add(bottom_left);
}
}
CalcScaleFactor
private static int calcScaleFactor(int rows, int cols) {
int idealRow, idealCol;
if (rows < cols) {
idealRow = 240;
idealCol = 320;
} else {
idealCol = 240;
idealRow = 320;
}
int val = Math.min(rows / idealRow, cols / idealCol);
if (val <= 0) {
return 1;
} else {
return val;
}
}