在放大的图像上查找缩放中心坐标

Posted

技术标签:

【中文标题】在放大的图像上查找缩放中心坐标【英文标题】:Finding zoom center coordinates on a zoomed-in image 【发布时间】:2021-01-12 15:20:56 【问题描述】:

我正在尝试在图像上实现以鼠标光标为中心的放大/缩小。为简单起见,假设我只需要在水平轴上放大/缩小。我使用 Qt 和 QPainter 来绘制我的场景,并使用 QWidget:: mouseWheelEvent 覆盖来计算缩放因子和缩放位置。

在显示代码之前,缩放因子和缩放位置定义了从原始图像转换为缩放图像所需的转换(换句话说,我不组合矩阵或类似的东西,但在绘制事件中计算绝对变换),最终可以在这里看到:

void MyWidget::paintEvent(QPaintEvent* e)

    QPainter painterthis;

    painter.translate(m_zoomCenterX, 0.);
    painter.scale(m_zoomFactor, 1.);
    painter.translate(-m_zoomCenterX, 0.);

    // content margins are all 0 btw
    painter.drawImage(contentsRect(), m_image);

在鼠标滚轮事件中,我尝试计算缩放中心与添加到缩放中心的新位置之间的比例差。缩放因子是根据角度增量简单地通过乘以一个常数来计算的。最后的 if-else 分支会进行一些边界检查(这是我想做的事情,以便图像在视图中保持拉伸并且不会移动到我不想移动的地方):

void MyWidget::wheelEvent(QWheelEvent* e)

    const QRect rect = contentsRect();
    // m_zoomCenterX is first initialized to 0 in the MyWidget constructor, m_zoomFactor to 1.
    const qreal diff = (e->pos().x() - m_zoomCenterX) / m_zoomFactor;
    qDebug() << diff;

    m_zoomCenterX += diff;
    static constexpr const qreal ZOOM_IN_FACTOR = 1.1,
                                 ZOOM_OUT_FACTOR = .9;
    m_zoomFactor *= e->angleDelta().y() > 0. ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR;

    if (m_zoomFactor < 1.)
    
        m_zoomFactor = 1.;
        m_zoomCenterX = 0.;
    
    else if (m_zoomFactor > 5.)
    
        m_zoomFactor = 5.;
    
    if (m_zoomCenterX < 0)
    
        m_zoomCenter = 0;
    
    else if (m_zoomCenterX >= rect.width())
    
        m_zoomCenterX = rect.width() - 1;
    

    update();

这似乎不起作用,一旦我在假设放大后尝试放大/缩小(参见图片:,抱歉,如果它不多),缩放中心位置可能没有正确计算但我不确定为什么。当光标位于新位置时,qDebug 行在连续滚动后给我非零差异,但在第一次滚动后它应该为零......

我想这一定是微不足道的事情,但我现在很困惑。

【问题讨论】:

【参考方案1】:

好吧,我让它工作了。我使用逆矩阵来计算新的缩放中心,但是一旦放大,它就会与鼠标位置不匹配,所以整个图像必须额外由inverted_position - mouse_position 平移。这是任何未来流浪者的最终代码。 m_extraTranslation 显然在构造函数中初始化了 0

QMatrix MyWidget::computeScaleMatrix() const noexcept

    QMatrix scaleMatrix1., 0., 0., 1., 0., 0.;
    scaleMatrix.translate(-m_extraTranslation, 0.);
    scaleMatrix.translate(m_zoomCenter.x(), 0.);
    scaleMatrix.scale(m_zoomFactor, 1.);
    scaleMatrix.translate(-m_zoomCenter.x(), 0.);
    return scaleMatrix;


void MyWidget::wheelEvent(QWheelEvent* e)

    const QMatrix invScaleMatrix = computeScaleMatrix().inverted();
    const QPointF invPos = invScaleMatrix.map(e->pos());

    m_zoomCenter.setX(invPos.x());
    static constexpr const qreal ZOOM_IN_FACTOR = 1.1,
                                 ZOOM_OUT_FACTOR = .9;
    m_zoomFactor *= e->angleDelta().y() > 0. ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR;

    m_extraTranslation = invPos.x() - e->pos().x();

    // here I'm making sure the image corners do not wander off inside the widget, this is necessary to do for zooming out
    const QMatrix scaleMatrix = computeScaleMatrix();
    const qreal startX = scaleMatrix.map(QPointF0., 0.).x(),
                endX = scaleMatrix.map(QPointF(contentsRect().width() - 1, 0.)).x();

    if (startX > 0.)
    
        m_extraTranslation += startX;
    
    else if (endX < contentsRect().width() - 1)
    
        m_extraTranslation -= contentsRect().width() - endX - 1;
    

    if (m_zoomFactor < 1.)
    
        m_zoomFactor = 1.;
        m_zoomCenter = 0, 0;
        m_extraTranslation = 0;
    
    else if (m_zoomFactor > 5.)
    
        m_zoomFactor = 5.;
    
    if (m_zoomCenter.x() < 0)
    
        m_zoomCenter.setX(0);
    
    else if (m_zoomCenter.x() >= contentsRect().width())
    
        m_zoomCenter.setX(contentsRect().width() - 1);
    

    update();
    e->accept();


void MyWidget::paintEvent(QPaintEvent* e)

    QPainter painterthis;
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform);

    painter.setMatrix(computeScaleMatrix()));

    painter.drawImage(contentsRect(), m_image);

将其扩展到 Y 轴应该很简单,因为它会遵循相同的逻辑。

【讨论】:

以上是关于在放大的图像上查找缩放中心坐标的主要内容,如果未能解决你的问题,请参考以下文章

在mfc中从中心缩放位图图像

MapView 以用户坐标为中心缩放

颤动 - 如何使用画布围绕中心旋转图像

以鼠标位置为中心的图像缩放

matlab图象缩放程序

用flash CS4 as3.0编程实现元件以舞台中心为中心点缩放中遇到的问题,求指教。