rn-remove-image-bg 0.0.14 → 0.0.15
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/lib/ImageProcessing.web.js +76 -9
- package/package.json +1 -1
- package/src/ImageProcessing.web.ts +84 -12
|
@@ -60,22 +60,89 @@ export async function removeBgImage(uri, options = {}) {
|
|
|
60
60
|
const result = Array.isArray(results) ? results[0] : results;
|
|
61
61
|
if (!result?.mask)
|
|
62
62
|
throw new Error('No mask returned');
|
|
63
|
-
//
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
: toDataUrl(result.mask, format, quality);
|
|
63
|
+
// Apply mask to original image
|
|
64
|
+
const original = await loadImage(uri);
|
|
65
|
+
const dataUrl = await applyMask(original, result.mask, format, quality);
|
|
67
66
|
if (debug)
|
|
68
67
|
console.log('[rmbg] Done');
|
|
69
68
|
onProgress?.(100);
|
|
70
69
|
return dataUrl;
|
|
71
70
|
}
|
|
72
|
-
function
|
|
71
|
+
async function applyMask(image, mask, format, quality) {
|
|
73
72
|
const canvas = document.createElement('canvas');
|
|
74
|
-
|
|
75
|
-
canvas.
|
|
73
|
+
// Use original image dimensions
|
|
74
|
+
canvas.width = image.width;
|
|
75
|
+
canvas.height = image.height;
|
|
76
76
|
const ctx = canvas.getContext('2d');
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
if (!ctx)
|
|
78
|
+
throw new Error('Could not get canvas context');
|
|
79
|
+
// Draw original image
|
|
80
|
+
ctx.drawImage(image, 0, 0);
|
|
81
|
+
// Get image data to manipulate pixels
|
|
82
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
83
|
+
const pixelData = imageData.data;
|
|
84
|
+
// Process mask
|
|
85
|
+
let maskData;
|
|
86
|
+
let maskWidth;
|
|
87
|
+
let maskHeight;
|
|
88
|
+
if (typeof mask === 'string') {
|
|
89
|
+
// If mask is a URL, load it
|
|
90
|
+
const maskImg = await loadImage(mask);
|
|
91
|
+
const maskCanvas = document.createElement('canvas');
|
|
92
|
+
maskCanvas.width = canvas.width;
|
|
93
|
+
maskCanvas.height = canvas.height;
|
|
94
|
+
const maskCtx = maskCanvas.getContext('2d');
|
|
95
|
+
if (!maskCtx)
|
|
96
|
+
throw new Error('Could not get mask context');
|
|
97
|
+
// Draw and resize mask to match image
|
|
98
|
+
maskCtx.drawImage(maskImg, 0, 0, canvas.width, canvas.height);
|
|
99
|
+
const maskImageData = maskCtx.getImageData(0, 0, canvas.width, canvas.height);
|
|
100
|
+
maskData = maskImageData.data;
|
|
101
|
+
maskWidth = canvas.width;
|
|
102
|
+
maskHeight = canvas.height;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// @ts-ignore - Transformers.js types are loose
|
|
106
|
+
maskData = mask.data;
|
|
107
|
+
maskWidth = mask.width;
|
|
108
|
+
maskHeight = mask.height;
|
|
109
|
+
}
|
|
110
|
+
// Helper to get alpha value from mask data
|
|
111
|
+
const getAlpha = (index, data, width, height, targetWidth, targetHeight) => {
|
|
112
|
+
// If dimensions match
|
|
113
|
+
if (width === targetWidth && height === targetHeight) {
|
|
114
|
+
// Check if mask is single channel (grayscale) or RGBA
|
|
115
|
+
if (data.length === width * height) {
|
|
116
|
+
return data[index / 4];
|
|
117
|
+
}
|
|
118
|
+
else if (data.length === width * height * 3) {
|
|
119
|
+
return data[Math.floor(index / 4) * 3];
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
return data[index]; // Assume RGBA red channel or alpha channel usage
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Nearest neighbor resizing
|
|
126
|
+
const x = (index / 4) % targetWidth;
|
|
127
|
+
const y = Math.floor((index / 4) / targetWidth);
|
|
128
|
+
const maskX = Math.floor(x * (width / targetWidth));
|
|
129
|
+
const maskY = Math.floor(y * (height / targetHeight));
|
|
130
|
+
const maskIndex = (maskY * width + maskX);
|
|
131
|
+
if (data.length === width * height) {
|
|
132
|
+
return data[maskIndex];
|
|
133
|
+
}
|
|
134
|
+
else if (data.length === width * height * 3) {
|
|
135
|
+
return data[maskIndex * 3];
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return data[maskIndex * 4];
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
// Apply alpha
|
|
142
|
+
for (let i = 0; i < pixelData.length; i += 4) {
|
|
143
|
+
const alpha = getAlpha(i, maskData, maskWidth, maskHeight, canvas.width, canvas.height) ?? 255;
|
|
144
|
+
pixelData[i + 3] = alpha;
|
|
145
|
+
}
|
|
79
146
|
ctx.putImageData(imageData, 0, 0);
|
|
80
147
|
return canvas.toDataURL(format === 'WEBP' ? 'image/webp' : 'image/png', quality / 100);
|
|
81
148
|
}
|
package/package.json
CHANGED
|
@@ -84,10 +84,9 @@ export async function removeBgImage(
|
|
|
84
84
|
const result = Array.isArray(results) ? results[0] : results;
|
|
85
85
|
if (!result?.mask) throw new Error('No mask returned');
|
|
86
86
|
|
|
87
|
-
//
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
: toDataUrl(result.mask, format, quality);
|
|
87
|
+
// Apply mask to original image
|
|
88
|
+
const original = await loadImage(uri);
|
|
89
|
+
const dataUrl = await applyMask(original, result.mask, format, quality);
|
|
91
90
|
|
|
92
91
|
if (debug) console.log('[rmbg] Done');
|
|
93
92
|
onProgress?.(100);
|
|
@@ -95,17 +94,90 @@ export async function removeBgImage(
|
|
|
95
94
|
return dataUrl;
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
function
|
|
99
|
-
|
|
97
|
+
async function applyMask(
|
|
98
|
+
image: HTMLImageElement,
|
|
99
|
+
mask: { width: number; height: number; data: Uint8Array | Uint8ClampedArray } | string,
|
|
100
100
|
format: OutputFormat,
|
|
101
101
|
quality: number
|
|
102
|
-
): string {
|
|
102
|
+
): Promise<string> {
|
|
103
103
|
const canvas = document.createElement('canvas');
|
|
104
|
-
|
|
105
|
-
canvas.
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
104
|
+
// Use original image dimensions
|
|
105
|
+
canvas.width = image.width;
|
|
106
|
+
canvas.height = image.height;
|
|
107
|
+
const ctx = canvas.getContext('2d');
|
|
108
|
+
if (!ctx) throw new Error('Could not get canvas context');
|
|
109
|
+
|
|
110
|
+
// Draw original image
|
|
111
|
+
ctx.drawImage(image, 0, 0);
|
|
112
|
+
|
|
113
|
+
// Get image data to manipulate pixels
|
|
114
|
+
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
115
|
+
const pixelData = imageData.data;
|
|
116
|
+
|
|
117
|
+
// Process mask
|
|
118
|
+
let maskData: Uint8ClampedArray | Uint8Array;
|
|
119
|
+
let maskWidth: number;
|
|
120
|
+
let maskHeight: number;
|
|
121
|
+
|
|
122
|
+
if (typeof mask === 'string') {
|
|
123
|
+
// If mask is a URL, load it
|
|
124
|
+
const maskImg = await loadImage(mask);
|
|
125
|
+
const maskCanvas = document.createElement('canvas');
|
|
126
|
+
maskCanvas.width = canvas.width;
|
|
127
|
+
maskCanvas.height = canvas.height;
|
|
128
|
+
const maskCtx = maskCanvas.getContext('2d');
|
|
129
|
+
if (!maskCtx) throw new Error('Could not get mask context');
|
|
130
|
+
|
|
131
|
+
// Draw and resize mask to match image
|
|
132
|
+
maskCtx.drawImage(maskImg, 0, 0, canvas.width, canvas.height);
|
|
133
|
+
const maskImageData = maskCtx.getImageData(0, 0, canvas.width, canvas.height);
|
|
134
|
+
maskData = maskImageData.data;
|
|
135
|
+
maskWidth = canvas.width;
|
|
136
|
+
maskHeight = canvas.height;
|
|
137
|
+
} else {
|
|
138
|
+
// @ts-ignore - Transformers.js types are loose
|
|
139
|
+
maskData = mask.data;
|
|
140
|
+
maskWidth = mask.width;
|
|
141
|
+
maskHeight = mask.height;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Helper to get alpha value from mask data
|
|
145
|
+
const getAlpha = (index: number, data: Uint8ClampedArray | Uint8Array, width: number, height: number, targetWidth: number, targetHeight: number) => {
|
|
146
|
+
// If dimensions match
|
|
147
|
+
if (width === targetWidth && height === targetHeight) {
|
|
148
|
+
// Check if mask is single channel (grayscale) or RGBA
|
|
149
|
+
if (data.length === width * height) {
|
|
150
|
+
return data[index / 4];
|
|
151
|
+
} else if (data.length === width * height * 3) {
|
|
152
|
+
return data[Math.floor(index / 4) * 3];
|
|
153
|
+
} else {
|
|
154
|
+
return data[index]; // Assume RGBA red channel or alpha channel usage
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Nearest neighbor resizing
|
|
159
|
+
const x = (index / 4) % targetWidth;
|
|
160
|
+
const y = Math.floor((index / 4) / targetWidth);
|
|
161
|
+
|
|
162
|
+
const maskX = Math.floor(x * (width / targetWidth));
|
|
163
|
+
const maskY = Math.floor(y * (height / targetHeight));
|
|
164
|
+
const maskIndex = (maskY * width + maskX);
|
|
165
|
+
|
|
166
|
+
if (data.length === width * height) {
|
|
167
|
+
return data[maskIndex];
|
|
168
|
+
} else if (data.length === width * height * 3) {
|
|
169
|
+
return data[maskIndex * 3];
|
|
170
|
+
} else {
|
|
171
|
+
return data[maskIndex * 4];
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Apply alpha
|
|
176
|
+
for (let i = 0; i < pixelData.length; i += 4) {
|
|
177
|
+
const alpha = getAlpha(i, maskData, maskWidth, maskHeight, canvas.width, canvas.height) ?? 255;
|
|
178
|
+
pixelData[i + 3] = alpha;
|
|
179
|
+
}
|
|
180
|
+
|
|
109
181
|
ctx.putImageData(imageData, 0, 0);
|
|
110
182
|
return canvas.toDataURL(format === 'WEBP' ? 'image/webp' : 'image/png', quality / 100);
|
|
111
183
|
}
|