Javascript 使用OpenCV.js检测角落中有正方形的帧

Javascript 使用OpenCV.js检测角落中有正方形的帧,javascript,opencv,image-processing,Javascript,Opencv,Image Processing,我一直在玩用Javascript和OpenCV.js创建填充表单扫描仪的游戏。 我基本上想做的是拍一张纸的照片,上面有一张填好的表格,能够扫描照片并分析表格中的答案。 第一步是实际找到图片中的形状,并应用透视变换获得纸张的“自上而下视图”。 我所做的是,我设法让脚本检测到这张纸,并应用转换,使它很好地扫描。我通过应用灰度,然后Canny边缘检测,迭代找到的边缘,找到了最大的一个,有4个角,并假设这一定是我的论文 这项工作相对来说很好,但脚本有时会对纸张的实际情况感到困惑——有时会检测到其他矩形

我一直在玩用Javascript和OpenCV.js创建填充表单扫描仪的游戏。 我基本上想做的是拍一张纸的照片,上面有一张填好的表格,能够扫描照片并分析表格中的答案。 第一步是实际找到图片中的形状,并应用透视变换获得纸张的“自上而下视图”。 我所做的是,我设法让脚本检测到这张纸,并应用转换,使它很好地扫描。我通过应用灰度,然后Canny边缘检测,迭代找到的边缘,找到了最大的一个,有4个角,并假设这一定是我的论文

这项工作相对来说很好,但脚本有时会对纸张的实际情况感到困惑——有时会检测到其他矩形并假定它们是纸张,有时拍摄纸张的背景非常轻,边缘不清晰(对比度不够)。这真的破坏了我以后的流程,当脚本认为它已经找到了文件,但实际上它是其他东西。我想改进纸张检测部分,这样我就可以始终确保检测到正确的东西。 我想-让我们在表单周围添加一个自定义框架,这将更容易检测并在角落中添加一些正方形(以再次检查找到的框架是否100%是我正在寻找的框架)

所以我创造了这样的东西:

现在,我想能够检测到帧的角,并确保“填充”的方块在角落里,以确保这是100%的帧,我正在寻找。 您能否就如何使用openCV实现它提供建议? 走这条路对吗?
谢谢

我以前也处理过类似的问题。我用OpenCV的C++实现,但是我有一些提示给你。 分割论文

<>为了实现更好的分割,考虑尝试。该技术将图像分割成N个簇,即将颜色相似的像素分组。然后,该组由一种颜色表示

与其他方法(例如纯二进制阈值法)相比,这种技术的优势在于,它可以识别多种颜色分布–这些颜色分布将分组在N个簇中。查看它(很抱歉链接,我还不允许发布直接图像):

这将帮助你更好地分割论文。该实现使用了称为“K-means”的聚类算法(稍后将详细介绍)。在我的示例中,我尝试了3个集群和5个算法“运行”(或尝试,因为K-means通常运行不止一次)

检测线路

很好。想检测边缘检测器产生的线条吗?在这里,至少有两个选项。第一个也是最简单的:使用霍夫线检测器。但是,正如您肯定已经看到的,调整Hough以找到您实际要查找的行可能很困难

过滤Hough返回的行的一个可能解决方案是运行“角度过滤器”,因为我们只寻找(接近)垂直和水平行。还可以按长度过滤线

这段代码给出了一个想法,您需要实际实现过滤器: //运行Hough的测线仪: cv::HoughLinesP(渐变、线条SP、1、cv_PI/180、最小投票数、最小线条长度、最大线条间距)

//处理点(线)
对于(size_t i=0;i
在上面的代码中,我实际使用的是线组件,而不是角度。因此,我的“角度”限制由两个最小组件长度定义:maxDy-y轴上的最大“增量”长度,以及x轴上的maxDx

线检测的另一个解决方案是利用这样一个事实,即您只查看具有角的线或它们之间约90度角的线。您可以运行形态过滤器,通过命中或未命中操作检测这些“模式”:

无论如何,回到Hough,这是我在应用角度/长度线过滤器后,在没有太多参数调整的情况下得到的检测:

酷。绿点表示直线的起点和终点。如你所见,有很多这样的人。我们如何“组合”它们?如果我们计算这些点的平均值呢?好的,但我们应该得到每个“象限”的线的平均值。如下图所示,我已将输入图像划分为4个象限(黄线):

每个象限——希望如此——将包含描述纸张角落的点。对于每个象限,检查哪些点落在给定象限上,并计算它们的平均值。这是总的想法

这是相当多的代码要写。幸运的是,如果我们稍微研究一下这个问题,我们可以看到,所有的绿点在一些非常明确的区域(或者,正如我们前面所说的“象限”)中都趋向于聚集

K-means将对类似值的数据进行分组,无论发生什么情况。它可以是像素,可以是空间点,可以是任何东西,只要给它你想要的数据集和集群数量,它就会吐出找到的集群和所说集群的方法——很好

如果使用Hough返回的线点运行K-means,则得到如所示的结果
cv::Mat imageQuantization( cv::Mat inputImage, int numberOfClusters = 3, int iterations = 5 ){

        //step 1 : map the src to the samples
        cv::Mat samples(inputImage.total(), 3, CV_32F);
        auto samples_ptr = samples.ptr<float>(0);
        for( int row = 0; row != inputImage.rows; ++row){
            auto src_begin = inputImage.ptr<uchar>(row);
            auto src_end = src_begin + inputImage.cols * inputImage.channels();
            //auto samples_ptr = samples.ptr<float>(row * src.cols);
            while(src_begin != src_end){
                samples_ptr[0] = src_begin[0];
                samples_ptr[1] = src_begin[1];
                samples_ptr[2] = src_begin[2];
                samples_ptr += 3; src_begin +=3;
            }
        }

        //step 2 : apply kmeans to find labels and centers
        int clusterCount = numberOfClusters; //Number of clusters to split the set by
        cv::Mat labels;
        int attempts = iterations; //Number of times the algorithm is executed using different initial labels
        cv::Mat centers;
        int flags = cv::KMEANS_PP_CENTERS;
        cv::TermCriteria criteria = cv::TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS,
                                                      10, 0.01 );

        //the call to kmeans:
        cv::kmeans( samples, clusterCount, labels, criteria, attempts, flags, centers );

        //step 3 : map the centers to the output
        cv::Mat clusteredImage( inputImage.size(), inputImage.type() );
        for( int row = 0; row != inputImage.rows; ++row ){
            auto clusteredImageBegin = clusteredImage.ptr<uchar>(row);
            auto clusteredImageEnd = clusteredImageBegin + clusteredImage.cols * 3;
            auto labels_ptr = labels.ptr<int>(row * inputImage.cols);

            while( clusteredImageBegin != clusteredImageEnd ){
                int const cluster_idx = *labels_ptr;
                auto centers_ptr = centers.ptr<float>(cluster_idx);
                clusteredImageBegin[0] = centers_ptr[0];
                clusteredImageBegin[1] = centers_ptr[1];
                clusteredImageBegin[2] = centers_ptr[2];
                clusteredImageBegin += 3; ++labels_ptr;
            }
        }   

        //return the output:
        return clusteredImage;
}
    cv::Mat testEdges;
    float lowerThreshold = 30;
    float upperThreshold = 3 * lowerThreshold;
    cv::Canny( testSegmented, testEdges, lowerThreshold, upperThreshold );
    // Process the points (lines)
    for( size_t i = 0; i < linesP.size(); i++ ) //points are stored in linesP
    {
        //get the line
        cv::Vec4i l = linesP[i]; //get the line

        //get the points:
        cv::Point startPoint = cv::Point( l[0], l[1] );
        cv::Point endPoint = cv::Point( l[2], l[3] );

        //filter horizontal & vertical:
        float dx = abs(startPoint.x - endPoint.x);
        float dy = abs(startPoint.y - endPoint.y);

        //angle filtering, delta y and delta x
        if ( (dy < maxDy) || (dx < maxDx) ){
          //got my target lines!
        }
    }