拿到手里的是环绕物体均匀旋转一圈的视频,这里使用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,这个就先这样。