当前位置: 首页 > news >正文

VisionMaster标定板像素标定,测量尺寸以及opencv/C++实现

1.VM的像素标定流程很简单、如下:

2.拿到标定结果对将图像尺寸转换成物理尺寸

使用了两种方法进行转换,实际证明单位转换更加准确。

使用opencv/c++进行标定,我的圆标定板的直径是7.5mm,圆心之间的距离是15.0mm

简单的像素标定:

void xsCeli::calculatePixelEquivalent(const cv::Mat& src, float real_distance_mm)
{// 加载图像//cv::Mat src = cv::imread("circle_pattern.jpg");if (src.empty()) {std::cerr << "Could not load image!" << std::endl;return;}cv::Mat gray;cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);  // 转灰度图// 霍夫变换检测圆std::vector<cv::Vec3f> circles;cv::HoughCircles(gray, circles, cv::HOUGH_GRADIENT,1,             // dp 分辨率比例50,            // minDist 圆心最小距离100, 30,       // Canny 边缘检测阈值10, 100);      // 半径范围// 只处理前两个圆(假设是已知间距的两个圆)if (circles.size() < 2) {std::cerr << "Not enough circles detected!" << std::endl;return;}// 提取前两个圆的圆心坐标cv::Point2f p1(circles[0][0], circles[0][1]);cv::Point2f p2(circles[1][0], circles[1][1]);// 计算像素距离float pixel_distance = cv::norm(p1 - p2);// 已知这两个圆的实际物理距离(单位:mm)//float real_distance_mm = 15.0;  // 举例:20mm// 像素当量:mm per pixelfloat mm_per_pixel = real_distance_mm / pixel_distance;std::cout << "Pixel distance: " << pixel_distance << " pixels" << std::endl;std::cout << "Real distance: " << real_distance_mm << " mm" << std::endl;std::cout << "Scale: " << mm_per_pixel << " mm/pixel" << std::endl;// 可视化结果for (size_t i = 0; i < circles.size(); ++i) {cv::Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));int radius = cvRound(circles[i][2]);cv::circle(src, center, radius, cv::Scalar(0, 255, 0), 2);cv::circle(src, center, 2, cv::Scalar(0, 0, 255), 3);  // 圆心}cv::imshow("Detected Circles", src);cv::waitKey(0);return;
}


我使用的银行卡进行的测试、

银行卡的长度通常是固定的,遵循国际标准ISO/IEC 7810 ID-1,这是针对识别卡物理特性的标准。根据这一标准,银行卡(包括信用卡和借记卡)的尺寸应为:

  • 长度:85.60毫米(约3.37英寸)
  • 宽度:53.98毫米(约2.13英寸)
  • 厚度:0.76毫米(约0.03英寸)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>// 全局变量:标定系数(像素/毫米)
double pixelToMm = 24.066;// 函数声明
cv::Mat preprocessImage(const cv::Mat& input);
std::vector<cv::Point> findTargetContour(const cv::Mat& processedImg);
cv::RotatedRect getBoundingRect(const std::vector<cv::Point>& contour);
double calculateWidth(const cv::RotatedRect& rect);
void calibrate(const cv::Mat& calibImg, double knownWidth);int main() {// 加载图像//cv::Mat src = cv::imread("circle_pattern.jpg");//if (src.empty()) {//	std::cerr << "Could not load image!" << std::endl;//	return -1;//}//xsCeli celi;//celi.calculatePixelEquivalent(src, 15.0f); // 假设实际距离为15mm//return 0;// 1. 读取图像(替换为实际相机获取或文件读取)cv::Mat image = cv::imread("23.jpg");if (image.empty()) {std::cerr << "无法读取图像!" << std::endl;return -1;}// 设置最大显示宽度和高度int max_width = 800;int max_height = 600;cv::Mat resized;double scale = std::min(max_width / (double)image.cols, max_height / (double)image.rows);if (scale < 1.0) { // 只有当图像大于 max_width/max_height 时才缩放cv::resize(image, resized, cv::Size(), scale, scale, cv::INTER_AREA);}else {resized = image; // 否则直接显示原图}image = resized; // 使用缩放后的图像// 2. 图像预处理cv::Mat processedImg = preprocessImage(image);// 3. 提取目标轮廓(假设已完成标定)std::vector<cv::Point> targetContour = findTargetContour(processedImg);if (targetContour.empty()) {std::cerr << "未找到目标轮廓!" << std::endl;return -1;}// 4. 获取最小包围矩形cv::RotatedRect boundingRect = getBoundingRect(targetContour);// 5. 计算宽度(基于标定系数)double widthMm = calculateWidth(boundingRect);widthMm = widthMm / scale;std::cout << "目标产品宽度:" << widthMm << " 毫米" << std::endl;// 6. 可视化结果cv::Mat resultImg = image.clone();// 1. 检查resultImg是否有效if (resultImg.empty()) {std::cerr << "Error: resultImg is empty!" << std::endl;return -1;}// 2. 检查targetContour是否有效if (targetContour.empty()) {std::cerr << "Error: targetContour is empty!" << std::endl;return -1;}// 3. 输出轮廓信息(可选)std::cout << "Drawing contour with " << targetContour.size() << " points" << std::endl;cv::drawContours(resultImg, std::vector<std::vector<cv::Point>>{ targetContour }, 0, cv::Scalar(0, 255, 0), 2);cv::ellipse(resultImg, boundingRect, cv::Scalar(0, 0, 255), 2);// 标注宽度cv::Point textPos(boundingRect.center.x - 50, boundingRect.center.y - 50);std::string widthText = "Width: " + std::to_string(widthMm) + " mm";cv::putText(resultImg, widthText, textPos, cv::FONT_HERSHEY_SIMPLEX, 0.7,cv::Scalar(255, 0, 0), 2);// 1. 画轮廓std::vector<std::vector<cv::Point>> contours{ targetContour };cv::drawContours(resultImg, contours, 0, cv::Scalar(0, 255, 0), 2);  // 绿色轮廓// 2. 获取并画旋转矩形cv::Point2f vertices[4];boundingRect.points(vertices);for (int i = 0; i < 4; ++i) {cv::line(resultImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 0, 255), 2);  // 红色矩形}cv::imshow("测量结果", resultImg);cv::waitKey(0);return 0;
}// 图像预处理:灰度转换、滤波、二值化
cv::Mat preprocessImage(const cv::Mat& input) {cv::Mat gray, blurred, binary;// 1. 灰度转换cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);// 2. 高斯滤波去噪(根据实际噪声调整参数)cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 0);// 3. 自适应阈值二值化(适应不均匀光照)cv::adaptiveThreshold(blurred, binary, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C,cv::THRESH_BINARY_INV, 11, 2);// 4. 形态学操作:闭运算填充小孔,开运算去除噪点cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::morphologyEx(binary, binary, cv::MORPH_CLOSE, kernel);cv::morphologyEx(binary, binary, cv::MORPH_OPEN, kernel);//cv::imshow("Processed Image", binary);// cv::waitKey(0);return binary;
}// 提取目标轮廓(筛选带倒圆角的矩形)
std::vector<cv::Point> findTargetContour(const cv::Mat& processedImg) {std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;// 1. 查找轮廓cv::findContours(processedImg, contours, hierarchy, cv::RETR_EXTERNAL,cv::CHAIN_APPROX_SIMPLE);if (contours.empty()) return {};// 2. 筛选目标轮廓(根据面积、形状因子筛选)std::vector<cv::Point> targetContour;double maxArea = 0;std::cout << "目标轮廓个数:" << contours.size() << std::endl;std::sort(contours.begin(), contours.end(),[](const auto& a, const auto& b) {return cv::contourArea(a) > cv::contourArea(b);});//for (const auto& contour : contours) {//    double area = cv::contourArea(contour);//    if (area < 1000) continue; // 过滤小面积噪声//    // 计算轮廓近似(关键:调整epsilon适应倒圆角)//    std::vector<cv::Point> approx;//    double epsilon = 0.02 * cv::arcLength(contour, true); // 2%的轮廓周长//    cv::approxPolyDP(contour, approx, epsilon, true);//    // 带倒圆角的矩形近似后应为8个顶点(4个边+4个圆角弧段)//    if (approx.size() > 6 && approx.size() < 12) {//        double areaRatio = area / (cv::boundingRect(contour).width *//            cv::boundingRect(contour).height);//        if (areaRatio > 0.8) { // 面积比接近矩形//            targetContour = contour;//            maxArea = area;//        }//    }//}targetContour = contours[0];return targetContour;
}// 获取最小包围旋转矩形
cv::RotatedRect getBoundingRect(const std::vector<cv::Point>& contour) {return cv::minAreaRect(contour);
}// 计算宽度(基于标定系数)
double calculateWidth(const cv::RotatedRect& rect) {// 旋转矩形的宽高可能需要根据角度判断哪条是宽度double width = rect.size.width;double height = rect.size.height;// 银行卡宽度通常小于长度,假设宽度是较短边return (width < height ? width : height) / pixelToMm;
}// 标定函数(使用已知宽度的标准物)
void calibrate(const cv::Mat& calibImg, double knownWidth) {cv::Mat processed = preprocessImage(calibImg);std::vector<cv::Point> calibContour = findTargetContour(processed);if (calibContour.empty()) {std::cerr << "标定图像轮廓提取失败!" << std::endl;return;}cv::RotatedRect calibRect = cv::minAreaRect(calibContour);double calibPixelWidth = (calibRect.size.width < calibRect.size.height? calibRect.size.width : calibRect.size.height);// 计算像素/毫米系数pixelToMm = calibPixelWidth / knownWidth;std::cout << "标定完成,像素/毫米系数:" << pixelToMm << std::endl;
}

得到以下结果:

测量结果有一定的误差、可以验证这样写是没有问题的。

这是标定图片与银行卡的图片连接,如有需要自取。
链接:https://pan.quark.cn/s/57d9f8a7508f

相关文章:

  • 【C语言极简自学笔记】重讲运算符
  • 自动打电话软件设计与实现
  • FPGA基础 -- Verilog行为级建模之alawys语句
  • FPGA基础 -- Verilog 行为级建模之条件语句
  • 爬虫技术:从数据获取到智能分析的进阶之路
  • Mac 安装 finalshell
  • WebFuture:PDF页面去掉下载按钮
  • 【算法 day06】LeetCode 454.四数相加II | 15. 三数之和 | 18. 四数之和
  • 23.查询增强
  • 【入门算法】枚举:有序穷举,分步排查
  • 【音视频】PJSIP库——pjsua命令使用详解
  • 嵌入式自学第四十二天
  • Java八股文——计算机网络「应用层篇」
  • 京东618带火四大消费 即时零售和生活服务迎来爆发
  • 一个.Net开发的功能强大、易于使用的流媒体服务器和管理系统
  • Redis 五种数据结构
  • keil新建工程文件结构和每个文件的作用解析(标准库版本)
  • 零知开源——STM32F4实现ILI9486显示屏UI界面系列教程(一):电子书阅读器功能
  • uniapp实现聊天中的接发消息自动滚动、消息定位和回到底部
  • QTableView为例:Qt模型视图委托(MVD)(Model-View-Delegate)
  • 网站推广内容/分享几个x站好用的关键词
  • 网站右下角浮动效果如何做/求网址
  • 网站建设主机耗电量/seo排名专业公司
  • 做网站需要备案吗/汕头网站制作设计
  • 最准做特马网站/全球疫情最新数据
  • 深圳网站建设与设计制作/ip域名查询网