apexify.js 3.2.5 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tsbuildinfo +1 -1
- package/README.md +4 -806
- package/change logs.md +19 -0
- package/dist/ai/ApexAI.d.ts +15 -6
- package/dist/ai/ApexAI.d.ts.map +1 -1
- package/dist/ai/ApexAI.js +102 -29
- package/dist/ai/ApexAI.js.map +1 -1
- package/dist/ai/buttons/tools.d.ts.map +1 -1
- package/dist/ai/buttons/tools.js +1 -1
- package/dist/ai/buttons/tools.js.map +1 -1
- package/dist/ai/functions/aivoice.d.ts +1 -0
- package/dist/ai/functions/aivoice.d.ts.map +1 -0
- package/dist/ai/functions/aivoice.js +2 -0
- package/dist/ai/functions/aivoice.js.map +1 -0
- package/dist/ai/functions/draw.d.ts +1 -1
- package/dist/ai/functions/draw.d.ts.map +1 -1
- package/dist/ai/functions/draw.js +12 -1
- package/dist/ai/functions/draw.js.map +1 -1
- package/dist/ai/functions/generateVoiceResponse.d.ts +1 -1
- package/dist/ai/functions/generateVoiceResponse.d.ts.map +1 -1
- package/dist/ai/functions/generateVoiceResponse.js +3 -3
- package/dist/ai/functions/generateVoiceResponse.js.map +1 -1
- package/dist/ai/models.d.ts +1 -1
- package/dist/ai/models.d.ts.map +1 -1
- package/dist/ai/models.js +46 -28
- package/dist/ai/models.js.map +1 -1
- package/dist/ai/utils.d.ts.map +1 -1
- package/dist/ai/utils.js.map +1 -1
- package/dist/canvas/ApexPainter.d.ts +10 -10
- package/dist/canvas/ApexPainter.d.ts.map +1 -1
- package/dist/canvas/ApexPainter.js +21 -26
- package/dist/canvas/ApexPainter.js.map +1 -1
- package/dist/canvas/utils/bg.d.ts +1 -2
- package/dist/canvas/utils/bg.d.ts.map +1 -1
- package/dist/canvas/utils/bg.js +2 -5
- package/dist/canvas/utils/bg.js.map +1 -1
- package/dist/canvas/utils/charts.js +26 -26
- package/dist/canvas/utils/charts.js.map +1 -1
- package/dist/canvas/utils/general functions.d.ts +7 -7
- package/dist/canvas/utils/general functions.d.ts.map +1 -1
- package/dist/canvas/utils/general functions.js +47 -52
- package/dist/canvas/utils/general functions.js.map +1 -1
- package/dist/canvas/utils/types.d.ts +1 -3
- package/dist/canvas/utils/types.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -1
- package/dist/index.js.map +1 -1
- package/lib/ai/ApexAI.ts +550 -0
- package/lib/ai/buttons/drawMenu.ts +361 -0
- package/lib/ai/buttons/tools.ts +550 -0
- package/lib/ai/functions/chunkString.ts +3 -0
- package/lib/ai/functions/draw.ts +440 -0
- package/lib/ai/functions/generateVoiceResponse.ts +177 -0
- package/lib/ai/functions/imageReader.ts +24 -0
- package/lib/ai/functions/readFiles.ts +34 -0
- package/lib/ai/functions/readImagess.ts +41 -0
- package/lib/ai/functions/shouldDrawImage.ts +7 -0
- package/lib/ai/functions/typeWriter.ts +24 -0
- package/lib/ai/models.ts +589 -0
- package/lib/ai/utils.ts +23 -0
- package/lib/canvas/ApexPainter.ts +572 -0
- package/lib/canvas/utils/bg.ts +79 -0
- package/lib/canvas/utils/charts.ts +524 -0
- package/lib/canvas/utils/circular.ts +17 -0
- package/lib/canvas/utils/customLines.ts +49 -0
- package/lib/canvas/utils/general functions.ts +434 -0
- package/lib/canvas/utils/imageProperties.ts +403 -0
- package/lib/canvas/utils/radius.ts +26 -0
- package/lib/canvas/utils/textProperties.ts +68 -0
- package/lib/canvas/utils/types.ts +417 -0
- package/lib/canvas/utils/utils.ts +59 -0
- package/lib/index.ts +38 -0
- package/lib/utils.ts +8 -0
- package/package.json +15 -2
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
import { cropOptions } from './types';
|
|
5
|
+
import { createCanvas, loadImage } from '@napi-rs/canvas';
|
|
6
|
+
import Jimp from 'jimp';
|
|
7
|
+
|
|
8
|
+
export async function loadImages(imagePath: string) {
|
|
9
|
+
try {
|
|
10
|
+
if (!imagePath) {
|
|
11
|
+
throw new Error("Image path is required.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (imagePath.startsWith("http")) {
|
|
15
|
+
const response = await axios.get<ArrayBuffer>(imagePath, {
|
|
16
|
+
responseType: "arraybuffer",
|
|
17
|
+
});
|
|
18
|
+
return sharp(response.data);
|
|
19
|
+
} else {
|
|
20
|
+
const absolutePath = path.join(process.cwd(), imagePath);
|
|
21
|
+
return sharp(absolutePath);
|
|
22
|
+
}
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error("Error loading image:", error);
|
|
25
|
+
throw new Error("Failed to load image");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function resizingImg(resizeOptions: any): Promise<any> {
|
|
30
|
+
try {
|
|
31
|
+
if (!resizeOptions.imagePath) {
|
|
32
|
+
throw new Error("Image path is required for resizing.");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let absoluteImagePath: string;
|
|
36
|
+
|
|
37
|
+
if (resizeOptions.imagePath.startsWith("http")) {
|
|
38
|
+
|
|
39
|
+
absoluteImagePath = resizeOptions.imagePath;
|
|
40
|
+
} else {
|
|
41
|
+
|
|
42
|
+
absoluteImagePath = path.join(process.cwd(), resizeOptions.imagePath);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const image = await loadImage(absoluteImagePath);
|
|
46
|
+
const canvas = createCanvas(resizeOptions.size?.width || 500, resizeOptions.size?.height || 500);
|
|
47
|
+
const ctx = canvas.getContext('2d');
|
|
48
|
+
|
|
49
|
+
if (!ctx) {
|
|
50
|
+
throw new Error('Unable to get 2D rendering context from canvas');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ctx.drawImage(image, 0, 0, resizeOptions.size?.width || 500, resizeOptions.size?.height || 500);
|
|
54
|
+
|
|
55
|
+
const resizedBuffer = await canvas.toBuffer('image/png');
|
|
56
|
+
|
|
57
|
+
return resizedBuffer;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Error resizing image:", error);
|
|
60
|
+
throw new Error("Failed to resize image");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function converter(imagePath: string, newExtension: string) {
|
|
65
|
+
try {
|
|
66
|
+
const validExtensions: (keyof sharp.FormatEnum)[] = ['jpeg', 'png', 'webp', 'tiff', 'gif', 'avif', 'heif', 'raw', 'pdf', 'svg'];
|
|
67
|
+
|
|
68
|
+
const newExt = newExtension.toLowerCase();
|
|
69
|
+
if (!validExtensions.includes(newExt as keyof sharp.FormatEnum)) {
|
|
70
|
+
throw new Error(`Invalid image format: ${newExt}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let image: sharp.Sharp;
|
|
74
|
+
|
|
75
|
+
if (imagePath.startsWith("http")) {
|
|
76
|
+
const response = await axios.get<ArrayBuffer>(imagePath, {
|
|
77
|
+
responseType: "arraybuffer",
|
|
78
|
+
});
|
|
79
|
+
image = sharp(Buffer.from(response.data));
|
|
80
|
+
} else {
|
|
81
|
+
if (!imagePath) {
|
|
82
|
+
throw new Error("Image path is required.");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const absolutePath = path.join(process.cwd(), imagePath);
|
|
86
|
+
image = sharp(absolutePath);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const convertedBuffer = await image.toFormat(newExt as keyof sharp.FormatEnum).toBuffer();
|
|
90
|
+
return convertedBuffer;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error("Error changing image extension:", error);
|
|
93
|
+
throw new Error("Failed to change image extension");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function applyColorFilters(imagePath: string, filterColor: string) {
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
let image: any;
|
|
101
|
+
|
|
102
|
+
if (imagePath.startsWith("http")) {
|
|
103
|
+
|
|
104
|
+
const pngBuffer = await converter(imagePath, "png");
|
|
105
|
+
image = sharp(pngBuffer);
|
|
106
|
+
} else {
|
|
107
|
+
|
|
108
|
+
const imagePathResolved = path.join(process.cwd(), imagePath);
|
|
109
|
+
image = sharp(imagePathResolved);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const hexColorRegex = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
113
|
+
if (!hexColorRegex.test(filterColor)) {
|
|
114
|
+
throw new Error("Invalid color format. Only hex colors are supported.");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
image.tint(filterColor);
|
|
118
|
+
|
|
119
|
+
const outputBuffer = await image.toBuffer();
|
|
120
|
+
return outputBuffer;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.error("Error applying color filter:", error);
|
|
123
|
+
throw new Error("Failed to apply color filter");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function imgEffects(imagePath: string, filters: any[]) {
|
|
128
|
+
try {
|
|
129
|
+
let jimpImage;
|
|
130
|
+
|
|
131
|
+
if (imagePath.startsWith("http")) {
|
|
132
|
+
const pngBuffer = await converter(imagePath, "png");
|
|
133
|
+
jimpImage = await Jimp.read(pngBuffer);
|
|
134
|
+
} else {
|
|
135
|
+
const imagePathResolved = path.join(process.cwd(), imagePath)
|
|
136
|
+
jimpImage = await Jimp.read(imagePathResolved);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const filter of filters) {
|
|
140
|
+
switch (filter.type) {
|
|
141
|
+
case "flip":
|
|
142
|
+
jimpImage.flip(filter.horizontal, filter.vertical);
|
|
143
|
+
break;
|
|
144
|
+
case "mirror":
|
|
145
|
+
jimpImage.mirror(filter.horizontal, filter.vertical);
|
|
146
|
+
break;
|
|
147
|
+
case "rotate":
|
|
148
|
+
jimpImage.rotate(filter.deg, filter.mode);
|
|
149
|
+
break;
|
|
150
|
+
case "brightness":
|
|
151
|
+
jimpImage.brightness(filter.value);
|
|
152
|
+
break;
|
|
153
|
+
case "contrast":
|
|
154
|
+
jimpImage.contrast(filter.value);
|
|
155
|
+
break;
|
|
156
|
+
case "dither565":
|
|
157
|
+
jimpImage.dither565();
|
|
158
|
+
break;
|
|
159
|
+
case "greyscale":
|
|
160
|
+
jimpImage.greyscale();
|
|
161
|
+
break;
|
|
162
|
+
case "invert":
|
|
163
|
+
jimpImage.invert();
|
|
164
|
+
break;
|
|
165
|
+
case "normalize":
|
|
166
|
+
jimpImage.normalize();
|
|
167
|
+
break;
|
|
168
|
+
case "autocrop":
|
|
169
|
+
jimpImage.autocrop(filter.tolerance || 0);
|
|
170
|
+
break;
|
|
171
|
+
case "crop":
|
|
172
|
+
jimpImage.crop(filter.x, filter.y, filter.w, filter.h);
|
|
173
|
+
break;
|
|
174
|
+
case "fade":
|
|
175
|
+
jimpImage.fade(filter.factor);
|
|
176
|
+
break;
|
|
177
|
+
case "opacity":
|
|
178
|
+
jimpImage.opacity(filter.factor);
|
|
179
|
+
break;
|
|
180
|
+
case "opaque":
|
|
181
|
+
jimpImage.opaque();
|
|
182
|
+
break;
|
|
183
|
+
case "gaussian":
|
|
184
|
+
jimpImage.gaussian(filter.radius);
|
|
185
|
+
break;
|
|
186
|
+
case "blur":
|
|
187
|
+
jimpImage.blur(filter.radius);
|
|
188
|
+
break;
|
|
189
|
+
case "posterize":
|
|
190
|
+
jimpImage.posterize(filter.levels);
|
|
191
|
+
break;
|
|
192
|
+
case "sepia":
|
|
193
|
+
jimpImage.sepia();
|
|
194
|
+
break;
|
|
195
|
+
case "pixelate":
|
|
196
|
+
jimpImage.pixelate(
|
|
197
|
+
filter.size,
|
|
198
|
+
filter.x,
|
|
199
|
+
filter.y,
|
|
200
|
+
filter.w,
|
|
201
|
+
filter.h,
|
|
202
|
+
);
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
console.error(`Unsupported filter type: ${filter.type}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const outputMimeType = jimpImage._originalMime || Jimp.MIME_PNG;
|
|
210
|
+
|
|
211
|
+
return await jimpImage.getBufferAsync(outputMimeType);
|
|
212
|
+
} catch (error: any) {
|
|
213
|
+
console.error("Error processing image:", error.message);
|
|
214
|
+
throw new Error("Failed to process image");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
export async function cropInner(options: cropOptions): Promise<any> {
|
|
220
|
+
try {
|
|
221
|
+
|
|
222
|
+
let image;
|
|
223
|
+
|
|
224
|
+
if (options.imageSource.startsWith('http')) {
|
|
225
|
+
image = await loadImage(options.imageSource);
|
|
226
|
+
} else {
|
|
227
|
+
image = await loadImage(path.join(process.cwd(), options.imageSource));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const width = Math.abs(options.coordinates[0].from.x - options.coordinates[1].to.x);
|
|
231
|
+
const height = Math.abs(options.coordinates[0].from.y - options.coordinates[2].to.y);
|
|
232
|
+
|
|
233
|
+
const canvas = createCanvas(width, height);
|
|
234
|
+
const ctx = canvas.getContext('2d');
|
|
235
|
+
|
|
236
|
+
if (options.radius === 'circular') {
|
|
237
|
+
const radius = Math.min(width, height) / 2;
|
|
238
|
+
ctx.arc(width / 2, height / 2, radius, 0, Math.PI * 2);
|
|
239
|
+
ctx.closePath();
|
|
240
|
+
ctx.clip();
|
|
241
|
+
} else if (typeof options.radius === 'number' && options.radius >= 0) {
|
|
242
|
+
ctx.beginPath();
|
|
243
|
+
ctx.moveTo(options.radius, 0);
|
|
244
|
+
ctx.lineTo(width - options.radius, 0);
|
|
245
|
+
ctx.quadraticCurveTo(width, 0, width, options.radius);
|
|
246
|
+
ctx.lineTo(width, height - options.radius);
|
|
247
|
+
ctx.quadraticCurveTo(width, height, width - options.radius, height);
|
|
248
|
+
ctx.lineTo(options.radius, height);
|
|
249
|
+
ctx.quadraticCurveTo(0, height, 0, height - options.radius);
|
|
250
|
+
ctx.lineTo(0, options.radius);
|
|
251
|
+
ctx.quadraticCurveTo(0, 0, options.radius, 0);
|
|
252
|
+
ctx.closePath();
|
|
253
|
+
ctx.clip();
|
|
254
|
+
} else if (options.radius !== null && options.radius !== 'circular' && typeof options.radius !== 'number' ) {
|
|
255
|
+
throw new Error('The "radius" option can only be "circular" or a non-negative number.');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
ctx.drawImage(image, options.coordinates[0].from.x, options.coordinates[0].from.y, width, height, 0, 0, width, height);
|
|
259
|
+
|
|
260
|
+
ctx.beginPath();
|
|
261
|
+
for (let i = 0; i < options.coordinates.length; i++) {
|
|
262
|
+
const coordinate = options.coordinates[i];
|
|
263
|
+
const nextCoordinate = options.coordinates[(i + 1) % options.coordinates.length];
|
|
264
|
+
const tension = coordinate.tension || 0;
|
|
265
|
+
const cp1x = coordinate.from.x + (nextCoordinate.from.x - coordinate.from.x) * tension;
|
|
266
|
+
const cp1y = coordinate.from.y + (nextCoordinate.from.y - coordinate.from.y) * tension;
|
|
267
|
+
const cp2x = coordinate.to.x - (nextCoordinate.to.x - coordinate.to.x) * tension;
|
|
268
|
+
const cp2y = coordinate.to.y - (nextCoordinate.to.y - coordinate.to.y) * tension;
|
|
269
|
+
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, coordinate.to.x, coordinate.to.y);
|
|
270
|
+
}
|
|
271
|
+
ctx.closePath();
|
|
272
|
+
|
|
273
|
+
const buffer = canvas.toBuffer('image/png');
|
|
274
|
+
|
|
275
|
+
return buffer;
|
|
276
|
+
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.error('An error occurred:', error);
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export async function cropOuter(options: cropOptions): Promise<any> {
|
|
284
|
+
try {
|
|
285
|
+
|
|
286
|
+
let image;
|
|
287
|
+
|
|
288
|
+
if (options.imageSource.startsWith('http')) {
|
|
289
|
+
image = await loadImage(options.imageSource);
|
|
290
|
+
} else {
|
|
291
|
+
image = await loadImage(path.join(process.cwd(), options.imageSource));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const canvas = createCanvas(image.width, image.height);
|
|
295
|
+
const ctx = canvas.getContext('2d');
|
|
296
|
+
|
|
297
|
+
ctx.drawImage(image, 0, 0);
|
|
298
|
+
|
|
299
|
+
ctx.beginPath();
|
|
300
|
+
ctx.moveTo(options.coordinates[0].from.x, options.coordinates[0].from.y);
|
|
301
|
+
for (let i = 0; i < options.coordinates.length; i++) {
|
|
302
|
+
const coordinate = options.coordinates[i];
|
|
303
|
+
const nextCoordinate = options.coordinates[(i + 1) % options.coordinates.length];
|
|
304
|
+
const tension = coordinate.tension || 0;
|
|
305
|
+
const cp1x = coordinate.from.x + (nextCoordinate.from.x - coordinate.from.x) * tension;
|
|
306
|
+
const cp1y = coordinate.from.y + (nextCoordinate.from.y - coordinate.from.y) * tension;
|
|
307
|
+
const cp2x = coordinate.to.x - (nextCoordinate.to.x - coordinate.to.x) * tension;
|
|
308
|
+
const cp2y = coordinate.to.y - (nextCoordinate.to.y - coordinate.to.y) * tension;
|
|
309
|
+
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, coordinate.to.x, coordinate.to.y);
|
|
310
|
+
}
|
|
311
|
+
ctx.closePath();
|
|
312
|
+
|
|
313
|
+
ctx.clip();
|
|
314
|
+
|
|
315
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
316
|
+
|
|
317
|
+
const buffer = canvas.toBuffer('image/png');
|
|
318
|
+
|
|
319
|
+
return buffer;
|
|
320
|
+
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('An error occurred:', error);
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
export async function detectColors(imagePath: string): Promise<{ color: string; frequency: string }[]> {
|
|
329
|
+
try {
|
|
330
|
+
let image: any;
|
|
331
|
+
if (imagePath.startsWith('http')) {
|
|
332
|
+
const response = await axios.get(imagePath, { responseType: 'arraybuffer' });
|
|
333
|
+
const buffer = Buffer.from(response.data, 'binary');
|
|
334
|
+
image = await loadImage(buffer);
|
|
335
|
+
} else {
|
|
336
|
+
const localImagePath = path.join(process.cwd(), imagePath);
|
|
337
|
+
image = await loadImage(localImagePath); }
|
|
338
|
+
|
|
339
|
+
const canvas = createCanvas(image.width, image.height);
|
|
340
|
+
const ctx = canvas.getContext('2d') as any;
|
|
341
|
+
|
|
342
|
+
ctx.drawImage(image, 0, 0, image.width, image.height);
|
|
343
|
+
|
|
344
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
345
|
+
const data = imageData.data;
|
|
346
|
+
|
|
347
|
+
const colorFrequency: { [color: string]: number } = {};
|
|
348
|
+
|
|
349
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
350
|
+
const red = data[i];
|
|
351
|
+
const green = data[i + 1];
|
|
352
|
+
const blue = data[i + 2];
|
|
353
|
+
|
|
354
|
+
const color = `${red},${green},${blue}`;
|
|
355
|
+
|
|
356
|
+
colorFrequency[color] = (colorFrequency[color] || 0) + 1;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const totalPixels = canvas.width * canvas.height;
|
|
360
|
+
|
|
361
|
+
const dominantColors = Object.entries(colorFrequency)
|
|
362
|
+
.filter(([color, frequency]: [string, number]) => (frequency / totalPixels) * 100 >= 2)
|
|
363
|
+
.map(([color, frequency]: [string, number]) => ({
|
|
364
|
+
color,
|
|
365
|
+
frequency: ((frequency / totalPixels) * 100).toFixed(2),
|
|
366
|
+
}));
|
|
367
|
+
|
|
368
|
+
return dominantColors;
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Error:', error);
|
|
371
|
+
return [];
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export async function removeColor(inputImagePath: string, colorToRemove: { red: number; green: number; blue: number }): Promise<Buffer | undefined> {
|
|
376
|
+
try {
|
|
377
|
+
let image: any;
|
|
378
|
+
if (inputImagePath.startsWith('http')) {
|
|
379
|
+
const response = await axios.get(inputImagePath, { responseType: 'arraybuffer' });
|
|
380
|
+
const buffer = Buffer.from(response.data, 'binary');
|
|
381
|
+
image = await loadImage(buffer);
|
|
382
|
+
} else {
|
|
383
|
+
const localImagePath = path.join(process.cwd(), inputImagePath);
|
|
384
|
+
image = await loadImage(localImagePath); }
|
|
385
|
+
|
|
386
|
+
const canvas = createCanvas(image.width, image.height);
|
|
387
|
+
const ctx = canvas.getContext('2d') as any;
|
|
388
|
+
|
|
389
|
+
ctx.drawImage(image, 0, 0, image.width, image.height);
|
|
390
|
+
|
|
391
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
392
|
+
|
|
393
|
+
for (let i = 0; i < imageData.data.length; i += 4) {
|
|
394
|
+
const red = imageData.data[i];
|
|
395
|
+
const green = imageData.data[i + 1];
|
|
396
|
+
const blue = imageData.data[i + 2];
|
|
397
|
+
const alpha = imageData.data[i + 3];
|
|
398
|
+
|
|
399
|
+
if (red === colorToRemove.red && green === colorToRemove.green && blue === colorToRemove.blue) {
|
|
400
|
+
imageData.data[i + 3] = 0;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
ctx.putImageData(imageData, 0, 0);
|
|
405
|
+
|
|
406
|
+
return canvas.toBuffer('image/png');
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error('Error:', error);
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export async function bgRemoval(imgURL: string, API_KEY: string): Promise<Buffer | undefined> {
|
|
414
|
+
try {
|
|
415
|
+
|
|
416
|
+
if (!API_KEY) {
|
|
417
|
+
throw new Error("API_KEY is required. Please visit remove.bg, create an account, and obtain your API key at: https://accounts.kaleido.ai/users/sign_in#api-key");
|
|
418
|
+
}
|
|
419
|
+
const response = await axios.post('https://api.remove.bg/v1.0/removebg', {
|
|
420
|
+
image_url: imgURL,
|
|
421
|
+
size: 'auto'
|
|
422
|
+
}, {
|
|
423
|
+
headers: {
|
|
424
|
+
'X-Api-Key': API_KEY
|
|
425
|
+
},
|
|
426
|
+
responseType: 'arraybuffer'
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
return Buffer.from(response.data, 'binary');
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.error('Error:', error);
|
|
432
|
+
return undefined;
|
|
433
|
+
}
|
|
434
|
+
}
|