vsegments 0.1.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.
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Visualization utilities for drawing bounding boxes and segmentation masks
3
+ */
4
+
5
+ const { createCanvas, loadImage, registerFont } = require('canvas');
6
+ const fs = require('fs').promises;
7
+ const path = require('path');
8
+
9
+ // Extended color palette
10
+ const COLORS = [
11
+ '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF',
12
+ '#FFA500', '#800080', '#FFC0CB', '#A52A2A', '#808080', '#F5F5DC',
13
+ '#40E0D0', '#FF7F50', '#E6E6FA', '#EE82EE', '#FFD700', '#C0C0C0',
14
+ '#000080', '#800000', '#008080', '#808000', '#FF6347', '#4B0082',
15
+ '#DC143C', '#00CED1', '#9370DB', '#FF1493', '#7FFF00', '#D2691E'
16
+ ];
17
+
18
+ /**
19
+ * Draw bounding boxes on an image
20
+ * @param {Canvas} canvas - Canvas with loaded image
21
+ * @param {BoundingBox[]} boxes - Array of bounding boxes
22
+ * @param {Object} options - Drawing options
23
+ * @returns {Canvas} - Canvas with bounding boxes drawn
24
+ */
25
+ function plotBoundingBoxes(canvas, boxes, options = {}) {
26
+ const {
27
+ lineWidth = 4,
28
+ fontSize = 14,
29
+ showLabels = true
30
+ } = options;
31
+
32
+ const ctx = canvas.getContext('2d');
33
+ const width = canvas.width;
34
+ const height = canvas.height;
35
+
36
+ ctx.lineWidth = lineWidth;
37
+ ctx.font = `${fontSize}px Arial`;
38
+
39
+ boxes.forEach((box, i) => {
40
+ const color = COLORS[i % COLORS.length];
41
+ const [absX1, absY1, absX2, absY2] = box.toAbsolute(width, height);
42
+
43
+ // Draw rectangle
44
+ ctx.strokeStyle = color;
45
+ ctx.strokeRect(absX1, absY1, absX2 - absX1, absY2 - absY1);
46
+
47
+ // Draw label
48
+ if (showLabels && box.label) {
49
+ ctx.fillStyle = color;
50
+ ctx.fillText(box.label, absX1 + 8, absY1 + fontSize + 6);
51
+ }
52
+ });
53
+
54
+ return canvas;
55
+ }
56
+
57
+ /**
58
+ * Overlay a mask on the image
59
+ * @param {Canvas} canvas - Canvas with image
60
+ * @param {Buffer} maskData - Mask data
61
+ * @param {number} width - Image width
62
+ * @param {number} height - Image height
63
+ * @param {string} color - Color to use for mask
64
+ * @param {number} alpha - Transparency (0-1)
65
+ */
66
+ function overlayMask(canvas, maskData, width, height, color, alpha) {
67
+ const ctx = canvas.getContext('2d');
68
+ const imageData = ctx.getImageData(0, 0, width, height);
69
+ const data = imageData.data;
70
+
71
+ // Parse color
72
+ const colorInt = parseInt(color.slice(1), 16);
73
+ const r = (colorInt >> 16) & 255;
74
+ const g = (colorInt >> 8) & 255;
75
+ const b = colorInt & 255;
76
+
77
+ // Apply mask
78
+ for (let i = 0; i < maskData.length; i++) {
79
+ const maskValue = maskData[i] / 255;
80
+ if (maskValue > 0) {
81
+ const idx = i * 4;
82
+ const maskAlpha = maskValue * alpha;
83
+
84
+ data[idx] = Math.round(data[idx] * (1 - maskAlpha) + r * maskAlpha);
85
+ data[idx + 1] = Math.round(data[idx + 1] * (1 - maskAlpha) + g * maskAlpha);
86
+ data[idx + 2] = Math.round(data[idx + 2] * (1 - maskAlpha) + b * maskAlpha);
87
+ }
88
+ }
89
+
90
+ ctx.putImageData(imageData, 0, 0);
91
+ }
92
+
93
+ /**
94
+ * Draw segmentation masks on an image
95
+ * @param {Canvas} canvas - Canvas with loaded image
96
+ * @param {SegmentationMask[]} masks - Array of segmentation masks
97
+ * @param {Object} options - Drawing options
98
+ * @returns {Canvas} - Canvas with masks drawn
99
+ */
100
+ function plotSegmentationMasks(canvas, masks, options = {}) {
101
+ const {
102
+ lineWidth = 4,
103
+ fontSize = 14,
104
+ alpha = 0.7,
105
+ showLabels = true
106
+ } = options;
107
+
108
+ const ctx = canvas.getContext('2d');
109
+ const width = canvas.width;
110
+ const height = canvas.height;
111
+
112
+ // Overlay masks first
113
+ masks.forEach((mask, i) => {
114
+ const color = COLORS[i % COLORS.length];
115
+ overlayMask(canvas, mask.mask, width, height, color, alpha);
116
+ });
117
+
118
+ // Draw bounding boxes and labels
119
+ ctx.lineWidth = lineWidth;
120
+ ctx.font = `${fontSize}px Arial`;
121
+
122
+ masks.forEach((mask, i) => {
123
+ const color = COLORS[i % COLORS.length];
124
+
125
+ // Draw bounding box
126
+ ctx.strokeStyle = color;
127
+ ctx.strokeRect(mask.x0, mask.y0, mask.x1 - mask.x0, mask.y1 - mask.y0);
128
+
129
+ // Draw label
130
+ if (showLabels && mask.label) {
131
+ ctx.fillStyle = color;
132
+ ctx.fillText(mask.label, mask.x0 + 8, mask.y0 - 6);
133
+ }
134
+ });
135
+
136
+ return canvas;
137
+ }
138
+
139
+ /**
140
+ * Load image into canvas
141
+ * @param {string} imagePath - Path to image file
142
+ * @param {number} maxSize - Maximum dimension
143
+ * @returns {Promise<Canvas>} - Canvas with loaded image
144
+ */
145
+ async function loadImageToCanvas(imagePath, maxSize = 1024) {
146
+ const img = await loadImage(imagePath);
147
+
148
+ // Calculate new dimensions
149
+ let width = img.width;
150
+ let height = img.height;
151
+
152
+ if (width > maxSize || height > maxSize) {
153
+ const scale = Math.min(maxSize / width, maxSize / height);
154
+ width = Math.round(width * scale);
155
+ height = Math.round(height * scale);
156
+ }
157
+
158
+ // Create canvas and draw image
159
+ const canvas = createCanvas(width, height);
160
+ const ctx = canvas.getContext('2d');
161
+ ctx.drawImage(img, 0, 0, width, height);
162
+
163
+ return canvas;
164
+ }
165
+
166
+ /**
167
+ * Save canvas to file
168
+ * @param {Canvas} canvas - Canvas to save
169
+ * @param {string} outputPath - Output file path
170
+ */
171
+ async function saveCanvas(canvas, outputPath) {
172
+ const buffer = canvas.toBuffer('image/png');
173
+ await fs.writeFile(outputPath, buffer);
174
+ }
175
+
176
+ module.exports = {
177
+ plotBoundingBoxes,
178
+ plotSegmentationMasks,
179
+ loadImageToCanvas,
180
+ saveCanvas,
181
+ COLORS
182
+ };