Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十一 简单给视频添加水印图片效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十一 简单给视频添加水印图片效果

目录

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单视频处理实战案例 之十一 简单给视频添加水印图片效果

一、简单介绍

二、简单给视频添加水印图片效果实现原理

三、简单给视频添加水印图片效果案例实现简单步骤

四、注意事项


一、简单介绍

Python是一种跨平台的计算机程序设计语言。是一种面向对象的动态类型语言,最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。Python是一种解释型脚本语言,可以应用于以下领域: Web 和 Internet开发、科学计算和统计、人工智能、教育、桌面界面开发、软件开发、后端开发、网络爬虫。

这里使用 Python  基于 OpenCV 进行视觉图像处理,......

二、简单给视频添加水印图片效果实现原理

视频水印是指在视频内容中添加图像、文字或其他标识符的技术,以表明视频的所有权、来源、版权信息或其他相关信息。视频水印可以是透明的或半透明的,通常位于视频画面的角落或其他不影响主要内容的位置。视频水印可以用于保护视频内容的版权,防止未经授权的复制和使用,以及增加视频的专业性和可识别性。

实现原理:

  1. 打开输入视频文件,并获取其每一帧的大小。
  2. 打开水印图像文件,并获取其大小。
  3. 遍历视频的每一帧,将水印图像叠加到视频帧上。
  4. 根据指定的位置参数确定水印在视频帧上的位置,并将水印图像叠加到相应的位置上。
  5. 将带有水印的视频帧写入输出视频文件。

实现方法:

  1. 使用 OpenCV 的 VideoCapture 类打开输入视频文件,并读取每一帧。
  2. 使用 OpenCV 的 VideoWriter 类创建输出视频文件,并指定视频编码器、帧率和大小。
  3. 使用 OpenCV 的 imread 函数读取水印图像。
  4. 遍历视频的每一帧,在每一帧上叠加水印图像。
  5. 根据指定的位置参数确定水印的位置,并将水印图像叠加到视频帧的相应位置上。
  6. 使用 OpenCV 的 addWeighted 函数将水印图像叠加到视频帧上,实现透明度叠加效果。
  7. 将带有水印的视频帧写入输出视频文件。

涉及了一些关键函数:

  1. cv2.VideoCapture(): 用于打开视频文件并创建一个视频捕获对象。在这里,它被用于打开输入视频文件并创建一个用于读取帧的视频捕获对象。

  2. cap.isOpened(): 用于检查视频是否成功打开。在这里,它被用于检查输入视频文件是否成功打开。

  3. cap.read(): 用于读取视频的下一帧。在这里,它被用于循环遍历视频的每一帧并读取帧内容。

  4. cv2.VideoWriter(): 用于创建一个视频写入对象,可以将帧写入视频文件。在这里,它被用于创建一个用于写入帧的视频写入对象。

  5. cv2.imread(): 用于读取图像文件。在这里,它被用于读取水印图像文件。

  6. cv2.imwrite(): 用于将图像写入文件。虽然在这里没有直接使用,但可以用它来保存添加水印后的帧。

  7. cv2.putText(): 用于向图像中添加文字。在这里,虽然没有直接使用,但可以用它来添加文字水印。

  8. cv2.addWeighted(): 用于将两个图像进行加权叠加。在这里,它被用于将水印图像叠加到视频帧上。

  9. cap.release(): 用于释放视频捕获对象的资源。

  10. out.release(): 用于释放视频写入对象的资源。

  11. cv2.destroyAllWindows(): 用于关闭所有窗口,通常在程序结束时调用。

三、简单给视频添加水印图片效果案例实现简单步骤

1、编写代码

2、运行效果

3、具体代码

"""
简单给视频添加水印图片效果
    1、打开输入视频文件,并获取其每一帧的大小。
    2、打开水印图像文件,并获取其大小。
    3、遍历视频的每一帧,将水印图像叠加到视频帧上。
    4、根据指定的位置参数确定水印在视频帧上的位置,并将水印图像叠加到相应的位置上。
    5、将带有水印的视频帧写入输出视频文件。
"""

import cv2
import os


def add_watermark_to_video(input_video_path, output_video_path, watermark_image_path, alpha=1.0,
                           position='bottom-right'):
    """
    简单给视频添加水印图片效果
    :param input_video_path: 原视频路径
    :param output_video_path: 添加水印后保存视频路径
    :param watermark_image_path: 水印图片路径
    :param alpha: 控制水印整体透明度的参数,范围为 0 到 1
    :param position: 控制水印位置的参数,可选值包括 'top-left', 'top-right', 'bottom-left', 'center', 'bottom-right'
    :return:
    """
    # 检查输入视频文件是否存在
    if not os.path.isfile(input_video_path):
        print("Error: Input video file does not exist.")
        return

    # 检查水印图像文件是否存在
    if not os.path.isfile(watermark_image_path):
        print("Error: Watermark image file does not exist.")
        return

    # 打开视频文件
    cap = cv2.VideoCapture(input_video_path)

    # 检查视频是否成功打开
    if not cap.isOpened():
        print("Error: Failed to open input video.")
        return

    # 获取视频的宽度和高度
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))

    # 创建VideoWriter对象用于写入输出视频
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_video_path, fourcc, 30.0, (width, height))

    # 读取水印图像
    watermark = cv2.imread(watermark_image_path, cv2.IMREAD_UNCHANGED)

    # 检查水印图像是否成功读取
    if watermark is None:
        print("Error: Failed to read watermark image.")
        return

    # 检查水印图像的透明度通道是否存在
    if watermark.shape[2] < 4:
        print("Error: Watermark image does not have an alpha channel.")
        return

    # 循环遍历视频的每一帧
    while True:
        ret, frame = cap.read()
        if not ret:
            break

        # 将水印叠加到当前帧上
        overlay = frame.copy()
        h, w = watermark.shape[:2]

        # 根据位置参数确定水印的位置
        if position == 'top-left':
            x_offset, y_offset = 10, 10
        elif position == 'top-right':
            x_offset, y_offset = width - w - 10, 10
        elif position == 'bottom-left':
            x_offset, y_offset = 10, height - h - 10
        elif position == 'center':
            x_offset, y_offset = (width - w) // 2, (height - h) // 2
        else:  # 默认为 'bottom-right'
            x_offset, y_offset = width - w - 10, height - h - 10

        for c in range(0, 3):
            overlay[y_offset:y_offset + h, x_offset:x_offset + w, c] = \
                watermark[:, :, c] * (watermark[:, :, 3] / 255.0 * alpha) + frame[y_offset:y_offset + h,
                                                                            x_offset:x_offset + w, c] * (
                        1.0 - watermark[:, :, 3] / 255.0 * alpha)

        # 将帧写入输出视频
        out.write(overlay)

    # 释放资源
    cap.release()
    out.release()
    cv2.destroyAllWindows()


def main():
    # 调用函数并指定输入和输出视频文件路径以及水印图像路径
    input_video_path = "Videos/CatRun.mp4"
    output_video_path = "Videos/CatRun_Wartermark.mp4"
    watermark_image_path = "Images/Watermark.png"
    alpha = 0.5  # 控制水印整体透明度的参数,范围为 0 到 1
    position = 'top-right'  # 控制水印位置的参数,可选值包括 'top-left', 'top-right', 'bottom-left', 'center', 'bottom-right'

    # 执行函数
    add_watermark_to_video(input_video_path, output_video_path, watermark_image_path, alpha, position)


if __name__ == "__main__":
    main()

四、注意事项

  1. 参数安全性:对于输入的参数,需要进行安全校验,以确保输入的参数值在有效范围内,避免出现错误。
  2. 图像大小匹配:确保水印图像的大小与视频帧的大小匹配,否则可能导致叠加时的错误。
  3. 透明度处理:根据水印图像的透明度信息,合理处理水印图像与视频帧的叠加透明度,以保持水印的透明效果。
  4. 位置处理:根据指定的位置参数,确保水印图像叠加到视频帧的正确位置上,避免覆盖重要信息或者显示不完整。
  5. 资源释放:在处理完所有帧之后,及时释放视频读取和写入的资源,以避免内存泄漏或资源浪费。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【保姆级讲解Element UI】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

负载均衡器如何工作,为什么如此重要?

现代应用程序和网站处理大量流量。负载均衡器是保证大型系统平稳运行的主要工具之一。 负载平衡器负责跨多个服务器路由客户端请求以分配负载并防止出现瓶颈。 这有助于最大限度地提高吞吐量、减少响应时间并优化资源使用。 负载均衡器的运行情况&#xff1a; (1).客户端请…

面试算法-176-验证二叉搜索树

题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。 节点的右子树只包含 大于 当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#x…

C++内存管理与模版(用法详解)

C/C中程序内存区域划分 内核空间&#xff08;用户代码不能读写&#xff09;栈&#xff08;函数中存放的变量&#xff09;内存映射段堆&#xff08;重点&#xff09;数据段&#xff08;静态区&#xff09;全局变量 / 静态变量代码段&#xff08;常量区&#xff09; 试分析下列…

大模型用到的位置编码汇总(面试)

不同于RNN、CNN等模型&#xff0c;对于Transformer模型来说&#xff0c;位置编码的加入是必不可少的&#xff0c;因为纯粹的Attention模块是无法捕捉输入顺序的&#xff0c;即无法区分不同位置的Token。为此我们大体有两个选择&#xff1a;想办法将位置信息融入到输入中&#x…

OpenHarmony轻量系统开发【12】OneNET云接入

12.1 OneNET云介绍 通常来说&#xff0c;一个物联网产品应当包括设备、云平台、手机APP。我将在鸿蒙系统上移植MQTT协议、OneNET接入协议&#xff0c;实现手机APP、网页两者都可以远程&#xff08;跨网络&#xff0c;不是局域网的&#xff09;访问开发板数据&#xff0c;并控制…

ActiveMQ 05 高级使用

Active MQ 05 高级使用 queue browser 可以查看队列中的消息而不消费&#xff0c;没有订阅的功能 JMSCorrelationID 用于消息之间的关联&#xff0c;给人一种会话的感觉 http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html JMSReplyTo …

codeforce #925 (div3) 题解

D. Divisible Pairs 给出数组 a a a&#xff0c;如果二元组 ( i , j ) (i,j) (i,j)满足 a i a j m o d x 0 & & a i − a j m o d y 0 a_i a_j mod x 0 \&\& a_i - a_j mod y 0 ai​aj​modx0&&ai​−aj​mody0&#xff0c;则beauty。其中 i &…

SpringBoot整合消息中间件(ActiveMQ,RabbitMQ,RocketMQ,Kafka)

消息中间件 消息消息队列JMS AMQPMQTTKafka Spring整合消息队列模拟消息队列的工作流程Spring整合ActiveMQSpring整合RabbitMQ直连交换机模式主题交换机模式 Spring整合RocketMQSpring整合kafka 消息 消息的发送方&#xff1a;生产者 消息的接收方&#xff1a;消费者 同步消息…

vite - WebAssembly入门

1. 初始化 vite 项目 1.1 安装 nvm&#xff08;可选&#xff09; brew update brew install nvm在 ~/.zshrc 添加 export NVM_DIR~/.nvm source $(brew --prefix nvm)/nvm.sh执行如下命令 source ~/.zshrc1.2 安装 node nvm install nodenvm ls -> …

1260. 二维网格迁移

1260. 二维网格迁移 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;错误经验吸取 原题链接&#xff1a; 1260. 二维网格迁移 https://leetcode.cn/problems/shift-2d-grid/description/ 完成情况&#xff1a; 解题思路&#xff1a; 这…

android不同版本(支持>10)获取当前连接的wifi名称

1、AndroidManifest.xml 配置权限 <uses-permission android:name"android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name"android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name&q…

磁盘管理和文件系统

一.磁盘基础 1.磁盘结构 &#xff08;1&#xff09;物理结构&#xff1a; 盘片&#xff1a;硬盘有多个盘片&#xff0c;每盘片2面 磁头&#xff1a;每面一个磁头 &#xff08;2&#xff09;硬盘的数据结构 扇区&#xff1a;盘片被分为多个扇形区域&#xff0c;每个扇区存…

山姆·奥特曼是如何成为亿万富豪的?

2017年夏天&#xff0c;Superhuman公司首席执行官拉胡尔沃拉&#xff08;Rahul Vohra&#xff09;开始疯狂向投资者一一发消息&#xff0c;缘由是他的初创公司尝试了谷歌浏览器Chrome的一项即将推出的更新。由于一个看似无害的代码更改&#xff0c;Superhuman的智能电子邮件服务…

Jackson 2.x 系列【23】注解内省 AnnotationIntrospector

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 前言2. AnnotationIntrospector3. JacksonAnnotationIntrospector4. Annotati…

SQL12 获取每个部门中当前员工薪水最高的相关信息

题目&#xff1a;获取每个部门中当前员工薪水最高的相关信息 注意了&#xff0c;这道题目&#xff0c;分组函数只能查出来&#xff1a;每个部门的最高薪水&#xff0c;group by dept_no &#xff0c;根据部门分组&#xff0c;绝对不能group by dept_no,emp_no&#xff0c;不能…

【刷题笔记】第二天

一道图论相关的题目 3108. 带权图里旅途的最小代价 结论&#xff1a; 做与运算&#xff0c;结果不会大于当前值&#xff0c;也就是说与运算只能导致结果不变或越来越小&#xff0c;所以要使得边的and值最小&#xff0c;就是把每一个联通块的所有边都and一遍。 方法1&#xf…

Vue项目实战:基于用户身份的动态路由管理

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

201403-3 命令行选项

100分 #include <bits/stdc.h> using namespace std;int main() {string line;cin >> line;map<char, bool> dct; // true:带参数 false:不带参数for (int i 0; i < line.size(); i){if (line[i] ! :){dct.insert(pair<char, bool>(line[i], fals…

ubuntu 23.10.1 mysql 安装

注&#xff1a;请进入root用户模式下操作&#xff0c;若没有&#xff0c;输入命令前加上sudo 1、更新软件包列表 apt update2、安装最新版的Mysql服务器 apt install mysql-server -y如果不加-y 会在安装过程中&#xff0c;系统将提示你设置MySQL的root密码。确保密码足够强…