qt+opencv实现视频中动态选取ROI

使用QLabel显示摄像头得到的图像(视频流),把QLabel提升为MyLabel,通过在MyLabel类中添加新的方法可以实现在图像中动态选取ROI。要想在视频流中动态选取ROI,思路是创建一张全白的图片,使用以上方法先实现在图像中选取ROI,再把摄像头得到的图像(视频流)使用原QLabel的方法添加到QLabel上去即可。

//mylabel.h
#include <QLabel>
#include <QMenu>

/* 缩放方向 */
enum EmDirection
{
    DIR_TOP = 0,
    DIR_BOTTOM,
    DIR_MIDDLE,
    DIR_NONE
};


#define EDGPADDING       5        //四周边缘可拉伸宽度
#define CORPADDING       5        //四角可拉伸宽度
#define EDGE_WIDTH       1        //边框宽度


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    MyLabel(QWidget *parent = nullptr);
    ~MyLabel();
    QRect getRoiRect() const;               //获取已经圈选的框 外部调用

protected:
    void paintEvent(QPaintEvent *event);
    void mousePressEvent(QMouseEvent *ev);
    void mouseMoveEvent(QMouseEvent *ev);
    void mouseReleaseEvent(QMouseEvent *ev);
    void keyPressEvent(QKeyEvent *ev);
    void contextMenuEvent(QContextMenuEvent *ev);

private:
    void initViewer();                         //初始化
    EmDirection region(const QPoint &point);   //设置鼠标形状
    void moveRect(const QPoint &mousePoint);   //移动矩形
    void scaleRect(const QPoint &mousePoint);  //缩放矩形

private:
    bool m_bPainterPressed;        //是否正在绘制
    bool m_bMovedPressed;          //是否正在拖动
    bool m_bScalePressed;          //是否正在缩放大小
    QPoint m_paintStartPoint;      //绘制的初始位置
    QPoint m_moveStartPoint;       //拖动的初始位置
    QRect m_roiRect;               //绘制的ROI
    EmDirection m_emCurDir;        //拖动的方向
    QImage m_backImage;            //背景图
    QMenu *m_pOptMenu;             //菜单
    QAction *m_pDelAction;         //删除动作
};

//mylabel.cpp
#include "mylabel.h"
#include <QPainter>
#include <QMouseEvent>
#include <QMenu>
#include <QAction>
#include <QStandardPaths>
#include <QFileDialog>
#include <QDebug>

MyLabel::MyLabel(QWidget *parent)
    : QLabel(parent)
{
    m_backImage = QImage(640,480, QImage::Format_Grayscale8);
    m_backImage.fill(QColor(255, 255, 255));
    this->initViewer();
}

MyLabel::~MyLabel()
{
}

void MyLabel::initViewer()
{
    m_bPainterPressed = false;
    m_bMovedPressed = false;
    m_bScalePressed = false;
    m_roiRect = QRect(0, 100, 640-1, 200);
    m_emCurDir = EmDirection::DIR_NONE;

    this->setMouseTracking(true);
    this->setFocusPolicy(Qt::StrongFocus);

    m_pOptMenu = new QMenu(this);
    m_pDelAction = new QAction(QStringLiteral("删除"), this);
    connect(m_pDelAction, &QAction::triggered, this, [&]() { m_roiRect = QRect(0, 0, 0, 0); });
    m_pOptMenu->addAction(m_pDelAction);
}

/**
 * @brief				获取绘制的矩形
 * @return				绘制的矩形
 */
QRect MyLabel::getRoiRect() const
{
    return m_roiRect;
}

/**
 * @brief				绘制矩形
 * @param event         绘图事件
 * @return				void
 */
void MyLabel::paintEvent(QPaintEvent * event)
{
    QLabel::paintEvent(event);

    QPen pen;
    pen.setColor(Qt::blue);
    pen.setWidth(EDGE_WIDTH);

    QPainter painter;
    painter.begin(this);
    painter.setPen(pen);
    painter.drawRect(m_roiRect);

    qDebug() << m_roiRect;
}

/**
 * @brief				鼠标按下事件 用于拖动绘制缩放起始
 * @param ev	        鼠标事件
 * @return				void
 */
void MyLabel::mousePressEvent(QMouseEvent * ev)
{
    if (ev->buttons() & Qt::LeftButton)
    {
        EmDirection dir = region(ev->pos());     //获取鼠标当前的位置

        if (dir == DIR_MIDDLE)
        {
            //鼠标在矩形中心位置
            this->setCursor(Qt::ClosedHandCursor);
            m_moveStartPoint.setX(ev->pos().x());
            m_moveStartPoint.setY(ev->pos().y());
            m_bMovedPressed = true;
        }
        else if (dir == DIR_NONE)
        {
            //鼠标在矩形外部
            this->setCursor(Qt::ArrowCursor);
            m_bPainterPressed = true;
            m_paintStartPoint.setX(ev->pos().x());
            m_paintStartPoint.setY(ev->pos().y());
        }
        else
        {
            //矩形在矩形边缘
            m_moveStartPoint.setX(ev->pos().x());
            m_moveStartPoint.setY(ev->pos().y());
            m_bScalePressed = true;
            m_emCurDir = dir;
        }
    }
}

/**
 * @brief				鼠标移动事件
 * @param ev         	鼠标事件
 * @return				void
 */
void MyLabel::mouseMoveEvent(QMouseEvent * ev)
{
    if (ev->buttons() & Qt::LeftButton)
    {
        if (m_bMovedPressed) //正在移动状态
            moveRect(ev->pos());
        else if (m_bScalePressed) //正在缩放大小状态
            scaleRect(ev->pos());

        update(); //更新界面
        return;
    }

    //根据鼠标的位置设置当前的鼠标形状
    EmDirection dir = region(ev->pos());

    if (dir == DIR_NONE)
        setCursor(Qt::ArrowCursor);
    else if (dir == DIR_MIDDLE)
        setCursor(Qt::OpenHandCursor);
}

/**
 * @brief				鼠标松开事件
 * @param ev	        鼠标事件
 * @return				void
 */
void MyLabel::mouseReleaseEvent(QMouseEvent * ev)
{
    //判断鼠标是否在矩形中
    if (m_roiRect.contains(ev->pos()))
    {
        //松开鼠标前是否正在拖放
        if (m_bMovedPressed)
            this->setCursor(Qt::OpenHandCursor);
        else
            this->setCursor(Qt::ArrowCursor);
    }

    m_paintStartPoint = QPoint();
    m_bMovedPressed = false;
    m_bPainterPressed = false;
    m_bScalePressed = false;
}

/**
 * @brief				键盘按下事件
 * @param ev	        键盘事件
 * @return				void
 */
void MyLabel::keyPressEvent(QKeyEvent * ev)
{
    if (ev->key() == Qt::Key_Delete)
    {
        m_roiRect = QRect(0,0,0,0);
        update();
    }
}

/**
 * @brief				右键菜单
 * @param ev	        菜单事件
 * @return				void
 */
void MyLabel::contextMenuEvent(QContextMenuEvent * ev)
{
    QPoint mousePos = ev->pos();

    if (m_roiRect.contains(mousePos))
        m_pOptMenu->exec(QCursor::pos());

    ev->accept();
}

/**
 * @brief				判断鼠标的位置
 * @param point         鼠标的位置
 * @return				EmDirection 方向
 */
EmDirection MyLabel::region(const QPoint & point)
{
    int mouseX = point.x();
    int mouseY = point.y();

    QPoint roiTopLeft = m_roiRect.topLeft();
    QPoint roiBottomRight = m_roiRect.bottomRight();

    EmDirection dir = DIR_NONE;
    if (mouseY <= roiTopLeft.y() + EDGPADDING && mouseY >= roiTopLeft.y() && mouseX >= roiTopLeft.x() && mouseX <= roiBottomRight.x())
    {
        //上边
        this->setCursor(Qt::SizeVerCursor);
        dir = DIR_TOP;
    }
    else if (mouseY >= roiBottomRight.y() - EDGPADDING && mouseY <= roiBottomRight.y() && mouseX >= roiTopLeft.x() && mouseX <= roiBottomRight.x())
    {
        //下边
        this->setCursor(Qt::SizeVerCursor);
        dir = DIR_BOTTOM;
    }
    else if(m_roiRect.contains(point))
    {
        //中间
        dir = DIR_MIDDLE;
    }
    else
    {
        dir = DIR_NONE;
    }

    return dir;
}

/**
 * @brief				绘制矩形
 * @param mousePoint    鼠标的位置
 * @return				void
 */
void MyLabel::moveRect(const QPoint & mousePoint)
{
    this->setCursor(Qt::ClosedHandCursor);

    int width = mousePoint.x() - m_moveStartPoint.x();
    int height = mousePoint.y() - m_moveStartPoint.y();

    QRect rt;
    rt.setRect(0,0,this->width(),this->height());
    QRect ret;
    ret.setX(m_roiRect.x());
    ret.setY(m_roiRect.y() + height);
    ret.setSize(m_roiRect.size());
    if(rt.contains(QPoint(m_roiRect.x(),m_roiRect.y() + height))
     &&rt.contains(QPoint(m_roiRect.x() + m_roiRect.width(),m_roiRect.y() + height +m_roiRect.height())))
    {
        m_roiRect = ret;
        m_moveStartPoint = mousePoint;
    }
}

/**
 * @brief				缩放矩形
 * @param mousePoint    鼠标的位置
 * @return				void
 */
void MyLabel::scaleRect(const QPoint & mousePoint)
{
    QRect newRect(m_roiRect.topLeft(), m_roiRect.bottomRight());
    QRect rt;
    rt.setRect(0,0,this->width(),this->height());

    //根据当前的缩放状态来改变矩形的位置大小信息
    switch (m_emCurDir)
    {
    case DIR_TOP:
        newRect.setTop(mousePoint.y());
        if(rt.contains(QPoint(newRect.x() + newRect.width(),newRect.y() - newRect.height() + m_roiRect.height())))
        {
            m_roiRect = newRect;
            m_moveStartPoint = mousePoint;
        }
        break;
    case DIR_BOTTOM:
        newRect.setBottom(mousePoint.y());
        if(rt.contains(QPoint(newRect.x() + newRect.width(),newRect.y() + newRect.height())))
        {
            m_roiRect = newRect;
            m_moveStartPoint = mousePoint;
        }
        break;
    }
}

完整工程项目见:下载地址
本项目已上传github:github

要使用QtOpenCV实现录制视频的功能,可以参考以下步骤: 1. 首先,确保你已经安装了QtOpenCV,并且配置好了开发环境。 2. 创建一个Qt项目,并在项目包含OpenCV的头文件和库文件。 3. 在项目创建一个Widget类,继承自QWidget,并在头文件添加必要的成员变量和函数。 4. 在Widget类的构造函数初始化摄像头,并设置视频的帧率、宽度和高度。 5. 创建一个定时器对象,并连接到一个槽函数readFrame(),用于读取摄像头的每一帧图像。 6. 在readFrame()函数,使用OpenCV的VideoCapture类读取摄像头的图像,并进行帧差法处理,判断是否存在物体运动。 7. 如果存在物体运动,可以选择保存当前帧的图像或者将当前帧写入视频文件。 8. 在Widget类的析构函数释放摄像头和定时器对象。 9. 在Qt的界面添加一个按钮,用于开始和停止录制视频。 10. 在按钮的槽函数,根据按钮的状态来控制录制视频的开始和停止。 以上是一个简单的实现录制视频的步骤,具体的代码实现可以参考引用\[1\]的示例代码。希望对你有帮助! #### 引用[.reference_title] - *1* *2* *3* [[视觉实战案例]Qt+OpenCV实现USB摄像头监测移动物体并录制视频功能(帧差法)](https://blog.csdn.net/fengyaowuhui/article/details/124656844)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

给算法爸爸上香

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

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

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

打赏作者

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

抵扣说明:

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

余额充值