眼镜检测

2024-03-08

我想做的是测量眼镜框的厚度。我有一个想法来测量框架轮廓的厚度(可能是更好的方法?)。到目前为止,我已经勾勒出眼镜框的轮廓,但线条不相交处存在间隙。我考虑过使用 HoughLinesP,但我不确定这是否是我需要的。

到目前为止,我已执行以下步骤:

  • 将图像转换为灰度
  • 在眼睛/眼镜区域周围创建投资回报率
  • 模糊图像
  • 放大图像(这样做是为了去除任何薄框眼镜)
  • 进行 Canny 边缘检测
  • 找到轮廓

结果如下:

到目前为止,这是我的代码:

//convert to grayscale
cv::Mat grayscaleImg;
cv::cvtColor( img, grayscaleImg, CV_BGR2GRAY );

//create ROI
cv::Mat eyeAreaROI(grayscaleImg, centreEyesRect);
cv::imshow("roi", eyeAreaROI);

//blur
cv::Mat blurredROI;
cv::blur(eyeAreaROI, blurredROI, Size(3,3));
cv::imshow("blurred", blurredROI);

//dilate thin lines
cv::Mat dilated_dst;
int dilate_elem = 0;
int dilate_size = 1;
int dilate_type = MORPH_RECT;

cv::Mat element = getStructuringElement(dilate_type, 
    cv::Size(2*dilate_size + 1, 2*dilate_size+1), 
    cv::Point(dilate_size, dilate_size));

cv::dilate(blurredROI, dilated_dst, element);
cv::imshow("dilate", dilated_dst);

//edge detection
int lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;    

cv::Canny(dilated_dst, dilated_dst, lowThreshold, lowThreshold*ratio, kernel_size);

//create matrix of the same type and size as ROI
Mat dst;
dst.create(eyeAreaROI.size(), dilated_dst.type());
dst = Scalar::all(0);

dilated_dst.copyTo(dst, dilated_dst);
cv::imshow("edges", dst);

//join the lines and fill in
vector<Vec4i> hierarchy;
vector<vector<Point>> contours;

cv::findContours(dilated_dst, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
cv::imshow("contours", dilated_dst);

我不完全确定下一步是什么,或者正如我上面所说,我是否应该使用 HoughLinesP 以及如何实现它。很感谢任何形式的帮助!


我认为有两个主要问题。

  1. 分割眼镜框

  2. 求分段框架的厚度

我现在将发布一种分割示例图像的眼镜的方法。也许这种方法也适用于不同的图像,但您可能必须调整参数,或者您可能能够使用主要思想。

主要思想是: 首先,找到图像中最大的轮廓,这应该是眼镜。其次,在之前找到的最大轮廓中找到两个最大的轮廓,这应该是镜框内的眼镜!

我使用此图像作为输入(这应该是您的模糊但不是扩张的图像):

// this functions finds the biggest X contours. Probably there are faster ways, but it should work...
std::vector<std::vector<cv::Point>> findBiggestContours(std::vector<std::vector<cv::Point>> contours, int amount)
{
    std::vector<std::vector<cv::Point>> sortedContours;

    if(amount <= 0) amount = contours.size();
    if(amount > contours.size()) amount = contours.size();

    for(int chosen = 0; chosen < amount; )
    {
        double biggestContourArea = 0;
        int biggestContourID = -1;
        for(unsigned int i=0; i<contours.size() && contours.size(); ++i)
        {
            double tmpArea = cv::contourArea(contours[i]);
            if(tmpArea > biggestContourArea)
            {
                biggestContourArea = tmpArea;
                biggestContourID = i;
            }
        }

        if(biggestContourID >= 0)
        {
            //std::cout << "found area: " << biggestContourArea << std::endl;
            // found biggest contour
            // add contour to sorted contours vector:
            sortedContours.push_back(contours[biggestContourID]);
            chosen++;
            // remove biggest contour from original vector:
            contours[biggestContourID] = contours.back();
            contours.pop_back();
        }
        else
        {
            // should never happen except for broken contours with size 0?!?
            return sortedContours;
        }

    }

    return sortedContours;
}

int main()
{
    cv::Mat input = cv::imread("../Data/glass2.png", CV_LOAD_IMAGE_GRAYSCALE);
    cv::Mat inputColors = cv::imread("../Data/glass2.png"); // used for displaying later
    cv::imshow("input", input);

    //edge detection
    int lowThreshold = 100;
    int ratio = 3;
    int kernel_size = 3;    

    cv::Mat canny;
    cv::Canny(input, canny, lowThreshold, lowThreshold*ratio, kernel_size);
    cv::imshow("canny", canny);

    // close gaps with "close operator"
    cv::Mat mask = canny.clone();
    cv::dilate(mask,mask,cv::Mat());
    cv::dilate(mask,mask,cv::Mat());
    cv::dilate(mask,mask,cv::Mat());
    cv::erode(mask,mask,cv::Mat());
    cv::erode(mask,mask,cv::Mat());
    cv::erode(mask,mask,cv::Mat());

    cv::imshow("closed mask",mask);

    // extract outermost contour
    std::vector<cv::Vec4i> hierarchy;
    std::vector<std::vector<cv::Point>> contours;
    //cv::findContours(mask, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
    cv::findContours(mask, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);


    // find biggest contour which should be the outer contour of the frame
    std::vector<std::vector<cv::Point>> biggestContour;
    biggestContour = findBiggestContours(contours,1); // find the one biggest contour
    if(biggestContour.size() < 1)
    {
        std::cout << "Error: no outer frame of glasses found" << std::endl;
        return 1;
    }

    // draw contour on an empty image
    cv::Mat outerFrame = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC1);
    cv::drawContours(outerFrame,biggestContour,0,cv::Scalar(255),-1);
    cv::imshow("outer frame border", outerFrame);

    // now find the glasses which should be the outer contours within the frame. therefore erode the outer border ;)
    cv::Mat glassesMask = outerFrame.clone();
    cv::erode(glassesMask,glassesMask, cv::Mat());
    cv::imshow("eroded outer",glassesMask);

    // after erosion if we dilate, it's an Open-Operator which can be used to clean the image.
    cv::Mat cleanedOuter;
    cv::dilate(glassesMask,cleanedOuter, cv::Mat());
    cv::imshow("cleaned outer",cleanedOuter);


    // use the outer frame mask as a mask for copying canny edges. The result should be the inner edges inside the frame only
    cv::Mat glassesInner;
    canny.copyTo(glassesInner, glassesMask);

    // there is small gap in the contour which unfortunately cant be closed with a closing operator...
    cv::dilate(glassesInner, glassesInner, cv::Mat());
    //cv::erode(glassesInner, glassesInner, cv::Mat());
    // this part was cheated... in fact we would like to erode directly after dilation to not modify the thickness but just close small gaps.
    cv::imshow("innerCanny", glassesInner);


    // extract contours from within the frame
    std::vector<cv::Vec4i> hierarchyInner;
    std::vector<std::vector<cv::Point>> contoursInner;
    //cv::findContours(glassesInner, contoursInner, hierarchyInner, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
    cv::findContours(glassesInner, contoursInner, hierarchyInner, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    // find the two biggest contours which should be the glasses within the frame
    std::vector<std::vector<cv::Point>> biggestInnerContours;
    biggestInnerContours = findBiggestContours(contoursInner,2); // find the one biggest contour
    if(biggestInnerContours.size() < 1)
    {
        std::cout << "Error: no inner frames of glasses found" << std::endl;
        return 1;
    }

    // draw the 2 biggest contours which should be the inner glasses
    cv::Mat innerGlasses = cv::Mat::zeros(mask.rows, mask.cols, CV_8UC1);
    for(unsigned int i=0; i<biggestInnerContours.size(); ++i)
        cv::drawContours(innerGlasses,biggestInnerContours,i,cv::Scalar(255),-1);

    cv::imshow("inner frame border", innerGlasses);

    // since we dilated earlier and didnt erode quite afterwards, we have to erode here... this is a bit of cheating :-(
    cv::erode(innerGlasses,innerGlasses,cv::Mat() );

    // remove the inner glasses from the frame mask
    cv::Mat fullGlassesMask = cleanedOuter - innerGlasses;
    cv::imshow("complete glasses mask", fullGlassesMask);

    // color code the result to get an impression of segmentation quality
    cv::Mat outputColors1 = inputColors.clone();
    cv::Mat outputColors2 = inputColors.clone();
    for(int y=0; y<fullGlassesMask.rows; ++y)
        for(int x=0; x<fullGlassesMask.cols; ++x)
        {
            if(!fullGlassesMask.at<unsigned char>(y,x))
                outputColors1.at<cv::Vec3b>(y,x)[1] = 255;
            else
                outputColors2.at<cv::Vec3b>(y,x)[1] = 255;

        }

    cv::imshow("output", outputColors1);

    /*
    cv::imwrite("../Data/Output/face_colored.png", outputColors1);
    cv::imwrite("../Data/Output/glasses_colored.png", outputColors2);
    cv::imwrite("../Data/Output/glasses_fullMask.png", fullGlassesMask);
    */

    cv::waitKey(-1);
    return 0;
}

我得到这个分割结果:

原始图像中的叠加会给您质量的印象:

和逆:

代码中有一些棘手的部分,而且还没有整理好。我希望这是可以理解的。

下一步是计算分段框架的厚度。我的建议是计算反转掩模的距离变换。由此,您将需要计算脊检测或骨架化掩模以找到脊。之后使用山脊距离的中值。

无论如何,我希望这篇文章可以对您有所帮助,尽管它还不是解决方案。

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

眼镜检测 的相关文章

  • 键盘加速器在 UWP 应用中停止工作

    我正在尝试将键盘加速器添加到 UWP 应用程序中的 CommandBar 菜单项 当应用程序启动时 这工作正常 但在我第一次打开溢出菜单后 加速器停止工作 这似乎不会发生在主要命令 菜单之外 上 只有溢出菜单内的辅助命令才会发生 此外 单击
  • 使用API​​隐藏程序标题栏

    它可以使用 c 和 windows api 删除窗口控制台标题栏 如果是的话如何 请 这个简单的应用程序隐藏并显示其所在控制台的标题栏 它会立即将控制台标题更改为 guid 以查找窗口句柄 然后 它使用 ToggleTitleBar 使用找
  • 静态类变量与外部变量相同,只是具有类作用域吗?

    在我看来 静态类变量与外部变量相同 因为你只需要declare它在static int x extern int x语句 并在其他地方实际定义它 通常在 cpp 文件中 静态类变量 h file class Foo static int x
  • 无法从 Web api POST 读取正文数据

    我正在尝试从新的 Asp Net Web Api 中的请求中提取一些数据 我有一个像这样的处理程序设置 public class MyTestHandler DelegatingHandler protected override Syst
  • 单线程公寓问题

    从我的主窗体中 我调用以下命令来打开一个新窗体 MyForm sth new MyForm sth show 一切都很好 但是这个表单有一个组合框 当我将其 AutoCompleteMode 切换为建议和追加时 我在显示表单时遇到了这个异常
  • 指向字节数组的指针

    由于 Misra C 的要求 我的一位同事想要使用指针声明 但我遇到了一些问题 Misra 安全关键指南 不会让我们纯粹的程序员使用指针 但会让我们对数组字节进行操作 他打算获取一个指向字节数组的指针 因此我们不会在堆栈上传递实际的数组 T
  • 无法加载文件或程序集“EntityFramework,版本=6.0.0.0”

    我究竟做错了什么 我该如何解决这个问题 我有一个包含多个项目的解决方案 它是一个 MVC NET 4 5 Web 应用程序 在调试模式下启动后调用其中一个项目时 出现此错误 导致此错误的项目具有以下参考 两个都是版本6 0 0 0 应用程序
  • 您可以在一个 Windows Azure 实例上部署多个 Web 应用程序吗?

    是否可以在一个 windows azure 小型计算实例中运行一堆 Web 应用程序 我正在考虑使用 Azure 作为放置一堆处于开发和非生产状态的项目 Web 应用程序 的地方 有些实际上已经被封存了 但我想在某个地方有一个活跃的实例 我
  • 格式化货币

    在下面的示例中 逗号是小数点分隔符 我有这个 125456 89 我想要这个 125 456 89 其他示例 23456789 89 gt 23 456 789 89 Thanks 看看这个例子 double value 12345 678
  • 更改 IdentityServer4 实体框架表名称

    我正在尝试更改由 IdentityServer4 的 PersistedGrantDb 和 ConfigurationDb 创建的默认表名称 并让实体框架生成正确的 SQL 例如 而不是使用实体IdentityServer4 EntityF
  • 特征密集稀疏矩阵乘积是线程化的吗?

    我知道稀疏密集产品是根据文档进行线程化的 https eigen tuxfamily org dox TopicMultiThreading html https eigen tuxfamily org dox TopicMultiThre
  • 使用 AdHocWorkspace 会导致“不支持语言‘C#’”。

    在VS2015中使用Microsoft CodeAnalysis CSharp Workspaces的RC2 这段代码会抛出异常 var tree CSharpSyntaxTree ParseText var workspace new A
  • 系统错误 124 - SHFileOperation 的 ERROR_INVALID_LEVEL

    我在使用时遇到问题SHFileOperation SHFileOperation SHFILEOPSTRUCT https stackoverflow com questions 9191415 shfileoperation shfile
  • .NET JIT 编译的代码缓存在哪里?

    NET 程序首先被编译为 MSIL 代码 当它被执行时 JIT编译器会将其编译为本机机器代码 我想知道 这些JIT编译的机器代码存储在哪里 它只存储在进程的地址空间中吗 但由于程序的第二次启动比第一次快得多 我认为即使在执行完成后 该本机代
  • 相当于 C# 中 Java 的“ByteBuffer.putType()”

    我正在尝试通过从 Java 移植代码来格式化 C 中的字节数组 在 Java 中 使用方法 buf putInt value buf putShort buf putDouble 等等 但我不知道如何将其移植到 C 我尝试过 MemoryS
  • “int i=1,2,3”和“int i=(1,2,3)”之间的区别 - 使用逗号运算符的变量声明[重复]

    这个问题在这里已经有答案了 int i 1 2 3 int i 1 2 3 int i i 1 2 3 这些说法有什么区别 我无法找出任何具体原因 Statement 1 Result Compile error 运算符的优先级高于 运算符
  • #pragma pack(16) 和 #pragma pack(8) 的效果总是相同吗?

    我正在尝试使用来对齐数据成员 pragma pack n http msdn microsoft com en us library 2e70t5y1 28v vs 100 29 aspx 以下面为例 include
  • 如何将对象转换为传递给函数的类型?

    这不会编译 但我想做的只是将对象转换为传递给函数的 t public void My Func Object input Type t t object ab TypeDescriptor GetConverter t ConvertFro
  • 使用 Chrome 和 Selenium 设置 LocalStorage

    我正在尝试使用 OpenQA Selenium 和 Chrome 设置本地存储键和值 我认为这相当微不足道 但我似乎无法让它发挥作用 我对 C 很陌生 所以我可能错过了一些东西 无论如何 我有这个功能 public static void
  • 启动画面后主窗口出现在其他窗口后面

    我有一个带有启动屏幕的 Windows 窗体应用程序 当我运行该应用程序时 启动屏幕显示正常 消失并加载应用程序的主窗体 但是 当我加载主窗体时 它出现在包含该应用程序的 Windows 资源管理器目录下 这是运行启动画面然后运行主窗体的代

随机推荐

  • 用户配置文件的布局不适用于 asp.net core 3.0

    在 Asp core 3 0 上 当用户单击其个人资料 Areas Identity Pages Account Manage layout cshtml 时 我的布局页面无法正常工作 当我登录后单击默认电子邮件时 我会看到 Manage
  • 重置 R 实例

    是否可以重置 R 实例 例如 如果我使用了命令 x lt 1 10 plot x x 从而用 x 变量污染了系统 在这种状态下 我可以在不关闭 R 并再次启动它的情况下恢复到干净状态吗 您可以使用以下命令从工作区中删除所有变量 rm lis
  • protobuf-net 不比二进制序列化快吗?

    我编写了一个程序 使用 XMLSerializer BinaryFormatter 和 ProtoBuf 序列化 Person 类 我认为 protobuf net 应该比其他两个更快 Protobuf 序列化比 XMLSerializat
  • MVC 4 中的防伪 cookie 令牌和表单字段令牌不匹配

    我在 ASP NET MVC 4 中使用默认登录模块 我没有更改默认应用程序中的任何代码 并将其托管在共享服务器上 我使用默认登录页面登录后 我让浏览器闲置了一段时间 然后 当我尝试执行任何控制器操作时 显然应用程序会重定向到登录页面 Au
  • Python Matplotlib - 未对齐的网格线和颜色填充

    我使用以下代码来生成一种二进制热图 import numpy as np import matplotlib colors as mlc import matplotlib pyplot as mlp states AAAA BBBB CC
  • 使用 Rails 连接到 Web 服务(HTTP 请求)?

    我正在使用 Ruby on Rails 3 并尝试实现 API 以从 Web 服务检索帐户信息 也就是说 我想连接到具有 Account 类的 Web 服务并从show在 URI 处路由的操作http
  • 带有绝对路径的“没有这样的文件或目录”

    我想导入 png 文件 import matplotlib pyplot as plt O plt imread C Users myusername Downloads Mathe Picture png 我有绝对路径 但它仍然给我错误
  • 如何从一个类调用另一个类的方法(iOS)

    这是一个非常基本的问题 但我进行了全面搜索 但无法找到一个足以让我理解的答案 我想要做的是在我的 iOS 应用程序的一个类中创建一个方法 然后从我的应用程序中的其他类调用该方法 有人可以准确解释我需要做什么才能实现这一目标吗 任何帮助将不胜
  • HTML5 拖放在拖动时更改光标(不使用 UI)

    我想改变cursor拖动时 所以我尝试这样 function drag event localStorage setItem no event target data no html css cursor move tr class mem
  • 使 Python 脚本与 xargs 一起工作

    让我的 Python 脚本与 xargs 良好配合的过程是什么 例如 我希望以下命令能够处理文本文件的每一行 并执行任意命令 cat servers txt hardware py m 本质上希望将每一行传递给 hardware py 脚本
  • 从 Java 写入 XML 文档 - 简单

    我知道 stackoverflow 上有很多关于从 Java 编写到 XML 的问题 但它太复杂了 我觉得我有一个非常简单的问题 但我无法弄清楚 因此 我有一个程序需要大量用户输入 并且当前正在创建并附加带有结果的文本文档 我将在这里发布我
  • 具有附加可绑定字段的 ASP.NET Server 控件

    我创建了一个自定义服务器控件 源自System Web Contols CheckBoxList定制如何CheckBoxList被渲染 我还想添加另一个可绑定字段并获取该字段的值CheckBoxList RenderItem 方法 我想要创
  • 构建时 PNG 生成不支持对其他资源的引用

    AndroidStudio 3 0 Android Gradle 插件 3 0
  • TableLayout中表行的索引

    我有一个 TableLayout 和一些未知数量的 TableRows 它们是根据数据库中的内容生成的 每行都附加了一个 OnClick 侦听器 但是 一旦发生单击 我就无法 有意义地 分辨它来自哪一行 有没有一种方法可以获取与 Table
  • lib 依赖项及其顺序

    有时 如果我们没有在 makefile 中按特定顺序列出库 则会失败 原因是 定义应该先于其使用 如何确定正确的顺序 实际上 在链接库时 使用应该在定义之前 在处理提供其定义的库文件之前 需要知道任何未解析的符号 恐怕您必须手动执行此命令
  • Facebook Graph API 返回空数据集

    我正在尝试使用 Graph API Explorer 为我的应用程序创建访问令牌 以使用 me accounts 查看我的页面 但是 每次我尝试此操作时 它都会返回一个空数据集 我已经选择了manage pages作为权限 但它仍然不起作用
  • 如何生成具有特定概率密度函数的随机数?

    我正在尝试对移动无线网络的阴影和快速衰落进行建模 对于快速衰落 瑞利衰落是一个合理的模型 信道响应的包络将是瑞利分布的 将该随机变量称为 R 其概率密度函数 PDF 为 PR r 2r exp r 2 r gt 0 2 2 http en
  • Hudson 和 Maven 的双单元测试报告

    我在 hudson 有一个 maven2 项目 当 cobertura 报告插件运行时 它会导致单元测试显示它们已经运行了两次 我不介意它们运行多次 但趋势图显示的测试数量是我们实际运行的两倍 有没有办法确保图表只显示一次 thanks J
  • Oracle 中的截断表出现错误

    我遇到的问题是 当我在 Oracle 中运行以下命令时 遇到错误 Truncate table mytable Errors ORA 02266 unique primary keys in table referenced by enab
  • 眼镜检测

    我想做的是测量眼镜框的厚度 我有一个想法来测量框架轮廓的厚度 可能是更好的方法 到目前为止 我已经勾勒出眼镜框的轮廓 但线条不相交处存在间隙 我考虑过使用 HoughLinesP 但我不确定这是否是我需要的 到目前为止 我已执行以下步骤 将