在线体验
前端代码 实现了三种功能
-
图片转txt
-
视频转txt
-
摄像头实时画面转txt www.fwhospitalgk.top/page/img2tx…
纯前端实现
<!DOCTYPE html>
<html>
<head>
<title>图片/视频转换为ASCII</title>
<meta name="viewport" charset="UTF-8" content="width=device-width, initial-scale=0.1">
<style>
#image-container {
font-family: monospace;
white-space: pre-line;
text-align: justify;
font-size: 4px;
line-height: 6px;
width: 8000px;
}
#file-input {
display: none;
}
#videoFileInput {
display: none;
}
.upload-btn {
display: inline-block;
color: white;
padding: 10px 20px;
font-size: 30px;
cursor: pointer;
border-radius: 5px;
margin-right: 50px;
}
.upload-btn:hover {
background-color: #18a0ff;
}
#canvas2 {
position: fixed;
top: 0;
right: 0;
z-index: 999;
display: block;
}
</style>
</head>
<body>
<form id="form1">
<label for="file-input" class="upload-btn" onclick="document.getElementById('form1').reset();"><img src="img.png"></label>
<input type="file" id="file-input" accept="image/*" />
<label for="videoFileInput" class="upload-btn" onclick="document.getElementById('form1').reset();"><img src="video.png"></label>
<input type="file" id="videoFileInput" accept="video/*" />
<div class="upload-btn" onclick="openCamera()"><img src="camera.png"></div>
</form>
<canvas id="canvas2" width="640" height="480"></canvas>
<div id="image-container"></div>
<script>
document.getElementById("file-input").addEventListener("change", function () {
canvasElement2.style.display = 'none';
const fileInput = document.getElementById("file-input");
const file = fileInput.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function (event) {
const image = new Image();
image.onload = function () {
const ascii = convertToASCII(image);
displayASCII(ascii);
};
image.src = event.target.result;
};
reader.readAsDataURL(file);
}
});
function convertToASCII(image) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
let ascii = "";
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const brightness = Math.round((r + g + b) / 3);
const character = getCharacter(brightness);
ascii += character;
if (i > 0 && (i / 4) % canvas.width === 0) {
ascii += "\n";
}
}
return ascii;
}
function getCharacter(brightness) {
// const characters = [".", "-", "*", "+", "=", "x", "X", "#", "%", "@", "W"];
const characters = ["W","@","%","#","X","x","=","+","*", "-", "."];
const index = Math.round(brightness / 255 * (characters.length - 1));
return characters[index];
}
function displayASCII(ascii) {
const imageContainer = document.getElementById("image-container");
imageContainer.textContent = ascii;
}
//////////////////////////////////
const videoFileInput = document.getElementById("videoFileInput");
const videoElement = document.createElement("video");
videoElement.preload = "auto";
videoElement.controls = true;
videoFileInput.addEventListener("change", (event) => {
canvasElement2.style.display = 'none';
const file = event.target.files[0];
if (file){
videoElement.src = URL.createObjectURL(file);
videoElement.addEventListener("canplaythrough", () => {
canvasElement2.width = videoElement.videoWidth;
canvasElement2.height = videoElement.videoHeight;
canvasElement.width = videoElement.videoWidth;
canvasElement.height = videoElement.videoHeight;
videoElement.play();
});
}
});
const canvasElement = document.createElement("canvas");
const context = canvasElement.getContext("2d");
const fps = 30;
const canvasElement2 = document.getElementById("canvas2");
const context2 = canvasElement2.getContext("2d");
videoElement.addEventListener("play", () => {
const interval = setInterval(() => {
if (videoElement.paused || videoElement.ended) {
clearInterval(interval);
return;
}
context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);
const imageData = context.getImageData(0, 0, canvasElement.width, canvasElement.height);
// 处理每一帧的图像数据
// console.log(imageData);
context2.putImageData(imageData, 0, 0);
//context.putImageData(imageData, 0, 0);
// 将ImageData转换为DataURL格式
const dataURL = canvasElement2.toDataURL();
// 创建新的Image对象并将其src属性设置为DataURL格式
const imageElement = new Image();
imageElement.onload = function () {
const ascii = convertToASCII(imageElement);
displayASCII(ascii);
};
imageElement.src = dataURL;
}, 1000 / fps);
});
//////////////////////////////////
function openCamera(){
// canvasElement2.style.display = 'block';
canvasElement2.width = 600;
canvasElement2.height = 450;
navigator.mediaDevices.getUserMedia({ video: true })
.then(function(stream) {
var videoTrack = stream.getVideoTracks()[0];
var imageCapture = new ImageCapture(videoTrack);
function drawVideoFrame() {
imageCapture.grabFrame().then(function(imageBitmap) {
context2.drawImage(imageBitmap, 0, 0, canvasElement2.width, canvasElement2.height);
// 将ImageData转换为DataURL格式
const dataURL = canvasElement2.toDataURL();
// 创建新的Image对象并将其src属性设置为DataURL格式
const imageElement = new Image();
imageElement.onload = function () {
const ascii = convertToASCII(imageElement);
displayASCII(ascii);
};
imageElement.src = dataURL;
requestAnimationFrame(drawVideoFrame);
});
}
drawVideoFrame();
})
.catch(function(err) {
console.log("无法访问摄像头:" + err);
});
}
</script>
</body>
</html>
java版本实现
package websocket.demo.controller;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* @Author Zhanglele
* @Date 2020/12/26 14:17
*/
public class GIF {
public static void main(String[] args) throws IOException {
String ffmpegPath = "D://soft/ffmpeg.exe";
//视频文件地址
String videoPath = "D://mp5/1.mp4";
String info = info(videoPath, ffmpegPath);
String[] split = info.split(",");
Integer frame=Integer.parseInt(split[0]);
String w=split[1];
String l=split[2];
Integer speed=50;//数值越小速度越快
processImg(videoPath,ffmpegPath,Integer.parseInt(w),Integer.parseInt(l));
long name = System.currentTimeMillis();
//解析为TXT文档
Integer row = txt(name, frame);//fream 视频共多少帧 row 每帧像素高度
System.out.println("row"+row);
payer(name,row,frame,speed);
}
//播放器
private static void payer(Long name,Integer row,Integer frame,Integer speed) {
JFrame ck = new JFrame("txt滚动");
ck.setBounds(0, 0, 1920, 1080);
JTextArea are = new JTextArea();
ck.add(are);
are.setBounds(0, 0, 1920, 1080);
are.setForeground(Color.black);
Font font = new Font("Consolas", Font.BOLD, 7);
are.setFont(font);
File file = new File("D://txt/"+name+".txt");
ck.setVisible(true);
try {
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
StringBuffer sb = new StringBuffer();
int b = 0;
int a = 0;
while (b < frame*row) {
b++;
a++;
String str = br.readLine();
sb.append(str + "\n");
if (a % row == 0) {
String str1 = sb.toString();
sb = new StringBuffer("");
are.setText(str1);
Thread.sleep(speed);
}
}
fr.close();
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean processImg(String veido_path, String ffmpeg_path, Integer w,Integer l) {
File file = new File(veido_path);
if (!file.exists()) {
System.err.println("路径[" + veido_path + "]对应的视频文件不存在!");
return false;
}
List<String> commands = new java.util.ArrayList<String>();
commands.add(ffmpeg_path);
commands.add("-y");
commands.add("-i");
commands.add(veido_path);
commands.add("-vf");
commands.add("scale="+w/2+":"+l/6);
commands.add("D://mp5/%01d.png");
try {
ProcessBuilder builder = new ProcessBuilder();
builder.command(commands);
builder.start();
Thread.sleep(3000L);
System.out.println("视频分解完成....");
System.out.println("开始解析为txt字符....");
Thread.sleep(6000L);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static Integer txt(Long name,Integer frame) throws IOException {
File F = new File("D://txt/"+name+".txt");
//如果文件不存在,就动态创建文件
if (!F.exists()) {
F.createNewFile();
}
FileWriter fw = null;
//writeDate 写入的内容
Integer row=0;
for (int i = 1; i < frame; i++) {
List<List<Integer>> imageGRB = getImageGRB("D://mp5/"+i+".png");
System.out.println("剩余"+(frame-i)+"帧");
row = fori(F, fw, imageGRB);
}
return row;
}
private static Integer fori(File f, FileWriter fw, List<List<Integer>> imageGRB) throws IOException {
String[] strings = {"M", "N", "H", "Q", "$", "O", "C", "?", "7", ">", "!", ":", "–", ";", "."};
//String[] strings = {"发", "阿", "的", "分", "他", "吗", "一", "要", "怕", "去", "我", "为", "其", "是", "对"};
List<List<String>> listStrings = new ArrayList<>();
try {
fw = new FileWriter(f, true);
for (List<Integer> list : imageGRB) {
List<String> stringList = new ArrayList<>();
for (Integer integer : list) {
int floor = (int) Math.floor(integer / 18);
//设置为:True,表示写入的时候追加数据
//回车并换行
fw.write(strings[floor]);
}
fw.write("\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
fw.close();
}
}
return imageGRB.size();
}
/**
* 获取图片RGB数组
*
* @param filePath
* @return
*/
public static List<List<Integer>> getImageGRB(String filePath) {
File file = new File(filePath);
List<List<Integer>> result = new ArrayList<>();
if (!file.exists()) {
return result;
}
try {
BufferedImage bufImg = ImageIO.read(file);
int height = bufImg.getHeight();
int width = bufImg.getWidth();
for (int i = 0; i < height; i++) {
List<Integer> list = new ArrayList<>();
for (int j = 0; j < width; j++) {
Color color = new Color(bufImg.getRGB(j, i));
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
//System.out.println("("+red+" "+green+" "+blue+")");
list.add((red + green + blue) / 3);
}
result.add(list);
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
public static String info(String videoPath,String ffmpegPath) {
//ffmepg工具地址
Integer frame=0;String w="";String l="";
// 拼接cmd命令语句
StringBuffer buffer = new StringBuffer();
buffer.append(ffmpegPath);
//注意要保留单词之间有空格
buffer.append(" -i ");
buffer.append(videoPath);
// 执行命令语句并返回执行结果
try {
Process process = Runtime.getRuntime().exec(buffer.toString());
InputStream in = process.getErrorStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line ;
Integer time=0;
System.out.println("视频信息");
while((line=br.readLine())!=null) {
// Stream #0.0(und): Video:h264, yuv420p, 960x544, 1223 kb/s, 30 fps, 30 tbr, 600 tbn, 1200 tbc
//根据
//根据字符匹配进行切割
if(line.trim().startsWith("Duration:")){
//根据字符匹配进行切割
String substring = line.trim().substring(0, line.trim().indexOf(","));
int length = substring.split(":").length;
String s = substring.split(":")[length - 1];
System.out.println("视频时间 = " + s+"秒");
time =(int) Math.floor(Double.parseDouble(s));
}
//一般包含fps的行就包含分辨率
if(line.contains("fps")){
String definition = line.split(",")[2];
w = definition.trim().split("x")[0];
l = definition.trim().split("x")[1];
System.out.println("分辨率 = " + w+"x"+l);
String[] split = line.split(",")[4].trim().split(" ");
System.out.println("帧数 = " + time*Integer.parseInt(split[0]));
frame = time*Integer.parseInt(split[0]);
}
}
}catch (Exception e){
e.printStackTrace();
}
return frame+","+w+","+l;
}
}