使用 OpenCV.js 检测角落里有正方形的框架

2024-02-10

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

这种方法效果相对较好,但有时脚本会混淆纸张的实际内容 - 有时会检测到其他矩形并假定其是纸张,有时拍摄纸张的背景非常亮并且有边缘不清晰(对比度不够)。当脚本认为它已经找到了纸张时,这确实破坏了我的流程,但实际上是其他东西。我想改进这个纸张检测部分,这样我就可以始终确保检测到正确的东西。 我想 - 让我们在表单周围添加一个自定义框架,这将更容易检测并在角落添加一些正方形(以仔细检查找到的框架是否 100% 是我正在寻找的框架)。

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

现在我希望能够检测框架的角落,并确保“填充”的方块位于角落中,以确保这 100% 是我正在寻找的框架。 您能建议一下如何用 openCV 实现它吗? 这是正确的方法吗? 谢谢!


我以前也研究过类似的问题。我使用 OpenCV 的 C++ 实现,但我有一些提示给您。

分割纸张

为了实现更好的细分,请考虑尝试图像量化 https://www.pyimagesearch.com/2014/07/07/color-quantization-opencv-using-k-means-clustering/。该技术将图像分割为N 个簇,即将颜色相似的像素分为一组。然后,该组由一种颜色表示。

该技术相对于其他技术(例如纯技术)的优势二进制阈值,是它可以识别多种颜色分布——那些将被分组的颜色分布N 个簇。看看(抱歉链接,我还不允许发布直接图像):

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

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;
}

请注意,该算法还生成两个附加矩阵。"Labels"是用标识其簇的整数标记的实际像素。“中心”mean每个簇的值。

检测边缘

现在,运行一个边缘检测器在此分割图像上。我们来试试坎尼吧。当然,您可以调整参数。在这里,我尝试了一个较低的门槛0f 30,和一个上限90。相当标准,只需确保上阈值遵循 = 3 * 下阈值的条件,按照 Canny 的建议。这是结果:

    cv::Mat testEdges;
    float lowerThreshold = 30;
    float upperThreshold = 3 * lowerThreshold;
    cv::Canny( testSegmented, testEdges, lowerThreshold, upperThreshold );

检测线条

好的。想要检测边缘检测器产生的线条吗?在这里,至少有两个选择。第一个也是最直接的:使用霍夫线检测器。然而,正如您肯定已经看到的那样,调整霍夫以找到您真正正在寻找的线条可能很困难。

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

此代码片段给出了想法,您需要实际实现过滤器: // 运行霍夫线检测器: cv::HoughLinesP(grad,linesP,1,CV_PI/180,minVotes,minLineLength,maxLineGap);

    // 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!
        }
    }

在上面的代码中,我实际上正在使用线组件,而不是角度。所以,我的“角度”限制由 2 定义最小元件长度: maxDy- y 轴上的最大“增量”长度,以及maxDx对于 x 轴。

线条检测的另一个解决方案是利用这样一个事实:您只查看具有以下特征的线条:CORNERS或者它们之间大约 90 度。您可以运行形态过滤器来通过以下方式检测这些“模式”:击中或没打中手术 :)

不管怎样,回到霍夫,这是我在没有太多参数调整并且应用角度/长度线滤波器之后得到的检测:

凉爽的。绿点代表线条的起点和终点。如您所见,有很多。怎样才能将它们“结合”起来呢?如果我们计算mean那些点?好的,但我们应该得到每个“象限”线的平均值。如下图所示,我将输入图像分为 4 个象限(黄线):

希望每个象限都包含描述纸张角的点。对于每个象限,检查哪些点落在给定象限上并计算它们的平均值。这就是总体思路。

这需要编写相当多的代码。幸运的是,如果我们稍微研究一下这个问题,我们就会发现所有的绿点都倾向于CLUSTER在一些非常明确的区域(或者,正如我们之前所说的,“象限”。)再次输入 K 均值。

无论如何,K 均值都会对具有相似值的数据进行分组。它可以是像素,它可以是空间点,它可以是任何东西,只要给它数据集和你想要的簇的数量,它就会吐出找到的簇和所述簇的含义——很好!

如果我使用 Hough 返回的线点运行 K 均值,我会得到最后一个图像中显示的结果。我还丢弃了与平均值相差太远的点。点的平均值通过“中心”矩阵返回,在这里它们以橙色呈现——非常接近!

希望其中一些对您有所帮助! :)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 OpenCV.js 检测角落里有正方形的框架 的相关文章

  • 我可以从 HTTP 请求中找到无线接入点的 BSSID(MAC 地址)吗?

    假设有人在咖啡店里无线连接到互联网 并向 johnsveryownserver com 发送 HTTP 请求 服务器端 有什么方法可以确定我的MAC地址吗 无线接入点他们连接到什么 请注意 我对他们机器的 MAC 地址不感兴趣 如果我无法使
  • Ext JS - 如何滚动到文本区域的底部

    这是我下面的代码 如何滚动到文本区域的底部 它一定是类似的东西 Ext getCmp output setScrollPosition Ext getCmp output getScrollHeight 这是我的文本区域代码 var myW
  • 将 jquery-mobile 与 Webpack 结合使用

    我正在尝试使用 webpack 加载 jquery mobile 但到目前为止还没有运气 我知道 jquery mobile 依赖于 jquery ui 而 jquery ui 又依赖于 jquery 如何在 Webpack 中设置这样的场
  • Flask wtf.quick_form 运行一些 javascript 并设置表单变量

    我正在创建博客文章 到目前为止已经使用普通的 html 表单完成了 我所做的一个有趣的想法是运行 javascript onclick 并使用页面中的额外数据在表单中设置一个隐藏变量 这很好地传递到服务器并通过 request form 获
  • 将 Sweet Alert 弹出窗口添加到 React 组件中的按钮

    我为 Bootstrap 和 React 找到了这个完美的 Sweet Alert 模块 我在 Meteor 应用程序中使用它 http djorg83 github io react bootstrap sweetalert http d
  • 禁用 JavaScript 中的右键单击

    当我尝试禁用右键单击时 它不起作用 我尝试使用下面的代码 document onclick function e console log e button if e button 2 e preventDefault return fals
  • 如何在 d3 js 中突出显示从根到选定节点的路径?

    我使用 d3 js 创建了一棵树 现在我创建了一个下拉菜单 其中包含树中所有节点的列表 现在 从下拉菜单中选择一个节点时 我想突出显示从根到该特定节点的路径 这个怎么做 首先创建一个 flatten 函数 它将分层数据变成一个 n 数组 f
  • 在 MVC Razor 中的 C# 和 Javascript 之间共享常量

    我想在服务器上的 C 和客户端上的 Javascript 中都使用字符串常量 我将常量封装在 C 类中 namespace MyModel public static class Constants public const string
  • 保存/导出Chrome的JavaScript控制台输入历史记录

    无论如何 我可以保存或导出 JavaScript 控制台的历史记录吗 input 控制台历史记录 在 Google Chrome 中 我不想保存输出或错误 因此将鼠标悬停在控制台框上 右键单击并选择Save as 不是解决方案 我不想每次都
  • 使react-leaflet能够离线使用

    我一直在使用反应传单 https github com PaulLeCam react leaflet图书馆 到目前为止运作良好 现在我希望网站预加载尽可能多的图块 以便网络应用程序 也是 PWA 可以在没有互联网的情况下使用 我找到了一些
  • iframe 重新加载按钮

    我浏览了很多网站 但似乎没有一个能正常工作 或者我不明白它们 我想要一个刷新某个 iframe 的简单按钮 该按钮将位于父页面上 并且 iframe 名称为 Right 有很多方法可以做到这一点 假设这个iframe markup 我们可以
  • 如何清除WebGL中的矩形区域?

    WebGL 有一个clear清除整个表面的方法 清除表面的特定矩形的最佳方法是什么 例如 我想将一个从 50 50 开始的 100x100 像素框设置为全零 ARGB 0 0 0 0 我现在能想到的就是用一个写入零的片段着色器绘制一个四边形
  • Keycloak javascript 适配器 `keycloak.init` 加载 404 iframe

    我正在尝试使用 javascript 适配器将 Keycloak 集成到我的客户端应用程序keycloak js 但是 我似乎无法让它发挥作用 这是我的代码 const keycloak new Keycloak realm my real
  • 如何在 e2e AngularJS 测试中进行文件上传?

    在我的一种观点中 我有一个文件上传控件 它支持通过拖放或单击按钮后打开的标准文件对话框上传文件 How to do this in my e2e tests1 1 Just one of the two options will be en
  • Jquery,清除/清空 tbody 元素的所有内容?

    我认为这会相当简单 但似乎空方法无法清除我拥有的 tbody 如果有人知道执行此操作的正确方法 我将不胜感激 我只想删除 tbody 中包含的所有内容 到目前为止我正在尝试 tbodyid empty HTML table tbody tr
  • window.showModalDialog 的等效跨浏览器解决方案是什么?

    window showModalDialog 的等效跨浏览器解决方案有哪些 showModalDialog 在 IE 和 FF 3 中引入 我个人认为没有 但是有很多 UI 工具包提供了这样的功能 例如jQuery UI http jque
  • 加载另一个 JS 脚本后加载

    这是我的代码 very big js file lots of html stuff 问题是 这些是异步加载的 有没有办法等待第二个脚本直到第一个脚本加载 如果您使用 jQuery 有一个非常简单的方法可以通过获取脚本 https api
  • ng-model 和值组合不适用于输入文本框

    我有两个输入文本框 我需要组合在两个文本框中输入的值并将其显示在第三个文本框中 如果我只使用value在第三个文本框中 Box 1
  • 替换两个引号之间的字符串

    我想转动一根绳子str hello my name is michael what s your s into hello my name is span class name michael span 我怎样才能在 JavaScript
  • pytesseract 无法从图像中识别复杂的数学公式

    我在用pytesseractpython 中的模块 pytesseract从图像中识别文本 但它不适用于包含复杂数学公式 例如根 推导 积分数学问题或方程 的图像 代码2 py Import modules from PIL import

随机推荐

  • OpenERP 6.1中创建菜单项时访问规则禁止的操作

    当我尝试创建新的菜单项以在 OpenERP 6 1 中打开窗口时 出现以下错误 访问错误 访问规则禁止的操作 或对已删除的文档执行的操作 操作 创建 文档类型 ir values 我总是可以使用绕过所有安全检查的神奇管理员帐户 但如果可能的
  • Java 小程序 --> ClassNotFound 异常

    我正在学习Java并阅读这本书 在本书中 我有一个Java applet 练习 我可以在 Eclipse 的 appletviewer 中运行它并且运行良好 但我在将小程序集成到 HTML 中时遇到问题 这是我的java代码 package
  • 如何在 Decision Manager 中导出和导入本地项目?

    我正在使用红帽决策管理器 我已经完成了我的项目 我想将其部署到另一台电脑上 我所能得到的只是一个 jar 文件 但是当我导入它时 决策管理器响应 未找到项目 希望有任何帮助 Thanks 从 Red Hat Decision Manager
  • 在类模板实例化中显式使用某些参数的默认值

    一个类模板可以有多个参数 这些参数都有默认值 template
  • 绘制两个 xts 对象

    我在用着xtsExtra绘制两个 xts 对象 考虑以下对plot xts的调用 plot xts merge a b screens c 1 2 它用于在两个单独的面板中绘制 xts 对象 a 和 b 如何控制 y 轴的间距 具体来说 我
  • 安卓蓝牙连接错误

    我在堆栈跟踪中收到以下消息 我可以找到蓝牙设备 但是当我尝试打开套接字时会发生这种情况 10 30 22 23 08 901 ERROR BTL CFG 8633 WARNING service brcm bt INQ FILTER BDA
  • 以 DirectX 编程方式创建纹理

    我试图通过提供 rgba 值数组 使用该数组创建 ID3D11Texture2D 资源 然后将其映射到我的 20 x 20 正方形 在屏幕上创建一个白色 20 x 20 像素正方形 以下是创建方形纹理和着色器资源视图的代码 void Squ
  • Intel Core 2 Duo 预取

    有人有过在 Core 2 Duo 处理器上使用预取指令的经验吗 我一直在使用 标准 预取集 prefetchnta prefetcht1等 在一系列 P4 机器上取得了成功 但是当在 Core 2 Duo 上运行代码时 似乎prefetch
  • Task.WhenAll() 和 foreach(任务中的 var task) 有什么区别

    经过几个小时的努力 我在我的应用程序中发现了一个错误 我认为下面的两个函数具有相同的行为 但事实证明它们并非如此 谁能告诉我幕后到底发生了什么 以及为什么他们的行为方式不同 public async Task MyFunction1 IEn
  • 获取 WooCommerce 中所有“库存”产品的数量

    我有一个网站 产品被视为贸易 交易 因此 当有人进行交易 购买产品 时 它就会缺货 显示当前可用产品的剩余数量 基本上是库存 的 PHP 代码片段是什么 例如 快点 仅 10 个交易 woocommerce gt 产品 可用 提前致谢 我尝
  • 在 JavaScript 中,如何检查数组是否有重复的多个值?

    很抱歉我英语说得不好 这些是我的简单代码 带有一些参数数组 if link indexOf x 1 y 2 z 3 1 link push x 1 y 2 z 3 else alert Duplicate 用于 for 循环但不提醒重复 您
  • Nodejs v0.10.x(freebsd)“X509_STORE_add_cert:证书已在哈希表中”

    我正在使用异步 Web api 并且在高于 v0 8 9 的 Nodejs 版本中遇到问题 uname a FreeBSD home 9 1 STABLE FreeBSD 9 1 STABLE 0 2 月 1 日星期五 10 38 27 E
  • 如何在 Angular 单元测试中有条件地刷新 $timeout

    我的代码是这样的 function doThing if invalidInput console error Invalid input return timeout function MyService doThing 1000 我想测
  • 如何在C++中实现big int

    我想在 C 中实现一个 big int 类作为编程练习 一个可以处理大于 long int 的数字的类 我知道已经有几个开源实现 但我想编写自己的实现 我正在尝试了解什么是正确的方法 据我了解 一般策略是将数字作为字符串获取 然后将其分解为
  • 如果 url 变量为空则重定向到主页

    我无法让它正常工作 我有一个 view php 页面 它使用 ID 变量来检索数据库信息 所以我的网址是这样的 view php id 23 如果用户删除了 url 的 23 部分 我试图让它将用户 重定向 到主页 所以当你访问时 view
  • javascript 函数在块语句内提升

    function foo foo 1 function foo foo 2 console log foo 1 谁能解释一下为什么这里输出 1 Edit 似乎存在实现差异 在 Chrome Firefox Nodejs 中输出为 1 但在
  • 如何使用 Python 2.6 在 Windows 上安装 PyGTK / PyGobject?

    我有一个依赖于 PyGTK PyGobject 和 PyCairo 的应用程序 我为在 Linux 上运行而构建了该应用程序 我想将它移植到Windows 但是当我执行时import gobject我明白了 Traceback most r
  • 使用 Gradle 运行特定的仪器单元测试

    有没有办法使用 Gradle 运行特定的 Android 仪器单元测试 我试过了 gradle Dtest single UnitTestName connectedInstrumentTest 但它似乎运行了包中的所有测试 Using t
  • 将组合框添加到 ListViewItem

    我正在使用 C 创建一个 Windows 窗体应用程序 该表单包含一个ListView 这是红十字会调度员的应用程序 ListView 有所有单位的列表 每个单位都有一个状态 此状态需要更改 因此 我想向每个 ListViewItem 添加
  • 使用 OpenCV.js 检测角落里有正方形的框架

    我一直在尝试使用 Javascript 和 OpenCV js 创建一个填写的表单扫描仪 我基本上想做的是拍摄一张写有填写好的表格的纸的照片 然后能够扫描照片并分析表格中的答案 第一步是实际找到图片中的形式并应用透视变换以获得论文的 自上而