@cornerstonejs/core
Version:
Cornerstone3D Core
103 lines (102 loc) • 4.2 kB
JavaScript
import { vec3 } from 'gl-matrix';
import { MetadataModules } from '../enums/index.js';
import * as metaData from '../metaData.js';
import isVideoTransferSyntax from './isVideoTransferSyntax.js';
export function getVideoImageDataMetadata(imageId) {
const imagePlaneModule = metaData.get(MetadataModules.IMAGE_PLANE, imageId);
let rowCosines = imagePlaneModule.rowCosines;
let columnCosines = imagePlaneModule.columnCosines;
const usingDefaultValues = imagePlaneModule.usingDefaultValues;
if (usingDefaultValues || rowCosines == null || columnCosines == null) {
rowCosines = [1, 0, 0];
columnCosines = [0, 1, 0];
}
const rowCosineVec = vec3.fromValues(rowCosines[0], rowCosines[1], rowCosines[2]);
const colCosineVec = vec3.fromValues(columnCosines[0], columnCosines[1], columnCosines[2]);
const scanAxisNormal = vec3.create();
vec3.cross(scanAxisNormal, rowCosineVec, colCosineVec);
const rows = imagePlaneModule.rows;
const columns = imagePlaneModule.columns;
const origin = (imagePlaneModule.imagePositionPatient || [0, 0, 0]);
const xSpacing = imagePlaneModule.columnPixelSpacing || 1;
const ySpacing = imagePlaneModule.rowPixelSpacing || 1;
const zSpacing = 1;
const xVoxels = imagePlaneModule.columns;
const yVoxels = imagePlaneModule.rows;
const zVoxels = 1;
return {
bitsAllocated: 8,
numberOfComponents: 3,
origin,
rows,
columns,
direction: [...rowCosineVec, ...colCosineVec, ...scanAxisNormal],
dimensions: [xVoxels, yVoxels, zVoxels],
spacing: [xSpacing, ySpacing, zSpacing],
hasPixelSpacing: !!imagePlaneModule.columnPixelSpacing,
numVoxels: xVoxels * yVoxels * zVoxels,
imagePlaneModule,
};
}
export function loadVideoStreamMetadata(imageId) {
const renderedUrl = getRenderedVideoUrl(imageId);
if (!renderedUrl) {
throw new Error(`Video Image ID ${imageId} does not have a rendered video view`);
}
const generalSeries = metaData.get(MetadataModules.GENERAL_SERIES, imageId);
const cine = metaData.get(MetadataModules.CINE, imageId) || {};
const instance = metaData.get(MetadataModules.INSTANCE, imageId);
const frameTime = cine.frameTime ?? cine.FrameTime;
return {
renderedUrl,
modality: generalSeries?.Modality ?? generalSeries?.modality,
metadata: getVideoImageDataMetadata(imageId),
cineRate: cine.cineRate ??
cine.recommendedDisplayFrameRate ??
(frameTime ? 1000 / Number(frameTime) : undefined),
numberOfFrames: cine.numberOfFrames ?? instance?.NumberOfFrames,
};
}
function getRenderedVideoUrl(imageId) {
const imageUrlModule = metaData.get(MetadataModules.IMAGE_URL, imageId);
if (imageUrlModule?.rendered) {
return imageUrlModule.rendered;
}
const transferSyntax = metaData.get(MetadataModules.TRANSFER_SYNTAX, imageId);
const isVideo = transferSyntax?.isVideo ||
isVideoTransferSyntax(transferSyntax?.transferSyntaxUID);
if (!isVideo) {
return;
}
const imageUrl = imageId.startsWith('wadors:')
? imageId.substring(7)
: imageId;
if (!imageUrl.includes('/frames/')) {
return;
}
return imageUrl
.replace('/frames/', '/rendered/')
.replace(/\/rendered\/\d+($|[?#])/, '/rendered$1');
}
export function normalizeVideoPlaybackInfo(args) {
const durationSeconds = Math.max(args.durationSeconds || 0, 0.001);
let numberOfFrames = args.numberOfFrames;
let fps = args.cineRate;
if (!numberOfFrames || numberOfFrames === 1) {
numberOfFrames = Math.max(1, Math.round(durationSeconds * (fps || 30)));
}
if (!fps) {
fps = Math.max(1, Math.round(numberOfFrames / durationSeconds));
}
return {
fps,
numberOfFrames,
frameRange: [1, numberOfFrames],
};
}
export function frameNumberToTimeSeconds(frameNumber, fps) {
return Math.max(0, frameNumber - 1) / Math.max(1, fps);
}
export function timeSecondsToFrameNumber(timeSeconds, fps) {
return 1 + Math.round(Math.max(0, timeSeconds) * Math.max(1, fps));
}