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

如何在Qt中无需OpenGL实现QImage的2D四边形变形?

不用切换到OpenGL当然可以实现!Qt本身就提供了原生的2D绘图能力来完成这种类似PS自由变换的四边形变形,我给你整理几个最实用的方案:

方案1:用QTransform的quadToQuad实现透视变形(最推荐)

Qt的QTransform类提供了一个非常实用的静态方法quadToQuad,专门用来创建从源四边形到目标四边形的投影变换矩阵——这正是你要的「把矩形图像变形到任意非矩形四边形」的功能,完全不需要OpenGL,纯2D绘图就能搞定。

这个方法支持透视变换(不是只能处理平行四边形的仿射变换),完美对应PS自由变换里的「扭曲/透视」效果。

代码示例

假设你有一张原始QImage,想要把它变形到自定义的非矩形四边形:

// 1. 定义源四边形:就是原始图像的四个顶点
const QSize imgSize = originalImage.size();
QPolygonF srcQuad{
    QPointF(0, 0),
    QPointF(imgSize.width(), 0),
    QPointF(imgSize.width(), imgSize.height()),
    QPointF(0, imgSize.height())
};

// 2. 定义目标四边形:你想要变形后的四个顶点(自定义坐标)
QPolygonF dstQuad{
    QPointF(50, 50),    // 左上角
    QPointF(300, 60),   // 右上角
    QPointF(280, 300),  // 右下角
    QPointF(30, 280)    // 左下角
};

// 3. 创建变换矩阵
QTransform transform = QTransform::quadToQuad(srcQuad, dstQuad);

// 两种使用方式:
// 方式A:直接在QPainter绘制时应用变换
QPainter painter(this);
painter.setTransform(transform);
painter.drawImage(0, 0, originalImage);

// 方式B:提前生成变形后的QImage,之后直接绘制
QImage transformedImg = originalImage.transformed(transform, Qt::SmoothTransformation);
painter.drawImage(0, 0, transformedImg);

注意:Qt::SmoothTransformation参数会启用双线性插值,让变形后的图像更平滑;如果追求性能可以换成Qt::FastTransformation(最近邻插值)。

方案2:手动逐像素映射(适合精细控制)

如果需要对像素映射做更定制化的处理(比如特殊的插值算法、局部区域调整),可以手动计算每个目标像素对应的源图像坐标,然后插值填充。这种方式灵活性最高,但性能不如原生方法,适合小图像或实时要求不高的场景。

核心思路

  1. 先通过quadToQuad创建变换矩阵,再获取它的逆变换(用来把目标点映射回源图像);
  2. 遍历目标图像的每个像素,判断是否在目标四边形内;
  3. 对在四边形内的像素,用逆变换计算对应的源图像坐标,再用双线性/最近邻插值获取像素颜色。

简化代码示例

QTransform transform = QTransform::quadToQuad(srcQuad, dstQuad);
QTransform inverseTransform = transform.inverted();

// 目标图像尺寸可以取目标四边形的包围盒大小
QRectF dstBounds = dstQuad.boundingRect();
QImage resultImg(dstBounds.size().toSize(), originalImage.format());

for (int y = 0; y < resultImg.height(); ++y) {
    for (int x = 0; x < resultImg.width(); ++x) {
        QPointF targetPoint(x + dstBounds.left(), y + dstBounds.top());
        // 判断点是否在目标四边形内
        if (!dstQuad.containsPoint(targetPoint, Qt::OddEvenFill)) {
            resultImg.setPixelColor(x, y, Qt::transparent);
            continue;
        }
        // 映射到源图像坐标
        QPointF srcPoint = inverseTransform.map(targetPoint);
        // 检查源坐标是否在图像范围内
        if (srcPoint.x() >= 0 && srcPoint.x() < imgSize.width() - 1 &&
            srcPoint.y() >= 0 && srcPoint.y() < imgSize.height() - 1) {
            // 双线性插值计算颜色(这里简化了颜色计算,实际可以用更高效的方式)
            int x0 = qFloor(srcPoint.x());
            int x1 = x0 + 1;
            int y0 = qFloor(srcPoint.y());
            int y1 = y0 + 1;

            qreal fx = srcPoint.x() - x0;
            qreal fy = srcPoint.y() - y0;

            QColor c00 = originalImage.pixelColor(x0, y0);
            QColor c01 = originalImage.pixelColor(x0, y1);
            QColor c10 = originalImage.pixelColor(x1, y0);
            QColor c11 = originalImage.pixelColor(x1, y1);

            QColor finalColor;
            finalColor.setRedF(c00.redF()*(1-fx)*(1-fy) + c10.redF()*fx*(1-fy) + c01.redF()*(1-fx)*fy + c11.redF()*fx*fy);
            finalColor.setGreenF(c00.greenF()*(1-fx)*(1-fy) + c10.greenF()*fx*(1-fy) + c01.greenF()*(1-fx)*fy + c11.greenF()*fx*fy);
            finalColor.setBlueF(c00.blueF()*(1-fx)*(1-fy) + c10.blueF()*fx*(1-fy) + c01.blueF()*(1-fx)*fy + c11.blueF()*fx*fy);
            finalColor.setAlphaF(c00.alphaF()*(1-fx)*(1-fy) + c10.alphaF()*fx*(1-fy) + c01.alphaF()*(1-fx)*fy + c11.alphaF()*fx*fy);

            resultImg.setPixelColor(x, y, finalColor);
        } else {
            resultImg.setPixelColor(x, y, Qt::transparent);
        }
    }
}
方案3:用QGraphicsView框架(适合已有图形视图的项目)

如果你的应用已经在使用QGraphicsView/QGraphicsScene,那直接把图像作为QGraphicsPixmapItem添加,然后给它设置变换矩阵就行——视图会自动帮你处理渲染,非常省心。

代码示例

QGraphicsScene* scene = new QGraphicsScene(this);
QGraphicsPixmapItem* pixmapItem = scene->addPixmap(QPixmap::fromImage(originalImage));

// 同样用quadToQuad创建变换矩阵
QTransform transform = QTransform::quadToQuad(srcQuad, dstQuad);
pixmapItem->setTransform(transform);

QGraphicsView* view = new QGraphicsView(scene, this);
view->show();

总的来说,方案1是最优先选择,代码简洁、性能好,完全满足你把图像变形到任意四边形的需求,和PS自由变换的效果一致。

内容的提问来源于stack exchange,提问作者cbuchart

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 09:10:36