mmdhx commited on
Commit
d9ae3dc
·
verified ·
1 Parent(s): c238572

Update egg-api.js

Browse files
Files changed (1) hide show
  1. egg-api.js +115 -164
egg-api.js CHANGED
@@ -1,160 +1,34 @@
1
  const express = require('express');
2
  const cv = require('opencv4nodejs');
3
  const Jimp = require('jimp');
4
- const ffmpeg = require('fluent-ffmpeg');
5
  const axios = require('axios');
6
  const fs = require('fs');
7
  const os = require('os');
8
  const path = require('path');
9
- const sharp = require('sharp');
10
  const serveStatic = require('serve-static');
11
 
12
  const app = express();
13
  const PORT = 7860;
 
14
  // 从文件夹加载所有分类器
15
- const loadClassifiersFromFolder = async (folderPath) => {
16
- const classifiers = [];
17
- const files = fs.readdirSync(folderPath);
18
- for (const file of files) {
19
- const filePath = path.join(folderPath, file);
20
- const classifier = new cv.CascadeClassifier(filePath);
21
- classifiers.push(classifier);
22
- }
23
- return classifiers;
24
- };
25
-
26
- // 下载并转换图像到内存中
27
- const downloadAndConvertImage = async (url) => {
28
- try {
29
- const response = await axios({
30
- method: 'get',
31
- url: url,
32
- responseType: 'arraybuffer'
33
- });
34
 
35
- const imageBuffer = await sharp(response.data)
36
- .toFormat('jpeg')
37
- .toBuffer();
38
-
39
- console.log('Image downloaded and converted successfully.');
40
- return imageBuffer;
41
- } catch (error) {
42
- console.error('Error downloading or converting image:', error);
43
- throw error;
44
- }
45
- };
46
-
47
- // 图像处理函数
48
- const processImage = async (imageBuffer, classifiersFolder, replacementImageBuffer) => {
49
- try {
50
- const classifiers = await loadClassifiersFromFolder(classifiersFolder);
51
-
52
- const jimpImage = await Jimp.read(imageBuffer);
53
- if (!jimpImage) {
54
- throw new Error('Failed to read the image with Jimp. The image file might be corrupted or not supported.');
55
- }
56
-
57
- const img = cv.imdecode(imageBuffer);
58
- if (img.empty) {
59
- throw new Error('Failed to read the image with OpenCV. The image file might be corrupted or not supported.');
60
- }
61
-
62
- const grayImg = img.bgrToGray();
63
- const allFaces = [];
64
- classifiers.forEach(classifier => {
65
- const faces = classifier.detectMultiScale(grayImg).objects;
66
- allFaces.push(...faces);
67
- });
68
-
69
- // 如果没有检测到人脸,抛出错误
70
- if (allFaces.length === 0) {
71
- throw new Error('No faces detected in the image.');
72
- }
73
-
74
- const replacementFace = await Jimp.read(replacementImageBuffer);
75
- allFaces.forEach((faceRect, i) => {
76
- const resizedReplacementFace = replacementFace.resize(faceRect.width, faceRect.height);
77
- const faceRegion = img.getRegion(faceRect);
78
-
79
- const replacementBuffer = resizedReplacementFace.bitmap.data;
80
- const centerX = faceRect.width / 2;
81
- const centerY = faceRect.height / 2;
82
- const maxRadius = Math.min(centerX, centerY);
83
- for (let y = 0; y < faceRect.height; y++) {
84
- for (let x = 0; x < faceRect.width; x++) {
85
- const distance = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2);
86
- if (distance <= maxRadius) {
87
- const idx = (y * faceRect.width + x) << 2;
88
- const [r, g, b, a] = replacementBuffer.slice(idx, idx + 4);
89
-
90
- if (a > 0) {
91
- faceRegion.set(y, x, new cv.Vec3(b, g, r));
92
- }
93
- }
94
- }
95
- }
96
- });
97
- const outputBuffer = cv.imencode('.jpg', img);
98
- return outputBuffer;
99
- } catch (error) {
100
- console.error('Error during image processing:', error);
101
- throw error;
102
- }
103
- };
104
-
105
- // Express接口
106
- app.get('/process-image', async (req, res) => {
107
- const { imageUrl, replacementImageUrl } = req.query;
108
-
109
- if (!imageUrl || !replacementImageUrl) {
110
- return res.status(400).send('Both image URL and replacement image URL are required');
111
- }
112
-
113
- const classifiersFolder = './classifiers';
114
- let replacementImageBuffer;
115
- try {
116
- // 下载并转换输入图片和替换图片到内存中
117
- const imageBuffer = await downloadAndConvertImage(imageUrl);
118
- if (replacementImageUrl === 'replace') {
119
- // 使用默认的本地替换图像路径
120
- const defaultReplacementImagePath = './replacement_face.png';
121
- replacementImageBuffer = await Jimp.read(defaultReplacementImagePath);
122
- } else {
123
- // 下载并转换替换图片到内存中
124
- replacementImageBuffer = await downloadAndConvertImage(replacementImageUrl);
125
- }
126
-
127
- // 处理图片
128
- const outputBuffer = await processImage(imageBuffer, classifiersFolder, replacementImageBuffer);
129
-
130
- // 返回处理后的图片
131
- res.set('Content-Type', 'image/jpeg');
132
- res.send(outputBuffer);
133
- } catch (error) {
134
- if (error.message === 'No faces detected in the image.') {
135
- res.status(404).send('No faces detected in the image.');
136
- } else {
137
- console.error('Error processing image:', error);
138
- res.status(500).send('Error processing image');
139
  }
140
- }
141
- });
142
 
143
- // 记录使用次数和内存占用的全局变量
144
- let usageCount = 0;
145
- let memoryUsage = 0;
146
-
147
- app.use(serveStatic(__dirname)); // 添加这一行,设置静态文件服务器
148
- app.get('/', (req, res) => {
149
- res.sendFile(path.join(__dirname, 'index.html')); // 修改这一行,发送HTML文件
150
- });
151
-
152
- app.listen(port, () => {
153
- console.log(`Server is running on http://localhost:${port}`);
154
- });
155
- app.use(express.json());
156
 
157
- // GIF转换为视频
158
  async function gifToVideo(inputGifPath, outputVideoPath) {
159
  console.log('开始将GIF转换为视频...');
160
  return new Promise((resolve, reject) => {
@@ -176,7 +50,7 @@ async function gifToVideo(inputGifPath, outputVideoPath) {
176
  });
177
  }
178
 
179
- // 添加将输出视频转换为 GIF 的函数
180
  async function videoToGif(inputVideoPath, outputGifPath) {
181
  console.log('开始将视频转换为GIF...');
182
  return new Promise((resolve, reject) => {
@@ -197,22 +71,90 @@ async function videoToGif(inputVideoPath, outputGifPath) {
197
  });
198
  }
199
 
200
- // 从文件夹加载所有分类器
201
- async function loadClassifiersFromFolder(folderPath) {
202
- console.log(`开始从文件夹 ${folderPath} 加载分类器...`);
203
- const classifiers = [];
 
 
 
 
 
204
 
205
- const files = fs.readdirSync(folderPath);
206
- for (const file of files) {
207
- const filePath = path.join(folderPath, file);
208
- const classifier = new cv.CascadeClassifier(filePath);
209
- classifiers.push(classifier);
210
- console.log(`加载分类器: ${file}`);
211
- }
212
 
213
- console.log('分类器加载完成');
214
- return classifiers;
215
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
  // 处理 GIF 的 Express 接口
218
  app.get('/processGif', async (req, res) => {
@@ -247,7 +189,7 @@ app.get('/processGif', async (req, res) => {
247
  // 将GIF转换为视频
248
  await gifToVideo(tempGifPath, tempVideoPath);
249
 
250
- // 读取视频文件
251
  console.log('开始读取视频文件...');
252
  const videoCapture = new cv.VideoCapture(tempVideoPath);
253
  const frameWidth = videoCapture.get(cv.CAP_PROP_FRAME_WIDTH);
@@ -256,7 +198,6 @@ app.get('/processGif', async (req, res) => {
256
  const videoWriter = new cv.VideoWriter(path.join(tempDir, 'out.mp4'), cv.VideoWriter.fourcc('avc1'), originalFps, new cv.Size(frameWidth, frameHeight));
257
  console.log('视频文件读取完成');
258
 
259
-
260
  // 处理每一帧
261
  console.log('开始处理每一帧...');
262
  while (true) {
@@ -286,9 +227,6 @@ app.get('/processGif', async (req, res) => {
286
  }
287
  }
288
  }
289
- const buffer = await resizedSubstituteImage.getBufferAsync(Jimp.MIME_PNG);
290
- const substituteMat = cv.imdecode(Buffer.from(buffer), cv.IMREAD_UNCHANGED);
291
- substituteMat.copyTo(frame.getRegion(rect));
292
  }
293
  } else {
294
  console.error('未检测到人脸或faces数组为空');
@@ -305,14 +243,27 @@ app.get('/processGif', async (req, res) => {
305
  await videoToGif(path.join(tempDir, 'out.mp4'), outputGifPath);
306
 
307
  res.set('Content-Type', 'image/gif');
308
- res.sendFile(outputGifPath);
 
 
 
 
 
 
 
 
 
 
309
  } catch (error) {
310
  console.error('处理GIF时出错:', error);
311
  res.status(500).send('内部服务器错误');
312
  }
313
  });
314
-
315
-
 
 
 
316
  app.listen(PORT, () => {
317
  console.log(`服务器运行在端口 ${PORT}`);
318
  });
 
1
  const express = require('express');
2
  const cv = require('opencv4nodejs');
3
  const Jimp = require('jimp');
 
4
  const axios = require('axios');
5
  const fs = require('fs');
6
  const os = require('os');
7
  const path = require('path');
8
+ const ffmpeg = require('fluent-ffmpeg');
9
  const serveStatic = require('serve-static');
10
 
11
  const app = express();
12
  const PORT = 7860;
13
+
14
  // 从文件夹加载所有分类器
15
+ async function loadClassifiersFromFolder(folderPath) {
16
+ console.log(`开始从文件夹 ${folderPath} 加载分类器...`);
17
+ const classifiers = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ const files = fs.readdirSync(folderPath);
20
+ for (const file of files) {
21
+ const filePath = path.join(folderPath, file);
22
+ const classifier = new cv.CascadeClassifier(filePath);
23
+ classifiers.push(classifier);
24
+ console.log(`加载分类器: ${file}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
 
 
26
 
27
+ console.log('分类器加载完成');
28
+ return classifiers;
29
+ }
 
 
 
 
 
 
 
 
 
 
30
 
31
+ // GIF 转换为视频
32
  async function gifToVideo(inputGifPath, outputVideoPath) {
33
  console.log('开始将GIF转换为视频...');
34
  return new Promise((resolve, reject) => {
 
50
  });
51
  }
52
 
53
+ // 视频转换为 GIF
54
  async function videoToGif(inputVideoPath, outputGifPath) {
55
  console.log('开始将视频转换为GIF...');
56
  return new Promise((resolve, reject) => {
 
71
  });
72
  }
73
 
74
+ // 处理图片的 Express 接口
75
+ app.get('/process-image', async (req, res) => {
76
+ try {
77
+ const { imageUrl } = req.query;
78
+ const tempDir = os.tmpdir();
79
+ const tempImagePath = path.join(tempDir, 'temp_image.jpg');
80
+ const outputImagePath = path.join(tempDir, 'output_image.jpg');
81
+ const replacementImagePath = path.join(__dirname, 'replacement_face.png');
82
+ const classifiersFolder = path.join(__dirname, 'classifiers');
83
 
84
+ console.log('接收到图片处理请求:', imageUrl);
 
 
 
 
 
 
85
 
86
+ // 下载图片到本地
87
+ console.log('开始下载图片...');
88
+ const response = await axios.get(imageUrl, { responseType: 'stream' });
89
+ const writer = fs.createWriteStream(tempImagePath);
90
+ response.data.pipe(writer);
91
+
92
+ // 等待文件写入完成
93
+ await new Promise((resolve, reject) => {
94
+ writer.on('finish', resolve);
95
+ writer.on('error', reject);
96
+ });
97
+
98
+ console.log('图片下载完成:', tempImagePath);
99
+
100
+ // 从文件夹动态加载分类器
101
+ const classifiers = await loadClassifiersFromFolder(classifiersFolder);
102
+
103
+ // 读取图片
104
+ console.log('开始读取图片...');
105
+ const image = await cv.imreadAsync(tempImagePath);
106
+ const grayImage = image.bgrToGray();
107
+
108
+ // 检测人脸
109
+ console.log('开始检测人脸...');
110
+ const faces = [];
111
+ classifiers.forEach(classifier => {
112
+ const allFaces = classifier.detectMultiScale(grayImage).objects;
113
+ faces.push(...allFaces);
114
+ });
115
+
116
+ const replacementFace = await Jimp.read(replacementImagePath);
117
+
118
+ if (Array.isArray(faces) && faces.length > 0) {
119
+ console.log(`检测到 ${faces.length} 张脸`);
120
+ for (const rect of faces) {
121
+ const resizedSubstituteImage = await replacementFace.clone().resize(rect.width, rect.height);
122
+ const substituteRegion = image.getRegion(rect);
123
+ for (let y = 0; y < rect.height; y++) {
124
+ for (let x = 0; x < rect.width; x++) {
125
+ const { r, g, b, a } = Jimp.intToRGBA(resizedSubstituteImage.getPixelColor(x, y));
126
+ if (a > 0) {
127
+ const color = new cv.Vec3(b, g, r);
128
+ substituteRegion.set(y, x, color);
129
+ }
130
+ }
131
+ }
132
+ }
133
+ } else {
134
+ console.error('未检测到人脸或faces数组为空');
135
+ }
136
+
137
+ // 保存处理后的图片
138
+ await cv.imwriteAsync(outputImagePath, image);
139
+ console.log('图片处理完成并保存:', outputImagePath);
140
+
141
+ // 发送处理后的图片
142
+ res.set('Content-Type', 'image/jpeg');
143
+ res.sendFile(outputImagePath, async (err) => {
144
+ if (err) {
145
+ console.error('发送文件时出错:', err);
146
+ res.status(500).send('内部服务器错误');
147
+ } else {
148
+ // 删除临时文件
149
+ fs.unlinkSync(tempImagePath);
150
+ fs.unlinkSync(outputImagePath);
151
+ }
152
+ });
153
+ } catch (error) {
154
+ console.error('处理图片时出错:', error);
155
+ res.status(500).send('内部服务器错误');
156
+ }
157
+ });
158
 
159
  // 处理 GIF 的 Express 接口
160
  app.get('/processGif', async (req, res) => {
 
189
  // 将GIF转换为视频
190
  await gifToVideo(tempGifPath, tempVideoPath);
191
 
192
+ // 读取视频文件
193
  console.log('开始读取视频文件...');
194
  const videoCapture = new cv.VideoCapture(tempVideoPath);
195
  const frameWidth = videoCapture.get(cv.CAP_PROP_FRAME_WIDTH);
 
198
  const videoWriter = new cv.VideoWriter(path.join(tempDir, 'out.mp4'), cv.VideoWriter.fourcc('avc1'), originalFps, new cv.Size(frameWidth, frameHeight));
199
  console.log('视频文件读取完成');
200
 
 
201
  // 处理每一帧
202
  console.log('开始处理每一帧...');
203
  while (true) {
 
227
  }
228
  }
229
  }
 
 
 
230
  }
231
  } else {
232
  console.error('未检测到人脸或faces数组为空');
 
243
  await videoToGif(path.join(tempDir, 'out.mp4'), outputGifPath);
244
 
245
  res.set('Content-Type', 'image/gif');
246
+ res.sendFile(outputGifPath, async (err) => {
247
+ if (err) {
248
+ console.error('发送GIF文件时出错:', err);
249
+ res.status(500).send('内部服务器错误');
250
+ } else {
251
+ // 删除临时文件
252
+ fs.unlinkSync(tempGifPath);
253
+ fs.unlinkSync(tempVideoPath);
254
+ fs.unlinkSync(outputGifPath);
255
+ }
256
+ });
257
  } catch (error) {
258
  console.error('处理GIF时出错:', error);
259
  res.status(500).send('内部服务器错误');
260
  }
261
  });
262
+ app.use(serveStatic(__dirname)); // 添加这一行,设置静态文件服务器
263
+ app.get('/', (req, res) => {
264
+ res.sendFile(path.join(__dirname, 'index.html')); // 修改这一行,发送HTML文件
265
+ });
266
+ // 启动 Express 服务器
267
  app.listen(PORT, () => {
268
  console.log(`服务器运行在端口 ${PORT}`);
269
  });