feat(metadata): metadata access improvement (#5292)

* feat(metadata): metadata access improvement

* feat: Make a few fields commonly used at the instance level inherit multiframe
This commit is contained in:
Devu-trenser 2025-11-06 19:16:01 +05:30 committed by GitHub
parent 9413219206
commit 16233d980f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 3 deletions

View File

@ -2,6 +2,7 @@ import dcmjs from 'dcmjs';
import pubSubServiceInterface from '../_shared/pubSubServiceInterface';
import createStudyMetadata from './createStudyMetadata';
import addProxyFields from '../../utils/addProxyFields';
const EVENTS = {
STUDY_ADDED: 'event::dicomMetadataStore:studyAdded',
@ -70,7 +71,7 @@ function _getStudy(StudyInstanceUID) {
* @returns {*} A series object
*/
function _getSeries(StudyInstanceUID, SeriesInstanceUID) {
if(!StudyInstanceUID) {
if (!StudyInstanceUID) {
const series = _model.studies.map(study => study.series).flat();
return series.find(aSeries => aSeries.SeriesInstanceUID === SeriesInstanceUID);
}
@ -176,7 +177,7 @@ const BaseImplementation = {
naturalizedDataset = dicomJSONDataset;
}
const { StudyInstanceUID } = naturalizedDataset;
const { StudyInstanceUID, NumberOfFrames: numberOfFrames } = naturalizedDataset;
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);
@ -185,10 +186,19 @@ const BaseImplementation = {
study = _model.studies[_model.studies.length - 1];
}
naturalizedDataset = numberOfFrames > 1 ? addProxyFields(naturalizedDataset) : naturalizedDataset;
study.addInstanceToSeries(naturalizedDataset);
},
addInstances(instances, madeInClient = false) {
const { StudyInstanceUID, SeriesInstanceUID } = instances[0];
const { StudyInstanceUID, SeriesInstanceUID, NumberOfFrames } = instances[0];
// For multiframe images (NumberOfFrames > 1), wrap each instance with a metadata proxy
// to enable fallback access for key image plane fields. This allows fields like
// ImagePositionPatient, ImageOrientationPatient, and PixelSpacing to be transparently
// resolved from parent or shared metadata if they are not directly present on the instance directly
if (NumberOfFrames > 1) {
instances = instances.map(instance => addProxyFields(instance));
}
let study = _model.studies.find(study => study.StudyInstanceUID === StudyInstanceUID);

View File

@ -0,0 +1,57 @@
/**
* fieldProxies has the set of fields which are proxied on a per-field basis
* to the child element.
*/
export const fieldProxies = [
'ImagePositionPatient',
'ImageOrientationPatient',
'PixelSpacing',
// more fields as necessary
];
const METADATA_PROXY_FLAG = Symbol('isMetadataProxy');
/**
* Adds proxy properties for a limited number of values which are sometimes
* defined in multiframe instance data.
*
* If a requested field is in `metadataFieldsToWrap`:
* - It first tries to return the value from the current instance.
* - If not found, it attempts to retrieve the value from the `_parentInstance`.
* - If still not found, it checks `_parentInstance._shared`.
* - If none exist, it returns `undefined`.
*
* For all other properties, it behaves like a regular property access.
*
* This allows graceful fallback access for DICOM metadata values that might be spread
* across nested or shared metadata structures.
*
* @param {Object} instance - The target instance object to wrap.
* @returns {Proxy} A proxy-wrapped instance with custom field resolution behavior.
*/
export function addProxyFields(instance) {
// Skip wrapping if already wrapped
if (!instance || instance[METADATA_PROXY_FLAG]) {
return instance;
}
for(const fieldProxy of fieldProxies) {
if( fieldProxy in instance) {
continue;
}
Object.defineProperty(instance,fieldProxy, {
configurable: true,
enumerable: true,
get: () => {
return instance._parentInstance?.[fieldProxy] ?? instance._parentInstance?._shared?.[fieldProxy];
}
});
}
// Mark this proxy to avoid double wrapping
Object.defineProperty(instance,METADATA_PROXY_FLAG, { value: true });
return instance;
}
export default addProxyFields;