Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# API Documentation

This document provides information on how to run the API endpoints for the Text Behind Image project. The API is built using Express.js and runs separately from the Next.js application to handle image processing tasks.

## Why a Separate API?

A separate Express.js API is implemented for this project to address limitations in the current browser-based implementation:

1. **Offload Computation**: The current implementation relies on the browser for all computation, which can be resource-intensive. A separate API allows these tasks to be performed server-side.

2. **Performance**: Image processing is computationally expensive. An API offloads this work from the client, improving overall application performance.

3. **Scalability**: A dedicated API enables independent scaling of image processing services as the application grows.

4. **Broader Compatibility**: Server-side processing ensures the application works across various devices and browsers, regardless of their computational capabilities.

5. **Resource Management**: Certain image processing libraries and operations are better suited for a controlled server environment than a browser context.

## Running the API

To start the API server:

1. Navigate to the project root directory.
2. Run the following command:

```
npm run api
```

3. The API server will start running on `http://localhost:3000` (or the port specified in your environment variables).

## API Endpoints

We have implemented two separate API endpoints for our image processing tasks:

1. **Remove Background**: This endpoint removes the background from an uploaded image.
2. **Preview Image**: This endpoint adds text to an image with a removed background and generates a preview.

The reason for implementing two separate endpoints is to allow for more flexibility and better performance. By separating the background removal and text addition processes, we can:

- Allow users to remove backgrounds without necessarily adding text.
- Cache or save the background-removed images for future use.
- Reduce processing time when users want to change only the text without re-processing the background removal.

### 1. Remove Background API

**Endpoint**: `/api/remove-background`

**Method**: POST

**Content-Type**: multipart/form-data

**Sample cURL Request**:
```bash
curl -X POST \
http://localhost:3000/api/remove-background \
-H 'Content-Type: multipart/form-data' \
-F 'image=@/path/to/your/image.jpeg'
```

**Expected Response**:

The API will save the image to the `uploads` directory and return the path to the removed background image.
```json
{
"success": true,
"removedBgImagePath": "/uploads/bg_removed_[filename].png",
"originalImagePath": "/uploads/[filename]"
}
```

### 2. Preview Image API

The Preview Image API is used to add text to an image with a removed background.

**Endpoint**: `/api/preview-image`

**Method**: POST

**Content-Type**: application/json

**Sample cURL Request**:
```bash
curl -X POST \
http://localhost:3000/api/preview-image \
-H 'Content-Type: application/json' \
-d '{
"originalImagePath": "path_to_your_root_directory/uploads/[filename]",
"removedBgImagePath": "path_to_your_root_directory/uploads/bg_removed_[filename].png",
"textParams": {
"text": "Sample Text",
"fontFamily": "Arial",
"fontSize": 100,
"fontWeight": 650,
"color": "#ea4500",
"top": 10,
"left": 2,
"rotation": -15,
"opacity": 1,
"shadowColor": "rgba(10, 10, 0, 0.5)",
"shadowSize": 10
}
}'
```

**Expected Response**:
The API will save the preview image to the `uploads` directory without returning the path to the preview image.


## Reason for Implementing Two Separate API Endpoints

The main reason for separating background removal and text addition is flexibility. This allows users to make multiple attempts at formatting text without needing to remove the background each time, significantly improving efficiency and user experience.
103 changes: 103 additions & 0 deletions api/bgremoval.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Importing necessary modules
const { removeBackground } = require('@imgly/background-removal-node');
const { createCanvas, loadImage } = require('canvas');
const fs = require('fs');

// Function to remove background from an image
async function removeImageBackground(imgSource) {
try {
// Removing background
const blob = await removeBackground(imgSource);

// Converting Blob to buffer
const buffer = Buffer.from(await blob.arrayBuffer());

// Generating data URL
const dataURL = `data:image/png;base64,${buffer.toString("base64")}`;

// Returning the data URL
return dataURL;
} catch (error) {
// Handling errors
throw new Error('Error removing background: ' + error);
}
}

// Function to add text to an image
async function addTextToImage(imagePath, textParams) {
const image = await loadImage(imagePath);
const canvas = createCanvas(image.width, image.height);
const ctx = canvas.getContext('2d');

ctx.drawImage(image, 0, 0);

ctx.font = `${textParams.fontWeight} ${textParams.fontSize}px ${textParams.fontFamily}`;
ctx.fillStyle = textParams.color;
ctx.globalAlpha = textParams.opacity;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

const x = canvas.width * (textParams.left + 50) / 100;
const y = canvas.height * (50 - textParams.top) / 100;

ctx.save();
ctx.translate(x, y);
ctx.rotate((textParams.rotation * Math.PI) / 180);

if (textParams.shadowSize > 0) {
ctx.shadowColor = textParams.shadowColor;
ctx.shadowBlur = textParams.shadowSize;
ctx.shadowOffsetX = textParams.shadowSize;
ctx.shadowOffsetY = textParams.shadowSize;
}

ctx.fillText(textParams.text, 0, 0);
ctx.restore();

return canvas.toBuffer('image/png');
}

// Function to preview an image
async function previewImage(originalImagePath, removedBgImagePath, textParams) {
const originalImage = await loadImage(originalImagePath);
const removedBgImage = await loadImage(removedBgImagePath);

const canvas = createCanvas(originalImage.width, originalImage.height);
const ctx = canvas.getContext('2d');

// Draw original image with text
const imageWithText = await addTextToImage(originalImagePath, textParams);
const imageWithTextLoaded = await loadImage(imageWithText);
ctx.drawImage(imageWithTextLoaded, 0, 0);

// Draw removed background image on top
ctx.globalCompositeOperation = 'source-atop';
ctx.drawImage(removedBgImage, 0, 0);

return canvas.toBuffer('image/png');
}

// Example usage
async function main() {
try {
// Path to the input image
const imgSource = 'sample.png';

// Removing background from the input image
const resultDataURL = await removeImageBackground(imgSource);

// Writing the result to a file (optional)
fs.writeFileSync('output.png', resultDataURL.split(';base64,').pop(), { encoding: 'base64' });

// Logging success message
console.log('Background removed successfully.');
} catch (error) {
// Logging error message
console.error('Error:', error.message);
}
}

// Calling the main function
// main();

module.exports = { removeImageBackground, addTextToImage, previewImage };
Binary file added api/sample-file-dog.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading