爆肝!用Python制作抖音爆款视频!

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爆肝!用Python制作抖音爆款视频!相关的知识,希望对你有一定的参考价值。

参考技术A 前几天我在抖音上刷到一个慢慢变老的视频,播放量居然有 30W+,当时就在想这视频 Python 可不可以做?经过一番搜索,我找到了腾讯云的人脸年龄变化 API,上面介绍说只要用户上传一张人脸图片,基于人脸编辑与生成算法,就可以输出一张人脸变老或变年轻的图片,并支持实现人脸不同年龄的变化。

第一步,在注册账号之后,打开 API 密钥管理页面( https://console.cloud.tencent.com/cam/capi )获取到 SecretId 和 SecretKey。

第二步,安装腾讯云的 SDK

在人脸年龄变化 API 中有一个 AgeInfo 参数,它包含了 Age 和 FaceRect 两个属性,其中 FaceRect 属性必须填人脸在照片中基于左上角的 X、Y 坐标和人脸的高度与宽度。所以先要调用人脸检测与分析 API 得到这些数据。

下面的示例图是在百度图片中截取的。

示例结果

在上面已经得到了各个人脸的 X、Y、Width、Height 属性,加上变老的年龄 Age,就可以请求年龄变化 API 了。

这里需要注意的是 models 模块,人脸检测 models 模块是在 tencentcloud.iai.v20200303 包下,人脸年龄变化的 models 是在 tencentcloud.ft.v20200304 下,两个 models 模块并不兼容。

示例结果

最后的视频可以将图片一张一张插入 PPT 幻灯片,点击保存为视频。

用 Python 制作抖音素材,下一个 30W+ 播放量等着你。

用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐

码个蛋(codeegg)第 690 次推文

作者: 二娃_

原文: https://juejin.im/post/5cb53e93e51d456e55623b07


起源


周末在家刷抖音的时候看到了这款网红时钟,都是 Android 平台的,想来何不自己实现一把。看抖音里大家发的视频,这款时钟基本分两类,一类是展示在「壁纸」上,一类是展示在「锁屏」上。


  • 展示到「壁纸」通过 LiveWallPaper 相关 API 可以做到,这也是本专题要实现的方式。

  • 展示到「锁屏」目测是使用各 ROM 厂商的相关 API,开发锁屏主题可以做到。


然而实现两者的基础便是拿起 Canvas Paint 等把它绘制出来,所以「上篇」我先用自定义 View 的方式把时钟画出来,在 Activity 中展示效果。「下篇」的时候再把该 View 结合 LiveWallPaper 设置到壁纸。抖音爆红文字时钟项目源码(https://github.com/drawf/SourceSet/blob/master/app/src/main/java/me/erwa/sourceset/view/TextClockView.kt


思考分析


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


这是我当时截图下来的参考,先分析下涉及到的元素及样式表现:


  1. 「圆中信息」圆中心的数字时间 + 数字日期 + 文字星期几,始终为白色

  2. 「时圈」一圈文字小时,一点、二点.. 十二点,当前点数为白色,其它为白色 + 透明度,如图中十点就是白色。

  3. 「分圈」一圈文字分钟,一分、二分.. 五十九分,六十分显示为空,同理,当前分钟为白色,其它白色 + 透明度。

  4. 「秒圈」一圈文字秒,一秒、二秒.. 五十九秒,六十秒显示为空,也是同理。


然后分析下动画效果:


  1. 每秒钟「秒圈」走一下,这一下的旋转角度为 360°/60=6°,并且走这一下的时候有个线性旋转过去的动画效果。

  2. 每分钟「分圈」走一下,旋转角度和动画效果跟「秒圈」相同。

  3. 每小时「时圈」走一下,旋转角度为 360°/12=30°,动画效果同上。


绘制静态图


1. 画布准备

基本是将画布背景填充黑色,然后将画布的原点移动到 View 大小的中心,这样方便思维理解与绘制。


//在onLayout方法中计算View去除padding后的宽高
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
mWidth = (measuredWidth - paddingLeft - paddingRight).toFloat()
mHeight = (measuredHeight - paddingTop - paddingBottom).toFloat()

//后文会涉及到
//统一用View宽度*系数来处理大小,这样可以联动适配样式
mHourR = mWidth * 0.143f
mMinuteR = mWidth * 0.35f
mSecondR = mWidth * 0.35f
}

//在onDraw方法将画布原点平移到中心位置
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (canvas == null) return
canvas.drawColor(Color.BLACK)//填充背景
canvas.save()
canvas.translate(mWidth / 2, mHeight / 2)//原点移动到中心

//绘制各元件,后文会涉及到
drawCenterInfo(canvas)
drawHour(canvas, mHourDeg)
drawMinute(canvas, mMinuteDeg)
drawSecond(canvas, mSecondDeg)

//从原点处向右画一条辅助线,之后要处理文字与x轴的对齐问题,稍后再说
canvas.drawLine(0f, 0f, mWidth, 0f, mHelperPaint)

canvas.restore()
}

2. 画「圆中信息」

经过第一步,可以在 AS 的 Xml Preview 中看到一屏黑色 + 一条从屏幕中心到右边界的红线。(一眼望去,还是挺美的)


/**
* 绘制圆中信息
*/

private fun drawCenterInfo(canvas: Canvas) {
Calendar.getInstance().run {
//绘制数字时间
val hour = get(Calendar.HOUR_OF_DAY)
val minute = get(Calendar.MINUTE)

mPaint.textSize = mHourR * 0.4f//字体大小根据「时圈」半径来计算
mPaint.alpha = 255
mPaint.textAlign = Paint.Align.CENTER
canvas.drawText("$hour:$minute", 0f, mPaint.getBottomedY(), mPaint)

//绘制月份、星期
val month = (this.get(Calendar.MONTH) + 1).let {
if (it < 10) "0$it" else "$it"
}
val day = this.get(Calendar.DAY_OF_MONTH)
val dayOfWeek = (get(Calendar.DAY_OF_WEEK) - 1).toText()//私有的扩展方法,将Int数字转换为 一、十一、二十等,后文绘制三个文字圈都会用该方法

mPaint.textSize = mHourR * 0.16f//字体大小根据「时圈」半径来计算
mPaint.alpha = 255
mPaint.textAlign = Paint.Align.CENTER
canvas.drawText("$month.$day 星期$dayOfWeek", 0f, mPaint.getTopedY(), mPaint)
}
}

/**
* 扩展获取绘制文字时在x轴上 垂直居中的y坐标
*/

private fun Paint.getCenteredY(): Float {
return this.fontSpacing / 2 - this.fontMetrics.bottom
}

/**
* 扩展获取绘制文字时在x轴上 贴紧x轴的上边缘的y坐标
*/

private fun Paint.getBottomedY(): Float {
return -this.fontMetrics.bottom
}

/**
* 扩展获取绘制文字时在x轴上 贴近x轴的下边缘的y坐标
*/

private fun Paint.getToppedY(): Float {
return -this.fontMetrics.ascent
}


其中要说一下 mPaint.getBottomedY() mPaint.getToppedY(), 这是两个扩展到 Paint 画笔上的两个 kotlin 方法。他们的作用是为了处理绘制文字时与 x 轴的对齐关系。canvas.drawText() 方法的第三个参数是 y 坐标,但这个指的是文字的 Baseline 的 y 坐标 , 所以写了工具方法来得到矫正后的 y 坐标。(这里就只抛出这个点吧,具体实现原理可先查阅 Paint 类的相关 API 就会明白,文末会贴出我拜读的文章链接)


拿绘制数字时间举例,展示下不同效果:


mPaint.getBottomedY() 替换成 0f(y 坐标为 0,就是文字的 Baseline 坐标为 0),文字使用 15:67 abc jqk,可以看到两者区别。(红线就是前文画的那条好美的辅助线)


canvas.drawText("15:67 测试文字 abc jqk", 0f, 0f, mPaint)

canvas.drawText("15:67 测试文字 abc jqk", 0f, mPaint.getBottomedY(), mPaint)


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


ok,「圆中信息」绘制后长这个样子:


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


3. 画「时圈」「分圈」「秒圈」

绘制思路就是 for 循环 12 次,每次将画布旋转 30° 乘以 i,然后在指定位置绘制文字,12 次后刚好一个圆圈。


该方法接收一个 degrees: Float 参数,是控制「时圈」整体的旋转的,后文就是不断改变该值,而产生动画效果的。并且因为三个圈的动画方向都是逆时针,所以这个 degrees 是个始终会是个负数。


/**
* 绘制小时
*/

private fun drawHour(canvas: Canvas, degrees: Float) {
mPaint.textSize = mHourR * 0.16f

//处理整体旋转
canvas.save()
canvas.rotate(degrees)

for (i in 0 until 12) {
canvas.save()

//从x轴开始旋转,每30°绘制一下「几点」,12次就画完了「时圈」
val iDeg = 360 / 12f * i
canvas.rotate(iDeg)

//这里处理当前时间点的透明度,因为degrees控制整体逆时针旋转
//iDeg控制绘制时顺时针,所以两者和为0时,刚好在x正半轴上,也就是起始绘制位置。
mPaint.alpha = if (iDeg + degrees == 0f) 255 else (0.6f * 255).toInt()
mPaint.textAlign = Paint.Align.LEFT

canvas.drawText("${(i + 1).toText()}点", mHourR, mPaint.getCenteredY(), mPaint)
canvas.restore()
}

canvas.restore()
}


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


同理绘制「分圈」「秒圈」

/**
* 绘制分钟
*/

private fun drawMinute(canvas: Canvas, degrees: Float) {
mPaint.textSize = mHourR * 0.16f

//处理整体旋转
canvas.save()
canvas.rotate(degrees)

for (i in 0 until 60) {
canvas.save()

val iDeg = 360 / 60f * i
canvas.rotate(iDeg)

mPaint.alpha = if (iDeg + degrees == 0f) 255 else (0.6f * 255).toInt()
mPaint.textAlign = Paint.Align.RIGHT

if (i < 59) {
canvas.drawText("${(i + 1).toText()}分", mMinuteR, mPaint.getCenteredY(), mPaint)
}
canvas.restore()
}

canvas.restore()
}

/**
* 绘制秒
*/

private fun drawSecond(canvas: Canvas, degrees: Float) {
mPaint.textSize = mHourR * 0.16f

//处理整体旋转
canvas.save()
canvas.rotate(degrees)

for (i in 0 until 60) {
canvas.save()

val iDeg = 360 / 60f * i
canvas.rotate(iDeg)

mPaint.alpha = if (iDeg + degrees == 0f) 255 else (0.6f * 255).toInt()
mPaint.textAlign = Paint.Align.LEFT

if (i < 59) {
canvas.drawText("${(i + 1).toText()}秒", mSecondR, mPaint.getCenteredY(), mPaint)
}
canvas.restore()
}

canvas.restore()
}


DuangDuang!!效果出来啦~


用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


4. 让时钟转起来

那么如何可以让时钟转起来呢?我们再看一下 onDraw() 中的代码,绘制三个圈的方法都会接收一个相应的 degrees: Float 参数,这个是控制一个圈的整体旋转的,而且要逆时针转,所以始终得是负数。


这样一来就好说了,只要控制这三个角度变化,就能让时钟动起来。


override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
...//省略

//绘制各元件,后文会涉及到
drawCenterInfo(canvas)
drawHour(canvas, mHourDeg)
drawMinute(canvas, mMinuteDeg)
drawSecond(canvas, mSecondDeg)

...//省略
}


那么首先定义三个角度的全局变量,并把他们与实际的时间关联起来,然后每隔一秒触发一次 View 的重绘即可。

//定义三个角度的全局变量
private var mHourDeg: Float by Delegates.notNull()
private var mMinuteDeg: Float by Delegates.notNull()
private var mSecondDeg: Float by Delegates.notNull()

/**
* 绘制方法
*/

fun doInvalidate() {
Calendar.getInstance().run {
val hour = get(Calendar.HOUR)
val minute = get(Calendar.MINUTE)
val second = get(Calendar.SECOND)

//这里将三个角度与实际时间关联起来,当前几点几分几秒,就把相应的圈逆时针旋转多少
mHourDeg = -360 / 12f * (hour - 1)
mMinuteDeg = -360 / 60f * (minute - 1)
mSecondDeg = -360 / 60f * (second - 1)

invalidate()
}
}


然后只需在 Activity 中使用 timer 每秒钟刷新一次 View 即可。效果如下图,会发现转是转起来的,但是却每秒一跳。再看一下咱们当时的分析:

每秒钟「秒圈」走一下,这一下的旋转角度为 360°/60=6°,并且走这一下的时候有个线性旋转过去的动画效果。

所以是还差一个线性旋转的效果。


//Activity中的代码
private var mTimer: Timer? = null
private fun caseTextClock()
{
setContentView(R.layout.activity_stage_text_clock)

mTimer = timer(period = 1000) {
runOnUiThread {
stage_textClock.doInvalidate()
}
}

}

override fun onDestroy() {
super.onDestroy()
mTimer?.cancel()
}

用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐


5. 让时钟转的优雅点

基于我们已经知道了,时钟动起来的本质就是在一段时间内(比如 150ms)不断的改变参数 degrees: Float 的值并触发重绘方法,这样就产生了人眼看到的动画效果。


所以,我们想让「秒圈」(三个圈的代表)转的更线性更优雅一点,就可以在要开始绘制新的一秒的时候,在前 150ms 线性的旋转 6°。


init {
//处理动画,声明全局的处理器
mAnimator = ValueAnimator.ofFloat(6f, 0f)//由6降到1
mAnimator.duration = 150
mAnimator.interpolator = LinearInterpolator()//插值器设为线性
doInvalidate()
}

/**
* 开始绘制
*/

fun doInvalidate() {
Calendar.getInstance().run {
val hour = get(Calendar.HOUR)
val minute = get(Calendar.MINUTE)
val second = get(Calendar.SECOND)

mHourDeg = -360 / 12f * (hour - 1)
mMinuteDeg = -360 / 60f * (minute - 1)
mSecondDeg = -360 / 60f * (second - 1)

//记录当前角度,然后让秒圈线性的旋转6°
val hd = mHourDeg
val md = mMinuteDeg
val sd = mSecondDeg

//处理动画
mAnimator.removeAllUpdateListeners()//需要移除先前的监听
mAnimator.addUpdateListener {
val av = (it.animatedValue as Float)

if (minute == 0 && second == 0) {
mHourDeg = hd + av * 5//时圈旋转角度是分秒的5倍,线性的旋转30°
}

if (second == 0) {
mMinuteDeg = md + av//线性的旋转6°
}

mSecondDeg = sd + av//线性的旋转6°

invalidate()
}
mAnimator.start()
}
}


就用这美丽优雅的时钟结尾吧~



文末


个人能力有限,如有不正之处欢迎大家批评指出,我会虚心接受并第一时间修改,以不误导大家。写作时参考以下文章,特别感谢。


  • 自定义 View 1-3 drawText() 文字的绘制(https://hencoder.com/ui-1-3/

  • Android关于Paint你所知道的和不知道的一切(https://juejin.im/post/5be29c206fb9a049ab0d1663


近期文章:






日问题:

你写过什么酷炫的效果,悄悄后台告诉我?


专属升级社区: 

以上是关于爆肝!用Python制作抖音爆款视频!的主要内容,如果未能解决你的问题,请参考以下文章

斗音视频制作技巧和分享技巧

用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐

百万点赞怎么来?Python批量制作抖音的卡点视频原来这么简单!

装逼篇 | Python制作抖音超火的九宫格视频

用Python制作视频

请教下制作抖音纯文字图片视频还有一边讲话配音要用到哪些软件?