ohif-viewer/extensions/cornerstone/src/utils/updateSegmentationStats.ts
Bill Wallace 48f2f6fb13
fix(HistoryMemo): Segmentation delete wasn't remembered (#5775)
Will address a few more comments in a separate PR released after this one into 3.13

* fix(HistoryMemo): Segmentation delete wasn't remembered

* Temporary add of package.json linking

* fix: Use newer dcmjs version and delete segmentation with history cs3d
2026-02-05 11:49:49 -05:00

193 lines
5.4 KiB
TypeScript

import * as cornerstoneTools from '@cornerstonejs/tools';
import cloneDeep from 'lodash.clonedeep';
interface BidirectionalAxis {
length: number;
// Add other axis properties as needed
}
interface BidirectionalData {
majorAxis: BidirectionalAxis;
minorAxis: BidirectionalAxis;
}
/**
* Updates the statistics for a segmentation by calculating stats for each segment
* and storing them in the segment's cachedStats property
*
* @param segmentation - The segmentation object containing segments to update stats for
* @param segmentationId - The ID of the segmentation
* @returns The updated segmentation object with new stats, or null if no updates were made
*/
export async function updateSegmentationStats({
segmentation,
segmentationId,
readableText,
}: {
segmentation: any;
segmentationId: string;
readableText: any;
}): Promise<any | null> {
if (!segmentation) {
console.debug('No segmentation found for id:', segmentationId);
return null;
}
const segmentIndices = Object.keys(segmentation.segments)
.map(index => parseInt(index))
.filter(index => index > 0); // Filter out segment 0 which is typically background
if (segmentIndices.length === 0) {
console.debug('No segments found in segmentation:', segmentationId);
return null;
}
const stats = await cornerstoneTools.utilities.segmentation.getStatistics({
segmentationId,
segmentIndices,
mode: 'individual',
});
if (!stats) {
return null;
}
const updatedSegmentation = cloneDeep(segmentation);
let hasUpdates = false;
// Loop through each segment's stats
Object.entries(stats).forEach(([segmentIndex, segmentStats]) => {
const index = parseInt(segmentIndex);
if (!updatedSegmentation.segments[index]) {
// This happens when a segment is being restored
console.warn('Segment not found to update cached stats:', index);
return;
}
if (!updatedSegmentation.segments[index].cachedStats) {
updatedSegmentation.segments[index].cachedStats = {};
hasUpdates = true;
}
// Get existing namedStats or initialize if not present
const namedStats = updatedSegmentation.segments[index].cachedStats.namedStats || {};
if (segmentStats.array) {
segmentStats.array.forEach(stat => {
// only gather stats that are in the readableText
if (!readableText[stat.name]) {
return;
}
if (stat && stat.name) {
namedStats[stat.name] = {
name: stat.name,
label: readableText[stat.name],
value: stat.value,
unit: stat.unit,
order: Object.keys(readableText).indexOf(stat.name),
};
}
});
if (readableText.volume) {
// Add volume if it exists but isn't in the array
if (segmentStats.volume && !namedStats.volume) {
namedStats.volume = {
name: 'volume',
label: 'Volume',
value: segmentStats.volume.value,
unit: segmentStats.volume.unit,
order: Object.keys(readableText).indexOf('volume'),
};
}
}
// Update the segment's cachedStats with namedStats
updatedSegmentation.segments[index].cachedStats.namedStats = namedStats;
hasUpdates = true;
}
});
return hasUpdates ? updatedSegmentation : null;
}
/**
* Updates a segment's statistics with bidirectional measurement data
*
* @param segmentationId - The ID of the segmentation
* @param segmentIndex - The index of the segment to update
* @param bidirectionalData - The bidirectional measurement data to add
* @param segmentationService - The segmentation service to use for updating the segment
* @returns Whether the update was successful
*/
export function updateSegmentBidirectionalStats({
segmentationId,
segmentIndex,
bidirectionalData,
segmentationService,
annotation,
}: {
segmentationId: string;
segmentIndex: number;
bidirectionalData: BidirectionalData;
segmentationService: AppTypes.SegmentationService;
annotation: any;
}) {
if (!segmentationId || segmentIndex === undefined || !bidirectionalData) {
console.debug('Missing required data for bidirectional stats update');
return null;
}
const segmentation = segmentationService.getSegmentation(segmentationId);
if (!segmentation || !segmentation.segments[segmentIndex]) {
console.debug('Segment not found:', segmentIndex, 'in segmentation:', segmentationId);
return null;
}
const updatedSegmentation = { ...segmentation };
const segment = updatedSegmentation.segments[segmentIndex];
if (!segment.cachedStats) {
segment.cachedStats = { namedStats: {} };
}
if (!segment.cachedStats.namedStats) {
segment.cachedStats.namedStats = {};
}
const { majorAxis, minorAxis, maxMajor, maxMinor } = bidirectionalData;
if (!majorAxis || !minorAxis) {
console.debug('Missing major or minor axis data');
return null;
}
let hasUpdates = false;
const namedStats = segment.cachedStats.namedStats;
// Only calculate and update if we have valid measurements
if (maxMajor > 0 && maxMinor > 0) {
namedStats.bidirectional = {
name: 'bidirectional',
label: 'Bidirectional',
annotationUID: annotation.annotationUID,
value: {
maxMajor,
maxMinor,
majorAxis,
minorAxis,
},
unit: 'mm',
};
hasUpdates = true;
}
if (hasUpdates) {
return updatedSegmentation;
}
return null;
}