Qt QGraphicsScene 基于视频的绘图

本文讨论了如何在基于视频的项目中优化图形绘制,从使用OpenGL受限到利用QGraphicsPixmapItem进行高效图片渲染,并介绍了如何通过控制点实现直线、矩形等图形的动态调整,以提高光学测量仪的性能和在线教育中的图形绘制效果。
摘要由CSDN通过智能技术生成

需求:

基于视频进行 图形的绘制。

方案:

 上一篇文章分享了如何将视频实时渲染到QGraphicsScene 系统里,并简单讲述了如何进行绘图,但在实际使用时还是发现了一些技巧,现在总结一下。

Qt 基于海康相机 的视频标绘-CSDN博客文章浏览阅读652次,点赞6次,收藏6次。利用qml 基于opengl 进行渲染,可以达到任意图形的绘制,但是帧率 只有25帧左右。如今要开发光学测量仪,发现使用QGraphicsPixmapItem 进行图片的渲染,可以利用QGraphicsItem 进行任务图形的叠加绘制,并且帧率目测大概在25帧所有,满足需求。曾经搞在线教育时,尝试在视频上进行文字或者图形的绘制,但是发现利用Qt widget 传sdk 句柄的方式,只能使用窗口叠加的方式(同时将QGraphicsPixmapItem放到最底层,即可达到在上边绘制任意图形的目的。https://blog.csdn.net/weixin_38416696/article/details/135844799?spm=1001.2014.3001.55021,效果

直线:

矩形:

圆:

弧:

2,实现

定义一个控制点,用于旋转或者改变大小。开始也尝试了 通过hover事件 来改变鼠标形状,但是这样判断地方有点多,通过使用控制点的方式,实现起来相对简单,使用上也还可以。

#ifndef BPOINTITEM_H
#define BPOINTITEM_H

#include <QObject>
#include <QAbstractGraphicsShapeItem>
#include <QPointF>
#include <QPen>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>
#include <QCursor>
#include <QKeyEvent>
#include <QList>
#include <QMutex>
#include <QThread>

//控制点
class EPointItem :public QObject, public QGraphicsItem {
	Q_OBJECT

public:
    enum class PointType{
        MovePoint=0,
        RotatePoint=1
    };

    EPointItem(QGraphicsItem* parent, QPointF p,PointType type = PointType::MovePoint);

public:
	virtual QRectF boundingRect() const override;
	virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;
	virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event) override;
	virtual void mousePressEvent(QGraphicsSceneMouseEvent* event) override;
	virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* event) override;

signals:
    void posMove(QPointF curPos, QPointF lastPos);
    void posRotate(QPointF curPos,QPointF lastPos);
    void posRelease();

private:
    PointType m_type;

};
#endif // BPOINTITEM_H



#include "bpointitem.h"
#include <QDebug>
#include <QtMath>

EPointItem::EPointItem(QGraphicsItem* parent, QPointF p,PointType type)
    : QGraphicsItem(parent),m_type(type)
{
    this->setPos(p);
    this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsFocusable);
    this->setCursor(Qt::PointingHandCursor);
}

QRectF EPointItem::boundingRect() const
{
    if(m_type==PointType::MovePoint){
        return QRectF(-4, -4, 8, 8);
    }else if(m_type==PointType::RotatePoint){
        return QRectF(-8, -8, 16, 16);
    }else{
        return QRectF(-4, -4, 8, 8);
    }
}

void EPointItem::mousePressEvent(QGraphicsSceneMouseEvent* event) {
    setSelected(true);
    QGraphicsItem::mousePressEvent(event);
}

void EPointItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
    emit posRelease();
    setSelected(false);
    QGraphicsItem::mouseReleaseEvent(event);
}

void EPointItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    if (isSelected()) {
        painter->setBrush(QBrush(Qt::green));
    }
    else {
        painter->setBrush(QBrush(Qt::white));
    }

    if(m_type==PointType::MovePoint){
        painter->setPen(QPen(Qt::gray, 1));
        painter->drawEllipse(-4, -4, 8, 8);
    }else if(m_type==PointType::RotatePoint){
        painter->setPen(QPen(Qt::gray, 2));
        painter->drawEllipse(-8, -8, 16, 16);
        painter->drawEllipse(-4, -4, 8, 8);
    }

}

void EPointItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event) {
    if (event->buttons() == Qt::LeftButton) {
        if(m_type==PointType::MovePoint)
            emit posMove(event->scenePos(), event->lastScenePos());
        else if(m_type==PointType::RotatePoint){
            emit posRotate(event->scenePos(), event->lastScenePos());
        }
    }
}

 以直线为例,实现的时候 先画一个普通的矩形,然后添加两个控制点,用于改变大小和旋转,通过控制点中 推拽的信号 来改变矩形的大小和旋转的角度,并且在矩形的paint函数中,更新控制点的位置。

class EItemUtil{
public:
    void rotateItem(QGraphicsItem* item,QPointF &curPos, QPointF &lastPos){
        // 设置中心点为原点
        QPointF originPos = item->boundingRect().center();
        // 从原点延伸出去两条线
        QLineF p1 = QLineF(originPos, item->mapFromScene(lastPos));
        QLineF p2 = QLineF(originPos, item->mapFromScene(curPos));
        // 两条线的夹角 为旋转角度
        qreal dRotateAngle = p2.angleTo(p1);
        item->setTransformOriginPoint(originPos);
        qreal dCurAngle = item->rotation() + dRotateAngle;

        while (dCurAngle > 360.0) {
            dCurAngle -= 360.0;
        }
        item->setRotation(dCurAngle);
        item->update();
    }
};

//直线
class ELine : public QObject, public QGraphicsRectItem
{
    Q_OBJECT
public:
    ELine(const QRectF& rect, QGraphicsItem* parent = nullptr);

protected:
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) override;

private:
    EPointItem* resizeItem;
    EPointItem* rotateItem;

    EItemUtil util;

private:
   //改变大小后 旋转中心会改变,这里要处理一下
    void resetCenter();
};

ELine::ELine(const QRectF& rect, QGraphicsItem* parent)
    : QGraphicsRectItem(rect, parent)
{
    setFlag(QGraphicsItem::ItemIsMovable);
    setFlag(QGraphicsItem::ItemSendsGeometryChanges);
    setAcceptHoverEvents(true);
    
    //定义一个旋转的控制点
    rotateItem = new EPointItem(this,QPointF(this->rect().x()+this->rect().width(), this->rect().y()),EPointItem::PointType::RotatePoint);
    connect(rotateItem, &EPointItem::posRotate, this, [this](QPointF curPos, QPointF lastPos) {
        util.rotateItem(this,curPos,lastPos);
    });

    //定义一个改变大小的控制点
    resizeItem = new  EPointItem(this, QPointF(this->rect().x(), this->rect().y()));
    resizeItem->setParentItem(this);
    connect(resizeItem, &EPointItem::posMove, this, [&](QPointF curPos, QPointF lastPos) {
        this->prepareGeometryChange();
        QRectF newRect = this->rect();

        qreal dMouseX = mapFromScene(curPos).x();
        qreal dMouseY = mapFromScene(curPos).y();

        double dRemainWidth = newRect.width();
        double dRemainHeight = newRect.height();

        dRemainWidth = newRect.right() - dMouseX;
        dRemainHeight = newRect.bottom() - dMouseY;
        if(dRemainHeight<0||dRemainWidth<0)
            return;

        newRect.setTopLeft(QPointF(newRect.right() - dRemainWidth, newRect.bottom() - dRemainHeight));
        this->setRect(newRect);
    });

    connect(resizeItem, &EPointItem::posRelease, this, [this]{
        resetCenter();
    });

}

void ELine::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    QPen mPen = QPen(Qt::red, 1);
    painter->setPen(mPen);
    painter->drawRect(rect());

    //更新两个控制点的位置
    resizeItem->setPos(QPointF(this->rect().x(), this->rect().y()));
    rotateItem->setPos(QPointF(this->rect().x()+this->rect().width(), this->rect().y()));
}

void ELine::resetCenter()
{
    //处理旋转后,改变大小的导致的旋转中心位置偏差
    auto rr = this->boundingRect();
    auto angle = this->rotation()*PI/180;
    auto p1 = rr.center();
    auto origin = this->transformOriginPoint();
    QPointF p2 = QPointF(0, 0);

    p2.setX(origin.x() + qCos(angle) * (p1.x() - origin.x()) - qSin(angle) * (p1.y() - origin.y()));
    p2.setY(origin.y() + qSin(angle) * (p1.x() - origin.x()) + qCos(angle) * (p1.y() - origin.y()));

    auto diff = p1 - p2;

    this->setRect(rr.adjusted(-diff.x(), -diff.y(), -diff.x(), -diff.y()));
    this->setTransformOriginPoint(this->boundingRect().center());
    this->update();
}

  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

土拨鼠不是老鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值