我尝试过两种方法: -
将图片转换为Mat
应用高斯模糊
然后canny边缘检测
查找轮廓
此方法的问题是:
然后我改变了我的方法,并尝试了高斯模糊/中位模糊后的自适应阈值处理,它更好,我能够在50%的情况下检测角落
我面临的当前问题是页面检测需要对比度和普通背景而没有任何反射。我认为它对于现实世界的使用来说过于理想化了。
这是我想要帮助的地方。即使是朝向解决方案的方向也受到高度赞赏,尤其是在java中谢谢你的期待 这个
具有明显的对比背景,效果非常好检测到4个角落 这张照片给出了麻烦,因为背景并不是最具对比度的
找到最初的最大轮廓
更新:中位数模糊并没有多大帮助,所以我追查了原因并发现页面边界是以零碎的方式检测到的,而不是单个轮廓,所以它检测到最大的轮廓作为因此,页面边界执行了一些形态学操作以关闭相对较小的间隙,所得到的最大轮廓肯定会得到改善,但它并不是最佳的。我有什么想法可以改善差距吗?
变形的原始图片
在变形图像中找到的最大轮廓
PS在理想情况下变形图像导致检测到错误的轮廓边界。在变形图像之前可以检查的任何条件也是奖励。谢谢
答案 0 :(得分:2)
如果你使用这样的方法:
public static RotatedRect getBestRectByArea(List<RotatedRect> boundingRects) {
RotatedRect bestRect = null;
if (boundingRects.size() >= 1) {
RotatedRect boundingRect;
Point[] vertices = new Point[4];
Rect rect;
double maxArea;
int ixMaxArea = 0;
// find best rect by area
boundingRect = boundingRects.get(ixMaxArea);
boundingRect.points(vertices);
rect = Imgproc.boundingRect(new MatOfPoint(vertices));
maxArea = rect.area();
for (int ix = 1; ix < boundingRects.size(); ix++) {
boundingRect = boundingRects.get(ix);
boundingRect.points(vertices);
rect = Imgproc.boundingRect(new MatOfPoint(vertices));
if (rect.area() > maxArea) {
maxArea = rect.area();
ixMaxArea = ix;
}
}
bestRect = boundingRects.get(ixMaxArea);
}
return bestRect;
}
private static Bitmap findROI(Bitmap sourceBitmap) {
Bitmap roiBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
Utils.bitmapToMat(sourceBitmap, sourceMat);
final Mat mat = new Mat();
sourceMat.copyTo(mat);
Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2GRAY);
Imgproc.threshold(mat, mat, 146, 250, Imgproc.THRESH_BINARY);
// find contours
List<MatOfPoint> contours = new ArrayList<>();
List<RotatedRect> boundingRects = new ArrayList<>();
Imgproc.findContours(mat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
// find appropriate bounding rectangles
for (MatOfPoint contour : contours) {
MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
boundingRects.add(boundingRect);
}
RotatedRect documentRect = getBestRectByArea(boundingRects);
if (documentRect != null) {
Point rect_points[] = new Point[4];
documentRect.points(rect_points);
for (int i = 0; i < 4; ++i) {
Imgproc.line(sourceMat, rect_points[i], rect_points[(i + 1) % 4], ROI_COLOR, ROI_WIDTH);
}
}
Utils.matToBitmap(sourceMat, roiBitmap);
return roiBitmap;
}
您可以为您的源图像获得如下结果:
或那:
如果您调整阈值并应用过滤器,则可以获得更好的效果。
答案 1 :(得分:1)
您可以使用以下一项或两项来选择一个轮廓:
使用BoundingRect
和ContourArea
评估squareness of each contour。 boundingRect()
返回正交rects。,为了处理任意旋转,请更好地使用minAreaRect()
来返回最佳旋转值。
迭代使用Cv.ApproxPoly
缩小为4边形状
var approxIter = 1;
while (true)
{
var approxCurve = Cv.ApproxPoly(largestContour, 0, null, ApproxPolyMethod.DP, approxIter, true);
var approxCurvePointsTmp = new[] { approxCurve.Select(p => new CvPoint2D32f((int)p.Value.X, (int)p.Value.Y)).ToArray() }.ToArray();
if (approxCurvePointsTmp[0].Length == 4)
{
corners = approxCurvePointsTmp[0];
break;
}
else if (approxCurvePointsTmp[0].Length < 4) throw new InvalidOperationException("Failed to decimate corner points");
approxIter++;
}
然而,如果轮廓检测由于噪声/对比度而为您提供两个独立的轮廓,则这些都不会有帮助。
我认为可以使用霍夫线变换来帮助检测线被分成两个轮廓的情况。
如果是这样,可以对连接轮廓的所有组合重复搜索,以查看是否找到更大/更矩形的匹配。
答案 2 :(得分:0)
停止依赖边缘检测,这是宇宙中最糟糕的方法,并切换到某种形式的图像分割。
纸张为白色,背景形成对比,这是您应该使用的信息。