从两个点和一个宽度生成旋转矩形

宽度

    ┌───────────┐
    │           │
P1●─┼───────────┼─●P2  长度 = |P1P2|
    │           │
    └───────────┘
        宽度
  1. 矩形中心 = P1和P2的中点
  2. 矩形长度 = |P1P2| (两点距离)
  3. 矩形宽度 = 输入的width参数
  4. 矩形角度 = 向量P1P2的方向
void __fastcall point_point_width_to_rrect(
    double x1,  // 第一个点x
    double y1,  // 第一个点y
    double x2,  // 第二个点x
    double y2,  // 第二个点y
    double width,  // 矩形宽度
    float *out    // 输出旋转矩形参数
)
{
  // 计算矩形的方向向量
  double dx = x2 - x1;  // x方向差值
  double dy = y2 - y1;  // y方向差值
  
  // 计算矩形的中心点(两点中点)
  float center_y = (y1 + y2) * 0.5f;
  float center_x = (x1 + x2) * 0.5f;
  
  out[1] = center_y;  // 中心点y坐标
  out[0] = center_x;  // 中心点x坐标
  
  // 计算两点间距离(这是矩形的长度)
  double distance_sq = dx * dx + dy * dy;
  double distance = sqrt(distance_sq);
  
  // 半长 = 长度的一半
  float half_length = distance * 0.5f;
  
  out[4] = width;          // 矩形的宽度
  out[3] = half_length;   // 矩形的一半长度
  
  // 计算矩形的旋转角度
  // atan2(dx, dy) 计算线段与y轴的夹角
  // 转换为度,并减去90度得到标准数学角度
  float angle = atan2(dx, dy) * 180.0f / 3.141592653589793f - 90.0f;
  out[2] = angle;  // 旋转角度
}

计算旋转矩形的四个顶点坐标。输出是12个double,所以可能是6个点(每个点x,y)。

#include <cmath>

// 计算旋转矩形的顶点
// 输入: center_x, center_y, angle_deg, half_width, half_height
// 输出: 6个点(可能是闭合多边形的顶点,包括起点重复)
void calculate_rotated_rectangle(
    float center_x, float center_y, float angle_deg, 
    float half_width, float half_height, 
    double* output)  // 输出数组,12个元素
{
    // 角度加90度后转弧度(可能是坐标系统不同)
    double rad = (angle_deg + 90.0) * M_PI / 180.0;
    double cos_a = cos(rad);
    double sin_a = sin(rad);
    
    // 计算矩形的轴向量
    // 由于角度加了90°,所以:
    // 水平轴方向: (sin_a, cos_a)  // 对应宽度方向
    // 垂直轴方向: (-cos_a, sin_a) // 对应高度方向
    
    double width_vec_x = sin_a * half_width;
    double width_vec_y = cos_a * half_width;
    
    double height_vec_x = -cos_a * half_height;
    double height_vec_y = sin_a * half_height;
    
    // 计算四个顶点
    // 顶点0: 中心 - 宽度向量 - 高度向量
    output[0] = center_x - width_vec_x - height_vec_x;
    output[1] = center_y - width_vec_y - height_vec_y;
    
    // 顶点1: 中心 + 宽度向量 - 高度向量
    output[2] = center_x + width_vec_x - height_vec_x;
    output[3] = center_y + width_vec_y - height_vec_y;
    
    // 顶点2: 中心 + 宽度向量 + 高度向量
    output[4] = center_x + width_vec_x + height_vec_x;
    output[5] = center_y + width_vec_y + height_vec_y;
    
    // 顶点3: 中心 - 宽度向量 + 高度向量
    output[6] = center_x - width_vec_x + height_vec_x;
    output[7] = center_y - width_vec_y + height_vec_y;
    
    // 如果输出是6个点(12个double),可能是:
    // 四个顶点 + 第一个顶点重复(形成闭合多边形)
    output[8] = output[0];  // 重复第一个点
    output[9] = output[1];
    output[10] = output[2]; // 或者可能是中心点?
    output[11] = output[3];
}

这是一个计算旋转矩形6个顶点的函数,可用于:

  • 绘制带边框的矩形(4个外角 + 2个内角或其他)
  • 特定的碰撞检测形状
  • 或者只是简单地计算矩形的4个角,但数组分配了12个元素,实际只用了8个

关键点:

  1. 输入角度加了90°,因为图像坐标系(Y轴向下)
  2. a1[3]a1[4] 是半宽和半高
  3. 通过旋转矩阵计算矩形的四个角点坐标

生成圆弧卡尺

/**
 * 生成圆形卡尺测量点
 * 
 * @param center_x 圆心X坐标
 * @param center_y 圆心Y坐标
 * @param radius 测量半径
 * @param width 卡尺宽度
 * @param step_angle 角度步长控制(度)
 * @param direction 方向:>=0顺时针,<0逆时针
 * @param num_points 指定点数(<=0时自动计算)
 * @param buffer_mgr 缓冲区管理器
 * @return 成功返回0,失败返回-1
 */
int generate_circular_caliper_points(
    double center_x,
    double center_y,
    double radius,
    double width,
    double step_angle,  // 控制点密度
    int direction,
    int num_points,
    BufferManager& buffer_mgr
) {
    // 1. 计算分段数
    int segments = num_points;
    if (segments <= 0) {
        // 自动计算:圆周长 / (step_angle * 3)
        // 这里 step_angle 是角度,需要转弧度
        double step_rad = step_angle * DEG_TO_RAD;
        double circumference = radius * TWO_PI;
        segments = static_cast<int>(circumference / (step_rad * 3.0));
    }
    
    // 确保不超过最大合理分段数
    double max_segments = radius * TWO_PI / (step_angle * DEG_TO_RAD * 3.0);
    if (segments > max_segments) {
        segments = static_cast<int>(max_segments);
    }
    
    if (segments <= 0) {
        return -1;  // 错误
    }
    
    // 2. 确保缓冲区足够
    buffer_mgr.ensure_capacity(segments);
    
    // 3. 角度增量
    double angle_increment = TWO_PI / segments;
    
    // 4. 生成测量点
    for (int i = 0; i < segments; i++) {
        // 计算当前角度
        double angle_rad = i * angle_increment;
        
        // 计算测量点坐标
        double x = center_x + sin(angle_rad) * radius;
        double y = center_y + cos(angle_rad) * radius;
        
        // 计算测量方向角度
        // 圆上点的法线方向是径向方向
        double normal_angle_rad = angle_rad;  // 从圆心指向测量点的方向
        double measure_angle_deg = normal_angle_rad * RAD_TO_DEG;
        
        // 方向调整
        if (direction >= 0) {  // 顺时针
            measure_angle_deg -= 90.0f;  // 垂直于法线方向
        } else {  // 逆时针
            measure_angle_deg = measure_angle_deg - 90.0f + 180.0f;
        }
        
        // 规整化角度到 [0, 360)
        while (measure_angle_deg >= 360.0) measure_angle_deg -= 360.0;
        while (measure_angle_deg < 0.0) measure_angle_deg += 360.0;
        
        // 写入缓冲区
        CaliperPoint* point = buffer_mgr.get_current();
        point->x = static_cast<float>(x);
        point->y = static_cast<float>(y);
        point->angle = static_cast<float>(measure_angle_deg);
        point->param1 = static_cast<float>(width);      // 存储宽度
        point->param2 = static_cast<float>(step_angle); // 存储步长参数
        
        buffer_mgr.advance(1);
    }
    
    return 0;  // 成功
}

测量函数

// 核心测量函数
    MeasurementResult measure(
        const unsigned char* image_data,
        int image_width,
        int image_height,
        const MeasurementParams& params,
        const char* edge_select_mode = "strongest"
    ) {
        MeasurementResult result;
        
        // 1. 计算方向向量
        double angle_rad = (params.angle_deg + 90.0) * M_PI / 180.0;
        double cos_a = cos(angle_rad);
        double sin_a = sin(angle_rad);
        
        // 2. 沿宽度方向生成采样点
        int width_samples = (int)(2 * params.width + 1);
        std::vector<double> sample_points_x(width_samples);
        std::vector<double> sample_points_y(width_samples);
        
        for (int i = 0; i < width_samples; i++) {
            double offset = i - params.width;
            sample_points_x[i] = params.start_x + offset * sin_a;
            sample_points_y[i] = params.start_y + offset * cos_a;
        }
        
        // 3. 对每个采样点,沿高度方向采样强度剖面
        int height_samples = (int)(2 * params.height + 1);
        std::vector<std::vector<double>> intensity_profiles(width_samples);
        std::vector<double> average_intensities(width_samples, 0.0);
        
        for (int i = 0; i < width_samples; i++) {
            std::vector<double>& profile = intensity_profiles[i];
            profile.resize(height_samples);
            
            double sum = 0.0;
            for (int j = 0; j < height_samples; j++) {
                double vert_offset = j - params.height;
                
                // 计算采样点坐标
                double px = sample_points_x[i] + vert_offset * (-cos_a);
                double py = sample_points_y[i] + vert_offset * sin_a;
                
                // 像素插值
                double pixel_value = 0.0;
                if (config_.interpolation_type == 0) {
                    pixel_value = bilinear_interpolate(image_data, image_width, 
                                                      image_height, px, py);
                } else {
                    pixel_value = bicubic_interpolate(image_data, image_width, 
                                                     image_height, px, py);
                }
                
                profile[j] = pixel_value;
                sum += pixel_value;
            }
            
            average_intensities[i] = sum / height_samples;
        }
        
        // 4. 高斯滤波
        std::vector<double> gaussian_kernel;
        generate_gaussian_kernel(gaussian_kernel, config_.sigma);
        
        std::vector<double> smoothed_intensities;
        gaussian_filter_1d(average_intensities, smoothed_intensities, gaussian_kernel);
        
        // 5. 计算一阶导数
        std::vector<double> derivatives;
        compute_derivative(smoothed_intensities, derivatives);
        
        // 6. 检测边缘
        std::vector<EdgeInfo> edges;
        detect_edges(derivatives, smoothed_intensities, edges, config_.threshold);
        
        result.edge_count = (int)edges.size();
        
        if (edges.empty()) {
            return result;  // 没有检测到边缘
        }
        
        // 7. 边缘选择
        EdgeInfo selected_edge;
        if (!select_edge(edges, edge_select_mode, selected_edge)) {
            return result;  // 选择失败
        }
        
        // 8. 转换为世界坐标
        double world_offset = (selected_edge.position - width_samples/2.0);
        double edge_x = params.start_x + world_offset * sin_a;
        double edge_y = params.start_y + world_offset * cos_a;
        
        // 9. 返回结果
        result.success = true;
        result.edge_x = edge_x;
        result.edge_y = edge_y;
        result.edge_strength = selected_edge.strength;
        
        return result;
    }
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐