100 行 C++ 代码,教你快速实现视频画面动态分割!

news2024/11/24 3:37:40

作者: 一去、二三里
个人微信号: iwaleon
微信公众号: 高效程序员

在进行视频或者图像处理时,经常会出现画面分割的场景。

当然了,这里说画面分割是对视频/图像画面的切割,即将同一视频/图像分割成不同的部分,然后进行显示输出,而不是让不同画面显示不同的视频/图像,这样做的好处是每一块视频/图像我们都能单独处理。

一起来看看,今天的视频画面分割器怎么操作吧!

在这里插入图片描述

实现细节

为了便于后续使用,我们封装一个网格类 GridView,使其继承自 QGraphicsView:

GridView::GridView(QWidget *parent)
    : QGraphicsView(parent)
{
    QGraphicsScene *scene = new QGraphicsScene(this);
    scene->setBackgroundBrush(QBrush(QColor(Qt::transparent)));
    setScene(scene);
    fitInView(scene->sceneRect(), Qt::KeepAspectRatio);
}

对于网格来说,由于行和列是不固定的,因此我们需要提供一个可以动态改变行列的接口。

一旦行和列发生变化,就行该立即重置所有的 item:

void GridView::setRowColumn(int rows, int columns)
{
    if (rows > 0 && columns > 0) {
        m_rowCount = rows;
        m_columnCount = columns;

        resetItems();
    }
}

为了实现重置,我们首先需要删除原有的 item,然后添加新的 item,并将他们布局到当前的行和列中:

void GridView::resetItems()
{
    removeItems();
    addItems();
    layoutItems();
}

以下是具体的删除和新增 item 的逻辑:

void GridView::removeItems()
{
    for (QGraphicsItem *item : scene()->items()) {
        PixmapItem *pixItem = dynamic_cast<PixmapItem *>(item);
        if (pixItem != nullptr) {
            scene()->removeItem(item);
            delete item;
            item = nullptr;
        }
    }
}

void GridView::addItems()
{
    int index = 0;

    // 添加 item
    for (int i = 0; i < m_rowCount; i++) {
        for (int j = 0; j < m_columnCount; j++) {
            PixmapItem *item = new PixmapItem(scene());
            item->setData(ITEM_INDEX, index);
            item->setData(ITEM_ROW, i);
            item->setData(ITEM_COLUMN, j);
            item->setZValue(0);  // 显示在底部
            index++;
        }
    }
}

其实,最重要的是布局的算法,根据场景大小,以及边距、间距等计算各个 item 的位置和大小:

void GridView::layoutItems()
{
    // 获取场景大小
    QRectF rect = scene()->sceneRect();
    // 计算去除边距的大小
    qreal leftRectWidth = rect.width()- m_margin * 2;
    qreal leftRectHeight = rect.height()- m_margin * 2;
    // 计算去除间距的大小
    if (m_columnCount > 1)
        leftRectWidth -= (m_columnCount - 1) * m_spacing;
    if (m_rowCount > 1)
        leftRectHeight -= (m_rowCount - 1) * m_spacing;
    // 计算每个 item 的大小
    qreal itemWidth = leftRectWidth / m_columnCount;
    qreal itemHeight = leftRectHeight / m_rowCount;

    // 设置每个 item 的位置与大小
    for (QGraphicsItem *item : scene()->items()) {
        PixmapItem *pixItem = dynamic_cast<PixmapItem *>(item);
        if (pixItem != nullptr) {
            int row = pixItem->data(ITEM_ROW).toInt();
            int column = pixItem->data(ITEM_COLUMN).toInt();

            // 根据 item 所在行列计算其坐标值
            qreal x = m_margin + column * (itemWidth + m_spacing);
            qreal y = m_margin + row * (itemHeight + m_spacing);
            pixItem->setGeometry(QRectF(x, y, itemWidth, itemHeight));
        }
    }
}

随后,就是图片的设置了。当图片改变时,我们也需要更新当前的画面:

void GridView::setPixmap(const QPixmap &pixmap)
{
    m_pixmap = pixmap;

    updatePixmap();
}

更新图片分为两步,首先是图片的分割,然后是 item 对图片的设置渲染:

void GridView::updatePixmap()
{
    QList<QPixmap> pixmaps = splitPixmap(m_pixmap);

    // 给每个 item 设置分割后的对应图片
    QList<QGraphicsItem *> items = scene()->items();
    for (int i = 0; i < items.count(); ++i) {
        PixmapItem *pixItem = dynamic_cast<PixmapItem *>(items.at(i));
        if (pixItem != nullptr) {
            int index = pixItem->data(ITEM_INDEX).toInt();
            if (index < pixmaps.count())
                pixItem->setPixmap(pixmaps.at(index));
        }
    }
}

图片分割主要是由 QPixmap::copy() 实现的,而具体切割的位置、大小可以根据行和列来计算:

QList<QPixmap> GridView::splitPixmap(const QPixmap &pixmap)
{
    QList<QPixmap> pixmaps;

    // 计算分割后图片的大小
    int width = pixmap.width() / m_columnCount;
    int height = pixmap.height() / m_rowCount;

    // 分割原始图片
    for (int i = 0; i < m_rowCount; i++) {
        for (int j = 0; j < m_columnCount; j++) {
            QPixmap pix = pixmap.copy(j * width, i * height, width, height);
            pixmaps.append(pix);
        }
    }

    return pixmaps;
}

最后,为了窗口缩放时 item 能够自己动态调整大小,因此需要在 resizeEvent() 中设置场景的大小并重新进行 item 的布局:

void GridView::resizeEvent(QResizeEvent *event)
{
    QGraphicsView::resizeEvent(event);

    QRectF sceneRect = QRectF(QPointF(0, 0), viewport()->size());
    scene()->setSceneRect(sceneRect);

    layoutItems();
}

现在,有了 GridView,要进行视频画面的动态分割,简直太容易了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/630767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

javassist 入门以及dubbo中的使用案例

javassite 入门 概述原理 简单的demo记录方法执行的时间带参数和返回值javassite 占位符 dubbo中的使用代理工厂 JavassistProxyFactory代理类 org.apache.dubbo.common.bytecode.Proxyorg.apache.dubbo.rpc.proxy.InvokerInvocationHandler创建类的工具类 ClassGenerator 概述…

uniapp-ios打包安装测试

我们在做uniapp需要打ios包测试的时候&#xff0c;会有证书私钥密码、证书profile文件、私钥证书三项必填项&#xff0c;这是苹果三件套&#xff0c;必须要有的。就是下图所示 下面说一下如何获取&#xff1a; 一、申请账号 1. 申请Apple id 登录&#xff1a; https://app…

Vue3:组件高级(下)

Vue3&#xff1a;组件高级&#xff08;下&#xff09; Date: May 25, 2023 Sum: ref引用、动态组件、插槽、自定义指令 目标&#xff1a; ◆ 能够知道如何使用 ref 引用 DOM 和组件实例 ◆ 能够知道 $nextTick 的调用时机 ◆ 能够说出 keep-alive 元素的作用 ◆ 能够掌握插…

TiDB亿级数据亚秒响应查询扩缩容

目录 1 查看数据分布2 当前集群部署拓扑3 扩容TiKV节点3.1 编写扩容脚本3.2 执行扩容命令3.2.1 命令格式3.2.2 执行命令 3.3 验证扩容信息3.3.1 查看节点信息3.3.2 通过dashboard查看 4 缩容TiKV节点4.1 查看节点信息4.2 执行缩容操作4.2.1 缩容命令4.2.2 执行命令 4.3 验证缩容…

Redis集群(分布式缓存):详解持久化、主从同步原理、哨兵机制、Cluster分片集群,实现高并发高可用

0、引言 单机式Redis存在以下问题&#xff0c;因此需要Redis集群化来解决这些问题 1、持久化 1.1 RDB&#xff08;Redis Database Backup file &#xff09;持久化 Redis数据快照&#xff0c;简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&#xff0c…

CSS 布局备忘录

CSS 布局 元素布局display:blockdisplay:inlinedisplay:inline-blockdisplay:inheritdisplay:none Position 布局Flex 布局父元素属性flex-directionflex-wrapflex-flowjustify-contentalign-itemsalign-content 子元素属性orderflex-growflex-shrinkflex-basisfelxalign-self …

电商--抢购总结

文章目录 业务流程业务难点技术难点技术方案技术方向具体落地客户端流控网关流控容器流控后端接口流控数据库流控 流控总结优化读取加速异步化流程处理系统扩容 压测监控 总结参考文献 业务流程 客户端抢购流程中会涉及到商品数据的读取用于商品展示&#xff0c;运营活动数据的…

MM32F3273G8P火龙果开发板MindSDK开发教程8 - MutilButton的移植

MM32F3273G8P火龙果开发板MindSDK开发教程8 - MutilButton的移植 1、MutilButton简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块&#xff0c;可无限量扩展按键&#xff0c;按键事件的回调异步处理方式可以简化你的程序结构&#xff0c;去除冗余的按键处理硬编…

NodeJS SessionToken验证⑧

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言登录鉴权Cookie&Session ExpressSession中间件 MVC演示登录鉴权JSON Web Token (JWT) Jsonwebtoken参数sign 方法verify 方法 封装JsonWebToke…

北邮22信通:第六章查找:BST树表(代码超详细逐步图解)

北邮22信通一枚~ 跟随课程进度每周更新数据结构与算法的代码和文章 持续关注作者 解锁更多邮苑信通专属代码~ 获取更多文章 请访问专栏&#xff1a; 北邮22信通_青山如墨雨如画的博客-CSDN博客 目录 讲解 1.构造函数 2.析构函数 3.查询函数 4.删除操作 &#xf…

全新出品!阿里 P5 工程师~P8 架构师晋升路线揭秘

阿里巴巴终于公开了从初级程序员到架构师的学习路线图&#xff0c;这里相对应的基本上就是从P5到P8的晋升体系&#xff01;今天老师将会带着大家从初级程序员开始一点点分享整个晋升体系&#xff01; 职级&#xff1a;初级程序员 薪资&#xff1a;6-12K 开发年限&#xff1a;0-…

PureComponent和Component的区别和底层处理机制

PureComponent和Component都是React中的组件类&#xff0c;但它们在实现细节和使用上有些差别。 Component是React中定义组件的基类&#xff0c;它的shouldComponentUpdate方法默认返回true&#xff0c;也就是说&#xff0c;每次调用setState或forceUpdate方法都会引发组件重新…

代码随想录第55天

1.判断子序列&#xff1a; 动态规划五部曲分析如下&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串s&#xff0c;和以下标j-1为结尾的字符串t&#xff0c;相同子序列的长度为dp[i][j]。 注意这里是判断s是否…

百度新闻源调整:自媒体权重降低,官方媒体优势突显

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 根据黑猫发稿的消息&#xff0c;自6月1日起&#xff0c;百度新闻源取消了大部分自媒体新闻源的收录&#xff0c;包括搜狐自媒体、企鹅号、网易号&#xff0c;甚至百度自己的百家号也受到了影响。 …

X2000 Linux 低功耗

一、进入休眠 当系统启动后&#xff0c;在命令终端输入&#xff1a; echo mem> /sys/power/state 即可立即进入休眠&#xff0c;功耗也随之降低。 二、配置中断唤醒GPIO 1、确认kernel默认配置文件 进入到/tools/iconfigtool/IConfigToolApp/路径下&#xff0c;执行./…

【Pm4py第四讲】关于Conversion

本节用于介绍pm4py中的转换函数&#xff0c;包括日志、事件流、数据块的转换、Petei网、流程树、BPMN的转换、可达图、面向对象日志等。 1.函数概述 本次主要介绍Pm4py中一些常见的转换函数&#xff0c;总览如下表&#xff1a; 函数名说明convert_log_to_networkx&#xff08;…

Java(30天拿下---第一天)

Java开发&#xff08;30天拿下---第一天&#xff09; 一 hello world以及JDK,JRE,JVM二 转义字符三 注释四 代码规范五 DOS命令&#xff08;了解&#xff09;六 变量1.加号的使用2.数据类型整型浮点型字符类型布尔类型自动类型转换强制类型转换String类型 七 API文档 一 hello …

React | Redux的使用详解

✨ 个人主页&#xff1a;CoderHing &#x1f5a5;️ React.js专栏&#xff1a;React.js Redux的使用详解 &#x1f64b;‍♂️ 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; &#x1f4ab; 系列专栏&#xff1a;吊打面试官系列 16天学会Vue 7天学会微信小程序 N…

亚马逊云科技Serverless数据分析,助力猎豹移动构建更高性价比数据仓库

也许你也听过这样一句话&#xff1a;“21世纪什么最贵&#xff1f;人才&#xff01;”当数字经济全面席卷而来&#xff0c;这个问题的答案不可置否地变为了“数据”。通过数据分析获取近乎实时的洞察&#xff0c;以驱动业务的全流程&#xff0c;是企业数字化转型的必经之路。借…

【文末送书】微服务拆分规范

目录 一. &#x1f981; 什么是微服务&#xff1f;二. &#x1f981; 拆分模型Ⅰ. 压力模型拆分1. 垂直拆分&#xff08;Vertical Decomposition&#xff09;2. 水平拆分&#xff08;Horizontal Decomposition&#xff09;3. 动态拆分&#xff08;Dynamic Decomposition&#x…