我一直在尝试检测点击图片上的最大矩形/正方形。我尝试了轮廓Android OpenCV Find Largest Square or Rectangle和霍夫变换http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/。
两者都像从画廊中选择的图像上的魅力。但是当我整合从相机拍摄的图像时,它大部分时间都会失败。
谁能告诉我背后的原因是什么?我在这里错过了什么?
据我所知,有可能像camscanner这样的扫描应用程序非常精美,准确度超过90%。
请建议。我已经尝试了很多。
供参考:
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;
}
这是用于检测作为角落的四个点的computerIntersect模块。
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);
}
当我将它应用于从相机拍摄的图像时,它会返回四个以上的点。
我尝试的另一种方法是:
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
}
感谢。
答案 0 :(得分:0)
非常感谢这么棒的帖子。这对我帮助很大。 希望这些代码可以帮到你。
public static String houghTransform(Mat original_image, String outFile) {
Mat imgSource = original_image;
Mat untouched = original_image.clone();
Imgproc.cvtColor(imgSource, imageGray, Imgproc.COLOR_RGB2GRAY);
Imgproc.GaussianBlur(imgSource, imgSource, new Size(11,11), 0);
// 2) AdaptiveThreshold -> classify as either black or white
Imgproc.adaptiveThreshold(imgSource, imgSource, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 5, 2);
// 3) Invert the image -> so most of the image is black
Core.bitwise_not(imgSource, imgSource);
// 4) Dilate -> fill the image using the MORPH_DILATE
Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_DILATE, new Size(3,3), new Point(1,1));
Imgproc.dilate(imgSource, imgSource, kernel);
// 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, 20, 60);
Mat lines = new Mat();
int threshold = 80;
int minLineSize = 40;
int lineGap = 20;
Imgproc.HoughLinesP(imgSource, lines, 1, Math.PI / 2, 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);
}
}
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());
ArrayList<Point> top = new ArrayList<Point>();
ArrayList<Point> bottom = new ArrayList<Point>();
for (int i = 0; i < corners.size(); i++) {
if (center.y > corners.get(i).y) {
top.add(corners.get(i));
} else {
bottom.add(corners.get(i));
}
}
if (top.size() > 0 && bottom.size() > 0) {
double largest_top_x = top.get(0).x;
double smallest_top_x = top.get(0).x;
double largest_top_y = top.get(0).y;
double smallest_top_y = top.get(0).y;
int min_top, max_top, min_bottom, max_bottom;
double largest_bottom_x = bottom.get(0).x;
double smallest_bottom_x = bottom.get(0).x;
double largest_bottom_y = bottom.get(0).y;
double smallest_bottom_y = bottom.get(0).y;
for (int i = 0; i < top.size(); i++) {
if (top.get(i).x > largest_top_x) {
largest_top_x = top.get(i).x;
min_top = i;
} else if (top.get(i).x < smallest_top_x) {
smallest_top_x = top.get(i).x;
max_top = i;
}
if (top.get(i).y > largest_top_y) {
largest_top_y = top.get(i).y;
min_top = i;
} else if (top.get(i).y < smallest_top_y) {
smallest_top_y = top.get(i).y;
max_top = i;
}
}
for (int i = 0; i < bottom.size(); i++) {
if (bottom.get(i).x > largest_bottom_x) {
largest_bottom_x = bottom.get(i).x;
min_top = i;
} else if (bottom.get(i).x < smallest_bottom_x) {
smallest_bottom_x = bottom.get(i).x;
max_top = i;
}
if (bottom.get(i).y > largest_bottom_y) {
largest_bottom_y = bottom.get(i).y;
min_top = i;
} else if (bottom.get(i).y < smallest_bottom_y) {
smallest_bottom_y = bottom.get(i).y;
max_top = i;
}
}
Core.circle(untouched, center, 20, new Scalar(255, 0, 0), 5); // p1
Core.circle(untouched, new Point(smallest_top_x, smallest_top_y),
20, new Scalar(255, 0, 0), 5);
Core.circle(untouched, new Point(largest_top_x, smallest_top_y),
20, new Scalar(255, 0, 0), 5);
Core.circle(untouched, new Point(smallest_bottom_x,
largest_bottom_y), 20, new Scalar(255, 0, 0), 5);
Core.circle(untouched,
new Point(largest_bottom_x, largest_bottom_y), 20,
new Scalar(255, 0, 0), 5);
Highgui.imwrite(outFile, untouched);
}
return outFile;
}