I have been trying to detect largest rectangle/square on clicked image. I tried both contour Android OpenCV Find Largest Square or Rectangle and hough transform http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/ .
Both work like charm on image selected from gallery. But when I integrate with image taken from camera , it fails most of the times.
Can anyone tell me what is the reason behind it ? what am I missing here ?
As far as I know ,it's possible as many scan apps like camscanner does it very beautifully with more than 90% accuracy.
Please suggest. I have tried alot.
For reference :
public static String houghTransform(Mat original_image ,String outFile) {
Mat imgSource = original_image;
Mat untouched = original_image.clone();
//convert the image to black and white
Imgproc.cvtColor(imgSource, imgSource, Imgproc.COLOR_BGR2GRAY);
//apply gaussian blur to smoothen lines of dots
Imgproc.GaussianBlur(imgSource, imgSource, new Size(5, 5), 0);
//adaptive threshold thresholds the image taking an optimum value for a local neighbourhood.
//Imgproc.adaptiveThreshold(imgSource, imgSource, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 3, 1);
//convert the image to black and white does (8 bit)
Imgproc.Canny(imgSource, imgSource, 80, 100);
Mat lines = new Mat();
int threshold = 70;
int minLineSize = 20;
int lineGap = 20;
Imgproc.HoughLinesP(imgSource, lines, 1, Math.PI/180, threshold, minLineSize, lineGap);
ArrayList<Point> corners = new ArrayList<Point>();
for (int i = 0; i < lines.cols(); i++)
{
for (int j = i+1; j < lines.cols(); j++)
{
Point pt = computeIntersect(lines.get(0,i), lines.get(0,j));
if (pt.x >= 0 && pt.y >= 0)
corners.add(pt);
}
}
L.v("Points corner size: ", ""+corners.size());
if(corners.size()<4){
return "";
}
Point center = new Point(0,0);
// Get mass center
for (int i = 0; i < corners.size(); i++){
center.x += corners.get(i).x;
center.y+= corners.get(i).y;
}
center.x = (center.x / corners.size());
center.y = (center.y / corners.size());
Core.circle(untouched, center, 20, new Scalar(255, 0, 0), 5); //p1 is colored red
Core.circle(untouched, corners.get(0), 20, new Scalar(255, 0, 0), 5);
Core.circle(untouched, corners.get(1), 20, new Scalar(255, 0, 0), 5);
Core.circle(untouched, corners.get(2), 20, new Scalar(255, 0, 0), 5);
Core.circle(untouched, corners.get(3), 20, new Scalar(255, 0, 0), 5);
Highgui.imwrite(outFile, untouched);
return outFile;
}
Here is the computerIntersect module to detect four points which are the corners.
private static Point computeIntersect(double[] a, double[] b) {
double x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];
double denom = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
Point pt = new Point();
if (denom!=0)
{
pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom;
pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom;
return pt;
}
else
return new Point(-1, -1);
}
Here it returns more than four points when I apply it for image taken from camera.
Another method I tried is:
public static String findLargestRectangle(Mat original_image ,String outFile) {
Mat imgSource = original_image;
Mat untouched = original_image.clone();
//convert the image to black and white
Imgproc.cvtColor(imgSource, imgSource, Imgproc.COLOR_BGR2GRAY);
//apply gaussian blur to smoothen lines of dots
Imgproc.GaussianBlur(imgSource, imgSource, new Size(5, 5), 0);
//convert the image to black and white does (8 bit)
Imgproc.Canny(imgSource, imgSource, 80, 100);
//find the contours
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(imgSource, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);
double maxArea = -1;
int maxAreaIdx = -1;
MatOfPoint temp_contour = contours.get(0); //the largest is at the index 0 for starting point
MatOfPoint2f approxCurve = new MatOfPoint2f();
MatOfPoint2f maxCurve = new MatOfPoint2f();
List<MatOfPoint> largest_contours = new ArrayList<MatOfPoint>();
for (int idx = 0; idx < contours.size(); idx++) {
temp_contour = contours.get(idx);
double contourarea = Imgproc.contourArea(temp_contour);
//compare this contour to the previous largest contour found
if (contourarea > maxArea) {
// Imgproc.drawContours(untouched, contours, maxAreaIdx, new Scalar(0, 255, 0), 1);
//check if this contour is a square
MatOfPoint2f new_mat = new MatOfPoint2f( temp_contour.toArray() );
int contourSize = (int)temp_contour.total();
Imgproc.approxPolyDP(new_mat, approxCurve, contourSize*0.05, true);
if (approxCurve.total() == 4) {
maxCurve = approxCurve;
maxArea = contourarea;
maxAreaIdx = idx;
largest_contours.add(temp_contour);
}
}
}
Imgproc.drawContours(imgSource, contours, maxAreaIdx, new Scalar(0, 255, 0), 5); //will draw the largest square/rectangle
}
Thanks.