Javascript 使用OpenCV.js检测角落中有正方形的帧
我一直在玩用Javascript和OpenCV.js创建填充表单扫描仪的游戏。 我基本上想做的是拍一张纸的照片,上面有一张填好的表格,能够扫描照片并分析表格中的答案。 第一步是实际找到图片中的形状,并应用透视变换获得纸张的“自上而下视图”。 我所做的是,我设法让脚本检测到这张纸,并应用转换,使它很好地扫描。我通过应用灰度,然后Canny边缘检测,迭代找到的边缘,找到了最大的一个,有4个角,并假设这一定是我的论文 这项工作相对来说很好,但脚本有时会对纸张的实际情况感到困惑——有时会检测到其他矩形并假定它们是纸张,有时拍摄纸张的背景非常轻,边缘不清晰(对比度不够)。这真的破坏了我以后的流程,当脚本认为它已经找到了文件,但实际上它是其他东西。我想改进纸张检测部分,这样我就可以始终确保检测到正确的东西。 我想-让我们在表单周围添加一个自定义框架,这将更容易检测并在角落中添加一些正方形(以再次检查找到的框架是否100%是我正在寻找的框架) 所以我创造了这样的东西: 现在,我想能够检测到帧的角,并确保“填充”的方块在角落里,以确保这是100%的帧,我正在寻找。 您能否就如何使用openCV实现它提供建议? 走这条路对吗?Javascript 使用OpenCV.js检测角落中有正方形的帧,javascript,opencv,image-processing,Javascript,Opencv,Image Processing,我一直在玩用Javascript和OpenCV.js创建填充表单扫描仪的游戏。 我基本上想做的是拍一张纸的照片,上面有一张填好的表格,能够扫描照片并分析表格中的答案。 第一步是实际找到图片中的形状,并应用透视变换获得纸张的“自上而下视图”。 我所做的是,我设法让脚本检测到这张纸,并应用转换,使它很好地扫描。我通过应用灰度,然后Canny边缘检测,迭代找到的边缘,找到了最大的一个,有4个角,并假设这一定是我的论文 这项工作相对来说很好,但脚本有时会对纸张的实际情况感到困惑——有时会检测到其他矩形
谢谢 我以前也处理过类似的问题。我用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!
}
}