最近产品有了一个天才的想法,做一个影集类似的功能,前面用图片制作影像、合成音频的操作都还挺顺利,但是到了给视频添加特效的时候就遇到了困难。
以前使用Pr或绘声绘影的时候就在想,这些软件背后的代码是什么样的呢?看来这一次我自己也可以亲手体验一番了。
这次主要遇到了两个问题:
- 在图片间插入过场动画 (这一篇将不会讲到这个)
- 在视频开头添加一个半透明的几何图案
图片合成
Google了一大票答案,很容易得出答案,OpenCV提供了<e>addWeighted</e>接口来做图片合成的事情。遂从手机上扒下来两张图来试试。
图一 ( 4032 x 1884 )
图二 ( 1126 x 1122 )
# video.py
import cv2
view1 = cv2.imread('view1.jpg')
view2 = cv2.imread('view2.jpg')
# addWeighted(src1, alpha, src2, beta, gamma, dst=None, dtype=None)
# alpha/beta 对应两张图片的透明度, 0是完全透明 1是完全不透明
view = cv2.addWeighted(view1, 0.7, view2, 0.5, 0)
cv2.imwrite('view.jpg', view)
运行一下,呀,报错了
Traceback (most recent call last):
File "video.py", line 270, in <module>
view = cv2.addWeighted(view1, 0.7, view2, 1, 0)
cv2.error: OpenCV(3.4.2) /io/opencv/modules/core/src/arithm.cpp:659: error: (-209:Sizes of input arguments do not match) The operation is neither 'array op array' (where arrays have the same size and the same number of channels), nor 'array op scalar', nor 'scalar op array' in function 'arithm_op'
通过报错信息,得知待合成图片的尺寸和<e>通道</e>必须要相等,这个尺寸好理解,通道是啥意思呢?
修改图片尺寸
修改一下上面的代码,使它变成
# video.py
import cv2
view1 = cv2.imread('view1.jpg')
width = int((4032 - 1126) / 2)
height = int((1884 - 1122) / 2)
# 暴力裁剪
# TODO: 这里有更好的裁剪方案
view1 = view1[height:height + 1122, width:width + 1126]
view2 = cv2.imread('view2.jpg')
view = cv2.addWeighted(view1, 0.7, view2, 0.5, 0)
cv2.imwrite('view.jpg', view)
成品
view.jpg ( 1126 x 1122 )
带Alpha通道的图片和普通照片的合成
合成完图片后,产品站在我身后,推了推眼镜,发现事情并不简单。
“给你一个框,给我放到图片里去”
背景实际上是透明的,这个黑色是在CSS中添加的,方便显示
我琢磨着,两张图的尺寸相同应该就能合成成一张图了吧!但是正如上文所提到的,图片中的通道数和也必须要一致才能调用<e>addWeighted</e>方法进行合成。
# video.py
import cv2
# 使用cv2.IMREAD_UNCHANGED 将会保留 PNG的Alpha通道
# 而直接读取PNG也可以进行图像混合,不过这种情况不在本次的讨论中
blank = cv2.imread('blank.png', cv2.IMREAD_UNCHANGED)
print(blank.shape) # (756, 567, 4)
view1 = cv2.imread('view1.jpg')
width = int((4032 - 756) / 2)
height = int((1884 - 567) / 2)
# 合适的裁剪
view1 = view1[height:height + 756, width:width + 567]
print(view1.shape) # (756, 567, 3)
由于通道不相等,两张图片并不能合成为一张图片,我们可以
- 去掉blank的alpha通道
- 为view1增加一条alpha通道
这里,我选择的是第二种方法
# video.py
import cv2
import numpy
# 实现细节
b_channel, g_channel, r_channel = cv2.split(view1)
# 添加alpha通道
alpha_channel = numpy.ones(b_channel.shape, dtype=b_channel.dtype) * 50
view1 = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))
print(view1) # (756, 567, 4)
成品
原文地址 >> https://code.evink.me/2018/11/post/mix-png-and-jpg-use-OpenCV/