@luxonis/visualizer-protobuf 3.1.11 → 3.1.12
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/dist/{FoxgloveServer-B98MHUu6.js → FoxgloveServer-Da9kV1cr.js} +1 -1
- package/dist/{communicator-Bzkx3Qfr.js → communicator-Dh2aL-rj.js} +1 -1
- package/dist/{depth-DlC6A3Te.js → depth-DiD1V2Sg.js} +15 -28
- package/dist/{deserialization.worker-CO4m6y8V.js → deserialization.worker-CxHfbNSI.js} +544 -325
- package/dist/{foxglove-protocol-yHdoBNaD.js → foxglove-protocol-BtLMVx24.js} +1 -1
- package/dist/{index-D063HnO-.js → index-8bTMarZg.js} +5 -5
- package/dist/{index-BXzWxQD4.js → index-9Qz76jnn.js} +7 -7
- package/dist/{index-Dqm1x8G2.js → index-B5oN0fQm.js} +1 -1
- package/dist/{index-CKE7xLWy.js → index-B8JZ9tAh.js} +5 -5
- package/dist/{index-CB1fYMKI.js → index-BFAcwVLJ.js} +5 -5
- package/dist/{index-DCsDqt-U.js → index-BtMMMbp8.js} +5 -5
- package/dist/{index-BYTk-1GW.js → index-BvC_JYBX.js} +5 -5
- package/dist/{index-BlROWu-J.js → index-BydKxany.js} +6 -6
- package/dist/{index-DtnnnQqJ.js → index-C-n27_wk.js} +5 -5
- package/dist/{index-DXmbPIsN.js → index-CQhsew4_.js} +5 -5
- package/dist/{index-Dvd5sByT.js → index-CVrY6MoS.js} +5 -5
- package/dist/{index-CDp9Pzg1.js → index-CwAmHvi3.js} +5 -5
- package/dist/{index-D_pNGJGQ.js → index-DXdd6C_Q.js} +5 -5
- package/dist/{index-DgUF0rJi.js → index-D_TjqfY9.js} +5 -5
- package/dist/{index-DCsBkCC7.js → index-DnaaNoIT.js} +5 -5
- package/dist/{index-D0BOBLGo.js → index-DuP_Wg3E.js} +5 -5
- package/dist/{index-B-g3aWAt.js → index-O1aT66OM.js} +5 -5
- package/dist/{index-BEUjTghZ.js → index-RxEWv1Fn.js} +5 -5
- package/dist/{index-Be6EYnox.js → index-gafqmUmi.js} +49 -35
- package/dist/{index-OIOsl1pr.js → index-oyn_LQK1.js} +5 -5
- package/dist/{index-D2sxEb00.js → index-yO9F9DL5.js} +5 -5
- package/dist/index.js +5 -5
- package/dist/lib/src/connection/foxglove-connection.d.ts.map +1 -1
- package/dist/lib/src/connection/foxglove-connection.js +7 -1
- package/dist/lib/src/connection/foxglove-connection.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js +2 -2
- package/dist/lib/src/messaging/deserialization/pointcloud/pointcloudFromDepth.worker.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/poitcloudPoolManager.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/pointcloud/utils.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/depth.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/depth.js +11 -1
- package/dist/lib/src/messaging/deserialization/video/depth.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/h264.d.ts +9 -6
- package/dist/lib/src/messaging/deserialization/video/h264.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/h264.js +104 -163
- package/dist/lib/src/messaging/deserialization/video/h264.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/h265.d.ts +0 -7
- package/dist/lib/src/messaging/deserialization/video/h265.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/h265.js +46 -123
- package/dist/lib/src/messaging/deserialization/video/h265.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/mjpeg.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/mjpeg.js +7 -8
- package/dist/lib/src/messaging/deserialization/video/mjpeg.js.map +1 -1
- package/dist/lib/src/messaging/deserialization/video/rgbd-to-pointcloud.d.ts +17 -0
- package/dist/lib/src/messaging/deserialization/video/rgbd-to-pointcloud.d.ts.map +1 -0
- package/dist/lib/src/messaging/deserialization/video/rgbd-to-pointcloud.js +320 -0
- package/dist/lib/src/messaging/deserialization/video/rgbd-to-pointcloud.js.map +1 -0
- package/dist/lib/src/messaging/deserialization.worker.d.ts +4 -0
- package/dist/lib/src/messaging/deserialization.worker.d.ts.map +1 -1
- package/dist/lib/src/messaging/deserialization.worker.js +16 -3
- package/dist/lib/src/messaging/deserialization.worker.js.map +1 -1
- package/dist/lib/src/messaging/protobuf.d.ts +12 -1
- package/dist/lib/src/messaging/protobuf.d.ts.map +1 -1
- package/dist/lib/src/messaging/protobuf.js +8 -0
- package/dist/lib/src/messaging/protobuf.js.map +1 -1
- package/dist/lib/src/messaging/utils.d.ts.map +1 -1
- package/dist/lib/src/messaging/utils.js +2 -0
- package/dist/lib/src/messaging/utils.js.map +1 -1
- package/dist/lib/src/output.css +2 -21
- package/dist/lib/src/panels/PointCloudPanel.d.ts.map +1 -1
- package/dist/lib/src/panels/PointCloudPanel.js +5 -1
- package/dist/lib/src/panels/PointCloudPanel.js.map +1 -1
- package/dist/lib/src/utils/metrics-manager.d.ts +1 -0
- package/dist/lib/src/utils/metrics-manager.d.ts.map +1 -1
- package/dist/lib/src/utils/metrics-manager.js +4 -2
- package/dist/lib/src/utils/metrics-manager.js.map +1 -1
- package/dist/lib/src/utils/poitcloud-sync.d.ts +4 -3
- package/dist/lib/src/utils/poitcloud-sync.d.ts.map +1 -1
- package/dist/lib/src/utils/poitcloud-sync.js +3 -1
- package/dist/lib/src/utils/poitcloud-sync.js.map +1 -1
- package/dist/{pointcloudFromDepth.worker-JLptpLHr.js → pointcloudFromDepth.worker-60RA4Ief.js} +2 -2
- package/dist/{protobuf-Be0G3NFz.js → protobuf-OcbVtxg1.js} +406 -218
- package/dist/{worker-NFznpx6V.js → worker-Be-hru8f.js} +3 -3
- package/dist/{worker-0Q6os6Ck.js → worker-C2MHIq3t.js} +4 -4
- package/package.json +2 -2
|
@@ -6,19 +6,9 @@ import { estimateObjectSize } from "@foxglove/studio-base/players/messageMemoryE
|
|
|
6
6
|
import { ensureWebCodecsSupported } from "../../../utils/compatibility.js";
|
|
7
7
|
import { DecoderRuntimeError } from "../../../utils/error.js";
|
|
8
8
|
import { Logger } from "../../../utils/logger";
|
|
9
|
-
import { parseMessage
|
|
9
|
+
import { parseMessage } from "../../utils.js";
|
|
10
|
+
import { copyVideoFrameToI420 } from "./h264.js";
|
|
10
11
|
const logger = Logger.getLogger();
|
|
11
|
-
function configureDecoder(decoder) {
|
|
12
|
-
decoder.configure({
|
|
13
|
-
codec: "hev1.1.6.L93.B0",
|
|
14
|
-
optimizeForLatency: true,
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
function ensureDecoderConfigured(decoder) {
|
|
18
|
-
if (decoder.state === "unconfigured") {
|
|
19
|
-
configureDecoder(decoder);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
12
|
function closeDecoder(decoder) {
|
|
23
13
|
if (!decoder) {
|
|
24
14
|
return;
|
|
@@ -35,114 +25,27 @@ function closeDecoder(decoder) {
|
|
|
35
25
|
}
|
|
36
26
|
function closeTopicDecoder(topic, topicDecoders) {
|
|
37
27
|
const decoderInfo = topicDecoders.get(topic);
|
|
38
|
-
topicDecoders.delete(topic);
|
|
39
28
|
closeDecoder(decoderInfo?.decoder);
|
|
40
29
|
decoderInfo?.timing.clear();
|
|
41
30
|
decoderInfo?.transformations.clear();
|
|
42
|
-
if (decoderInfo) {
|
|
43
|
-
decoderInfo.pendingFrame = undefined;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function isUnconfiguredCodecError(error) {
|
|
47
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
48
|
-
return /cannot call ["']decode["'] on an unconfigured (?:codec|decoder)/i.test(message);
|
|
49
|
-
}
|
|
50
|
-
function resetTopicDecoder({ topic, topicDecoders, callback, onError, }) {
|
|
51
|
-
const existingDecoderInfo = topicDecoders.get(topic);
|
|
52
|
-
closeDecoder(existingDecoderInfo?.decoder);
|
|
53
|
-
const decoder = createVideoDecoder({ topic, callback, topicDecoders, onError });
|
|
54
|
-
try {
|
|
55
|
-
configureDecoder(decoder);
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
closeDecoder(decoder);
|
|
59
|
-
closeTopicDecoder(topic, topicDecoders);
|
|
60
|
-
onError(new DecoderRuntimeError(topic, error instanceof Error ? error.message : "Failed to configure H.265 decoder."));
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
if (existingDecoderInfo) {
|
|
64
|
-
existingDecoderInfo.decoder = decoder;
|
|
65
|
-
existingDecoderInfo.timing.clear();
|
|
66
|
-
existingDecoderInfo.transformations.clear();
|
|
67
|
-
existingDecoderInfo.pendingFrame = undefined;
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
topicDecoders.set(topic, {
|
|
71
|
-
decoder,
|
|
72
|
-
timing: new Map(),
|
|
73
|
-
transformations: new Map(),
|
|
74
|
-
pendingFrame: undefined,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
return decoder;
|
|
78
|
-
}
|
|
79
|
-
function decodePendingFrame(topic, topicDecoders, callback, onError) {
|
|
80
|
-
const decoderInfo = topicDecoders.get(topic);
|
|
81
|
-
if (!decoderInfo) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
const decoder = decoderInfo.decoder;
|
|
85
|
-
if (decoder.state === "closed" || decoder.decodeQueueSize > 0) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
const pendingFrame = decoderInfo.pendingFrame;
|
|
89
|
-
if (!pendingFrame) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
decoderInfo.pendingFrame = undefined;
|
|
93
|
-
decoderInfo.timing.set(pendingFrame.timestamp, pendingFrame.receiveTime);
|
|
94
|
-
decoderInfo.transformations.set(pendingFrame.timestamp, pendingFrame.transformation);
|
|
95
|
-
try {
|
|
96
|
-
ensureDecoderConfigured(decoder);
|
|
97
|
-
if (decoder.state !== "configured" || topicDecoders.get(topic)?.decoder !== decoder) {
|
|
98
|
-
closeTopicDecoder(topic, topicDecoders);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const chunk = new EncodedVideoChunk({
|
|
102
|
-
type: "key",
|
|
103
|
-
data: pendingFrame.data,
|
|
104
|
-
timestamp: pendingFrame.timestamp,
|
|
105
|
-
});
|
|
106
|
-
decoder.decode(chunk);
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
if (isUnconfiguredCodecError(error)) {
|
|
110
|
-
decoderInfo.pendingFrame = pendingFrame;
|
|
111
|
-
const replacementDecoder = resetTopicDecoder({ topic, topicDecoders, callback, onError });
|
|
112
|
-
if (replacementDecoder?.state === "configured") {
|
|
113
|
-
decodePendingFrame(topic, topicDecoders, callback, onError);
|
|
114
|
-
}
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
closeTopicDecoder(topic, topicDecoders);
|
|
118
|
-
onError(new DecoderRuntimeError(topic, error instanceof Error ? error.message : "Failed to decode H.265 frame."));
|
|
119
|
-
}
|
|
120
31
|
}
|
|
121
32
|
function createVideoDecoder({ topic, callback, topicDecoders, onError, }) {
|
|
122
|
-
|
|
123
|
-
decoder = new VideoDecoder({
|
|
33
|
+
return new VideoDecoder({
|
|
124
34
|
output: async (frame) => {
|
|
125
|
-
|
|
126
|
-
frame.close();
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const buffer = new Uint8Array(frame.allocationSize());
|
|
130
|
-
await frame.copyTo(buffer);
|
|
35
|
+
const { data: buffer, width, height } = await copyVideoFrameToI420(frame, "H265");
|
|
131
36
|
const receiveTime = topicDecoders.get(topic)?.timing.get(frame.timestamp)
|
|
132
37
|
??
|
|
133
38
|
fromMicros(frame.timestamp);
|
|
134
|
-
topicDecoders.get(topic)?.timing.delete(frame.timestamp);
|
|
135
39
|
const transformation = topicDecoders.get(topic)?.transformations.get(frame.timestamp);
|
|
136
40
|
topicDecoders.get(topic)?.transformations.delete(frame.timestamp);
|
|
137
|
-
const [encoding, step] = parsePixelFormat(frame.format);
|
|
138
41
|
const foxgloveMessage = {
|
|
139
42
|
timestamp: receiveTime,
|
|
140
43
|
frame_id: `h265-${topic}-frame`,
|
|
141
|
-
width
|
|
142
|
-
height
|
|
44
|
+
width,
|
|
45
|
+
height,
|
|
143
46
|
data: buffer,
|
|
144
|
-
encoding,
|
|
145
|
-
step:
|
|
47
|
+
encoding: "yuv420p",
|
|
48
|
+
step: width,
|
|
146
49
|
};
|
|
147
50
|
callback({
|
|
148
51
|
topic,
|
|
@@ -153,20 +56,13 @@ function createVideoDecoder({ topic, callback, topicDecoders, onError, }) {
|
|
|
153
56
|
depthaiTransformation: transformation,
|
|
154
57
|
});
|
|
155
58
|
frame.close();
|
|
156
|
-
if (topicDecoders.get(topic)?.decoder === decoder) {
|
|
157
|
-
decodePendingFrame(topic, topicDecoders, callback, onError);
|
|
158
|
-
}
|
|
159
59
|
},
|
|
160
60
|
error: (error) => {
|
|
161
|
-
if (topicDecoders.get(topic)?.decoder !== decoder) {
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
61
|
logger.error(error);
|
|
165
62
|
closeTopicDecoder(topic, topicDecoders);
|
|
166
63
|
onError(new DecoderRuntimeError(topic, error.message));
|
|
167
64
|
},
|
|
168
65
|
});
|
|
169
|
-
return decoder;
|
|
170
66
|
}
|
|
171
67
|
export async function deserializeEncodedFrameH265({ topic, message, topicDecoders, callback, onError, }) {
|
|
172
68
|
const { receiveTime } = parseMessage(message);
|
|
@@ -175,28 +71,55 @@ export async function deserializeEncodedFrameH265({ topic, message, topicDecoder
|
|
|
175
71
|
return;
|
|
176
72
|
}
|
|
177
73
|
const existingDecoderInfo = topicDecoders.get(topic);
|
|
178
|
-
if (!existingDecoderInfo || existingDecoderInfo.decoder.state
|
|
179
|
-
const decoder =
|
|
180
|
-
|
|
74
|
+
if (!existingDecoderInfo || existingDecoderInfo.decoder.state === "closed") {
|
|
75
|
+
const decoder = createVideoDecoder({ topic, callback, topicDecoders, onError });
|
|
76
|
+
try {
|
|
77
|
+
decoder.configure({
|
|
78
|
+
codec: "hev1.1.6.L93.B0",
|
|
79
|
+
optimizeForLatency: true
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
closeDecoder(decoder);
|
|
84
|
+
onError(new DecoderRuntimeError(topic, error instanceof Error ? error.message : "Failed to configure H.265 decoder."));
|
|
181
85
|
return;
|
|
182
86
|
}
|
|
87
|
+
if (existingDecoderInfo) {
|
|
88
|
+
existingDecoderInfo.decoder = decoder;
|
|
89
|
+
existingDecoderInfo.timing.clear();
|
|
90
|
+
existingDecoderInfo.transformations.clear();
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
topicDecoders.set(topic, {
|
|
94
|
+
decoder,
|
|
95
|
+
timing: new Map(),
|
|
96
|
+
transformations: new Map(),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
183
99
|
}
|
|
184
100
|
const decoderInfo = topicDecoders.get(topic);
|
|
185
101
|
if (!decoderInfo) {
|
|
186
102
|
return;
|
|
187
103
|
}
|
|
104
|
+
if (decoderInfo.decoder.decodeQueueSize > 60) {
|
|
105
|
+
decoderInfo.decoder.reset();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
188
108
|
const microTimestamp = receiveTime.sec * 1_000_000 + Math.floor(receiveTime.nsec / 1_000);
|
|
109
|
+
decoderInfo.timing.set(microTimestamp, receiveTime);
|
|
110
|
+
decoderInfo.transformations.set(microTimestamp, message.transformation);
|
|
189
111
|
const frame = {
|
|
112
|
+
type: 'key',
|
|
190
113
|
data: message.data,
|
|
191
|
-
timestamp: microTimestamp
|
|
192
|
-
receiveTime,
|
|
193
|
-
transformation: message.transformation,
|
|
114
|
+
timestamp: microTimestamp
|
|
194
115
|
};
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
116
|
+
try {
|
|
117
|
+
const chunk = new EncodedVideoChunk(frame);
|
|
118
|
+
decoderInfo.decoder.decode(chunk);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
closeTopicDecoder(topic, topicDecoders);
|
|
122
|
+
onError(new DecoderRuntimeError(topic, error instanceof Error ? error.message : "Failed to decode H.265 frame."));
|
|
198
123
|
}
|
|
199
|
-
decoderInfo.pendingFrame = frame;
|
|
200
|
-
decodePendingFrame(topic, topicDecoders, callback, onError);
|
|
201
124
|
}
|
|
202
125
|
//# sourceMappingURL=h265.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"h265.js","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/h265.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,oEAAoE;AACpE,0DAA0D;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAM9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"h265.js","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/h265.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mjpeg.d.ts","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/mjpeg.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"mjpeg.d.ts","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/mjpeg.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAEnE,OAAO,EAAE,YAAY,EAAE,MAAM,6CAA6C,CAAC;AAK3E,MAAM,MAAM,yBAAyB,GAAG;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CACzC,CAAC;AAEF,wBAAsB,qBAAqB,CAAC,EAC1C,KAAK,EACL,OAAO,EACP,QAAQ,EACT,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC3C"}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/
|
|
4
4
|
import { estimateObjectSize } from "@foxglove/studio-base/players/messageMemoryEstimation.js";
|
|
5
5
|
import { ensureWebCodecsSupported } from "../../../utils/compatibility.js";
|
|
6
|
-
import { parseMessage
|
|
6
|
+
import { parseMessage } from "../../utils.js";
|
|
7
|
+
import { copyVideoFrameToI420 } from "./h264.js";
|
|
7
8
|
export async function deserializeMJPEGFrame({ topic, message, callback }) {
|
|
8
9
|
const { receiveTime } = parseMessage(message);
|
|
9
10
|
const supported = ensureWebCodecsSupported("ImageDecoder");
|
|
@@ -15,17 +16,15 @@ export async function deserializeMJPEGFrame({ topic, message, callback }) {
|
|
|
15
16
|
data: message.data
|
|
16
17
|
});
|
|
17
18
|
const { image: frame } = await decoder.decode();
|
|
18
|
-
const buffer =
|
|
19
|
-
await frame.copyTo(buffer);
|
|
20
|
-
const [encoding, step] = parsePixelFormat(frame.format);
|
|
19
|
+
const { data: buffer, width, height } = await copyVideoFrameToI420(frame, "MJPEG");
|
|
21
20
|
const foxgloveMessage = {
|
|
22
21
|
timestamp: receiveTime,
|
|
23
22
|
frame_id: "camera",
|
|
24
|
-
width
|
|
25
|
-
height
|
|
23
|
+
width,
|
|
24
|
+
height,
|
|
26
25
|
data: buffer,
|
|
27
|
-
encoding,
|
|
28
|
-
step:
|
|
26
|
+
encoding: "yuv420p",
|
|
27
|
+
step: width,
|
|
29
28
|
};
|
|
30
29
|
frame.close();
|
|
31
30
|
callback({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mjpeg.js","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/mjpeg.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,oEAAoE;AACpE,0DAA0D;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAI9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"mjpeg.js","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/mjpeg.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,oEAAoE;AACpE,0DAA0D;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0DAA0D,CAAC;AAI9F,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAQjD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,EAC1C,KAAK,EACL,OAAO,EACP,QAAQ,EACkB;IAC1B,MAAM,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAE7C,MAAM,SAAS,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC;QAC/B,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;KACnB,CAAC,CAAC;IAEH,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAEnF,MAAM,eAAe,GAAa;QAChC,SAAS,EAAE,WAAW;QACtB,QAAQ,EAAE,QAAQ;QAClB,KAAK;QACL,MAAM;QACN,IAAI,EAAE,MAAM;QACZ,QAAQ,EAAE,SAAS;QACnB,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,QAAQ,CAAC;QACP,KAAK;QACL,WAAW;QACX,OAAO,EAAE,eAAe;QACxB,WAAW,EAAE,kBAAkB,CAAC,eAAe,CAAC;QAChD,UAAU,EAAE,mBAAmB;QAC/B,qBAAqB,EAAE,OAAO,CAAC,cAAc;KAC9B,CAAC,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MessageEvent } from "@foxglove/studio-base/players/types";
|
|
2
|
+
import { type PointCloudWithDimensions } from "./depth";
|
|
3
|
+
import { type TopicDecoders } from "./h264";
|
|
4
|
+
import type { RGBDData } from "../../../protobuf.generated/RGBDData";
|
|
5
|
+
import type { NeuralCameraIntrinsics } from "../pointcloud/utils";
|
|
6
|
+
export type DeserializeRGBDFrameArgs = {
|
|
7
|
+
topic: string;
|
|
8
|
+
topicDecoders: TopicDecoders;
|
|
9
|
+
cameraIntrinsics: NeuralCameraIntrinsics;
|
|
10
|
+
maxStereoDepth: number;
|
|
11
|
+
message: RGBDData;
|
|
12
|
+
callback: (event: MessageEvent) => void;
|
|
13
|
+
onError: (error: Error) => void;
|
|
14
|
+
};
|
|
15
|
+
export type PointCloudWithColorAndDepth = PointCloudWithDimensions;
|
|
16
|
+
export declare function deserializeRGBDFrameToPointCloud({ topic, topicDecoders, cameraIntrinsics, maxStereoDepth, message, callback, }: DeserializeRGBDFrameArgs): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=rgbd-to-pointcloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rgbd-to-pointcloud.d.ts","sourceRoot":"","sources":["../../../../../../src/messaging/deserialization/video/rgbd-to-pointcloud.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAExE,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAkD,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAK5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAIrE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAGlE,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,EAAE,sBAAsB,CAAC;IACzC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,QAAQ,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAqVnE,wBAAsB,gCAAgC,CAAC,EACrD,KAAK,EACL,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,OAAO,EACP,QAAQ,GACT,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4E1C"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at http://mozilla.org/MPL/2.0/
|
|
4
|
+
import { wrap } from "comlink";
|
|
5
|
+
import { NumericType } from "@foxglove/schemas";
|
|
6
|
+
import { estimateObjectSize } from "@foxglove/studio-base/players/messageMemoryEstimation";
|
|
7
|
+
import { deserializeDepthFrame } from "./depth";
|
|
8
|
+
import { convertNv12ToI420, deserializeEncodedFrameH264 } from "./h264";
|
|
9
|
+
import { deserializeEncodedFrameH265 } from "./h265";
|
|
10
|
+
import { deserializeMJPEGFrame } from "./mjpeg";
|
|
11
|
+
import { Profile } from "../../../protobuf.generated/EncodedFrame";
|
|
12
|
+
import { Type } from "../../../protobuf.generated/ImgFrame";
|
|
13
|
+
import { parseMessage } from "../../utils";
|
|
14
|
+
import { Logger } from "../../../utils/logger";
|
|
15
|
+
import { uint8ArrayToUint16Array } from "../pointcloud/utils";
|
|
16
|
+
const RGBD_POINTCLOUD_WORKER_COUNT = 4;
|
|
17
|
+
let cachedRGBDHasGPU;
|
|
18
|
+
let rgbdHasGPUPromise;
|
|
19
|
+
let rgbdPointCloudWorkers;
|
|
20
|
+
let rgbdPointCloudWorkerIndex = 0;
|
|
21
|
+
const logger = Logger.getLogger();
|
|
22
|
+
function ensureEvenByteLength(data) {
|
|
23
|
+
if (data.length % 2 === 0) {
|
|
24
|
+
return data;
|
|
25
|
+
}
|
|
26
|
+
const padded = new Uint8Array(data.length + 1);
|
|
27
|
+
padded.set(data);
|
|
28
|
+
return padded;
|
|
29
|
+
}
|
|
30
|
+
async function detectRGBDGPUSupport() {
|
|
31
|
+
if (cachedRGBDHasGPU != undefined) {
|
|
32
|
+
return cachedRGBDHasGPU;
|
|
33
|
+
}
|
|
34
|
+
if (rgbdHasGPUPromise) {
|
|
35
|
+
return await rgbdHasGPUPromise;
|
|
36
|
+
}
|
|
37
|
+
rgbdHasGPUPromise = (async () => {
|
|
38
|
+
if (typeof navigator.gpu === "undefined") {
|
|
39
|
+
cachedRGBDHasGPU = false;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
44
|
+
cachedRGBDHasGPU = adapter != undefined;
|
|
45
|
+
return cachedRGBDHasGPU;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
cachedRGBDHasGPU = false;
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
rgbdHasGPUPromise = undefined;
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
return await rgbdHasGPUPromise;
|
|
56
|
+
}
|
|
57
|
+
function getRGBDPointCloudWorkerSlot() {
|
|
58
|
+
if (!rgbdPointCloudWorkers) {
|
|
59
|
+
rgbdPointCloudWorkers = Array.from({ length: RGBD_POINTCLOUD_WORKER_COUNT }, () => {
|
|
60
|
+
const worker = new Worker(new URL("../pointcloud/pointcloudFromDepth.worker", import.meta.url), {
|
|
61
|
+
type: "module",
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
worker,
|
|
65
|
+
remote: wrap(worker),
|
|
66
|
+
activeJobs: 0,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const slots = rgbdPointCloudWorkers;
|
|
71
|
+
const candidateSlots = slots
|
|
72
|
+
.map((slot, index) => ({ slot, index }))
|
|
73
|
+
.sort((left, right) => {
|
|
74
|
+
if (left.slot.activeJobs !== right.slot.activeJobs) {
|
|
75
|
+
return left.slot.activeJobs - right.slot.activeJobs;
|
|
76
|
+
}
|
|
77
|
+
const leftDistance = (left.index - rgbdPointCloudWorkerIndex + slots.length) % slots.length;
|
|
78
|
+
const rightDistance = (right.index - rgbdPointCloudWorkerIndex + slots.length) % slots.length;
|
|
79
|
+
return leftDistance - rightDistance;
|
|
80
|
+
});
|
|
81
|
+
const selected = candidateSlots[0];
|
|
82
|
+
rgbdPointCloudWorkerIndex = (selected.index + 1) % slots.length;
|
|
83
|
+
return selected.slot;
|
|
84
|
+
}
|
|
85
|
+
function repackYuv420pToI420(imageFrame, width, height) {
|
|
86
|
+
const specs = imageFrame.fb ?? imageFrame.sourceFb;
|
|
87
|
+
const source = ensureEvenByteLength(imageFrame.data);
|
|
88
|
+
const yStride = specs?.stride || width;
|
|
89
|
+
const uvWidth = Math.floor(width / 2);
|
|
90
|
+
const uvHeight = Math.floor(height / 2);
|
|
91
|
+
const uvStride = yStride > width ? Math.floor(yStride / 2) : uvWidth;
|
|
92
|
+
const yPlaneSize = width * height;
|
|
93
|
+
const uvPlaneSize = uvWidth * uvHeight;
|
|
94
|
+
const uOffset = specs?.p1Offset || yStride * height;
|
|
95
|
+
const vOffset = specs?.p2Offset || uOffset + uvStride * uvHeight;
|
|
96
|
+
const i420 = new Uint8Array(yPlaneSize + 2 * uvPlaneSize);
|
|
97
|
+
for (let row = 0; row < height; row++) {
|
|
98
|
+
const srcOffset = row * yStride;
|
|
99
|
+
const dstOffset = row * width;
|
|
100
|
+
i420.set(source.subarray(srcOffset, srcOffset + width), dstOffset);
|
|
101
|
+
}
|
|
102
|
+
for (let row = 0; row < uvHeight; row++) {
|
|
103
|
+
const srcUOffset = uOffset + row * uvStride;
|
|
104
|
+
const srcVOffset = vOffset + row * uvStride;
|
|
105
|
+
const dstOffset = row * uvWidth;
|
|
106
|
+
i420.set(source.subarray(srcUOffset, srcUOffset + uvWidth), yPlaneSize + dstOffset);
|
|
107
|
+
i420.set(source.subarray(srcVOffset, srcVOffset + uvWidth), yPlaneSize + uvPlaneSize + dstOffset);
|
|
108
|
+
}
|
|
109
|
+
return i420;
|
|
110
|
+
}
|
|
111
|
+
function toMatrix3x3(matrix) {
|
|
112
|
+
const rows = matrix?.arrays;
|
|
113
|
+
if (!rows || rows.length < 3) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const normalizedRows = rows
|
|
117
|
+
.slice(0, 3)
|
|
118
|
+
.map((row) => row?.values.slice(0, 3).map((value) => value ?? 0));
|
|
119
|
+
if (normalizedRows.some((row) => !row || row.length < 3)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
return normalizedRows;
|
|
123
|
+
}
|
|
124
|
+
function multiply3x3(left, right) {
|
|
125
|
+
return [
|
|
126
|
+
[
|
|
127
|
+
left[0][0] * right[0][0] + left[0][1] * right[1][0] + left[0][2] * right[2][0],
|
|
128
|
+
left[0][0] * right[0][1] + left[0][1] * right[1][1] + left[0][2] * right[2][1],
|
|
129
|
+
left[0][0] * right[0][2] + left[0][1] * right[1][2] + left[0][2] * right[2][2],
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
left[1][0] * right[0][0] + left[1][1] * right[1][0] + left[1][2] * right[2][0],
|
|
133
|
+
left[1][0] * right[0][1] + left[1][1] * right[1][1] + left[1][2] * right[2][1],
|
|
134
|
+
left[1][0] * right[0][2] + left[1][1] * right[1][2] + left[1][2] * right[2][2],
|
|
135
|
+
],
|
|
136
|
+
[
|
|
137
|
+
left[2][0] * right[0][0] + left[2][1] * right[1][0] + left[2][2] * right[2][0],
|
|
138
|
+
left[2][0] * right[0][1] + left[2][1] * right[1][1] + left[2][2] * right[2][1],
|
|
139
|
+
left[2][0] * right[0][2] + left[2][1] * right[1][2] + left[2][2] * right[2][2],
|
|
140
|
+
],
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
function extractIntrinsicsFromTransformation(transformation) {
|
|
144
|
+
const sourceIntrinsicMatrix = toMatrix3x3(transformation?.sourceIntrinsicMatrix);
|
|
145
|
+
if (!sourceIntrinsicMatrix) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
const transformationMatrix = toMatrix3x3(transformation?.transformationMatrix);
|
|
149
|
+
const effectiveIntrinsicMatrix = transformationMatrix
|
|
150
|
+
? multiply3x3(transformationMatrix, sourceIntrinsicMatrix)
|
|
151
|
+
: sourceIntrinsicMatrix;
|
|
152
|
+
const fx = effectiveIntrinsicMatrix[0][0];
|
|
153
|
+
const fy = effectiveIntrinsicMatrix[1][1];
|
|
154
|
+
const cx = effectiveIntrinsicMatrix[0][2];
|
|
155
|
+
const cy = effectiveIntrinsicMatrix[1][2];
|
|
156
|
+
if (![fx, fy, cx, cy].every((value) => Number.isFinite(value))) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
left: {
|
|
161
|
+
focalLenght: { x: fx, y: fy },
|
|
162
|
+
principalPoint: { x: cx, y: cy },
|
|
163
|
+
},
|
|
164
|
+
right: {
|
|
165
|
+
focalLenght: { x: fx, y: fy },
|
|
166
|
+
principalPoint: { x: cx, y: cy },
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function resolveRGBDIntrinsics(message, fallbackIntrinsics) {
|
|
171
|
+
return (extractIntrinsicsFromTransformation(message.depthImgFrame?.transformation) ??
|
|
172
|
+
extractIntrinsicsFromTransformation(message.depthEncodedFrame?.transformation) ??
|
|
173
|
+
extractIntrinsicsFromTransformation(message.colorImgFrame?.transformation) ??
|
|
174
|
+
extractIntrinsicsFromTransformation(message.colorEncodedFrame?.transformation) ??
|
|
175
|
+
fallbackIntrinsics);
|
|
176
|
+
}
|
|
177
|
+
async function extractEncodedColorFrame(encodedFrame, topicDecoders, topic) {
|
|
178
|
+
const decodeFrame = async () => await new Promise((resolve, reject) => {
|
|
179
|
+
const callback = (event) => {
|
|
180
|
+
resolve(event.message);
|
|
181
|
+
};
|
|
182
|
+
const rejectDecode = (error) => {
|
|
183
|
+
reject(error);
|
|
184
|
+
};
|
|
185
|
+
const args = {
|
|
186
|
+
topic,
|
|
187
|
+
message: encodedFrame,
|
|
188
|
+
callback,
|
|
189
|
+
};
|
|
190
|
+
if (encodedFrame.profile === Profile.AVC) {
|
|
191
|
+
void deserializeEncodedFrameH264({ ...args, topicDecoders, onError: rejectDecode }).catch(rejectDecode);
|
|
192
|
+
}
|
|
193
|
+
else if (encodedFrame.profile === Profile.JPEG) {
|
|
194
|
+
void deserializeMJPEGFrame(args).catch(rejectDecode);
|
|
195
|
+
}
|
|
196
|
+
else if (encodedFrame.profile === Profile.HEVC) {
|
|
197
|
+
void deserializeEncodedFrameH265({ ...args, topicDecoders, onError: rejectDecode }).catch(rejectDecode);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
reject(new Error(`Unsupported RGBD color profile ${Profile[encodedFrame.profile] ?? encodedFrame.profile}`));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
const decoded = await decodeFrame();
|
|
204
|
+
return {
|
|
205
|
+
data: ensureEvenByteLength(decoded.data),
|
|
206
|
+
width: decoded.width,
|
|
207
|
+
height: decoded.height,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function extractColorFrame(encodedFrame, imageFrame, topicDecoders, topic) {
|
|
211
|
+
if (encodedFrame) {
|
|
212
|
+
return extractEncodedColorFrame(encodedFrame, topicDecoders, topic);
|
|
213
|
+
}
|
|
214
|
+
if (imageFrame) {
|
|
215
|
+
const { width, height, type } = parseMessage(imageFrame);
|
|
216
|
+
const specs = imageFrame.fb ?? imageFrame.sourceFb;
|
|
217
|
+
if (type === Type.NV12) {
|
|
218
|
+
const stride = specs?.stride || width;
|
|
219
|
+
const uvOffset = specs?.p1Offset || stride * height;
|
|
220
|
+
return Promise.resolve({
|
|
221
|
+
data: convertNv12ToI420(ensureEvenByteLength(imageFrame.data), width, height, stride, stride, 0, uvOffset),
|
|
222
|
+
width,
|
|
223
|
+
height,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (type === Type.YUV420p) {
|
|
227
|
+
return Promise.resolve({
|
|
228
|
+
data: repackYuv420pToI420(imageFrame, width, height),
|
|
229
|
+
width,
|
|
230
|
+
height,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return Promise.reject(new Error(`Unsupported RGBD color ImgFrame type ${Type[type]}`));
|
|
234
|
+
}
|
|
235
|
+
return Promise.resolve({
|
|
236
|
+
data: new Uint8Array(),
|
|
237
|
+
width: 0,
|
|
238
|
+
height: 0,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
function extractDepthFrame(encodedFrame, imageFrame) {
|
|
242
|
+
if (encodedFrame) {
|
|
243
|
+
return ensureEvenByteLength(encodedFrame.data);
|
|
244
|
+
}
|
|
245
|
+
if (imageFrame) {
|
|
246
|
+
return ensureEvenByteLength(imageFrame.data);
|
|
247
|
+
}
|
|
248
|
+
return new Uint8Array();
|
|
249
|
+
}
|
|
250
|
+
function parseDimensions(message) {
|
|
251
|
+
const obj = {
|
|
252
|
+
depthWidth: 0,
|
|
253
|
+
depthHeight: 0,
|
|
254
|
+
};
|
|
255
|
+
if (message.depthEncodedFrame) {
|
|
256
|
+
const { width, height } = message.depthEncodedFrame ?? { width: 1920, height: 1200 };
|
|
257
|
+
obj.depthWidth = width;
|
|
258
|
+
obj.depthHeight = height;
|
|
259
|
+
}
|
|
260
|
+
else if (message.depthImgFrame) {
|
|
261
|
+
const { width, height } = parseMessage(message.depthImgFrame);
|
|
262
|
+
obj.depthWidth = width;
|
|
263
|
+
obj.depthHeight = height;
|
|
264
|
+
}
|
|
265
|
+
return obj;
|
|
266
|
+
}
|
|
267
|
+
export async function deserializeRGBDFrameToPointCloud({ topic, topicDecoders, cameraIntrinsics, maxStereoDepth, message, callback, }) {
|
|
268
|
+
const workerSlot = getRGBDPointCloudWorkerSlot();
|
|
269
|
+
workerSlot.activeJobs += 1;
|
|
270
|
+
const { receiveTime } = parseMessage(message);
|
|
271
|
+
const { depthWidth, depthHeight } = parseDimensions(message);
|
|
272
|
+
const rgbdIntrinsics = resolveRGBDIntrinsics(message, cameraIntrinsics);
|
|
273
|
+
const hasGPU = await detectRGBDGPUSupport();
|
|
274
|
+
const colorFrame = await extractColorFrame(message.colorEncodedFrame, message.colorImgFrame, topicDecoders, topic);
|
|
275
|
+
const uint8DepthFrames = extractDepthFrame(message.depthEncodedFrame, message.depthImgFrame);
|
|
276
|
+
const depthFrames = uint8ArrayToUint16Array(uint8DepthFrames);
|
|
277
|
+
try {
|
|
278
|
+
const pointCloud = await workerSlot.remote.depthToPointcloudGPU([depthFrames], depthWidth, depthHeight, rgbdIntrinsics.right.focalLenght.x, rgbdIntrinsics.right.focalLenght.y, rgbdIntrinsics.right.principalPoint.x, rgbdIntrinsics.right.principalPoint.y, [colorFrame.data], colorFrame.width, colorFrame.height, maxStereoDepth, { hasGPU });
|
|
279
|
+
const foxgloveMessage = {
|
|
280
|
+
timestamp: message.tsDevice ?? receiveTime,
|
|
281
|
+
frame_id: `pointcloud-${topic}-frame`,
|
|
282
|
+
point_stride: 16,
|
|
283
|
+
pose: {
|
|
284
|
+
position: { x: 0, y: 0, z: 0 },
|
|
285
|
+
orientation: { x: 0, y: 0, z: 1, w: 0 }
|
|
286
|
+
},
|
|
287
|
+
width: depthWidth,
|
|
288
|
+
height: depthHeight,
|
|
289
|
+
fields: [
|
|
290
|
+
{ name: "x", offset: 0, type: NumericType.FLOAT32 },
|
|
291
|
+
{ name: "y", offset: 4, type: NumericType.FLOAT32 },
|
|
292
|
+
{ name: "z", offset: 8, type: NumericType.FLOAT32 },
|
|
293
|
+
{ name: "red", offset: 12, type: NumericType.UINT8 },
|
|
294
|
+
{ name: "green", offset: 13, type: NumericType.UINT8 },
|
|
295
|
+
{ name: "blue", offset: 14, type: NumericType.UINT8 },
|
|
296
|
+
{ name: "alpha", offset: 15, type: NumericType.UINT8 },
|
|
297
|
+
],
|
|
298
|
+
data: pointCloud[0]
|
|
299
|
+
};
|
|
300
|
+
callback({
|
|
301
|
+
topic,
|
|
302
|
+
receiveTime,
|
|
303
|
+
message: foxgloveMessage,
|
|
304
|
+
sizeInBytes: estimateObjectSize(foxgloveMessage),
|
|
305
|
+
schemaName: "foxglove.PointCloud.Colored"
|
|
306
|
+
});
|
|
307
|
+
if (message.depthImgFrame) {
|
|
308
|
+
void deserializeDepthFrame({
|
|
309
|
+
topic,
|
|
310
|
+
message: message.depthImgFrame,
|
|
311
|
+
callback,
|
|
312
|
+
maxStereoDepth: maxStereoDepth / 1000,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
finally {
|
|
317
|
+
workerSlot.activeJobs = Math.max(0, workerSlot.activeJobs - 1);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=rgbd-to-pointcloud.js.map
|