| 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; |
| const outputDir = './output'; |
|
|
| |
| if (!fs.existsSync(outputDir)) { |
| fs.mkdirSync(outputDir); |
| } |
|
|
| |
| function processImage(url, inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, callback) { |
| axios.get(url, { responseType: 'arraybuffer' }) |
| .then(response => { |
| return sharp(response.data).toFile(inputImagePath); |
| }) |
| .then(() => { |
| return replaceFaces(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath); |
| }) |
| .then(() => { |
| console.log('Image processed successfully.'); |
| callback(null); |
| }) |
| .catch(error => { |
| console.error('Error processing image:', error); |
| callback(error); |
| }); |
| } |
|
|
| |
| function loadClassifiersFromFolder(folderPath, callback) { |
| const classifiers = []; |
| fs.readdir(folderPath, (err, files) => { |
| if (err) { |
| callback(err); |
| return; |
| } |
| let completed = 0; |
| files.forEach(file => { |
| const filePath = path.join(folderPath, file); |
| const classifier = new cv.CascadeClassifier(filePath); |
| classifiers.push(classifier); |
| completed++; |
| if (completed === files.length) { |
| callback(null, classifiers); |
| } |
| }); |
| }); |
| } |
|
|
| |
| function replaceFaces(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, callback) { |
| cv.imreadAsync(inputImagePath, (err, img) => { |
| if (err) { |
| callback(err); |
| return; |
| } |
| loadClassifiersFromFolder(classifiersFolder, (err, classifiers) => { |
| if (err) { |
| callback(err); |
| return; |
| } |
| Jimp.read(replacementImagePath, (err, replacementFace) => { |
| if (err) { |
| callback(err); |
| return; |
| } |
| const grayImg = img.bgrToGray(); |
| const allFaces = []; |
| classifiers.forEach(classifier => { |
| const faces = classifier.detectMultiScale(grayImg).objects; |
| allFaces.push(...faces); |
| }); |
|
|
| allFaces.forEach(faceRect => { |
| const resizedReplacementFace = replacementFace.resize(faceRect.width, faceRect.height); |
| const faceRegion = img.getRegion(faceRect); |
| const replacementBuffer = resizedReplacementFace.bitmap.data; |
| for (let y = 0; y < faceRect.height; y++) { |
| for (let x = 0; x < faceRect.width; x++) { |
| 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)); |
| } |
| } |
| } |
| }); |
|
|
| cv.imwrite(outputImagePath, img); |
| callback(null); |
| }); |
| }); |
| }); |
| } |
|
|
| |
| app.get('/process-image', (req, res) => { |
| const { imageUrl } = req.query; |
|
|
| if (!imageUrl) { |
| return res.status(400).send('Image URL is required'); |
| } |
|
|
| const inputImagePath = path.join(outputDir, 'input.jpg'); |
| const outputImagePath = path.join(outputDir, 'output.jpg'); |
| const replacementImagePath = './replacement_face.png'; |
| const classifiersFolder = './classifiers'; |
|
|
| processImage(imageUrl, inputImagePath, outputImagePath, classifiersFolder, replacementImagePath, (err) => { |
| if (err) { |
| console.error('Error processing image:', err); |
| return res.status(500).send('Error processing image'); |
| } |
| res.sendFile(outputImagePath, { root: '.' }); |
| }); |
| }); |
|
|
| app.listen(port, () => { |
| console.log(`Server is running on http://localhost:${port}`); |
| }); |