luoyutianyang commited on
Commit
88e86a8
·
verified ·
1 Parent(s): 19b351d
Files changed (1) hide show
  1. egg-api.js +108 -101
egg-api.js CHANGED
@@ -7,122 +7,129 @@ const axios = require('axios');
7
  const sharp = require('sharp');
8
 
9
  const app = express();
10
- const port = 7860; // 将端口改为 7860
11
- const outputDir = './output';
12
 
13
- // 确保输出目录存在
14
- if (!fs.existsSync(outputDir)) {
15
- fs.mkdirSync(outputDir);
 
 
 
 
 
 
 
16
  }
17
 
18
- // 并行处理图片下载和处理
19
- function processImage(url, inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, callback) {
20
- axios.get(url, { responseType: 'arraybuffer' })
21
- .then(response => {
22
- return sharp(response.data).toFile(inputImagePath);
23
- })
24
- .then(() => {
25
- return replaceFaces(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath);
26
- })
27
- .then(() => {
28
- console.log('Image processed successfully.');
29
- callback(null);
30
- })
31
- .catch(error => {
32
- console.error('Error processing image:', error);
33
- callback(error);
34
- });
 
 
35
  }
36
 
37
- // 加载所有分类器
38
- function loadClassifiersFromFolder(folderPath, callback) {
39
- const classifiers = [];
40
- fs.readdir(folderPath, (err, files) => {
41
- if (err) {
42
- callback(err);
43
- return;
44
- }
45
- let completed = 0;
46
- files.forEach(file => {
47
- const filePath = path.join(folderPath, file);
48
- const classifier = new cv.CascadeClassifier(filePath);
49
- classifiers.push(classifier);
50
- completed++;
51
- if (completed === files.length) {
52
- callback(null, classifiers);
53
- }
54
- });
 
 
 
55
  });
56
- }
57
 
58
- // 人脸替换函数
59
- function replaceFaces(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, callback) {
60
- cv.imreadAsync(inputImagePath, (err, img) => {
61
- if (err) {
62
- callback(err);
63
- return;
64
- }
65
- loadClassifiersFromFolder(classifiersFolder, (err, classifiers) => {
66
- if (err) {
67
- callback(err);
68
- return;
 
 
 
 
 
 
 
69
  }
70
- Jimp.read(replacementImagePath, (err, replacementFace) => {
71
- if (err) {
72
- callback(err);
73
- return;
74
- }
75
- const grayImg = img.bgrToGray();
76
- const allFaces = [];
77
- classifiers.forEach(classifier => {
78
- const faces = classifier.detectMultiScale(grayImg).objects;
79
- allFaces.push(...faces);
80
- });
81
-
82
- allFaces.forEach(faceRect => {
83
- const resizedReplacementFace = replacementFace.resize(faceRect.width, faceRect.height);
84
- const faceRegion = img.getRegion(faceRect);
85
- const replacementBuffer = resizedReplacementFace.bitmap.data;
86
- for (let y = 0; y < faceRect.height; y++) {
87
- for (let x = 0; x < faceRect.width; x++) {
88
- const idx = (y * faceRect.width + x) << 2;
89
- const [r, g, b, a] = replacementBuffer.slice(idx, idx + 4);
90
- if (a > 0) {
91
- faceRegion.set(y, x, new cv.Vec3(b, g, r));
92
- }
93
- }
94
- }
95
- });
96
-
97
- cv.imwrite(outputImagePath, img);
98
- callback(null);
99
- });
100
- });
101
- });
102
  }
103
 
104
  // Express接口
105
- app.get('/process-image', (req, res) => {
106
- const { imageUrl } = req.query;
107
 
108
- if (!imageUrl) {
109
- return res.status(400).send('Image URL is required');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
 
112
- const inputImagePath = path.join(outputDir, 'input.jpg'); // 使用jpg格式保存
113
- const outputImagePath = path.join(outputDir, 'output.jpg');
114
- const replacementImagePath = './replacement_face.png';
115
- const classifiersFolder = './classifiers';
116
 
117
- processImage(imageUrl, inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, (err) => {
118
- if (err) {
119
- console.error('Error processing image:', err);
120
- return res.status(500).send('Error processing image');
121
- }
122
- res.sendFile(outputImagePath, { root: '.' });
123
- });
124
  });
125
 
126
  app.listen(port, () => {
127
- console.log(`Server is running on http://localhost:${port}`);
128
- });
 
7
  const sharp = require('sharp');
8
 
9
  const app = express();
10
+ const port = 7860;
 
11
 
12
+ // 从文件夹加载所有分类器
13
+ async function loadClassifiersFromFolder(folderPath) {
14
+ const classifiers = [];
15
+ const files = fs.readdirSync(folderPath);
16
+ for (const file of files) {
17
+ const filePath = path.join(folderPath, file);
18
+ const classifier = new cv.CascadeClassifier(filePath);
19
+ classifiers.push(classifier);
20
+ }
21
+ return classifiers;
22
  }
23
 
24
+ // 下载并转换图像到内存中
25
+ async function downloadAndConvertImage(url) {
26
+ try {
27
+ const response = await axios({
28
+ method: 'get',
29
+ url: url,
30
+ responseType: 'arraybuffer'
31
+ });
32
+
33
+ const imageBuffer = await sharp(response.data)
34
+ .toFormat('jpeg')
35
+ .toBuffer();
36
+
37
+ console.log('Image downloaded and converted successfully.');
38
+ return imageBuffer;
39
+ } catch (error) {
40
+ console.error('Error downloading or converting image:', error);
41
+ throw error;
42
+ }
43
  }
44
 
45
+ // 图像处理函数
46
+ async function processImage(imageBuffer, classifiersFolder, replacementImageBuffer) {
47
+ try {
48
+ const classifiers = await loadClassifiersFromFolder(classifiersFolder);
49
+
50
+ // 使用 Jimp 验证图片文件是否有效
51
+ const jimpImage = await Jimp.read(imageBuffer);
52
+ if (!jimpImage) {
53
+ throw new Error('Failed to read the image with Jimp. The image file might be corrupted or not supported.');
54
+ }
55
+
56
+ const img = cv.imdecode(imageBuffer);
57
+ if (img.empty) {
58
+ throw new Error('Failed to read the image with OpenCV. The image file might be corrupted or not supported.');
59
+ }
60
+
61
+ const grayImg = img.bgrToGray();
62
+ const allFaces = [];
63
+ classifiers.forEach(classifier => {
64
+ const faces = classifier.detectMultiScale(grayImg).objects;
65
+ allFaces.push(...faces);
66
  });
 
67
 
68
+ const replacementFace = await Jimp.read(replacementImageBuffer);
69
+ allFaces.forEach((faceRect, i) => {
70
+ const resizedReplacementFace = replacementFace.resize(faceRect.width, faceRect.height)
71
+ const faceRegion = img.getRegion(faceRect)
72
+
73
+ const replacementBuffer = resizedReplacementFace.bitmap.data
74
+ const centerX = faceRect.width / 2
75
+ const centerY = faceRect.height / 2
76
+ const maxRadius = Math.min(centerX, centerY)
77
+ for (let y = 0; y < faceRect.height; y++) {
78
+ for (let x = 0; x < faceRect.width; x++) {
79
+ const distance = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2)
80
+ if (distance <= maxRadius) {
81
+ const idx = (y * faceRect.width + x) << 2
82
+ const [r, g, b, a] = replacementBuffer.slice(idx, idx + 4)
83
+
84
+ if (a > 0) {
85
+ faceRegion.set(y, x, new cv.Vec3(b, g, r))
86
  }
87
+ }
88
+ }
89
+ }
90
+ })
91
+ const outputBuffer = cv.imencode('.jpg', img);
92
+ return outputBuffer;
93
+ } catch (error) {
94
+ console.error('Error during image processing:', error);
95
+ throw error;
96
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
 
99
  // Express接口
100
+ app.get('/process-image', async (req, res) => {
101
+ const { imageUrl, replacementImageUrl } = req.query;
102
 
103
+ if (!imageUrl || !replacementImageUrl) {
104
+ return res.status(400).send('Both image URL and replacement image URL are required');
105
+ }
106
+
107
+ const classifiersFolder = './classifiers';
108
+ let replacementImageBuffer;
109
+ try {
110
+ // 下载并转换输入图片和替换图片到内存中
111
+ const imageBuffer = await downloadAndConvertImage(imageUrl);
112
+ if (replacementImageUrl === 'replace') {
113
+ // 使用默认的本地替换图像路径
114
+ const defaultReplacementImagePath = './replacement_face.png';
115
+ replacementImageBuffer = await Jimp.read(defaultReplacementImagePath);
116
+ } else {
117
+ // 下载并转换替换图片到内存中
118
+ replacementImageBuffer = await downloadAndConvertImage(replacementImageUrl);
119
  }
120
 
121
+ // 处理图片
122
+ const outputBuffer = await processImage(imageBuffer, classifiersFolder, replacementImageBuffer);
 
 
123
 
124
+ // 返回处理后的图片
125
+ res.set('Content-Type', 'image/jpeg');
126
+ res.send(outputBuffer);
127
+ } catch (error) {
128
+ console.error('Error processing image:', error);
129
+ res.status(500).send('Error processing image');
130
+ }
131
  });
132
 
133
  app.listen(port, () => {
134
+ console.log(`Server is running on http://localhost:${port}`);
135
+ });