【学习/搬运】Aegisub-motion用blender进行追踪(侧重于blender追踪数据的导出)
-阿坪_
编辑于 2023年03月28日 02:08
blender

分享一篇原作者写于2016.10的aegisub+blender进行画面追踪的文章

原文章地址:https://subarashii-no-fansub.github.io/Subbing-Tutorial/Tracking-Motion/


你需要准备的:

  1. Aegisub本体

  2. aegisub-motion的插件

  3. blender(开源且免费的软件) 或者 mocha pro (win版)


在阅读本篇之前,请先熟悉aeg的具体操作

观看本篇前请观看以下教程:

  1. 无悠大佬的《Aegisub+Mocha制作追踪字幕教程,和违和感说再见》:网页链接​

  2. IHKK大佬的《Aegisub 追踪插件 motion mocha》: 网页链接​

通过以上两篇了解aeg+mocha如何进行追踪。

---如果你已经拥有win版的mocha pro,请按照以上视频步骤进行操作,两位up主都是非常好的大佬,足以让你学会这个插件的使用。

---如果你像我一样因为某些原因只能使用Blender,那么还请继续看下去。

总的来说就是,你按照无悠和IHKK的教程,安装好自动化脚本,在使用Mocha那步换成Blender,在Blender里进行运动追踪。在blender导出追踪数据的时候用到了下面的.py脚本插件,把数据转换成txt文本。再复制粘贴到aeg里,继续按照教程操作。


简单翻译一下下面这篇:

在Blender中进行运动追踪

有很多在用TypeSetting的人在用Mocha进行运动追踪

但由于我是Linux系统,上面这种方法不是最优解

所有我写了一篇教程教大家如何在免费而且开源的Blender里进行追踪。你也可以在Windows系统里用这种方法。

  • 安装 Aegisub Motion 插件

在github里下载,并且解压。把autoload和include里的文件放到你aeg的automation的对应文件夹里。

安装Blender和x264

  • 进行追踪

在Aegisub里

-你需要在aeg里加载一个视频

1.确定你要追踪的对象的第一帧和最后一帧,并且记录下相应的起始帧和结束帧。

2.结束第一步之后,第二步要配置一下Aegisub-Motion。

选好你在aeg里要进行追踪的那一行,在自动化里找到插件,选择trim setting。确认第一行是?video,第二行是x264。然后保存。最后打开trim,把你要追踪的这一小段剪下来。

3.在Blender里

打开运动跟踪界面进行跟踪(就不翻译了,请观看这个教程《IanHubert教你使用Blender进行运动跟踪和实景合成网页链接​

4.做完上面这些,我们需要装一个插件把运动数据输出到aegisub-motion里。只需要一次

-保存/下载 aae-exort.py (脚本信息我会扔在最后

-在Blender里 编辑->设置->插件->安装->找到aae-exort.py的位置并且安装它。最后激活名为Import-Export:Export:AE 6.0 Keyframe Data的插件(在插件界面点一下勾选的小方框)

5.现在可以导出了,选择导出里的AE 6.0 Keyframe Data。他会导出一个txt,复制粘贴txt里的内容到aeg自动化里motion插件的“应用”界面,粘贴数据到文本框里,运行之后你就可以得到你想要的了。(具体“应用”界面设置还请看无悠和IHKK的教程)

1p

2p

以上就是aeg+blender进行运动追踪的过程了。


aae-exort.py的脚本信息如下

代码块
Python
自动换行
复制代码
# Copyright (c) 2013, Martin Herkt
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.

bl_info = {
    "name": "Export: Adobe After Effects 6.0 Keyframe Data",
    "description": "Export motion tracking markers to Adobe After Effects 6.0 compatible files",
    "author": "Martin Herkt",
    "version": (0, 1, 4),
    "blender": (2, 80, 0),
    "location": "File > Export > Adobe After Effects 6.0 Keyframe Data",
    "warning": "",
    "category": "Import-Export",
    }

import bpy, mathutils, math

def write_files(prefix, context):
    scene = context.scene
    fps = scene.render.fps / scene.render.fps_base

    clipno = 0

    for clip in bpy.data.movieclips:
        trackno = 0

        for track in clip.tracking.tracks:
            with open("{0}_c{1:02d}_t{2:02d}.txt".format(prefix, clipno, trackno), "w") as f:

                frameno = clip.frame_start
                startarea = None
                startwidth = None
                startheight = None
                startrot = None

                data = []
				
                f.write("Adobe After Effects 6.0 Keyframe Data\r\n\r\n")
                f.write("\tUnits Per Second\t{0:.3f}\r\n".format(fps))
                f.write("\tSource Width\t{0}\r\n".format(clip.size[0]))
                f.write("\tSource Height\t{0}\r\n".format(clip.size[1]))
                f.write("\tSource Pixel Aspect Ratio\t1\r\n")
                f.write("\tComp Pixel Aspect Ratio\t1\r\n\r\n")

                while frameno <= clip.frame_duration:
                    marker = track.markers.find_frame(frameno)
                    frameno += 1

                    if not marker or marker.mute:
                        continue

                    coords = marker.co
                    corners = marker.pattern_corners

                    area = 0
                    width = math.sqrt((corners[1][0] - corners[0][0]) * (corners[1][0] - corners[0][0]) + (corners[1][1] - corners[0][1]) * (corners[1][1] - corners[0][1]))
                    height = math.sqrt((corners[3][0] - corners[0][0]) * (corners[3][0] - corners[0][0]) + (corners[3][1] - corners[0][1]) * (corners[3][1] - corners[0][1]))
                    for i in range(1,3):
                        x1 = corners[i][0] - corners[0][0]
                        y1 = corners[i][1] - corners[0][1]
                        x2 = corners[i+1][0] - corners[0][0]
                        y2 = corners[i+1][1] - corners[0][1]
                        area += x1 * y2 - x2 * y1

                    area = abs(area / 2)

                    if startarea == None:
                        startarea = area
                        
                    if startwidth == None:
                        startwidth = width
                    if startheight == None:
                        startheight = height

                    zoom = math.sqrt(area / startarea) * 100
                    
                    xscale = width / startwidth * 100
                    yscale = height / startheight * 100

                    p1 = mathutils.Vector(corners[0])
                    p2 = mathutils.Vector(corners[1])
                    mid = (p1 + p2) / 2
                    diff = mid - mathutils.Vector((0,0))

                    rotation = math.atan2(diff[0], diff[1]) * 180 / math.pi

                    if startrot == None:
                        startrot = rotation
                        rotation = 0
                    else:
                        rotation -= startrot - 360

                    x = coords[0] * clip.size[0]
                    y = (1 - coords[1]) * clip.size[1]

                    data.append([marker.frame, x, y, xscale, yscale, rotation])

                posline = "\t{0}\t{1:.3f}\t{2:.3f}\t0"
                scaleline = "\t{0}\t{1:.3f}\t{2:.3f}\t100"
                rotline = "\t{0}\t{1:.3f}"

                positions = "\r\n".join([posline.format(d[0], d[1], d[2]) for d in data]) + "\r\n\r\n"
                scales = "\r\n".join([scaleline.format(d[0], d[3], d[4]) for d in data]) + "\r\n\r\n"
                rotations = "\r\n".join([rotline.format(d[0], d[5]) for d in data]) + "\r\n\r\n"

                f.write("Anchor Point\r\n")
                f.write("\tFrame\tX pixels\tY pixels\tZ pixels\r\n")
                f.write(positions)

                f.write("Position\r\n")
                f.write("\tFrame\tX pixels\tY pixels\tZ pixels\r\n")
                f.write(positions)

                f.write("Scale\r\n")
                f.write("\tFrame\tX percent\tY percent\tZ percent\r\n")
                f.write(scales)

                f.write("Rotation\r\n")
                f.write("\tFrame Degrees\r\n")
                f.write(rotations)

                f.write("End of Keyframe Data\r\n")

                trackno += 1

            clipno += 1
    return {'FINISHED'}

from bpy_extras.io_utils import ExportHelper
from bpy.props import StringProperty

class ExportAFXKey(bpy.types.Operator, ExportHelper):
    """Export motion tracking markers to Adobe After Effects 6.0 compatible files"""
    bl_idname = "export.afxkey"
    bl_label = "Export to Adobe After Effects 6.0 Keyframe Data"
    filename_ext = ""
    filter_glob = StringProperty(default="*", options={'HIDDEN'})

    def execute(self, context):
        return write_files(self.filepath, context)

classes = (
    ExportAFXKey,
)

def menu_func_export(self, context):
    self.layout.operator(ExportAFXKey.bl_idname, text="Adobe After Effects 6.0 Keyframe Data")


def register():
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
    bpy.types.TOPBAR_MT_file_export.append(menu_func_export)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):
        unregister_class(cls)
    bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)

if __name__ == "__main__":
    register()
复制成功


写在最后:

  • aeg终究只是一个字幕软件,尽管开源让它变得十分强壮,但仍不足以替代任何专业性极强的视频制作软件。aeg的学习,只是让我比普通人多会那么一点点,无法和写脚本的大佬,以及和专业视频制作的大佬去“比较”。

  • 我对Aeg、Mocha、Blender都了解不多,有任何问题我也无法解决,还请自行寻找答案。

  • 软件、插件的下载地址,还请在上面三篇教程里自行寻找。

  • 希望这篇专栏对你有帮助。

2023.3.27