| import express from 'express'; |
| import fs from 'fs'; |
| import path from 'path'; |
| import cv from 'opencv4nodejs'; |
| import Jimp from 'jimp'; |
| import axios from 'axios'; |
| import sharp from 'sharp'; |
|
|
| const app = express(); |
| const port = 7860; |
| const outputDir = './output'; |
|
|
| |
| if (!fs.existsSync(outputDir)){ |
| fs.mkdirSync(outputDir); |
| } |
| |
| 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, dest) { |
| try { |
| const response = await axios({ |
| method: 'get', |
| url: url, |
| responseType: 'arraybuffer' |
| }); |
|
|
| await sharp(response.data) |
| .toFormat('jpeg') |
| .toFile(dest); |
|
|
| console.log('Image downloaded and converted successfully.'); |
| } catch (error) { |
| console.error('Error downloading or converting image:', error); |
| throw error; |
| } |
| } |
|
|
| |
| async function processImage(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath) { |
| try { |
| const classifiers = await loadClassifiersFromFolder(classifiersFolder); |
| |
| if (!fs.existsSync(inputImagePath)) { |
| throw new Error(`Input image not found at ${inputImagePath}`); |
| } |
|
|
| |
| const jimpImage = await Jimp.read(inputImagePath); |
| if (!jimpImage) { |
| throw new Error('Failed to read the image with Jimp. The image file might be corrupted or not supported.'); |
| } |
|
|
| const img = cv.imread(inputImagePath); |
| 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(replacementImagePath); |
| allFaces.forEach((faceRect, i) => { |
| 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); |
| } catch (error) { |
| console.error('Error during image processing:', error); |
| throw error; |
| } |
| } |
|
|
| |
| app.get('/process-image', async (req, res) => { |
| const { imageUrl } = req.query; |
|
|
| if (!imageUrl) { |
| return res.status(400).send('Image URL is required'); |
| } |
|
|
| const inputImagePath = path.join(outputDir, 'test.jpg'); |
| const outputImagePath = path.join(outputDir, 'image_with_replaced_faces.jpg'); |
| const replacementImagePath = './replacement_face.png'; |
| const classifiersFolder = './classifiers'; |
|
|
| try { |
| |
| await downloadAndConvertImage(imageUrl, inputImagePath); |
| |
| if (!fs.existsSync(inputImagePath) || fs.statSync(inputImagePath).size === 0) { |
| throw new Error('Downloaded image file is not found or is empty.'); |
| } |
| |
| await processImage(inputImagePath, outputImagePath, classifiersFolder, replacementImagePath); |
|
|
| |
| res.sendFile(outputImagePath, { root: '.' }); |
| } 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}`); |
| }); |
|
|