You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt图像标注应用如何绘制像素掩码并获取鼠标扫过的全部像素坐标

解决方案

操作系统对鼠标事件的采样频率是固定的,Qt本身不会也不可能返回鼠标扫过的每一个像素的事件点,快速移动时相邻两次事件的坐标间隔变大是正常现象。你不需要尝试获取全量鼠标事件,只需要对相邻两个采样点做线段插值补全中间点即可。

具体实现步骤

  • 首先在你的标注相关类中新增两个私有成员:存储上一次鼠标事件坐标的QPointF lastMousePos,以及标记当前是否处于手绘状态的bool isDrawing = false。
  • 在mousePressEvent回调中:
    1. 记录当前点击坐标到lastMousePos
    2. 将isDrawing置为true
    3. 把当前点加入你的掩码像素列表,完成单点绘制
  • 在mouseMoveEvent回调中:
    1. 先判断isDrawing状态,非绘制状态直接返回
    2. 获取当前事件对应的场景坐标currentMousePos = event.scenePos()
    3. 调用插值算法生成lastMousePos到currentMousePos之间的所有像素坐标,全部加入掩码像素列表
    4. 更新lastMousePos为当前的currentMousePos
    5. 触发场景重绘
  • 在mouseReleaseEvent回调中:
    1. 将isDrawing置为false

插值实现

因为你需要存储所有掩码像素的坐标列表,所以不能直接调用QPainter::drawLine绘制,需要先生成插值点。最常用的轻量方案是Bresenham直线算法,也可以用简单的线性插值实现,示例代码如下:

// 生成两点之间所有像素坐标的线性插值实现
QVector<QPoint> getLinePoints(QPoint start, QPoint end) {
    QVector<QPoint> points;
    int dx = abs(end.x() - start.x());
    int dy = abs(end.y() - start.y());
    int step = dx > dy ? dx : dy;

    if (step == 0) {
        points.append(start);
        return points;
    }

    float xIncrement = (float)(end.x() - start.x()) / step;
    float yIncrement = (float)(end.y() - start.y()) / step;

    float currentX = start.x();
    float currentY = start.y();
    for (int i = 0; i <= step; i++) {
        points.append(QPoint(round(currentX), round(currentY)));
        currentX += xIncrement;
        currentY += yIncrement;
    }

    // 去重避免重复存储相同坐标
    auto last = std::unique(points.begin(), points.end());
    points.erase(last, points.end());
    return points;
}

优化建议

  • 如果要保证坐标和底层图片像素完全对齐,需要把scenePos转换为QGraphicsPixmapItem的局部坐标后再做取整,避免场景缩放、平移带来的坐标偏移
  • 绘制时可以仅新增绘制两次事件之间的插值点,不需要每次都全量重绘所有掩码点,能大幅提升大尺寸图片标注时的性能
  • 如果需要支持不同画笔粗细,可对每个插值点做对应半径的膨胀处理,把覆盖范围内的所有像素都加入掩码列表

内容的提问来源于stack exchange,提问作者Zézouille

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:09:02