为什么cvFindContours()方法在javacv中无法正确检测轮廓?

19

我在StackOverflow上浏览了许多问题,并成功开发了一个能够正确检测正方形和矩形的小程序。以下是我的样例代码:

public static CvSeq findSquares(final IplImage src, CvMemStorage storage) {
    CvSeq squares = new CvContour();
    squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage);
    IplImage pyr = null, timg = null, gray = null, tgray;
    timg = cvCloneImage(src);
    CvSize sz = cvSize(src.width(), src.height());
    tgray = cvCreateImage(sz, src.depth(), 1);
    gray = cvCreateImage(sz, src.depth(), 1);
    // cvCvtColor(gray, src, 1);
    pyr = cvCreateImage(cvSize(sz.width() / 2, sz.height() / 2), src.depth(), src.nChannels());
    // down-scale and upscale the image to filter out the noise
    // cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5);
    // cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5);
    // cvSaveImage("ha.jpg",timg);
    CvSeq contours = new CvContour();
    // request closing of the application when the image window is closed
    // show image on window
    // find squares in every color plane of the image
    for (int c = 0; c < 3; c++) {
        IplImage channels[] = { cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1) };
        channels[c] = cvCreateImage(sz, 8, 1);
        if (src.nChannels() > 1) {
            cvSplit(timg, channels[0], channels[1], channels[2], null);
        } else {
            tgray = cvCloneImage(timg);
        }
        tgray = channels[c];
        // // try several threshold levels
        for (int l = 0; l < N; l++) {
            // hack: use Canny instead of zero threshold level.
            // Canny helps to catch squares with gradient shading
            if (l == 0) {
                // apply Canny. Take the upper threshold from slider
                // and set the lower to 0 (which forces edges merging)
                cvCanny(tgray, gray, 0, thresh, 5);
                // dilate canny output to remove potential
                // // holes between edge segments
                cvDilate(gray, gray, null, 1);
            } else {
                // apply threshold if l!=0:
                cvThreshold(tgray, gray, (l + 1) * 255 / N, 255,
                        CV_THRESH_BINARY);
            }
            // find contours and store them all as a list
            cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
            CvSeq approx;
            // test each contour
            while (contours != null && !contours.isNull()) {
                if (contours.elem_size() > 0) {
                    approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours) * 0.02, 0);
                    if (approx.total() == 4 && Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 && cvCheckContourConvexity(approx) != 0) {
                        double maxCosine = 0;
                        for (int j = 2; j < 5; j++) {
                            // find the maximum cosine of the angle between
                            // joint edges
                            double cosine = Math.abs(angle(
                                            new CvPoint(cvGetSeqElem(
                                                    approx, j % 4)),
                                            new CvPoint(cvGetSeqElem(
                                                    approx, j - 2)),
                                            new CvPoint(cvGetSeqElem(
                                                    approx, j - 1))));
                            maxCosine = Math.max(maxCosine, cosine);
                        }
                        if (maxCosine < 0.2) {
                            CvRect x = cvBoundingRect(approx, l);
                            if ((x.width() * x.height()) < 50000) {
                                System.out.println("Width : " + x.width()
                                        + " Height : " + x.height());
                                cvSeqPush(squares, approx);
                            }
                        }
                    }
                }
                contours = contours.h_next();
            }
            contours = new CvContour();
        }
    }
    return squares;
}

我使用这张图片来检测矩形和正方形。

enter image description here

我需要识别以下输出。

enter image description here

并且

enter image description here

当我运行上述代码时,它只检测到以下矩形。但我不知道原因是什么。请有人解释一下原因。
这是我得到的输出结果。

enter image description here

请友善地解释上述代码中的问题,并提供一些建议来检测这些正方形和矩形。

我不立即看到你的代码有什么问题,但可能只是你的其中一个条件过于严格。例如,你测试了近似中最多有4个线段,但你确定永远不会出现5个线段吗?如果不了解opencv近似的工作原理,当近似算法在弯曲之前刚好被种子点触发时,可能会生成虚假的线段。此外,轮廓内的面积是否始终大于1000,这是你进行测试的条件吗? - dvhamme
3
打印算法的中间步骤(例如,canny之后,阈值之后等)有助于了解发生了什么,并在此处发布以寻求帮助。 - Rui Marques
1个回答

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接