前言
闲着无聊刷抖音的时候,看到作者制作了一个大米跳舞视频,看评论是把视频一张张截图打印出来,然后把图片放到玻璃下面,用大米一张张摆出来,最后合成视频。作为一名程序员,这也太麻烦了吧。于是站在专业的角度分析了一下代码实现的可行性。即
1.获取视频的每一帧
2.去掉每一帧的背景只保留人物
3.把每一帧透明部分填充大米图片,有颜色的部分填充白色
4.修改后的图片合成视频
如果能解决上面四个问题,那么就可以实现大米跳舞视频的制作了。
实现
- 第一个问题好说,利用canvas实时绘制视频帧,同时把canvas转成图片并下载。这里介绍一个简单的方法,使用ios快捷指令,如下:
2. 第二个问题用backgroundremover配合nodejs脚本实现,把视频帧的背景去掉,只保留人物
"use strict";
const { exit } = require('process');
const exec = require("child_process").exec;
let i = 1;
(function removebg() {
return new Promise(function (resolve, reject) {
if(i<2070){
var cmd = `backgroundremover -i "/Users/9240z/personal/handleVideo/teset/${i}.png" -o "/Users/9240z/personal/handleVideo/removebg/${i}.png"`;
exec(cmd, function (err, stdout, stderr) {
if (err) {
reject(err);
} else if (stderr.lenght > 0) {
reject(new Error(stderr.toString()));
} else {
return removebg()
}
});
i++
}else{
exit()
}
});
})();
- 第三个问题,先遍历每张图片的像素点,然后更改像素点的值,在合适的区域填充大米图片并下载(这一步需要根据图片的色调灵活处理)
- 遍历每张图片的像素点,然后更改像素点的值
var ImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
for (let i = 0; i < ImageData.data.length; i += 4) {
if (ImageData.data[i] !== 0 || ImageData.data[i + 1] !== 0 || ImageData.data[i + 2] !== 0) {
ImageData.data[i] = 0;
ImageData.data[i + 1] = 0;
ImageData.data[i + 2] = 0;
ImageData.data[i + 3] = 255;
} else if (ImageData.data[i] === 0 && ImageData.data[i + 1] === 0 && ImageData.data[i + 2] === 0) {
ImageData.data[i] = 255;
ImageData.data[i + 1] = 255;
ImageData.data[i + 2] = 255;
ImageData.data[i + 3] = 255;
}
}
ctx.putImageData(ImageData, 0, 0);
- 在白色区域填充大米图片
for (let j = 0; j < canvas.height / mHeight; j++) {
for (let i = 0; i < canvas.width / mWidth; i++) {
if (ImageData.data[i * mWidth * 4 + j * mHeight * canvas.width * 4] === 255 && ImageData.data[i * mWidth * 4 + j * mHeight * canvas.width * 4 + 1] === 255 && ImageData.data[i * mWidth * 4 + j * mHeight * canvas.width * 4 + 2] === 255) {
ctx.putImageData(mData[0].data, i * mWidth, j * mHeight, 0, 0, mWidth, mHeight);
}
}
}
- 下载
function downloadFile(content, fileName) { //下载base64图片
var base64ToBlob = function (code) {
let parts = code.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {
type: contentType
});
};
let aLink = document.createElement('a');
let blob = base64ToBlob(content); //new Blob([content]);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = fileName;
aLink.href = URL.createObjectURL(blob);
aLink.click();
};
- 第四个问题也可以借助ios快捷指令,如下:
这样你就得到了大米跳舞视频了,但是这个大米很生硬,于是换成了俄罗斯方块。效果如下