AI-人工智能-实现将静态图片和视频合成为类似iPhone的Live Photo(动态照片)效果

实现将静态图片和视频合成为类似iPhone的Live Photo(动态照片)效果

可以使用Python结合OpenCV和图像处理库来完成

技术说明

  1. Live Photo原理:iPhone的Live Photo实际上是3秒的MOV视频+一张高分辨率JPEG
  2. 格式选择
    • .mov是最兼容的格式
    • .heic是苹果专用格式(需要额外库)
  3. 优化建议
    • 短视频长度建议2-3秒
    • 帧率建议8-15fps
    • 确保所有素材分辨率一致

使用静态图片+短视频合成Live Photo

首先需要环境的安装,我在这里建议你使用conda

pip install opencv-python numpy pillow imageio imageio-ffmpeg
# 如需HEIC格式支持
pip install pyheif pillow-heif
  • 上代码
import cv2
import numpy as np
from PIL import Image
import imageio
import osdef create_live_photo(static_image_path, video_path, output_path, duration=3.0):"""创建类似iPhone Live Photo的效果参数:static_image_path: 主静态图片路径video_path: 短视频路径(3秒左右)output_path: 输出路径(.mov或.heic)duration: 视频持续时间(秒)"""# 读取静态图片static_img = cv2.imread(static_image_path)if static_img is None:raise ValueError("无法加载静态图片")# 读取视频cap = cv2.VideoCapture(video_path)fps = cap.get(cv2.CAP_PROP_FPS)total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))# 调整视频长度target_frames = int(duration * fps)frame_step = max(1, total_frames // target_frames)# 处理视频帧processed_frames = []frame_count = 0while True:ret, frame = cap.read()if not ret:breakif frame_count % frame_step == 0:# 调整帧大小与静态图匹配frame = cv2.resize(frame, (static_img.shape[1], static_img.shape[0]))processed_frames.append(frame)frame_count += 1if len(processed_frames) >= target_frames:breakcap.release()# 确保有足够的帧if len(processed_frames) < 5:raise ValueError("视频太短或帧率太低")# 创建输出 - 两种格式选择if output_path.lower().endswith('.mov'):# 输出为QuickTime MOV格式(类似Live Photo)fourcc = cv2.VideoWriter_fourcc(*'avc1')out = cv2.VideoWriter(output_path, fourcc, fps, (static_img.shape[1], static_img.shape[0]))for frame in processed_frames:out.write(frame)out.release()elif output_path.lower().endswith('.heic') or output_path.lower().endswith('.jpg'):# 需要pyheif库处理HEIC格式try:import pyheiffrom pillow_heif import register_heif_openerregister_heif_opener()# 创建动态序列with imageio.get_writer(output_path, mode='I', fps=fps) as writer:# 先写入静态图片作为封面writer.append_data(cv2.cvtColor(static_img, cv2.COLOR_BGR2RGB))# 写入动态帧for frame in processed_frames:writer.append_data(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))except ImportError:raise ImportError("需要安装pyheif和pillow-heif库来处理HEIC格式")else:raise ValueError("不支持的输出格式,请使用.mov或.heic")# 使用示例
create_live_photo(static_image_path="main_photo.jpg",video_path="short_clip.mp4",output_path="output_live.mov",duration=3.0
)

在这里插入图片描述
在这里插入图片描述

import cv2
import numpy as np
from PIL import Image
import os
import subprocess
from typing import List
import logging# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)def create_live_photo(static_image_path: str,video_path: str,output_prefix: str,duration: float = 3.0,fade_duration: float = 0.5,with_audio: bool = True
) -> None:"""创建专业级Live Photo效果"""try:# 确保输出目录存在output_dir = os.path.dirname(output_prefix)if output_dir and not os.path.exists(output_dir):os.makedirs(output_dir, exist_ok=True)# 读取静态图片static_img = cv2.imread(static_image_path)if static_img is None:raise ValueError(f"无法加载静态图片: {static_image_path}")# 处理视频帧frames, fps = process_video_frames(video_path, static_img.shape, duration,fade_duration)# 输出MOV格式mov_path = f"{output_prefix}.mov"create_mov_file(frames, fps, static_img.shape[:2], mov_path)# 处理音频(如果需要)if with_audio:final_mov_path = f"{output_prefix}_with_audio.mov"if not extract_and_merge_audio(video_path, mov_path, final_mov_path):logger.warning("音频合并失败,将使用无音频版本")if os.path.exists(final_mov_path):os.remove(final_mov_path)os.rename(mov_path, final_mov_path)logger.info(f"最终输出文件: {final_mov_path}")except Exception as e:logger.error(f"程序运行出错: {str(e)}", exc_info=True)raisedef extract_and_merge_audio(source_video: str,video_without_audio: str,output_path: str
) -> bool:"""使用ffmpeg合并音频,返回是否成功"""try:# 首先检查源视频是否有音频流check_cmd = ['ffprobe','-v', 'error','-select_streams', 'a','-show_entries', 'stream=codec_type','-of', 'csv=p=0',source_video]result = subprocess.run(check_cmd, capture_output=True, text=True)if result.returncode != 0 or 'audio' not in result.stdout:logger.warning(f"源视频 {source_video} 没有音频流")return False# 合并音频cmd = ['ffmpeg','-y','-i', video_without_audio,'-i', source_video,'-c:v', 'copy','-c:a', 'aac','-map', '0:v:0','-map', '1:a:0?',  # 添加?表示可选'-shortest',output_path]subprocess.run(cmd, check=True)return Trueexcept subprocess.CalledProcessError as e:logger.error(f"FFmpeg错误: {e.stderr.decode('utf-8')}")return Falseexcept Exception as e:logger.error(f"音频合并失败: {str(e)}")return Falsedef create_heic_with_new_api(frames: List[np.ndarray],fps: float,cover_image: np.ndarray,output_path: str
) -> None:"""使用pillow_heif的新API生成HEIC"""try:# 确保目录存在os.makedirs(os.path.dirname(output_path), exist_ok=True)# 转换图像cover_pil = Image.fromarray(cv2.cvtColor(cover_image, cv2.COLOR_BGR2RGB))frame_pils = [Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) for frame in frames]# 使用HeifFile APIheif_file = pillow_heif.HeifFile()heif_file.add_from_pillow(cover_pil, quality=95)for frame in frame_pils:heif_file.add_from_pillow(frame, quality=85)# 保存文件with open(output_path, "wb") as f:heif_file.save(f, quality=90)if os.path.getsize(output_path) < 1024:raise ValueError("生成的HEIC文件过小")logger.info(f"成功生成HEIC文件: {output_path}")except Exception as e:if os.path.exists(output_path):os.remove(output_path)raise RuntimeError(f"HEIC生成失败: {str(e)}")def process_video_frames(video_path: str,target_size: tuple,duration: float,fade_duration: float
) -> tuple:"""处理视频帧并添加特效"""cap = cv2.VideoCapture(video_path)if not cap.isOpened():raise ValueError(f"无法打开视频文件: {video_path}")fps = cap.get(cv2.CAP_PROP_FPS)total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))# 计算需要提取的帧target_frames = int(duration * fps)frame_step = max(1, total_frames // target_frames)# 提取并处理帧processed_frames = []frame_count = 0while True:ret, frame = cap.read()if not ret:breakif frame_count % frame_step == 0:frame = cv2.resize(frame, (target_size[1], target_size[0]))processed_frames.append(frame)frame_count += 1if len(processed_frames) >= target_frames:breakcap.release()# 验证帧数if len(processed_frames) < 5:raise ValueError("视频太短或帧率太低,至少需要5帧")# 添加淡入淡出效果processed_frames = add_fade_effect(processed_frames, fade_duration, fps)return processed_frames, fpsdef add_fade_effect(frames: List[np.ndarray],fade_duration: float,fps: float
) -> List[np.ndarray]:"""添加专业级淡入淡出效果"""fade_frames = int(fade_duration * fps)# 淡入效果(开头)for i in range(min(fade_frames, len(frames))):alpha = i / fade_framesframes[i] = cv2.addWeighted(frames[i], alpha,frames[-1], 1-alpha,0)# 淡出效果(结尾)for i in range(1, min(fade_frames + 1, len(frames))):alpha = i / fade_framesframes[-i] = cv2.addWeighted(frames[-i], alpha,frames[0], 1-alpha,0)return framesdef create_mov_file(frames: List[np.ndarray],fps: float,frame_size: tuple,output_path: str
) -> None:"""创建高质量MOV文件"""# 确保目录存在os.makedirs(os.path.dirname(output_path), exist_ok=True)fourcc = cv2.VideoWriter_fourcc(*'avc1')out = cv2.VideoWriter(output_path, fourcc, fps, (frame_size[1], frame_size[0]))if not out.isOpened():raise ValueError(f"无法创建视频文件: {output_path}")for frame in frames:out.write(frame)out.release()logger.info(f"已生成MOV文件: {output_path}")def extract_and_merge_audio(source_video: str,video_without_audio: str,output_path: str
) -> None:"""使用ffmpeg合并音频"""try:# 确保目录存在os.makedirs(os.path.dirname(output_path), exist_ok=True)cmd = ['ffmpeg','-y','-i', video_without_audio,'-i', source_video,'-c:v', 'copy','-c:a', 'aac','-map', '0:v:0','-map', '1:a:0','-shortest',output_path]subprocess.run(cmd, check=True)logger.info(f"已合并音频到: {output_path}")except subprocess.CalledProcessError as e:raise RuntimeError(f"音频合并失败: {str(e)}")def test_heic_support():"""测试HEIC支持"""try:test_img = Image.new("RGB", (100, 100), "red")test_path = "heic_test.heic"# 使用新API测试heif_file = pillow_heif.HeifFile()heif_file.add_from_pillow(test_img)with open(test_path, "wb") as f:heif_file.save(f)if os.path.exists(test_path) and os.path.getsize(test_path) > 0:os.remove(test_path)logger.info("HEIC支持测试通过")return Trueraise RuntimeError("生成的测试文件无效")except Exception as e:raise RuntimeError(f"HEIC支持测试失败: {str(e)}")if __name__ == "__main__":try:logger.info("开始生成Live Photo...")create_live_photo(static_image_path="1.jpg",video_path="1.mp4",output_prefix="output/live_photo",duration=3.0,fade_duration=0.6,with_audio=True)logger.info("Live Photo生成完成!")except Exception as e:logger.error(f"程序终止: {str(e)}", exc_info=True)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构之排序

目录 排序的概念及引用 排序的概念 常见的排序算法 常见排序算法的实现 插入排序 1.直接插入排序&#xff1a; 2.希尔排序( 缩小增量排序 ) 选择排序 直接选择排序 堆排序 交换排序 冒泡排序 快速排序 1&#xff09;Hoare版 2&#xff09;挖坑法 3&#xff09;…

从“泛读”到“精读”:合合信息文档解析如何让大模型更懂复杂文档?

从“泛读”到“精读”&#xff1a;合合信息文档解析如何让大模型更懂复杂文档&#xff1f; 一、引言&#xff1a;破解文档“理解力”瓶颈二、核心功能&#xff1a;合合信息的“破局”亮点功能亮点1&#xff1a;复杂图表的高精度解析图表解析&#xff1a;为大模型装上精准“标尺…

NoSQL 数据库的适用场景与局限性分析

NoSQL(Not Only SQL)数据库是一类非关系型数据库,通过灵活的数据模型和分布式架构解决传统关系型数据库在扩展性、性能和数据多样性上的瓶颈。以下从技术特性、适用场景、不适用场景及行业实践展开分析: 一、NoSQL数据库的核心技术特性 四大数据模型 文档型:以JSON/BSON格…

Pycharm(七):几个简单案例

一.剪刀石头布 需求&#xff1a;和电脑玩剪刀石头布游戏 考察点&#xff1a;1.随机数&#xff1b;2.判断语句 import random # numrandom.randint(1,3) # print(num) # print(**30) #1.录入玩家手势 playerint(input(请输入手势&#xff1a;&#xff08;1.剪刀 2.石头 3&…

Reactive编程:什么是Reactive编程?Reactive编程思想

文章目录 **1. Reactive编程概述****1.1 什么是Reactive编程&#xff1f;****1.1.1 Reactive编程的定义****1.1.2 Reactive编程的历史****1.1.3 Reactive编程的应用场景****1.1.4 Reactive编程的优势** **1.2 Reactive编程的核心思想****1.2.1 响应式&#xff08;Reactive&…

【数学建模】动态规划算法(Dynamic Programming,简称DP)详解与应用

动态规划算法详解与应用 文章目录 动态规划算法详解与应用引言动态规划的基本概念动态规划的设计步骤经典动态规划问题1. 斐波那契数列2. 背包问题3. 最长公共子序列(LCS) 动态规划的优化技巧动态规划的应用领域总结 引言 动态规划(Dynamic Programming&#xff0c;简称DP)是一…

Linux基础之软硬链接

参考链接&#xff1a;https://baijiahao.baidu.com/s?id1770724291436944734&wfrspider&forpc 一、定义 1.硬链接&#xff08;Hard Link&#xff09; 硬链接是指多个文件名指向同一个物理文件的链接关系。它们在文件系统中具有相同的inode号&#xff08;索引节点号…

python每日十题(13)

一般把计算机完成一条指令所花费的时间称为一个指令周期。指令周期越短&#xff0c;指令执行就越快。本题答案为D选项。 顺序程序具有顺序性、封闭性和可再现性的特点&#xff0c;使得程序设计者能够控制程序执行的过程(包括执行顺序、执行时间&#xff09;&#xff0c;对程序执…

0328-内存图2

是否正确待定&#xff1a; Perso类 package com.qc.内存图2;public class Perso {public int age;public String name;public static int flag;public void m1() {}public static void m2() {}Overridepublic String toString() {return "Perso [age" age "…

Java 开发中的 AI 黑科技:如何用 AI 工具自动生成 Spring Boot 项目脚手架?

在 Java 开发领域&#xff0c;搭建 Spring Boot 项目脚手架是一项耗时且繁琐的工作。传统方式下&#xff0c;开发者需要手动配置各种依赖、编写基础代码&#xff0c;过程中稍有疏忽就可能导致配置错误&#xff0c;影响开发进度。如今&#xff0c;随着 AI 技术的迅猛发展&#x…

一文详解k8s体系架构知识

0.云原生 1.k8s概念 1. k8s集群的两种管理角色 Master&#xff1a;集群控制节点&#xff0c;负责具体命令的执行过程。master节点通常会占用一股独立的服务器&#xff08;高可用部署建议用3台服务器&#xff09;&#xff0c;是整个集群的首脑。 Master节点一组关键进程&#xf…

ubuntu下docker 安装 graylog 6.1

下载docker compose相关仓库 https://github.com/Graylog2/docker-compose 按readme所述&#xff0c;拷贝.env.example并重命名 .env 按.env中的说明创建密码和密钥 创建GRAYLOG_PASSWORD_SECRET 用: pwgen -N 1 -s 96 创建GRAYLOG_ROOT_PASSWORD_SHA2 用: echo -n yourpa…

创新驱动 智领未来丨中威电子全景展示高速公路数字化创新成果

在数字经济与新型基础设施建设深度融合的背景下&#xff0c;中国智慧交通产业正迎来前所未有的发展机遇。3月27日&#xff0c;第27届中国高速公路信息化大会暨技术产品博览会在青岛市红岛国际会议展览中心盛大开幕。作为高速公路信息化领域的创新先锋&#xff0c;中威电子&…

计算机期刊征稿 | 计算机-网络系统:物联网系统架构、物联网使能技术、物联网通信和网络协议、物联网服务和应用以及物联网的社会影响

IEEE Internet of Things Journal 学科领域&#xff1a; 计算机-网络系统 期刊类型&#xff1a; SCI/SSCI/AHCI 收录数据库&#xff1a; SCI(SCIE),EI ISSN&#xff1a; 2327-4662 中科院&#xff1a; 1区 影响因子&#xff1a; 8.2 JCR&#xff1a; Q1 IEEE Internet…

springBoot统一响应类型3.3版本

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

mapbox基础,加载popup弹出窗

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️popup 弹出窗 api1.3.1 ☘️构造函数1.…

MySQL基础语法1

目录 #1.创建和删除数据库 ​编辑#2.如果有lyt就删除,没有则创建一个新的lyt #3.切换到lyt数据库下 #4.创建数据表并设置列及其属性,name是关键词要用name包围 ​编辑 #5.删除数据表 #5.查看创建的student表 #6.向student表中添加数据,数据要与列名一一对应 #7.查询st…

【ESP32S3】esp32获取串口数据并通过http上传到前端

通过前面的学习&#xff08;前面没发过&#xff0c;因为其实就是跑它的demo&#xff09;了解到串口配置以及开启线程实现功能的工作流程&#xff0c;与此同时还有esp32作为STA节点&#xff0c;将数据通过http发送到服务器。 将这两者联合 其实是可以得到一个&#xff1a;esp32获…

CSS 美化页面(二)

一、CSS 属性详解 1、字体属性 (Font) 属性描述值示例简写属性font-family设置字体系列"Arial", sans-serif font: italic small-caps bold 16px/1.5 "Arial", sans-serif; font-size设置字体大小16px, 1.2em, 1remfont-weight设置字体粗细normal, bold,…

win32汇编环境,网络编程入门之十四

;win32汇编环境,网络编程入门之十四 ;在这一教程里&#xff0c;学习一下&#xff0c;如何得到网页的标题 ;这里需要理解一下html语言&#xff0c;<title> </title>标签对里面的内容即为网页的标题 ;其原理是把返回的字符串&#xff0c;按字节进行检查&#xff0c;发…