人脸集锦视频制作教程

本实践教程旨在通过详细介绍智能媒体服务(IMS)的人脸搜索与视频剪辑功能,指导用户如何高效地创建和编辑人脸集锦。教程内容涵盖了智能媒资检索的基本操作、人脸搜索的API调用流程、Timeline配置以及高级模板的应用,帮助您快速上手并创建出高质量的人脸集锦视频。

前言

随着视频内容的爆炸性增长,如何快速定位并整理出关键人物出现的视频片段成为了一项重要的工作。人脸集锦作为一种有效的视频呈现方式,能够直观展示特定在不同场景下的活动,广泛应用于影视制作、新闻报道、社交娱乐等多个领域。本教程将通过智能媒体服务的强大功能,为您展示如何轻松实现这一目标。

应用场景示例

场景一:体育赛事中的运动员个人集锦(以马拉松为例)

在马拉松等体育赛事中,主办方或媒体机构常常需要制作运动员的精彩集锦视频,以展现运动员在比赛中的高光时刻,如冲刺瞬间、超越对手等。这些集锦视频对于赛事的宣传、回顾以及运动员的个人推广都具有重要意义。

场景二:游乐场游客的个人游玩精彩集锦

游乐场为了提升游客体验,常常希望为游客制作个性化的游玩片段集锦,记录他们在各类游乐项目中的精彩瞬间,如过山车上的尖叫、旋转木马上的欢笑等。这些集锦可以作为游客的纪念品,也可以用于游乐场的社交媒体宣传。

场景三:宴会花絮视频制作(以婚宴为例)

在婚庆行业中,婚礼视频是记录新人幸福瞬间、传承美好回忆的重要载体。无论是婚礼当天的花絮,还是新人的旅拍视频,都需要经过精心剪辑和编排,以展现新人的爱情故事和婚礼的浪漫氛围。

场景四:演唱会或音乐会的人脸粉丝集锦

在大型演唱会或音乐会上,主办方经常需要制作一段关于现场观众的人脸集锦,以展示观众的热情和参与度。这些人脸集锦可以通过大屏幕展示,增强现场氛围,也可以作为社交媒体宣传的素材。

除此之外,人脸集锦还可以广泛应用于多个领域,包括个人回忆录或家庭纪念视频、旅游宣传、企业年会或活动回顾互动等场景。

使用流程

前提条件

需安装IMS服务端SDK,详情请参见准备工作,如已安装请忽略本步骤。

步骤一:通过人脸检索截取视频片段信息

通过智能媒体服务的人脸检索功能对视频素材进行检索,以获取包含人脸的媒资片段信息。该步骤详见专属教程:如何对海量媒资进行人脸搜索

步骤二:对人脸视频片段进行剪辑

接下来将为您介绍两种人脸集锦视频的制作方式,内容将涵盖适用场景、适用步骤、SDK调用示例以及案例展示等几个方面。

方式一:通过时间线Timeline创作视频

适用场景
  • 对视频内容要求高度自定义:Timeline提供了高度的自定义能力,允许用户精确控制每一个剪辑点、特效、转场和音频。您能够根据自己的创意和需求,自由组合和调整素材,创作出独一无二的视频作品。

  • 对灵活性要求较高:Timeline剪辑不受模板限制,可以根据项目需求随时调整剪辑方案。无论是剪辑顺序、特效添加还是音频处理,都可以灵活应对,满足多样化的创作需求。

使用步骤

基于步骤一中人脸检索的结果,您可以使用SubmitMediaProducingJob - 提交剪辑合成作业中的Timeline配置说明来完成人脸集锦视频的剪辑,可以参考下文中的SDK调用示例。若有更高的剪辑需求,例如特效设置、转场效果、音频处理等高级剪辑配置,建议阅读和参考视频剪辑基础参数Timeline配置说明,以便更全面地了解如何利用视频剪辑功能实现人脸集锦视频的制作。

SDK调用示例

展开查看代码示例

package com.example;

import java.util.*;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.aliyun.ice20201109.Client;
import com.aliyun.ice20201109.models.*;
import com.aliyun.teaopenapi.models.Config;


/**
 *  需要maven引入二方包依赖:
 *   <dependency>
 *      <groupId>com.aliyun</groupId>
 *      <artifactId>ice20201109</artifactId>
 *      <version>2.3.0</version>
 *  </dependency>
 *  <dependency>
 *      <groupId>com.alibaba</groupId>
 *      <artifactId>fastjson</artifactId>
 *      <version>1.2.9</version>
 *  </dependency>
 */
public class SubmitFaceEditingJobService {

    static final String regionId = "cn-shanghai";
    static final String bucket = "<your-bucket>";

    /*** 基于人脸搜结果,提交人脸集锦剪辑任务 ****/
    public static void submitEditingJob(String mediaId, SearchMediaClipByFaceResponse response) throws Exception {

        Client iceClient = initClient();

        JSONArray intervals = buildIntervals(response);
        JSONObject editingTimeline = buildEditingTimeline(mediaId, intervals);

        // 提交普通剪辑任务
        SubmitMediaProducingJobRequest request1 = new SubmitMediaProducingJobRequest();
        request1.setTimeline(editingTimeline.toJSONString());
        request1.setOutputMediaTarget("oss-object");
        JSONObject outputConfig = new JSONObject();
        outputConfig.put("MediaURL",
                         "https://" + bucket + ".oss-" + regionId + "/testEditing/" + System.currentTimeMillis() + ".mp4");
        request1.setOutputMediaConfig(outputConfig.toJSONString());

        SubmitMediaProducingJobResponse response1 = iceClient.submitMediaProducingJob(request1);
        System.out.println("JobId: " + response1.getBody().getJobId());
    }

    public Client initClient() throws Exception {
        // 阿里云账号AccessKey拥有所有API的访问权限,建议您使用RAM用户进行API访问或日常运维。
        // 本示例以将AccessKey ID和 AccessKey Secret保存在环境变量为例说明。配置方法请参见:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials?spm=a2c4g.11186623.0.0.423350fbOTFdOB#2a38e5c14b4em
        com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client();

        Config config = new Config();
        config.setCredential(credentialClient);

        // 如需硬编码AccessKey ID和AccessKey Secret,代码如下,但强烈建议不要把AccessKey ID和AccessKey Secret保存到工程代码里,否则可能导致AccessKey泄露,威胁您账号下所有资源的安全。
        // config.accessKeyId = <第二步创建的AccessKey ID>;
        // config.accessKeySecret = <第二步创建的AccessKey Secret>;
        config.endpoint = "ice." + regionId + ".aliyuncs.com";
        config.regionId = regionId;
        return new Client(config);
    }

    public JSONArray buildIntervals(SearchMediaClipByFaceResponse response) {
        JSONArray intervals = new JSONArray();
        List<SearchMediaClipByFaceResponseBodyMediaClipListOccurrencesInfos> occurrencesInfos =
        response.getBody().getMediaClipList().get(0).getOccurrencesInfos();
        for (SearchMediaClipByFaceResponseBodyMediaClipListOccurrencesInfos occurrence: occurrencesInfos) {
            Float startTime = occurrence.getStartTime();
            Float endTime = occurrence.getEndTime();

            // 可自行调整筛选逻辑
            // 过滤小于2s的片段
            if (endTime - startTime < 2) {
                continue;
            }
            // 片段大于6s截断
            if (endTime - startTime > 6) {
                endTime = startTime + 6;
            }

            JSONObject interval = new JSONObject();
            interval.put("In", startTime);
            interval.put("Out", endTime);
            intervals.add(interval);
        }

        return intervals;
    }

    public JSONObject buildEditingTimeline(String mediaId, JSONArray intervals) {
        JSONObject timeline = new JSONObject();
        JSONArray videoTracks = new JSONArray();
        JSONObject videoTrack = new JSONObject();
        JSONArray videoTrackClips = new JSONArray();

        for (int i = 0; i < intervals.size(); i++) {
            JSONObject interval = intervals.getJSONObject(i);
            Float in = interval.getFloat("In");
            Float out = interval.getFloat("Out");
            JSONObject videoTrackClip = new JSONObject();
            videoTrackClip.put("MediaId", mediaId);
            videoTrackClip.put("In", in);
            videoTrackClip.put("Out", out);

            // 配置视频静音,按需调整
            JSONArray effects = new JSONArray();
            JSONObject effect = new JSONObject();
            effect.put("Type", "Volume");
            effect.put("Gain", 0);
            effects.add(effect);
            videoTrackClip.put("Effects", effects);
            videoTrackClips.add(videoTrackClip);
        }

        videoTrack.put("VideoTrackClips", videoTrackClips);
        videoTracks.add(videoTrack);

        // 配置音频轨,按需调整
        JSONArray audioTracks = new JSONArray();
        JSONObject audioTrack = new JSONObject();
        JSONArray audioTrackClips = new JSONArray();
        JSONObject audioTrackClip = new JSONObject();
        // 需使用您名下的音频媒资
        audioTrackClip.put("MediaId", "92796460cd4571ed91c6e7e7c45b****");
        audioTrackClip.put("LoopMode", true);
        audioTrackClips.add(audioTrackClip);
        audioTrack.put("AudioTrackClips", audioTrackClips);
        audioTracks.add(audioTrack);

        timeline.put("VideoTracks", videoTracks);
        timeline.put("AudioTracks", audioTracks);

        return timeline;
    }

}

展开查看TimeLine参数示例

{
  "VideoTracks": [
    {
      "VideoTrackClips": [
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 54.106018,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 56.126015
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 271.47302,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 277.393
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 326.03903,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 331.959
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 372.20602,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 375.126
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 383.03903,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 388.959
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 581.339,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 587.25903
        },
        {
          "MediaId": "b5a003f0cd3f71ed919fe7e7c45b****",
          "In": 602.339,
          "Effects": [
            {
              "Type": "Volume",
              "Gain": 0
            }
          ],
          "Out": 607.293
        }
      ]
    }
  ],
  "AudioTracks": [
    {
      "AudioTrackClips": [
        {
          "LoopMode": true,
          "MediaId": "icepublic-0c4475c3936f3a8743850f2da942ceee"
        }
      ]
    }
  ]
}

方式二:通过高级模板创作视频

适用场景
  • 创作快速高效:高级模板已经预设了剪辑方案、特效和转场等,用户只需将自己的素材填充到模板中即可完成视频制作,大大节省了制作时间,使得即便是没有视频制作经验的用户也能轻松上手。

  • 期望视频风格统一:模板保证了视频作品风格的统一性和专业性,使得非专业用户也能制作出看起来专业且吸引人的视频。

使用步骤

基于步骤一中人脸检索的结果,您可以使用SubmitMediaProducingJob - 提交剪辑合成作业中的“高级模板+ClipsParam”来完成人脸集锦视频的剪辑,可以参考下文中的SDK调用示例,该示例以公共模板IceSys_VETemplate_s100241为例,根据人脸检索的结果配置视频切片。如果您需要自创的高级模板来进行人脸集锦的视频创作,建议阅读和参考操作指南-如何使用高级模板

SDK调用示例

展开查看代码示例

package com.example;

import java.util.*;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import com.aliyun.ice20201109.Client;
import com.aliyun.ice20201109.models.*;
import com.aliyun.teaopenapi.models.Config;


/**
 *  需要maven引入二方包依赖:
 *   <dependency>
 *      <groupId>com.aliyun</groupId>
 *      <artifactId>ice20201109</artifactId>
 *      <version>2.3.0</version>
 *  </dependency>
 *  <dependency>
 *      <groupId>com.alibaba</groupId>
 *      <artifactId>fastjson</artifactId>
 *      <version>1.2.9</version>
 *  </dependency>
 */
public class SubmitFaceEditingJobService {

    static final String regionId = "cn-shanghai";
    static final String bucket = "<your-bucket>";

    /*** 基于人脸搜结果,提交人脸集锦剪辑任务 ****/
    public static void submitEditingJob(String mediaId, SearchMediaClipByFaceResponse response) throws Exception {

        Client iceClient = initClient();
        JSONArray intervals = buildIntervals(response);

        // 提交高级模板任务,以公共模板IceSys_VETemplate_s100241为例,根据人脸结果配置视频切片
        JSONObject clipParams = buildClipParams(mediaId, intervals);

        SubmitMediaProducingJobRequest request2 = new SubmitMediaProducingJobRequest();
        request2.setTemplateId("IceSys_VETemplate_s100241");
        request2.setClipsParam(clipParams.toJSONString());
        request2.setOutputMediaTarget("oss-object");
        outputConfig = new JSONObject();
        outputConfig.put("MediaURL",
                         "https://" + bucket + ".oss-" + regionId + "/testTemplate/" + System.currentTimeMillis() + ".mp4");
        request2.setOutputMediaConfig(outputConfig.toJSONString());

        SubmitMediaProducingJobResponse response2 = iceClient.submitMediaProducingJob(request2);
        System.out.println("JobId: " + response2.getBody().getJobId());
    }

    public Client initClient() throws Exception {
        // 阿里云账号AccessKey拥有所有API的访问权限,建议您使用RAM用户进行API访问或日常运维。
        // 本示例以将AccessKey ID和 AccessKey Secret保存在环境变量为例说明。配置方法请参见:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials?spm=a2c4g.11186623.0.0.423350fbOTFdOB#2a38e5c14b4em
        com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client();

        Config config = new Config();
        config.setCredential(credentialClient);

        // 如需硬编码AccessKey ID和AccessKey Secret,代码如下,但强烈建议不要把AccessKey ID和AccessKey Secret保存到工程代码里,否则可能导致AccessKey泄露,威胁您账号下所有资源的安全。
        // config.accessKeyId = <第二步创建的AccessKey ID>;
        // config.accessKeySecret = <第二步创建的AccessKey Secret>;
        config.endpoint = "ice." + regionId + ".aliyuncs.com";
        config.regionId = regionId;
        return new Client(config);
    }

    public JSONArray buildIntervals(SearchMediaClipByFaceResponse response) {
        JSONArray intervals = new JSONArray();
        List<SearchMediaClipByFaceResponseBodyMediaClipListOccurrencesInfos> occurrencesInfos =
        response.getBody().getMediaClipList().get(0).getOccurrencesInfos();
        for (SearchMediaClipByFaceResponseBodyMediaClipListOccurrencesInfos occurrence: occurrencesInfos) {
            Float startTime = occurrence.getStartTime();
            Float endTime = occurrence.getEndTime();

            // 可自行调整筛选逻辑
            // 过滤小于2s的片段
            if (endTime - startTime < 2) {
                continue;
            }
            // 片段大于6s截断
            if (endTime - startTime > 6) {
                endTime = startTime + 6;
            }

            JSONObject interval = new JSONObject();
            interval.put("In", startTime);
            interval.put("Out", endTime);
            intervals.add(interval);
        }

        return intervals;
    }

    public JSONObject buildClipParams(String mediaId, JSONArray intervals) {
        JSONObject clipParams = new JSONObject();
        for (int i = 0; i< intervals.size(); i++) {
            JSONObject interval = intervals.getJSONObject(i);
            Float in = interval.getFloat("In");
            Float out = interval.getFloat("Out");
            clipParams.put("Media" + i, mediaId);
            clipParams.put("Media" + i + ".clip_start", in);
        }
        return clipParams;
    }


}

展开查看clipParams参数示例

{
	"Media0": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media0.clip_start": 54.066017,
	"Media1": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media1.clip_start": 67.33301,
	"Media2": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media2.clip_start": 271.47302,
	"Media3": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media3.clip_start": 326.03903,
	"Media4": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media4.clip_start": 372.20602,
	"Media5": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media5.clip_start": 383.03903,
	"Media6": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media6.clip_start": 581.339,
	"Media7": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media7.clip_start": 587.25903,
	"Media8": "b5a003f0cd3f71ed919fe7e7c45b****",
	"Media8.clip_start": 602.339
}

相关参考文档