@midscene/shared 0.4.0
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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/es/constants.js +15 -0
- package/dist/es/img.js +239 -0
- package/dist/es/index.js +7 -0
- package/dist/lib/constants.js +42 -0
- package/dist/lib/img.js +284 -0
- package/dist/lib/index.js +28 -0
- package/dist/types/constants.d.ts +10 -0
- package/dist/types/img.d.ts +111 -0
- package/dist/types/index.d.ts +3 -0
- package/package.json +74 -0
- package/src/constants/index.ts +10 -0
- package/src/img/box-select.ts +179 -0
- package/src/img/index.ts +9 -0
- package/src/img/info.ts +71 -0
- package/src/img/transform.ts +240 -0
- package/src/index.ts +3 -0
- package/src/modern-app-env.d.ts +1 -0
- package/src/types/index.ts +11 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import Jimp from 'jimp';
|
|
3
|
+
import type { Rect } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Saves a Base64-encoded image to a file
|
|
7
|
+
*
|
|
8
|
+
* @param options - An object containing the Base64-encoded image data and the output file path
|
|
9
|
+
* @param options.base64Data - The Base64-encoded image data
|
|
10
|
+
* @param options.outputPath - The path where the image will be saved
|
|
11
|
+
* @throws Error if there is an error during the saving process
|
|
12
|
+
*/
|
|
13
|
+
export async function saveBase64Image(options: {
|
|
14
|
+
base64Data: string;
|
|
15
|
+
outputPath: string;
|
|
16
|
+
}): Promise<void> {
|
|
17
|
+
const { base64Data, outputPath } = options;
|
|
18
|
+
// Remove the base64 data prefix (if any)
|
|
19
|
+
const base64Image = base64Data.split(';base64,').pop() || base64Data;
|
|
20
|
+
|
|
21
|
+
// Converts base64 data to buffer
|
|
22
|
+
const imageBuffer = Buffer.from(base64Image, 'base64');
|
|
23
|
+
|
|
24
|
+
// Use Jimp to process the image and save it to the specified location
|
|
25
|
+
const image = await Jimp.read(imageBuffer);
|
|
26
|
+
await image.writeAsync(outputPath);
|
|
27
|
+
|
|
28
|
+
console.log('Image successfully written to file.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Transforms an image path into a base64-encoded string
|
|
33
|
+
* @param inputPath - The path of the image file to be encoded
|
|
34
|
+
* @returns A Promise that resolves to a base64-encoded string representing the image file
|
|
35
|
+
*/
|
|
36
|
+
export async function transformImgPathToBase64(inputPath: string) {
|
|
37
|
+
// Use Jimp to process images and generate base64 data
|
|
38
|
+
const image = await Jimp.read(inputPath);
|
|
39
|
+
const buffer = await image.getBufferAsync(Jimp.MIME_PNG);
|
|
40
|
+
return buffer.toString('base64');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Resizes an image from a base64-encoded string
|
|
45
|
+
*
|
|
46
|
+
* @param base64Data - A base64-encoded string representing the image
|
|
47
|
+
* @returns A Promise that resolves to a base64-encoded string representing the resized image
|
|
48
|
+
* @throws An error if the width or height cannot be determined from the metadata
|
|
49
|
+
*/
|
|
50
|
+
export async function resizeImg(base64Data: string) {
|
|
51
|
+
// Remove the base64 data prefix (if any)
|
|
52
|
+
const base64Image = base64Data.split(';base64,').pop() || base64Data;
|
|
53
|
+
|
|
54
|
+
// Converts base64 data to buffer
|
|
55
|
+
const imageBuffer = Buffer.from(base64Image, 'base64');
|
|
56
|
+
|
|
57
|
+
const image = await Jimp.read(imageBuffer);
|
|
58
|
+
const { width, height } = image.bitmap;
|
|
59
|
+
if (!width || !height) {
|
|
60
|
+
throw Error('undefined width or height with url');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const newSize = calculateNewDimensions(width, height);
|
|
64
|
+
|
|
65
|
+
image.resize(newSize.width, newSize.height);
|
|
66
|
+
const buffer = await image.getBufferAsync(Jimp.MIME_PNG);
|
|
67
|
+
return buffer.toString('base64');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Calculates new dimensions for an image while maintaining its aspect ratio.
|
|
72
|
+
*
|
|
73
|
+
* This function is designed to resize an image to fit within a specified maximum width and height
|
|
74
|
+
* while maintaining the original aspect ratio. If the original width or height exceeds the maximum
|
|
75
|
+
* dimensions, the image will be scaled down to fit.
|
|
76
|
+
*
|
|
77
|
+
* @param {number} originalWidth - The original width of the image.
|
|
78
|
+
* @param {number} originalHeight - The original height of the image.
|
|
79
|
+
* @returns {Object} An object containing the new width and height.
|
|
80
|
+
* @throws {Error} Throws an error if the width or height is not a positive number.
|
|
81
|
+
*/
|
|
82
|
+
export function calculateNewDimensions(
|
|
83
|
+
originalWidth: number,
|
|
84
|
+
originalHeight: number,
|
|
85
|
+
) {
|
|
86
|
+
// In low mode, the image is scaled to 512x512 pixels and 85 tokens are used to represent the image.
|
|
87
|
+
// In high mode, the model looks at low-resolution images and then creates detailed crop images, using 170 tokens for each 512x512 pixel tile. In practical applications, it is recommended to control the image size within 2048x768 pixels
|
|
88
|
+
const maxWidth = 768; // Maximum width
|
|
89
|
+
const maxHeight = 2048; // Maximum height
|
|
90
|
+
let newWidth = originalWidth;
|
|
91
|
+
let newHeight = originalHeight;
|
|
92
|
+
|
|
93
|
+
// Calculate the aspect ratio
|
|
94
|
+
const aspectRatio = originalWidth / originalHeight;
|
|
95
|
+
|
|
96
|
+
// Width adjustment
|
|
97
|
+
if (originalWidth > maxWidth) {
|
|
98
|
+
newWidth = maxWidth;
|
|
99
|
+
newHeight = newWidth / aspectRatio;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Adjust height
|
|
103
|
+
if (newHeight > maxHeight) {
|
|
104
|
+
newHeight = maxHeight;
|
|
105
|
+
newWidth = newHeight * aspectRatio;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
width: Math.round(newWidth),
|
|
110
|
+
height: Math.round(newHeight),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Trims an image and returns the trimming information, including the offset from the left and top edges, and the trimmed width and height
|
|
116
|
+
*
|
|
117
|
+
* @param image - The image to be trimmed. This can be a file path or a Buffer object containing the image data
|
|
118
|
+
* @returns A Promise that resolves to an object containing the trimming information. If the image does not need to be trimmed, this object will be null
|
|
119
|
+
*/
|
|
120
|
+
export async function trimImage(image: string | Buffer): Promise<{
|
|
121
|
+
trimOffsetLeft: number; // attention: trimOffsetLeft is a negative number
|
|
122
|
+
trimOffsetTop: number; // so as trimOffsetTop
|
|
123
|
+
width: number;
|
|
124
|
+
height: number;
|
|
125
|
+
} | null> {
|
|
126
|
+
const jimpImage = await Jimp.read(
|
|
127
|
+
Buffer.isBuffer(image) ? image : Buffer.from(image),
|
|
128
|
+
);
|
|
129
|
+
const { width, height } = jimpImage.bitmap;
|
|
130
|
+
|
|
131
|
+
if (width <= 3 || height <= 3) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const trimmedImage = jimpImage.autocrop();
|
|
136
|
+
const { width: trimmedWidth, height: trimmedHeight } = trimmedImage.bitmap;
|
|
137
|
+
|
|
138
|
+
const trimOffsetLeft = (width - trimmedWidth) / 2;
|
|
139
|
+
const trimOffsetTop = (height - trimmedHeight) / 2;
|
|
140
|
+
|
|
141
|
+
if (trimOffsetLeft === 0 && trimOffsetTop === 0) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
trimOffsetLeft: -trimOffsetLeft,
|
|
147
|
+
trimOffsetTop: -trimOffsetTop,
|
|
148
|
+
width: trimmedWidth,
|
|
149
|
+
height: trimmedHeight,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Aligns an image's coordinate system based on trimming information
|
|
155
|
+
*
|
|
156
|
+
* This function takes an image and a center rectangle as input. It first extracts the center
|
|
157
|
+
* rectangle from the image using Jimp and converts it to a buffer. Then, it calls
|
|
158
|
+
* the trimImage function to obtain the trimming information of the buffer image. If there is no
|
|
159
|
+
* trimming information, the original center rectangle is returned. If there is trimming information,
|
|
160
|
+
* a new rectangle is created based on the trimming information, with its top-left corner
|
|
161
|
+
* positioned at the negative offset of the trimming from the original center rectangle's top-left
|
|
162
|
+
* corner, and its width and height set to the trimmed image's dimensions.
|
|
163
|
+
*
|
|
164
|
+
* @param image The image file path or buffer to be processed
|
|
165
|
+
* @param center The center rectangle of the image, which is used to extract and align
|
|
166
|
+
* @returns A Promise that resolves to a rectangle object representing the aligned coordinates
|
|
167
|
+
* @throws Error if there is an error during image processing
|
|
168
|
+
*/
|
|
169
|
+
// export async function alignCoordByTrim(
|
|
170
|
+
// image: string | Buffer,
|
|
171
|
+
// centerRect: Rect,
|
|
172
|
+
// ): Promise<Rect> {
|
|
173
|
+
// const isBuffer = Buffer.isBuffer(image);
|
|
174
|
+
// let jimpImage;
|
|
175
|
+
// if (isBuffer) {
|
|
176
|
+
// jimpImage = await Jimp.read(image);
|
|
177
|
+
// } else {
|
|
178
|
+
// jimpImage = await Jimp.read(image);
|
|
179
|
+
// }
|
|
180
|
+
|
|
181
|
+
// const { width, height } = jimpImage.bitmap;
|
|
182
|
+
// if (width <= 3 || height <= 3) {
|
|
183
|
+
// return centerRect;
|
|
184
|
+
// }
|
|
185
|
+
// const zeroSize: Rect = {
|
|
186
|
+
// left: 0,
|
|
187
|
+
// top: 0,
|
|
188
|
+
// width: -1,
|
|
189
|
+
// height: -1,
|
|
190
|
+
// };
|
|
191
|
+
// const finalCenterRect: Rect = { ...centerRect };
|
|
192
|
+
// if (centerRect.left > width || centerRect.top > height) {
|
|
193
|
+
// return zeroSize;
|
|
194
|
+
// }
|
|
195
|
+
|
|
196
|
+
// if (finalCenterRect.left < 0) {
|
|
197
|
+
// finalCenterRect.width += finalCenterRect.left;
|
|
198
|
+
// finalCenterRect.left = 0;
|
|
199
|
+
// }
|
|
200
|
+
|
|
201
|
+
// if (finalCenterRect.top < 0) {
|
|
202
|
+
// finalCenterRect.height += finalCenterRect.top;
|
|
203
|
+
// finalCenterRect.top = 0;
|
|
204
|
+
// }
|
|
205
|
+
|
|
206
|
+
// if (finalCenterRect.left + finalCenterRect.width > width) {
|
|
207
|
+
// finalCenterRect.width = width - finalCenterRect.left;
|
|
208
|
+
// }
|
|
209
|
+
// if (finalCenterRect.top + finalCenterRect.height > height) {
|
|
210
|
+
// finalCenterRect.height = height - finalCenterRect.top;
|
|
211
|
+
// }
|
|
212
|
+
|
|
213
|
+
// if (finalCenterRect.width <= 3 || finalCenterRect.height <= 3) {
|
|
214
|
+
// return finalCenterRect;
|
|
215
|
+
// }
|
|
216
|
+
|
|
217
|
+
// try {
|
|
218
|
+
// const croppedImage = jimpImage.crop(
|
|
219
|
+
// centerRect.left,
|
|
220
|
+
// centerRect.top,
|
|
221
|
+
// centerRect.width,
|
|
222
|
+
// centerRect.height,
|
|
223
|
+
// );
|
|
224
|
+
// const buffer = await croppedImage.getBufferAsync(Jimp.MIME_PNG);
|
|
225
|
+
// const trimInfo = await trimImage(buffer);
|
|
226
|
+
// if (!trimInfo) {
|
|
227
|
+
// return centerRect;
|
|
228
|
+
// }
|
|
229
|
+
|
|
230
|
+
// return {
|
|
231
|
+
// left: centerRect.left - trimInfo.trimOffsetLeft,
|
|
232
|
+
// top: centerRect.top - trimInfo.trimOffsetTop,
|
|
233
|
+
// width: trimInfo.width,
|
|
234
|
+
// height: trimInfo.height,
|
|
235
|
+
// };
|
|
236
|
+
// } catch (e) {
|
|
237
|
+
// console.log(jimpImage.bitmap);
|
|
238
|
+
// throw e;
|
|
239
|
+
// }
|
|
240
|
+
// }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types='@modern-js/module-tools/types' />
|