@eyepop.ai/eyepop-render-2d 3.14.7 → 3.15.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/README.md +10 -10
- package/dist/eyepop.render2d.index.d.mts +3 -0
- package/dist/eyepop.render2d.index.d.ts +3 -0
- package/dist/eyepop.render2d.index.js +72 -34
- package/dist/eyepop.render2d.index.js.map +1 -1
- package/dist/eyepop.render2d.index.mjs +72 -34
- package/dist/eyepop.render2d.index.mjs.map +1 -1
- package/dist/eyepop.render2d.min.js +1 -1
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ const example_image_path = 'examples/example.jpg'
|
|
|
46
46
|
|
|
47
47
|
context.drawImage(image, 0, 0)
|
|
48
48
|
|
|
49
|
-
const endpoint = await EyePop.
|
|
49
|
+
const endpoint = await EyePop.workerEndpoint().connect()
|
|
50
50
|
try {
|
|
51
51
|
let results = await endpoint.process({ source: { path: example_image_path } })
|
|
52
52
|
for await (let result of results) {
|
|
@@ -93,7 +93,7 @@ const example_image_path = 'examples/example.jpg'
|
|
|
93
93
|
|
|
94
94
|
const endpoint = await EyePop.workerEndpoint({auth: {oAuth2: true}}).connect();
|
|
95
95
|
await endpoint.changePop({ components: [{
|
|
96
|
-
type:
|
|
96
|
+
type: 'inference',
|
|
97
97
|
model: 'eyepop.person:latest',
|
|
98
98
|
categoryName: 'person'
|
|
99
99
|
}]})
|
|
@@ -116,7 +116,7 @@ Change this rendering behaviour by passing in rendering rule(s), e.g.:
|
|
|
116
116
|
|
|
117
117
|
```javascript
|
|
118
118
|
// ...
|
|
119
|
-
Render2d.renderer(context, [
|
|
119
|
+
Render2d.renderer(context, [Render2d.renderFace()]).draw(result)
|
|
120
120
|
// ...
|
|
121
121
|
```
|
|
122
122
|
|
|
@@ -129,11 +129,11 @@ Most prebuild render classes provide a reasonable defaults, as shown below.
|
|
|
129
129
|
|
|
130
130
|
```typescript
|
|
131
131
|
Render2d.renderBox({
|
|
132
|
-
showClass
|
|
133
|
-
showConfidence
|
|
134
|
-
showTraceId
|
|
135
|
-
showNestedClasses
|
|
136
|
-
target
|
|
132
|
+
showClass: true,
|
|
133
|
+
showConfidence: false,
|
|
134
|
+
showTraceId: false,
|
|
135
|
+
showNestedClasses: false,
|
|
136
|
+
target: '$..objects.*',
|
|
137
137
|
})
|
|
138
138
|
```
|
|
139
139
|
|
|
@@ -157,8 +157,8 @@ Render2d.renderHand({
|
|
|
157
157
|
|
|
158
158
|
```typescript
|
|
159
159
|
Render2d.renderFace({
|
|
160
|
-
showLabels
|
|
161
|
-
target
|
|
160
|
+
showLabels: false,
|
|
161
|
+
target: '$..objects[?(@.classLabel=="face")]',
|
|
162
162
|
})
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -178,16 +178,19 @@ type RenderTextOptions = {
|
|
|
178
178
|
showText: boolean;
|
|
179
179
|
fitToBounds: boolean;
|
|
180
180
|
textPadding?: number;
|
|
181
|
+
maxLines?: number;
|
|
181
182
|
} & RenderTarget;
|
|
182
183
|
declare class RenderText implements Render {
|
|
183
184
|
target: string;
|
|
184
185
|
fitToBounds: boolean;
|
|
185
186
|
textPadding: number;
|
|
187
|
+
maxLines: number;
|
|
186
188
|
private context;
|
|
187
189
|
private style;
|
|
188
190
|
constructor(options?: Partial<RenderTextOptions>);
|
|
189
191
|
start(context: CanvasRenderingContext2D, style: Style): void;
|
|
190
192
|
draw(element: PredictedObject, xOffset: number, yOffset: number, xScale: number, yScale: number, streamTime: StreamTime): void;
|
|
193
|
+
private wrapLine;
|
|
191
194
|
drawText(text: string, context: CanvasRenderingContext2D, xPos: number, yPos: number, style: Style, fontSize: number): void;
|
|
192
195
|
static toTitleCase(str: string): string;
|
|
193
196
|
getMinFontSize(context: CanvasRenderingContext2D, element: any, width: number, height: number, style: any, scaleToFit?: boolean): number;
|
|
@@ -178,16 +178,19 @@ type RenderTextOptions = {
|
|
|
178
178
|
showText: boolean;
|
|
179
179
|
fitToBounds: boolean;
|
|
180
180
|
textPadding?: number;
|
|
181
|
+
maxLines?: number;
|
|
181
182
|
} & RenderTarget;
|
|
182
183
|
declare class RenderText implements Render {
|
|
183
184
|
target: string;
|
|
184
185
|
fitToBounds: boolean;
|
|
185
186
|
textPadding: number;
|
|
187
|
+
maxLines: number;
|
|
186
188
|
private context;
|
|
187
189
|
private style;
|
|
188
190
|
constructor(options?: Partial<RenderTextOptions>);
|
|
189
191
|
start(context: CanvasRenderingContext2D, style: Style): void;
|
|
190
192
|
draw(element: PredictedObject, xOffset: number, yOffset: number, xScale: number, yScale: number, streamTime: StreamTime): void;
|
|
193
|
+
private wrapLine;
|
|
191
194
|
drawText(text: string, context: CanvasRenderingContext2D, xPos: number, yPos: number, style: Style, fontSize: number): void;
|
|
192
195
|
static toTitleCase(str: string): string;
|
|
193
196
|
getMinFontSize(context: CanvasRenderingContext2D, element: any, width: number, height: number, style: any, scaleToFit?: boolean): number;
|
|
@@ -589,7 +589,7 @@ var RenderFace = class {
|
|
|
589
589
|
let expressions = [];
|
|
590
590
|
if (face.classes) {
|
|
591
591
|
for (let i = 0; i < face.classes.length; i++) {
|
|
592
|
-
if (face.classes[i].confidence > 0.95) {
|
|
592
|
+
if ((face.classes[i].confidence ?? 0) > 0.95) {
|
|
593
593
|
const label = EXPRESSION_LABELS.get(face.classes[i].classLabel);
|
|
594
594
|
if (label) {
|
|
595
595
|
expressions.push(label);
|
|
@@ -3420,7 +3420,7 @@ var RenderHand = class {
|
|
|
3420
3420
|
let minZ = -1;
|
|
3421
3421
|
for (let i2 = 0; i2 < keyPoints.points.length; i2++) {
|
|
3422
3422
|
const keyPoint = keyPoints.points[i2];
|
|
3423
|
-
labelsToPoints.set(keyPoint.classLabel, keyPoint);
|
|
3423
|
+
labelsToPoints.set(keyPoint.classLabel ?? "<undefined>", keyPoint);
|
|
3424
3424
|
if (keyPoint.z) {
|
|
3425
3425
|
if (keyPoint.z > maxZ) {
|
|
3426
3426
|
maxZ = keyPoint.z;
|
|
@@ -3548,7 +3548,7 @@ var RenderPose = class {
|
|
|
3548
3548
|
let minZ = -1;
|
|
3549
3549
|
for (let i2 = 0; i2 < keyPoints.points.length; i2++) {
|
|
3550
3550
|
const keyPoint = keyPoints.points[i2];
|
|
3551
|
-
labelsToPoints.set(keyPoint.classLabel, keyPoint);
|
|
3551
|
+
labelsToPoints.set(keyPoint.classLabel ?? "<undefined>", keyPoint);
|
|
3552
3552
|
if (keyPoint.z) {
|
|
3553
3553
|
if (keyPoint.z > maxZ) {
|
|
3554
3554
|
maxZ = keyPoint.z;
|
|
@@ -3592,9 +3592,9 @@ var RenderPose = class {
|
|
|
3592
3592
|
const radius = MAX_RADIUS - (z - minZ) * (MAX_RADIUS - MIN_RADIUS) / (maxZ - minZ);
|
|
3593
3593
|
context.beginPath();
|
|
3594
3594
|
context.arc(x, y, radius, 0, Math.PI * 2, false);
|
|
3595
|
-
if (p.classLabel
|
|
3595
|
+
if (p.classLabel?.includes("left")) {
|
|
3596
3596
|
context.fillStyle = style.colors.left_color;
|
|
3597
|
-
} else if (p.classLabel
|
|
3597
|
+
} else if (p.classLabel?.includes("right")) {
|
|
3598
3598
|
context.fillStyle = style.colors.right_color;
|
|
3599
3599
|
} else {
|
|
3600
3600
|
context.fillStyle = style.colors.primary_color;
|
|
@@ -3935,9 +3935,9 @@ var RenderKeyPoints = class {
|
|
|
3935
3935
|
if (!this.showLabels) continue;
|
|
3936
3936
|
let label;
|
|
3937
3937
|
if (typeof p.visible == "undefined") {
|
|
3938
|
-
label = p.classLabel;
|
|
3938
|
+
label = p.classLabel ?? "<undefined>";
|
|
3939
3939
|
} else {
|
|
3940
|
-
label = `${p.classLabel} (${p.visible ? "visible" : "invisible"})`;
|
|
3940
|
+
label = `${p.classLabel ?? "<undefined>"} (${p.visible ? "visible" : "invisible"})`;
|
|
3941
3941
|
}
|
|
3942
3942
|
context.font = style.font;
|
|
3943
3943
|
context.fillStyle = "#ffffff";
|
|
@@ -3957,10 +3957,12 @@ var RenderText = class {
|
|
|
3957
3957
|
this.target = DEFAULT_TARGET;
|
|
3958
3958
|
this.fitToBounds = true;
|
|
3959
3959
|
this.textPadding = 0.1;
|
|
3960
|
-
|
|
3960
|
+
this.maxLines = 12;
|
|
3961
|
+
const { target = "$..objects[?(@.texts)]", fitToBounds = true, textPadding = 0.1, maxLines = 12 } = options;
|
|
3961
3962
|
this.target = target;
|
|
3962
3963
|
this.fitToBounds = fitToBounds;
|
|
3963
3964
|
this.textPadding = Math.max(0.02, Math.min(0.2, textPadding));
|
|
3965
|
+
this.maxLines = maxLines;
|
|
3964
3966
|
}
|
|
3965
3967
|
start(context, style) {
|
|
3966
3968
|
this.context = context;
|
|
@@ -3982,37 +3984,74 @@ var RenderText = class {
|
|
|
3982
3984
|
padding = Math.min(padding, w * this.textPadding, h * this.textPadding);
|
|
3983
3985
|
const boundingBoxWidth = w - padding * 2;
|
|
3984
3986
|
const boundingBoxHeight = h - padding * 2;
|
|
3985
|
-
|
|
3986
|
-
let label = "";
|
|
3987
|
+
const baseFontSize = parseInt(style.font.split(" ")[0]);
|
|
3987
3988
|
const fontType = style.font.split(" ")[1];
|
|
3989
|
+
const rawLines = [];
|
|
3990
|
+
for (const entry of element.texts) {
|
|
3991
|
+
const text = entry?.text || "";
|
|
3992
|
+
if (!text) continue;
|
|
3993
|
+
rawLines.push(...text.split("\n"));
|
|
3994
|
+
}
|
|
3995
|
+
if (rawLines.length === 0) return;
|
|
3996
|
+
const isShortSingleLine = rawLines.length === 1 && rawLines[0].length <= 60;
|
|
3997
|
+
let fontSize;
|
|
3998
|
+
if (this.fitToBounds && isShortSingleLine) {
|
|
3999
|
+
fontSize = this.getMinFontSize(context, element, boundingBoxWidth, boundingBoxHeight, style, true);
|
|
4000
|
+
} else {
|
|
4001
|
+
fontSize = baseFontSize;
|
|
4002
|
+
if (this.fitToBounds) {
|
|
4003
|
+
const maxFontFromHeight = Math.floor(boundingBoxHeight * 0.12);
|
|
4004
|
+
fontSize = Math.min(fontSize, Math.max(maxFontFromHeight, 10));
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
3988
4007
|
context.font = `${fontSize}px ${fontType}`;
|
|
3989
|
-
|
|
3990
|
-
const
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
height: lineHeight
|
|
4001
|
-
});
|
|
4008
|
+
const wrappedLines = [];
|
|
4009
|
+
for (const line of rawLines) {
|
|
4010
|
+
const wrapped = this.wrapLine(context, line, boundingBoxWidth);
|
|
4011
|
+
wrappedLines.push(...wrapped);
|
|
4012
|
+
}
|
|
4013
|
+
let displayLines = wrappedLines;
|
|
4014
|
+
const truncated = this.maxLines > 0 && wrappedLines.length > this.maxLines;
|
|
4015
|
+
if (truncated) {
|
|
4016
|
+
displayLines = wrappedLines.slice(0, this.maxLines);
|
|
4017
|
+
const last = displayLines[displayLines.length - 1];
|
|
4018
|
+
displayLines[displayLines.length - 1] = last.replace(/\s+\S*$/, "") + "\u2026";
|
|
4002
4019
|
}
|
|
4003
4020
|
const spacing = fontSize * 0.2;
|
|
4004
|
-
|
|
4005
|
-
|
|
4021
|
+
const totalTextHeight = displayLines.length * fontSize + (displayLines.length - 1) * spacing;
|
|
4022
|
+
if (totalTextHeight > boundingBoxHeight && displayLines.length > 0) {
|
|
4023
|
+
const scaleFactor = boundingBoxHeight / totalTextHeight;
|
|
4024
|
+
fontSize = Math.max(8, Math.floor(fontSize * scaleFactor));
|
|
4025
|
+
context.font = `${fontSize}px ${fontType}`;
|
|
4006
4026
|
}
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4027
|
+
const finalSpacing = fontSize * 0.2;
|
|
4028
|
+
const finalLineHeight = fontSize + finalSpacing;
|
|
4029
|
+
const finalTotalHeight = displayLines.length * finalLineHeight - finalSpacing;
|
|
4030
|
+
let textY = element.y * yScale + yOffset + (h - finalTotalHeight) / 2;
|
|
4031
|
+
const textX = element.x * xScale + xOffset + padding;
|
|
4032
|
+
for (const line of displayLines) {
|
|
4033
|
+
this.drawText(line, context, textX, textY, style, fontSize);
|
|
4034
|
+
textY += finalLineHeight;
|
|
4013
4035
|
}
|
|
4014
4036
|
}
|
|
4015
|
-
|
|
4037
|
+
wrapLine(context, text, maxWidth) {
|
|
4038
|
+
if (!text) return [""];
|
|
4039
|
+
if (context.measureText(text).width <= maxWidth) return [text];
|
|
4040
|
+
const words = text.split(" ");
|
|
4041
|
+
const lines = [];
|
|
4042
|
+
let current = "";
|
|
4043
|
+
for (const word of words) {
|
|
4044
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
4045
|
+
if (context.measureText(candidate).width > maxWidth && current) {
|
|
4046
|
+
lines.push(current);
|
|
4047
|
+
current = word;
|
|
4048
|
+
} else {
|
|
4049
|
+
current = candidate;
|
|
4050
|
+
}
|
|
4051
|
+
}
|
|
4052
|
+
if (current) lines.push(current);
|
|
4053
|
+
return lines.length > 0 ? lines : [text];
|
|
4054
|
+
}
|
|
4016
4055
|
drawText(text, context, xPos, yPos, style, fontSize) {
|
|
4017
4056
|
context.fillStyle = "#ffffff";
|
|
4018
4057
|
context.textAlign = "left";
|
|
@@ -4024,10 +4063,9 @@ var RenderText = class {
|
|
|
4024
4063
|
static toTitleCase(str) {
|
|
4025
4064
|
if (!str) return "";
|
|
4026
4065
|
return str.replace(/\w\S*/g, function(txt) {
|
|
4027
|
-
return txt.charAt(0).toUpperCase() + txt.
|
|
4066
|
+
return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
|
|
4028
4067
|
});
|
|
4029
4068
|
}
|
|
4030
|
-
// Gets the optimal font size for all labels to fit in the bounding box
|
|
4031
4069
|
getMinFontSize(context, element, width, height, style, scaleToFit = false) {
|
|
4032
4070
|
const textLines = (element.texts || []).map((t) => t?.text || "").filter((t) => t);
|
|
4033
4071
|
const textCount = textLines.length;
|