File size: 4,391 Bytes
645e6ce
 
 
 
 
 
 
 
 
f1ef6c1
 
4d6d7ae
 
 
 
 
 
 
 
 
 
645e6ce
 
4d6d7ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645e6ce
 
4d6d7ae
f1ef6c1
4d6d7ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645e6ce
 
f1ef6c1
4d6d7ae
f1ef6c1
 
 
 
 
 
 
4d6d7ae
 
f1ef6c1
 
 
 
 
 
 
 
4d6d7ae
645e6ce
4d6d7ae
f1ef6c1
4d6d7ae
 
 
 
 
 
645e6ce
 
 
4d6d7ae
f1ef6c1
645e6ce
f1ef6c1
 
4d6d7ae
645e6ce
4d6d7ae
f1ef6c1
4d6d7ae
f1ef6c1
4d6d7ae
f1ef6c1
 
 
 
 
 
 
 
 
4d6d7ae
f1ef6c1
4d6d7ae
 
 
 
 
 
 
 
645e6ce
 
 
4d6d7ae
f1ef6c1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
const express = require('express');
const fs = require('fs');
const path = require('path');
const cv = require('opencv4nodejs');
const Jimp = require('jimp');
const axios = require('axios');
const sharp = require('sharp');

const app = express();
const port = 7860;

// 从文件夹加载所有分类器
async function loadClassifiersFromFolder(folderPath) {
  const classifiers = [];
  const files = fs.readdirSync(folderPath);
  for (const file of files) {
    const filePath = path.join(folderPath, file);
    const classifier = new cv.CascadeClassifier(filePath);
    classifiers.push(classifier);
  }
  return classifiers;
}

// 下载并转换图像到内存中
async function downloadAndConvertImage(url) {
  try {
    const response = await axios({
      method: 'get',
      url: url,
      responseType: 'arraybuffer'
    });

    const imageBuffer = await sharp(response.data)
      .toFormat('jpeg')
      .toBuffer();

    console.log('Image downloaded and converted successfully.');
    return imageBuffer;
  } catch (error) {
    console.error('Error downloading or converting image:', error);
    throw error;
  }
}

// 图像处理函数
async function processImage(imageBuffer, classifiersFolder, replacementImageBuffer) {
  try {
    const classifiers = await loadClassifiersFromFolder(classifiersFolder);

    // 使用 Jimp 验证图片文件是否有效
    const jimpImage = await Jimp.read(imageBuffer);
    if (!jimpImage) {
      throw new Error('Failed to read the image with Jimp. The image file might be corrupted or not supported.');
    }

    const img = cv.imdecode(imageBuffer);
    if (img.empty) {
      throw new Error('Failed to read the image with OpenCV. The image file might be corrupted or not supported.');
    }

    const grayImg = img.bgrToGray();
    const allFaces = [];
    classifiers.forEach(classifier => {
      const faces = classifier.detectMultiScale(grayImg).objects;
      allFaces.push(...faces);
    });

    const replacementFace = await Jimp.read(replacementImageBuffer);
    allFaces.forEach((faceRect, i) => {
      const resizedReplacementFace = replacementFace.resize(faceRect.width, faceRect.height)
      const faceRegion = img.getRegion(faceRect)
  
      const replacementBuffer = resizedReplacementFace.bitmap.data
      const centerX = faceRect.width / 2
      const centerY = faceRect.height / 2
      const maxRadius = Math.min(centerX, centerY)
      for (let y = 0; y < faceRect.height; y++) {
        for (let x = 0; x < faceRect.width; x++) {
          const distance = Math.sqrt((x - centerX) ** 2 + (y - centerY) ** 2)
          if (distance <= maxRadius) {
            const idx = (y * faceRect.width + x) << 2
            const [r, g, b, a] = replacementBuffer.slice(idx, idx + 4)
  
            if (a > 0) {
              faceRegion.set(y, x, new cv.Vec3(b, g, r))
            }
          }
        }
      }
    })
    const outputBuffer = cv.imencode('.jpg', img);
    return outputBuffer;
  } catch (error) {
    console.error('Error during image processing:', error);
    throw error;
  }
}

// Express接口
app.get('/process-image', async (req, res) => {
  const { imageUrl, replacementImageUrl } = req.query;

  if (!imageUrl || !replacementImageUrl) {
    return res.status(400).send('Both image URL and replacement image URL are required');
  }

  const classifiersFolder = './classifiers';
  let replacementImageBuffer;
  try {
    // 下载并转换输入图片和替换图片到内存中
    const imageBuffer = await downloadAndConvertImage(imageUrl);
    if (replacementImageUrl === 'replace') {
      // 使用默认的本地替换图像路径
      const defaultReplacementImagePath = './replacement_face.png';
      replacementImageBuffer = await Jimp.read(defaultReplacementImagePath);
    } else {
      // 下载并转换替换图片到内存中
      replacementImageBuffer = await downloadAndConvertImage(replacementImageUrl);
    }

    // 处理图片
    const outputBuffer = await processImage(imageBuffer, classifiersFolder, replacementImageBuffer);

    // 返回处理后的图片
    res.set('Content-Type', 'image/jpeg');
    res.send(outputBuffer);
  } catch (error) {
    console.error('Error processing image:', error);
    res.status(500).send('Error processing image');
  }
});

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});