OpenCV 真圆度测量

最近一个项目需要在图像上测量一些小孔的真圆度。因此专门研究了一下真圆度计算问题。

对于一个轮廓,我们可以求出这个轮廓的外接圆和内切圆。这两个圆的半径差定义为真圆度。这个数值越小,表示这个圆越标准。

外接圆在 OpenCV 中有现成的函数来计算,但是内切圆是没有的。去算内切圆难度还是蛮大的。

为此,我采用了个变通的方法。对这个轮廓先计算最小二乘拟合圆。之后计算轮廓上的各个点到圆心的距离。最大距离减去最小距离的差可以作为真圆度的一个近似的计算。

真圆度的计算代码封装到了一个类里。这个类的头文件如下:

#ifndef TRUEROUNDNESS_H
#define TRUEROUNDNESS_H#include <opencv2/opencv.hpp>/*** @brief The TrueRoundness class 真圆度计算。只实现了一种最基本的算法。*/
class TrueRoundness
{
public:/*** @brief TrueRoundness 真圆度计算的类的构造函数。考虑到除去噪声影响,可以排除部分数据点。* @param top 计算真圆度时,最大直径排除前 top 个点。* @param bottom 计算真圆度时,最小直径排除最后 bottom 个点。*/TrueRoundness(unsigned int top = 0, unsigned int bottom = 0);//可以去除两头的一部分数据/*** @brief TrueRoundness 真圆度计算的类的构造函数。考虑到除去噪声影响,可以排除部分数据点。* @param top_percent 计算真圆度时,最大直径排除前 top_percent % 的点。* @param bottom_percent 计算真圆度时,最小直径排除最后 bottom_percent % 的点。*/TrueRoundness(double top_percent, double bottom_percent); //去除两头一定比例的数据/*** @brief operator ()计算真圆度。首先计算出拟合圆,然后计算轮廓上到点到圆心的最远距离和最近距离。之差就是真圆度。* @param [in] contour 轮廓上的点。* @param [out] r_min* @param [out] r_max* @return  返回真圆度。如果结果小于 0 ,则表示计算失败。通常是拟合圆失败。*/double operator() (const std::vector<cv::Point2i> &contour, double &r_min, double &r_max);double operator() (const std::vector<cv::Point2f> &contour, double &r_min, double &r_max);/*** @brief circleLeastFit 最小二乘法拟合圆。这个函数是辅助函数,通常不需要使用。* @param points 圆的轮廓上的点* @param center_x 计算出的圆心坐标 x* @param center_y 计算出的圆心坐标 y* @param radius 计算出的半径值* @return true 表示拟合成功。false 表示失败。*/bool circleLeastFit(const std::vector<cv::Point2i> &points, double &center_x, double &center_y, double &radius);bool circleLeastFit(const std::vector<cv::Point2f> &points, double &center_x, double &center_y, double &radius);/*** @brief sort 辅助函数,对边缘数据进行排序,离圆心远的数据排在前面。* @param contour 轮廓上的点* @param center_x 圆心坐标 x* @param center_y 圆心坐标 y*/void sort( std::vector<cv::Point2i> &contour, double center_x, double center_y );void sort( std::vector<cv::Point2f> &contour, double center_x, double center_y );/*** @brief distance 辅助函数,计算一个点到圆心的距离* @param p* @param center_x* @param center_y* @return*/double distance(cv::Point2i p, double center_x, double center_y);double distance(cv::Point2f p, double center_x, double center_y);
private:int m_type = 0;int m_top = 0;int m_bottom = 0;double m_top_percent = 0;double m_bottom_percent = 0;
};#endif // TRUEROUNDNESS_H

实现代码如下:

#include "TrueRoundness.h"TrueRoundness::TrueRoundness(unsigned int top, unsigned int bottom)
{m_type = 0;m_top = top;m_bottom = bottom;
}TrueRoundness::TrueRoundness(double top_percent, double bottom_percent)
{m_type = 1;m_top_percent = top_percent;m_bottom_percent = bottom_percent;
}double TrueRoundness::operator()(const std::vector<cv::Point2i> &contour, double &r_min, double &r_max)
{double center_x;double center_y;double radius;bool ret = circleLeastFit(contour, center_x, center_y, radius);if(!ret) return -1;std::vector<cv::Point2i> newcontour = contour;sort(newcontour, center_x, center_y);int N = newcontour.size();if(m_type == 0){if(m_top > N || m_bottom > N){return -2;}int N1 = m_top;int N2 = N - 1 - m_bottom;if(distance(newcontour[N1], center_x, center_y) < radius || distance(newcontour[N2], center_x, center_y) > radius){return -3;}r_max = distance(newcontour[N1], center_x, center_y);r_min = distance(newcontour[N2], center_x, center_y);return  r_max - r_min ;}else if(m_type == 1){if(m_top_percent >= 1 || m_bottom_percent >= 1) return -4;int N1 = 0, N2 = 0;for(int i = 0; i < N; i++){double r = distance(newcontour[i], center_x, center_y);if(r > radius) N1 ++;if(r < radius) N2 ++;}N1 = N1 * (m_top_percent);N2 = N - 1 - N2 * (m_bottom_percent);r_max = distance(newcontour[N1], center_x, center_y);r_min = distance(newcontour[N2], center_x, center_y);return  r_max - r_min ;}return -5;
}double TrueRoundness::operator()(const std::vector<cv::Point2f> &contour, double &r_min, double &r_max)
{double center_x;double center_y;double radius;bool ret = circleLeastFit(contour, center_x, center_y, radius);if(!ret) return -1;std::vector<cv::Point2f> newcontour = contour;sort(newcontour, center_x, center_y);int N = newcontour.size();if(m_type == 0){if(m_top > N || m_bottom > N){return -2;}int N1 = m_top;int N2 = N - 1 - m_bottom;if(distance(newcontour[N1], center_x, center_y) < radius || distance(newcontour[N2], center_x, center_y) > radius){return -3;}r_max = distance(newcontour[N1], center_x, center_y);r_min = distance(newcontour[N2], center_x, center_y);return  r_max - r_min ;}else if(m_type == 1){if(m_top_percent >= 1 || m_bottom_percent >= 1) return -4;int N1 = 0, N2 = 0;for(int i = 0; i < N; i++){double r = distance(newcontour[i], center_x, center_y);if(r > radius) N1 ++;if(r < radius) N2 ++;}N1 = N1 * (m_top_percent);N2 = N - 1 - N2 * (m_bottom_percent);r_max = distance(newcontour[N1], center_x, center_y);r_min = distance(newcontour[N2], center_x, center_y);return  r_max - r_min ;}return -5;
}bool TrueRoundness::circleLeastFit(const std::vector<cv::Point2i> &points, double &center_x, double &center_y, double &radius)
{center_x = 0.0;center_y = 0.0;radius = 0.0;if (points.size() < 3){return false;}double sum_x = 0.0, sum_y = 0.0;double sum_x2 = 0.0, sum_y2 = 0.0;double sum_x3 = 0.0, sum_y3 = 0.0;double sum_xy = 0.0, sum_x1y2 = 0.0, sum_x2y1 = 0.0;int N = points.size();for (int i = 0; i < N; i++){double x = points[i].x;double y = points[i].y;double x2 = x * x;double y2 = y * y;sum_x += x;sum_y += y;sum_x2 += x2;sum_y2 += y2;sum_x3 += x2 * x;sum_y3 += y2 * y;sum_xy += x * y;sum_x1y2 += x * y2;sum_x2y1 += x2 * y;}double C, D, E, G, H;double a, b, c;C = N * sum_x2 - sum_x * sum_x;D = N * sum_xy - sum_x * sum_y;E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x;G = N * sum_y2 - sum_y * sum_y;H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y;a = (H * D - E * G) / (C * G - D * D);b = (H * C - E * D) / (D * D - G * C);c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N;center_x = a / (-2);center_y = b / (-2);radius = sqrt(a * a + b * b - 4 * c) / 2;return true;
}bool TrueRoundness::circleLeastFit(const std::vector<cv::Point2f> &points, double &center_x, double &center_y, double &radius)
{center_x = 0.0;center_y = 0.0;radius = 0.0;if (points.size() < 3){return false;}double sum_x = 0.0, sum_y = 0.0;double sum_x2 = 0.0, sum_y2 = 0.0;double sum_x3 = 0.0, sum_y3 = 0.0;double sum_xy = 0.0, sum_x1y2 = 0.0, sum_x2y1 = 0.0;int N = points.size();for (int i = 0; i < N; i++){double x = points[i].x;double y = points[i].y;double x2 = x * x;double y2 = y * y;sum_x += x;sum_y += y;sum_x2 += x2;sum_y2 += y2;sum_x3 += x2 * x;sum_y3 += y2 * y;sum_xy += x * y;sum_x1y2 += x * y2;sum_x2y1 += x2 * y;}double C, D, E, G, H;double a, b, c;C = N * sum_x2 - sum_x * sum_x;D = N * sum_xy - sum_x * sum_y;E = N * sum_x3 + N * sum_x1y2 - (sum_x2 + sum_y2) * sum_x;G = N * sum_y2 - sum_y * sum_y;H = N * sum_x2y1 + N * sum_y3 - (sum_x2 + sum_y2) * sum_y;a = (H * D - E * G) / (C * G - D * D);b = (H * C - E * D) / (D * D - G * C);c = -(a * sum_x + b * sum_y + sum_x2 + sum_y2) / N;center_x = a / (-2);center_y = b / (-2);radius = sqrt(a * a + b * b - 4 * c) / 2;return true;
}void TrueRoundness::sort(std::vector<cv::Point2i> &contour, double center_x, double center_y)
{auto cmp = [center_x, center_y](cv::Point2i p1, cv::Point2i p2){double r1 = hypot(p1.x - center_x, p1.y - center_y);double r2 = hypot(p2.x - center_x, p2.y - center_y);return r1 > r2;};std::sort( contour.begin(), contour.end(), cmp );}void TrueRoundness::sort(std::vector<cv::Point2f> &contour, double center_x, double center_y)
{auto cmp = [center_x, center_y](cv::Point2i p1, cv::Point2i p2){double r1 = hypot(p1.x - center_x, p1.y - center_y);double r2 = hypot(p2.x - center_x, p2.y - center_y);return r1 > r2;};std::sort( contour.begin(), contour.end(), cmp );
}double TrueRoundness::distance(cv::Point2i p, double center_x, double center_y)
{return hypot(p.x - center_x, p.y - center_y);
}double TrueRoundness::distance(cv::Point2f p, double center_x, double center_y)
{return hypot(p.x - center_x, p.y - center_y);
}

下面是个简单的测试用例。首先我们需要个测试图片:

下面是测试代码:

#include <QCoreApplication>
#include <QDebug>
#include <opencv2/opencv.hpp>#include "TrueRoundness.h"
bool circleLeastFit(const std::vector<cv::Point2i> &points, double &center_x, double &center_y, double &radius);
int main(int argc, char *argv[])
{QCoreApplication a(argc, argv);cv::Mat image;image = cv::imread("E:\\testimage\\round.png", cv::IMREAD_COLOR);cv::Mat gray;cv::cvtColor(image, gray, cv::COLOR_BGRA2GRAY);cv::Canny(gray ,gray, 40, 120);std::vector<std::vector<cv::Point2i>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(gray, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);double center_x, center_y;double radius, r_min, r_max;TrueRoundness tr(1U, 1U);tr.circleLeastFit(contours[0], center_x, center_y, radius);double t = tr(contours[0], r_min, r_max);cv::circle(image, cv::Point(center_x, center_y), r_min, cv::Scalar(0, 255, 0), 1);cv::circle(image, cv::Point(center_x, center_y), r_max, cv::Scalar(0, 0, 255), 1);qDebug() << t;cv::imshow("circle", image);return a.exec();
}

输出的结果如下:

OpenCV 真圆度测量相关推荐

  1. 用 Python 和 OpenCV 来测量相机到目标的距离

    用 Python 和 OpenCV 来测量相机到目标的距离 http://python.jobbole.com/84378/ 几天前,一个叫 Cameron 的 PyImageSearch 读者发来邮 ...

  2. python opencv卡尺测量边缘距离

    opencv 卡尺法 测量边缘距离 参考来源 :https://github.com/crackwitz/metrology-demo 前言 一.测量方法 二.测量步骤 1.获取直线的像素 2.高斯滤 ...

  3. python opencv实时显示测量数据_python OpenCV 宽度测量

    机器视觉第六次实验 一.实验目的 通过OpenCV第六次进行实验,对图片进行宽度测量. 二.实验内容 对图片进行宽度测量. 三.实验过程 我使用的是python语言+openCV对图片进行宽度测量的功 ...

  4. python 机器视觉测量_python OpenCV 宽度测量

    机器视觉第六次实验 一.实验目的 通过OpenCV第六次进行实验,对图片进行宽度测量. 二.实验内容 对图片进行宽度测量. 三.实验过程 我使用的是python语言+openCV对图片进行宽度测量的功 ...

  5. pythonopencv测距_如何在opencv中测量两点之间的距离(像素到厘米)

    你好,我有一个代码,可以测量两点之间的距离,但我认为结果是以像素为单位的,所以想得到距离,但以厘米为单位. 我使用的是微软LifecamHD-3000摄像头,我真的不知道在代码的这一点上该怎么做. T ...

  6. OpenCV测量物体的尺寸技能 get~

    点击上方"Python爬虫与数据挖掘",进行关注 回复"书籍"即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 盛年不重来,一日难再晨. 及时当勉 ...

  7. 基于OpenCV的单目摄像机测距

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|新机器视觉 我的论文方向目前是使用单目摄像头实现机器人对人 ...

  8. opencv 通过网络连接工业相机_单目摄像机测距(python+opencv)

    点击上方"新机器视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 我的论文方向目前是使用单目摄像头实现机器人对人的跟随,首先单目摄像 ...

  9. 单目摄像机测距(python+opencv)(转载)

    我的论文方向目前是使用单目摄像头实现机器人对人的跟随,首先单目摄像头与kinect等深度摄像头最大的区别是无法有效获取深度信息,那就首先从这方面入手,尝试通过图像获取摄像头与人的距离. 在网上看了几天 ...

最新文章

  1. ucos-iii串口用信号量及环形队列中断发送,用内建消息队列中断接收
  2. 整理《Mastering OpenCV with Practical Computer Vision Projects》中第8章用Eigenfaces或Fisherfaces进行人脸识别操作流程
  3. 网络拓扑系列 - 网络拓扑的“管理”元素
  4. Qt designer设计界面
  5. 同一批电脑cpu序列号_新电脑到手后,你会验货吗?怎么检查新电脑?
  6. pfile文件怎么恢复格式_回收站清空的文件怎么恢复?值得收藏的恢复方法
  7. java的编译器怎么出来_怎样掌握ava编译器的使用,教程在这里,如何进行Java初级学习...
  8. PHPCMS V9 按浏览次数排行调用文章
  9. 浅析网络编程之Socket模型
  10. The Number of Products
  11. Intellij IDEA 识别不了@Slf4j和log的问题
  12. 微信客服系统开发SDK使用教程-给好友发消息任务
  13. 国外动态住宅ip怎么使用?
  14. 兆能Z82机顶盒-卡刷-刷机固件
  15. java speex回声消除_android – Speex回音消除配置
  16. 云开发【云函数的使用】
  17. 名帖72 颜真卿 楷书《竹山堂连句》
  18. 浅谈ElasticSearch
  19. 同步与异步通信的区别
  20. Spring 实战(第 5 版)

热门文章

  1. 再发国外网友关于meego的设计
  2. js css 特效网站收藏
  3. 标定协议之CCP协议基础知识介绍
  4. 怎么在线图片识别文字?这里有你需要的方法
  5. 简单的入门Android开发
  6. 屏的像素与传输速率_摄像头像素与分辨率(1080P)的关系
  7. mine模拟器android 7.0,mine模拟器3.1.7安卓版
  8. 联咏系列平台编译器安装及配置
  9. 如何安装联想计算机系统,如何一键安装联想电脑系统
  10. oppo手机删除计算机怎样恢复,【数据恢复篇】oppo手机删掉的照片怎么恢复