Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | 137x 137x 137x 1096x 137x 137x 137x 959x 137x 137x | /**
* The default display-set split rules as pure JSON data: no functions, and no
* imports of application state. A server that indexes a study and a viewer
* that splits a series compile the same data, so both get the same splits.
*
* Each name in a rule is a DICOM keyword or a context name of
* `displaySetContext.ts`. {@link createDisplaySetSplitRules} compiles the data
* strictly. See "Sharing rules between applications" in
* `packages/docs/docs/concepts/cornerstone-metadata/display-sets.md`.
*
* @module displaySetRules
*/
import { compileDisplaySetSplitRules } from './compileSplitRules';
/**
* @typedef {import('./displaySetRulesTypes').DisplaySetRules} DisplaySetRules
* @typedef {import('./displaySetRulesTypes').CreateDisplaySetSplitRulesOptions} CreateDisplaySetSplitRulesOptions
* @typedef {import('./types').SplitRuleSet} SplitRuleSet
*/
/** Modalities whose multi-slice series are reconstructable into a volume. */
const VOLUME_MODALITIES = ['CT', 'MR', 'PT', 'NM'];
/** Modalities whose multi-frame instances are cine clips. */
const CINE_MODALITIES = ['US', 'XA', 'RF', 'ES', 'XC'];
/** Multi-frame SOP classes of cine modalities that hold a volume, not a clip. */
const VOLUME_MULTIFRAME_SOP_CLASSES = [
'1.2.840.10008.5.1.4.1.1.6.2', // Enhanced US Volume
'1.2.840.10008.5.1.4.1.1.13.1.1', // X-Ray 3D Angiographic
'1.2.840.10008.5.1.4.1.1.13.1.2', // X-Ray 3D Craniofacial
];
/** A list as the source text of an expression array literal. */
const quotedList = (values) => `[${values.map((v) => `'${v}'`).join(', ')}]`;
/** Modalities that acquire one image per instance rather than a stack. */
const SINGLE_IMAGE_MODALITIES = ['CR', 'DX', 'MG'];
/**
* An image instance that actually carries a raster (`Rows` has a value). The
* volume rules require it.
*/
const RENDERABLE_IMAGE = { isImage: true, Rows: { notNull: true } };
/**
* One display set for each revision chain of a series (each document, when it
* has no predecessor). The active instance sorts last, and every instance of
* the chain stays in the display set.
*/
const REVISION_CHAIN = {
groupBy: ['SeriesInstanceUID', 'revisionChainUid'],
sortBy: ['isActiveRevision', 'InstanceNumber'],
attributes: { activeSopInstanceUid: 'revisionChainUid' },
};
/**
* A rule for one kind of non-image object: the instances of its SOP class
* list, one display set for each series, built by the handler of the host.
*/
const objectRule = (priority, kind, handler, description) => ({
priority,
description,
handler,
matches: { [kind]: true },
groupBy: ['SeriesInstanceUID'],
});
/**
* The default display-set split rules, as data.
*
* The key is the rule id. Rules are evaluated in ascending `priority` and the
* first match wins per instance, so the priorities are part of the contract.
*
* The defaults use the priorities `1..n`, and the catch-all `unsupported` runs
* last, at `Number.MAX_SAFE_INTEGER`. A rule with a priority below `0` runs
* before all of them. A rule with a priority above
* `DEFAULT_SPLIT_RULE_PRIORITY_LIMIT` (10000) runs after every other default
* rule, and still before `unsupported`, so it sees the instances that no other
* default claims.
*
* A rule with `handler` makes display sets that the host builds with its own
* code for that kind of object. The image rules name no handler.
*
* @type {DisplaySetRules}
*/
export const displaySetRules = {
video: {
priority: 1,
description:
'A video-only SOP class, or a video transfer syntax, or (with no known ' +
'transfer syntax) a long multi-frame secondary capture. One display set ' +
'per instance, shown on a video viewport.',
handler: 'video',
matches: { isVideo: true },
groupBy: ['SOPInstanceUID'],
},
ecg: {
priority: 2,
description:
'ECG / waveform SOP classes. One display set per instance, shown on a ' +
'waveform viewport rather than an image viewport.',
handler: 'ecg',
matches: { isEcg: true },
groupBy: ['SOPInstanceUID'],
},
wholeslide: {
priority: 3,
description:
'VL Whole Slide Microscopy (or modality SM). All pyramid levels of the ' +
'series form a single whole-slide display set.',
handler: 'wholeSlide',
matches: { isWsi: true },
groupBy: ['SeriesInstanceUID'],
},
singleImageModality: {
priority: 4,
description:
'CR / DX / MG, which acquire one image per instance. One display set ' +
'per instance, so each view of a mammography series is its own display ' +
'set.',
matches: { Modality: SINGLE_IMAGE_MODALITIES, isImage: true },
groupBy: ['SOPInstanceUID'],
},
multiFrame: {
priority: 5,
description:
'Multi-frame image instances, one display set per instance. A cine clip ' +
'(a slice location, or a US, XA, RF, ES or XC multi-frame that is not a ' +
'volume) is flagged isClip. Another multi-frame instance, such as an ' +
'enhanced CT, is not a clip.',
context: {
// US, XA and other cine clips have no SliceLocation.
isCineClip:
`SliceLocation != null || (Modality in ${quotedList(CINE_MODALITIES)}` +
` && !(SOPClassUID in ${quotedList(VOLUME_MULTIFRAME_SOP_CLASSES)}))`,
},
matches: { isImage: true, isMultiFrame: true },
// DICOM groups an object by InstanceNumber, but no known data needs it,
// and much code expects one instance in a multi-frame display set.
groupBy: ['SOPInstanceUID'],
attributes: {
isClip: 'isCineClip',
// NumberOfFrames is frequently naturalized as a string (e.g. '30').
numImageFrames: { expression: 'Number(NumberOfFrames)' },
},
},
mixedDimensionalityBValue: {
priority: 6,
description:
'Diffusion MR that mixes 4D b-value frames with trailing frames that have ' +
'none. The undefined-b-value frames are not part of the 4D set, so they ' +
'split off - otherwise 4D rendering is applied to the 3D portion.',
context: {
// A series value: some instances of the series have a b-value, and
// some have none.
isMixedBValue:
'series.instances.count(DiffusionBValue) > 0 && ' +
'series.instances.count(DiffusionBValue) < series.instances.count()',
hasNoBValue: 'DiffusionBValue == null',
},
matches: { Modality: 'MR', isMixedBValue: true, ...RENDERABLE_IMAGE },
groupBy: ['SeriesInstanceUID', 'hasNoBValue'],
},
volume3d: {
priority: 7,
description:
'Multi-slice CT / MR / PT / NM, which reconstruct into a volume.',
matches: {
Modality: VOLUME_MODALITIES,
seriesInstanceCount: { greaterThan: 1 },
...RENDERABLE_IMAGE,
},
groupBy: ['SeriesInstanceUID'],
},
defaultImageRule: {
priority: 8,
description:
'Fallback for any remaining image SOP class, also with no Rows in the ' +
'metadata. Grouped one display set per series.',
matches: { isImage: true },
},
segmentation: objectRule(
9,
'isSegmentation',
'segmentation',
'Segmentations (segmentationSopClassUids). One display set per series.'
),
rtStructureSet: objectRule(
10,
'isRtStructureSet',
'rtStructureSet',
'RT structure sets (rtStructureSetSopClassUids). One display set per series.'
),
parametricMap: objectRule(
11,
'isParametricMap',
'parametricMap',
'Parametric maps (parametricMapSopClassUids). One display set per series.'
),
structuredReport: {
...objectRule(
12,
'isStructuredReport',
'structuredReport',
'Structured reports (structuredReportSopClassUids), also Comprehensive ' +
'3D SR. One display set per series. Manufacturer and ' +
'ManufacturerModelName name the writer of the SR.'
),
attributes: {
Manufacturer: 'Manufacturer',
ManufacturerModelName: 'ManufacturerModelName',
},
},
encapsulatedDocument: {
...objectRule(
13,
'isEncapsulatedDocument',
'encapsulatedDocument',
'Encapsulated documents (encapsulatedDocumentSopClassUids). One ' +
'display set per revision chain (PredecessorDocumentsSequence) of ' +
'the series; activeSopInstanceUid names the current revision.'
),
...REVISION_CHAIN,
},
bulkAnnotation: objectRule(
14,
'isBulkAnnotation',
'bulkAnnotation',
'Microscopy bulk annotations (bulkAnnotationSopClassUids). One display ' +
'set per series.'
),
chart: {
...objectRule(
15,
'isChart',
'chart',
'Charts (chartSopClassUids, empty by default: a host adds its chart ' +
'SOP class). One display set per revision chain of the series; ' +
'activeSopInstanceUid names the current revision.'
),
...REVISION_CHAIN,
},
/**
* Gives an application a way to list and identify each object that it cannot
* render. A rule that supports one of these formats takes a lower priority.
*/
unsupported: {
priority: Number.MAX_SAFE_INTEGER,
description:
'Catch-all for objects that no other rule claims (presentation states, ' +
'other non-image objects). Produces one display set for each series, ' +
'marked isDisplayable: false, so the objects are surfaced rather than ' +
'silently dropped. Runs last.',
handler: 'unsupported',
// No `matches`: claims whatever is left.
groupBy: ['SeriesInstanceUID'],
},
};
/**
* Compiles the display set rules into a `SplitRuleSet` with the same keys and
* priorities, for `splitImageIdsBySplitRules` / `groupInstancesBySplitRules`.
* A malformed rule set throws here, at setup, and not during a split.
*
* ```js
* const rules = createDisplaySetSplitRules(displaySetRules); // = compiledDisplaySetRules
* ```
*
* See "Extending the display set rules" in
* `packages/docs/docs/concepts/cornerstone-metadata/display-sets.md`.
*
* @param {DisplaySetRules} [rules=displaySetRules] - the rules as data, keyed by rule id.
* @param {CreateDisplaySetSplitRulesOptions} [options] - the global context, and named extension points.
* @returns {SplitRuleSet} compiled rules, keyed by rule id.
*/
export function createDisplaySetSplitRules(
rules = displaySetRules,
options = {}
) {
return compileDisplaySetSplitRules(rules, options);
}
|