三维重建的步骤-1(采集的是环绕物体均匀旋转一圈的视频)

拿到手里的是环绕物体均匀旋转一圈的视频,这里使用Agisoft Metashape Professional进行处理。

一、抽帧

1、当下载的版本支持导入视频处理的话,就可以直接导入视频

这里的帧步骤计算可以参考下面这个方法:

复制代码
第1步:查视频帧率
右键点击你的视频文件 → 属性 → 详细信息 → 找到 "帧速率"(单位是 fps)
常见值:24、25、30、50、60
记下这个数字,比如 30

第2步:算总帧数
总帧数 = 视频总秒数 × 帧率
视频总秒数怎么算:
比如 2分33秒 = 2×60 + 33 = 153秒
举例:
153秒 × 30fps = 4590帧(总帧数)

第3步:算帧步骤
帧步骤 = 总帧数 ÷ 你想要的张数
举例:
4590帧 ÷ 37张 = 124
→ 在软件"帧步骤"里填 124

然后点击ok,软件就会自动进行抽帧。

2、当下载的版本或电脑本身不支持导入视频处理

使用代码将视频均分进行抽帧,别人建议的是抽37帧,但是我试过之后感觉效果不是很行,所以我这里抽了60帧,看着效果不好当然也可以自己调整(按角度或按时间,保证覆盖 360°)。参考代码如下,按照实际情况自行调整,自己不会调整可以问问ai有什么好建议:

python 复制代码
import cv2
import os
import sys
from pathlib import Path


def extract_frames(video_path, output_folder, num_frames=60):
    """
    从视频中抽取指定数量的帧
    """
    # 检查视频文件是否存在
    if not os.path.exists(video_path):
        print(f"错误: 视频文件 {video_path} 不存在")
        return

    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)

    # 获取视频文件名(不含扩展名)
    video_name = Path(video_path).stem

    # 打开视频
    cap = cv2.VideoCapture(video_path)

    if not cap.isOpened():
        print(f"错误: 无法打开视频文件 {video_path}")
        return

    # 获取视频总帧数
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    if total_frames == 0:
        print(f"错误: 视频 {video_name} 没有帧")
        cap.release()
        return

    print(f"视频 '{video_name}' 总帧数: {total_frames}")

    # 计算采样间隔
    if total_frames <= num_frames:
        frame_indices = list(range(total_frames))
    else:
        step = total_frames / num_frames
        frame_indices = [int(i * step) for i in range(num_frames)]

    extracted_count = 0

    for idx, frame_num in enumerate(frame_indices):
        cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
        ret, frame = cap.read()

        if not ret:
            print(f"警告: 无法读取第 {frame_num} 帧")
            continue

        frame_filename = f"{video_name}_{extracted_count + 1:06d}.jpg"
        frame_path = os.path.join(output_folder, frame_filename)
        cv2.imwrite(frame_path, frame)
        extracted_count += 1

        print(f"已提取: {extracted_count}/{num_frames} 帧", end='\r')

    cap.release()
    print(f"\n完成! 从 '{video_name}' 提取了 {extracted_count} 帧到 {output_folder}")
    return extracted_count


def process_folder(input_folder, output_base_folder, num_frames=60):
    """
    处理指定文件夹中的所有MP4视频
    """
    # 检查输入路径是文件还是文件夹
    if os.path.isfile(input_folder):
        # 如果是单个文件,直接处理
        if input_folder.lower().endswith('.mp4'):
            output_folder = os.path.join(output_base_folder, Path(input_folder).stem)
            print(f"处理视频: {Path(input_folder).stem}")
            extract_frames(input_folder, output_folder, num_frames)
        else:
            print(f"错误: {input_folder} 不是MP4文件")
        return

    # 以下是文件夹处理逻辑
    if not os.path.exists(input_folder):
        print(f"错误: 输入文件夹 {input_folder} 不存在")
        return

    video_files = []
    for file in os.listdir(input_folder):
        if file.lower().endswith('.mp4'):
            video_files.append(os.path.join(input_folder, file))

    if not video_files:
        print(f"在 {input_folder} 中没有找到MP4文件")
        return

    print(f"找到 {len(video_files)} 个MP4视频文件")

    os.makedirs(output_base_folder, exist_ok=True)

    for video_path in video_files:
        video_name = Path(video_path).stem
        output_folder = os.path.join(output_base_folder, video_name)
        print(f"\n处理视频: {video_name}")
        extract_frames(video_path, output_folder, num_frames)


def main():
    # 配置参数
    input_path = r"E:\cotton2026\test\VID_20260703_142950.mp4"  # 修改为你的视频文件夹路径,也可以指定单个文件
    output_folder = r"E:\cotton2026\pic"  # 修改为输出文件夹路径
    frames_to_extract = 60  # 要抽取的帧数

    # 如果提供了命令行参数
    if len(sys.argv) > 1:
        input_path = sys.argv[1]
    if len(sys.argv) > 2:
        output_folder = sys.argv[2]
    if len(sys.argv) > 3:
        try:
            frames_to_extract = int(sys.argv[3])
        except ValueError:
            print("帧数必须是整数,使用默认值60")

    print(f"输入路径: {input_path}")
    print(f"输出文件夹: {output_folder}")
    print(f"每视频抽帧数: {frames_to_extract}")

    process_folder(input_path, output_folder, frames_to_extract)


if __name__ == "__main__":
    main()

二、对齐照片

工作流程-对齐照片

对齐之后就会看到下面这样的样子:

首先需要确保一圈都有一帧,这样才没有缺失,但是也不能保证它能重建完全,还需后续的调整。

生成这种小点之后,可以删除掉不需要的点:

出现上面的这种,帧也圈红了的情况就不行,这个应该是需要留着的。

删完之后,点击"工作流程-生成模型"

三、生成点云

"工作流程-生成点云"

看着能生成密集点云就可以。

至少生成的点云保证能看到茎和叶。

这个结果可以在下面这个里面找:

上面的这个是我还没删除完全生成的点云,效果也不是很好,只是作为一个使用软件的举例,但是最好是试试其他的帧数还能不能更全面。

ok,这个就先这样。

相关推荐
qq_22589174661 小时前
基于Flask的空气质量监测与预测分析系统
开发语言·后端·python·信息可视化·django·flask
Dxy12393102162 小时前
Python XPath position() 完整使用指南,避坑合集(lxml适用)
前端·javascript·python
AndrewHZ2 小时前
图像处理入门009 | OpenCV 图像读取与显示:imread/imshow 全解析
图像处理·python·opencv·算法·计算机视觉·图像显示
卷无止境2 小时前
手写 SQL 在 Tortoise ORM 里到底能派上什么用场
后端·python·fastapi
龙虾PRO2 小时前
2026 DeepSeek Harness 部署完整教程:npx 一键启动至 Python SDK 全流程接入
开发语言·python
卷无止境2 小时前
FastAPI、Tortoise ORM 与 PostgreSQL 三件套 是否好用呢?
后端·python·fastapi
CTA量化套保11 小时前
近期零基础量化学习:先分阶段,再用 AI 检查缺口
人工智能·python
清水白石00811 小时前
Python 死锁排查全攻略:从线程卡死到锁依赖定位与工程化修复
linux·网络·python
Elias不吃糖12 小时前
Langfuse 入门:Trace、Prompt、Dataset、Experiment、Evaluator
前端·python·prompt·langfuse