We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文为博客迁移过来,原文链接: 视频转字符画:2018-7-30
先看一下效果,原视频是这样的
我们要实现的效果是这样子的,之所以找这个视频是因为...这个视频和背景的对比度比较高做出来比较有辨识度,没有其他的意思 ( *・ω・)✄╰ひ╯
先把视频丢到部门技术群问有没有关键字,给了一个keyword 图片转字符串 于是照着这个思路去gayhub找资源拼乐高!
input[type="file"]
URL.createObjectURL
ctx.drawImage
ctx.getImageData
ctx.fillText
video.currentTime
既然大概的思路已经理清,接下来就是具体的编码,把想法写出来的过程
首先我们先确定下html需要哪些元素
大概是长这样:
<input type="file" id="inputFile" accept=".mp4" /> <canvas id="canvasShow"></canvas> <video id="video"></video>
接下来js文件,我们要先对 input 绑定个监听事件,拿到文件url之后设置给video 这里要注意两点,一个是 url用完不用的话,用 URL.revokeObjectURL 释放资源; 一个是我们这里用了 await 在domVide.onCanplay之前不做任何操作,防止视频没有加载完就操作,有黑屏风险。
url
URL.revokeObjectURL
await
如果对 es6、es7、es8不熟悉的小伙伴要去补一下了~ 现在基本不会这些基本语法都看不懂demo= = 附上阮一峰老师的ES6教程,又想起面试被问ES7有什么新特性 简直是*了狗
domInput.addEventListener('change', async({target: {files }})=> { const file = files[0]; const url = URL.createObjectURL(file); domVideo.src = urlrl; await new Promise(res=> domVideo.addEventListener('canplay', res)); // next ====> handleVideoInit() });
拿到视频之后,我们要把当前这一个时刻的图像渲染到canvas里面 先用ctx.drawImage(video, 0, 0, width, height) 把video dom当前屏渲染进canvas 再用ctx.getImageData(0, 0, width, height) 获取图片的色值来做处理 可以通过调整 img2Text 来选择渲染出来的图片是想要怎样的(由哪些字符组成等等) 比如把 textList改为 ['Aa', 'Bv', 'Cc', 'Dd', '#', '&', '@', '$', '*', '?', ';', '^', '·', '·', '·', '·'],辨识度会高一点
ctx.drawImage(video, 0, 0, width, height)
ctx.getImageData(0, 0, width, height)
img2Text
/* domVide => video元素 size => 存放video等元素的长宽 canvasVideo => 存放video当前的图像的canvas canvasShow => 存放最后展示效果的canvas */ const size = {w: 0, h: 0}; const canvasVideo = document.createElement('canvas'); function handleVideoInit() { domVideo.currentTime = 0; size.w = domVideo.width = canvasVideo.width = canvasShow.width = domVideo.videoWidth * .5; size.h = domVideo.height = canvasVideo.height = canvasShow.height = domVideo.videoHeight * .5; video2Img(); } function video2Img() { const { w, h } = size; ctxVideo.drawImage(domVideo, 0, 0, w, h); const { data } = ctxVideo.getImageData(0, 0, w, h); ctxShow.clearRect(0, 0, w, h); for (let _h= 0; _h< h; _h+= 8) { for (let _w= 0; _w< w; _w+= 8) { const index = (_w + w * _h) * 4; const r = data[index + 0]; const g = data[index + 1]; const b = data[index + 2]; const gray = .299 * r + .587 * g + .114 * b; ctxShow.fillText(img2Text(gray), _w, _h + 8); } } } function img2Text(g) { const i = g % 16 === 0 ? parseInt(g / 16) - 1 : parseInt(g/ 16); return ['#', '&', '@', '%', '$', 'w', '*', '+', 'o', '?', '!', ';', '^', ',', '.', ' '][i]; }
到这一步,其实已经实现了把一张图片变为字符填充图了,剩下的工作无非就是把视频变成一张张的图片,然后重复执行这些逻辑
我们改一下 video2Img 函数,将其实现为能持续调用的形式, 再添加一个函数 clear 用来清理垃圾
clear
这里用到的是 window.requestAnimationFrame 去持续调用
function video2Img({ timePoint= 0, curT= Date.now(), prevT= Date.now(), prevInterval, }) { const { w, h } = size; ctxVideo.drawImage(domVideo, 0, 0, w, h); drawOnce(); let _interval = Math.max((curT - prevT), 16) / 1000; if (curT - prevT !== 0) _interval -= prevInterval; await new Promise(res=> setTimeout(res, _interval*1000)); const nextTimePoint = _interval + timePoint; if (nextTimePoint > domVideo.duration) return clear(); tId = window.requestAnimationFrame(()=> video2Img({ timePoint: nextTimePoint, prevT: curT, curT: Date.now(), prevInterval: _interval, })); } function drawOnce() { const { data } = ctxVideo.getImageData(0, 0, w, h); ctxShow.clearRect(0, 0, w, h); for (let _h= 0; _h< h; _h+= 8) { for (let _w= 0; _w< w; _w+= 8) { const index = (_w + w * _h) * 4; const r = data[index + 0]; const g = data[index + 1]; const b = data[index + 2]; const gray = .299 * r + .587 * g + .114 * b; ctxShow.fillText(img2Text(gray), _w, _h + 8); } } } function cleart() { const {w, h} = size; lastUrl && URL.revokeObjectURL(lastUrl); tId && window.cancelAnimationFrame(tId); ctxShow.clearRect(0, 0, w, h); ctxVideo.clearRect(0, 0, w, h); }
至此,功能基本都实现了,下面提供在线的呆毛和github仓库地址~
video转图片忘了是在github看哪个项目的,ctx.drawImage(video, 0, 0, width, height)这个是看完才知道的。
图片转字符基本是看这个大哥的github
在找方案的时候看到的一个像素图实现,挺有趣的,以前实现马赛克是拿周围像素值取平均去做,这个哥们是直接放大截图 更简单粗暴传送门
The text was updated successfully, but these errors were encountered:
No branches or pull requests
先看一下效果,原视频是这样的
我们要实现的效果是这样子的,之所以找这个视频是因为...这个视频和背景的对比度比较高做出来比较有辨识度,没有其他的意思 ( *・ω・)✄╰ひ╯
起因
某天一个基友在群里问我,在抖音看到一种视频,问我是不是能实现。我说可以的~ 于是当天晚上花了一个多小时折腾了一个粗糙版本...
先把视频丢到部门技术群问有没有关键字,给了一个keyword 图片转字符串 于是照着这个思路去gayhub找资源拼乐高!
整体思路
input[type="file"]
获取文件URL.createObjectURL
来获取视频的路径ctx.drawImage
我们可以把某个 video 当前的图像渲染到 canvas里面ctx.getImageData
可以获取当前canvas 里面图片的色值,利用公式计算出灰度ctx.fillText
重绘进去video.currentTime
来获得视频的某一时刻图像,重复上述重绘过程既然大概的思路已经理清,接下来就是具体的编码,把想法写出来的过程
具体编码实现
获取视频文件
首先我们先确定下html需要哪些元素
大概是长这样:
接下来js文件,我们要先对 input 绑定个监听事件,拿到文件url之后设置给video
这里要注意两点,一个是
url
用完不用的话,用URL.revokeObjectURL
释放资源; 一个是我们这里用了await
在domVide.onCanplay之前不做任何操作,防止视频没有加载完就操作,有黑屏风险。如果对 es6、es7、es8不熟悉的小伙伴要去补一下了~ 现在基本不会这些基本语法都看不懂demo= =
附上阮一峰老师的ES6教程,又想起面试被问ES7有什么新特性 简直是*了狗
将视频渲染到canvas
拿到视频之后,我们要把当前这一个时刻的图像渲染到canvas里面
先用
ctx.drawImage(video, 0, 0, width, height)
把video dom当前屏渲染进canvas再用
ctx.getImageData(0, 0, width, height)
获取图片的色值来做处理可以通过调整
img2Text
来选择渲染出来的图片是想要怎样的(由哪些字符组成等等)比如把 textList改为 ['Aa', 'Bv', 'Cc', 'Dd', '#', '&', '@', '$', '*', '?', ';', '^', '·', '·', '·', '·'],辨识度会高一点
到这一步,其实已经实现了把一张图片变为字符填充图了,剩下的工作无非就是把视频变成一张张的图片,然后重复执行这些逻辑
持续调用渲染字符视频
我们改一下 video2Img 函数,将其实现为能持续调用的形式, 再添加一个函数
clear
用来清理垃圾这里用到的是 window.requestAnimationFrame 去持续调用
源码与demo
至此,功能基本都实现了,下面提供在线的呆毛和github仓库地址~
在线呆毛
github 源码
video转图片忘了是在github看哪个项目的,
ctx.drawImage(video, 0, 0, width, height)
这个是看完才知道的。图片转字符基本是看这个大哥的github
在找方案的时候看到的一个像素图实现,挺有趣的,以前实现马赛克是拿周围像素值取平均去做,这个哥们是直接放大截图 更简单粗暴传送门
The text was updated successfully, but these errors were encountered: