基于OpenCV(C++)实现的亚像素级简易直线卡尺测量工具

摘要:根据一维边缘提取原理,基于OpenCV(c++)库开发了简易直线卡尺测量工具,能实现亚像素级精度。使用OpenCV库读取图片,在图片上画出指定个数的ROI区域、参考直线以及ROI区域的箭头方向,定义容器记录相关数据。使用OpenCV的回调函数以及编写的旋转函数,使得整个ROI区域、参考线和箭头可由鼠标拖动,键盘能够操作旋转。根据记录的数据计算每个ROI区域的采样点,使用双线性插值算法获取采样点的灰度值,再计算每组采样点的灰度平均值作为新的一组数据,将其求导并过滤,随后获取这组数据的谷值和峰值。引入距离和对比度评分系统,计算谷值或峰值与参考直线的距离以及将对比度除以255,为距离和对比度赋上不同的权重,计算最终得分获取得分最高的谷值或峰值。获取谷值或峰值附近的三个点拟合抛物线并求最大值或最小值,获取最值对应点实际坐标,使用RANSAC算法进行过滤,过滤之后拟合成直线。

c++代码如下:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cmath>
#include"Scale_image.h"
#include <algorithm>
#include <Eigen/Dense>

using namespace cv;

//用opencv(c++)版在图片上生成一条直线,把直线十等分,会得到十个点,在十个点的基础上生成矩形构成的roi,
//我还需要这一条直线和十个矩形roi作为一个整体能够用鼠标拖动
//直线的起始点和结束点尽量在图片的中心区域
//直线和矩形在原图上显示出来

//20240109 实现按键旋转直线
//20240110 实现鼠标拖动直线

// 定义全局变量
cv::Mat tempImg;
cv::Mat tempImg_Scale;
cv::Point2d startPt, endPt; // 参考直线的起始点和结束点
bool isDragging = false; // 标记当前是否正在进行拖动操作
cv::Point2d dragStart; // 记录拖动开始时鼠标的位置
cv::Point2d rotate_endPt;//定义直线旋转后的终点坐标
std::vector<cv::Point2d> leftArrowTip_set;//箭头左侧端点集合
std::vector<cv::Point2d> rightArrowTip_set;//箭头右侧端点集合

// 矩形的长和宽
double rectLength = 10.0;
double rectWidth = 16.0;

// 计算向量的长度
double length(const cv::Point2d& vec) {
    return std::sqrt(vec.x * vec.x + vec.y * vec.y);
}

// 计算向量的单位向量
cv::Point2d normalize(const cv::Point2d& vec) {
    double len = length(vec);
    return cv::Point2d(vec.x / len, vec.y / len);
}

// 计算与给定向量垂直的向量
cv::Point2d perpendicular(const cv::Point2d& vec) {
    return cv::Point2d(-vec.y, vec.x);
}

// 在图像上绘制带箭头的线
// img:要绘制箭头的图像
// start:箭头的起始点
// end:箭头的结束点
// color:箭头的颜色,默认为绿色(0, 255, 0)
// thickness:箭头线的宽度,默认为 1
// lineType:线的类型,默认为抗锯齿线 cv::LINE_AA
// arrowLength:箭头长度与箭身长度的比例因子,默认为 0.1
void drawArrow(cv::Mat& img, const cv::Point2d& start, const cv::Point2d& end,
    const cv::Scalar& color = cv::Scalar(0, 255, 0), int thickness = 1,
    int lineType = cv::LINE_AA, double arrowLength = 0.2) {
    // 绘制箭身
    cv::line(img, start, end, color, thickness, lineType);

    // 计算箭头方向向量
    cv::Point2d arrowDir = end - start;
    // cout << end << "456" << start << endl;

    // 计算箭头向量,这里通过将单位箭头方向向量乘以箭头长度(通过比例因子和箭身长度计算)
    // 单位箭头方向向量 * 比例因子 * 箭身长度
    cv::Point2d arrowVec = normalize(arrowDir) * arrowLength * length(arrowDir);

    // 计算箭头左侧端点
    // 从箭头终点开始,减去垂直于箭头方向向量的一部分(控制箭头宽度)
    // 再减去箭头向量
    cv::Point2d leftArrowTip = end - perpendicular(arrowDir) * 0.08 * arrowLength * length(arrowDir) - arrowVec;

    // 计算箭头右侧端点
    // 从箭头终点开始,加上垂直于箭头方向向量的一部分(控制箭头宽度)
    // 再减去箭头向量
    cv::Point2d rightArrowTip = end + perpendicular(arrowDir) * 0.08 * arrowLength * length(arrowDir) - arrowVec;

    // 绘制箭头左侧线
    cv::line(img, end, leftArrowTip, color, thickness, lineType);

    // 绘制箭头右侧线
    cv::line(img, end, rightArrowTip, color, thickness, lineType);
}

// 旋转直线功能函数
void rotate(cv::Mat img, int angle, int rotate_keys, cv::Point2d* startPt, cv::Point2d* endPt)//旋转直线
{
    /*
    rotate_keys:为设置按键按了几次参数
    angle:按下之后旋转多少度
    */

    const double PI = 3.14159265358979323846;
    // 计算 (x2 - x1)^2 + (y2 - y1)^2
    double squaredDiffX = std::pow(endPt->x - startPt->x, 2);
    double squaredDiffY = std::pow(endPt->y - startPt->y, 2);
    // 计算距离
    double R = std::sqrt(squaredDiffX + squaredDiffY);

    //std::cos 函数的参数是以弧度为单位的,而不是角度。弧度 = 角度×(π / 180)。
    endPt->x = startPt->x + R * std::cos((rotate_keys * angle) * (PI / 180.0)); //旋转后的终点坐标的x值
    endPt->y = startPt->y - R * std::sin((rotate_keys * angle) * (PI / 180.0)); //旋转后的终点坐标的y值
}

// 获取参考直线的起始点和终止点
void get_line_point(cv::Mat& img, cv::Point2d* startPt, cv::Point2d* endPt)
{
    // 初始化直线的起始点和结束点,尽量在图片中心区域
    int centerX = img.cols / 2; // 计算图像宽度方向的中心位置
    int centerY = img.rows / 2; // 计算图像高度方向的中心位置
    *startPt = Point2d(centerX - 100, centerY); // 设置直线起始点,在中心位置左侧100像素
    *endPt = Point2d(centerX + 100, centerY); // 设置直线结束点,在中心位置右侧100像素
    //std::cout << *endPt << std::endl;
}

// 绘制roi区域
void drawing_roi(int numbers_roi, cv::Mat& img) {
    /*
    numbers_roi:指定矩形框的数量
    */

    // 清空容器,防止上一轮的位置还留在容器中
    leftArrowTip_set.clear();
    rightArrowTip_set.clear();
    // 计算直线方向向量
    cv::Point2d lineDir = endPt - startPt;//坐标相减可以表示为一个向量
    // 计算向量的长度
    double lenth = std::sqrt(lineDir.x * lineDir.x + lineDir.y * lineDir.y);//sqrt是开根号
    // 计算向量的单位向量
    cv::Point2d unitLineDir = cv::Point2d(lineDir.x / lenth, lineDir.y / lenth);
    //cv::Point2d unitLineDir = normalize(lineDir);

   // 计算直线的总长度;
    double totalLength = cv::norm(lineDir);
    // 计算相邻矩形中心的间距
    double interval = totalLength / numbers_roi;

    for (int i = 0; i < numbers_roi; ++i) {

        // 计算矩形中心位置
        cv::Point2d rectCenter = startPt + unitLineDir * interval * i;
        // 计算与给定向量垂直的向量
        cv::Point2d perpendicularDir = cv::Point2d(-unitLineDir.y, unitLineDir.x);
        // 计算矩形的四个顶点
        cv::Point2d  topLeft = rectCenter - perpendicularDir * rectWidth / 2 - unitLineDir * rectLength / 2;
        cv::Point2d  bottomLeft = rectCenter + perpendicularDir * rectWidth / 2 - unitLineDir * rectLength / 2;
        cv::Point2d  bottomRight = rectCenter + perpendicularDir * rectWidth / 2 + unitLineDir * rectLength / 2;
        cv::Point2d  topRight = rectCenter - perpendicularDir * rectWidth / 2 + unitLineDir * rectLength / 2;

        //绘制矩形
        cv::line(img, topLeft, topRight, cv::Scalar(0, 255, 0), 1);
        cv::line(img, topRight, bottomRight, cv::Scalar(0, 255, 0), 1);
        cv::line(img, bottomRight, bottomLeft, cv::Scalar(0, 255, 0), 1);
        cv::line(img, bottomLeft, topLeft, cv::Scalar(0, 255, 0), 1);

        // 计算短边中心线的起点和终点
        cv::Point2d shortCenterStart = rectCenter - perpendicularDir * rectWidth / 2;
        cv::Point2d shortCenterEnd = rectCenter + perpendicularDir * rectWidth / 2;

        // 绘制带箭头的短边中心线
        drawArrow(img, shortCenterStart, shortCenterEnd);
        leftArrowTip_set.push_back(shortCenterStart);// 将目前roi区域的箭头的左侧端点加入容器中
        rightArrowTip_set.push_back(shortCenterEnd);// 将目前roi区域的箭头的右侧端点加入容器中
    }
}

// 确定旋转矩形内采样点
std::vector<std::vector<cv::Point2d>> getSamplingPoints(const cv::Point2d& start, const cv::Point2d& end, double width, int num_points_per_side, int numbers_point) {
    /*
       * std::vector<std::vector<cv::Point2d>> 表示返回值类型,是一个二维向量,其中外层向量存储每组采样点,内层向量存储每组采样点中的两个点(矩形中心线两侧的点);
       * width 是旋转矩形的宽度;
       * num_points_per_sid:垂直于中心线取的点的个数
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值