Sort instances/study by acquisition datetime

Good Morning,
currently running the latest version of ohif (3.8.0-beta.54) .
I am trying to understand if there is a way to SORT instances INSIDE a study (cine/Img within an exam).
the reason for this is, for instance , cariologies interpret exams by the acquisition date & time .
unfortunately, when displaying studies using ohif - the displayed order isn’t by the acquisition order.
for me I think its quite clear to sort it by this way BUT i Couldn’t find any docs regarding this , only sorting studies(exams) by their dates which this is not the case here.
Hope i clarify myself correctly ,
Dan

Hey there, if I am not mistaken the order whne you are inside of the study is dictated by the hanging protocol applied. If no rule is applied the order can be a bit random, which might be the case for the default protocol.

You could try to create one that applies to the proper modality and add it on the longitudinal mode (the basic mode).

Again, fast answer, but hope it helps. Cheers!

Thanks for your response!
I am using the google health care hanging protocol , tried to do a research on google if they have some configuration about this, found non
Do you know maybe if I can handle it by Ohif?
B.R, Dan

As far as I know it has to be done by Ohif. Sorry for the late answer, was just leaving work.

The file I was referring to is located in extensions > default > src > getHangingProtocolModule.js, there you can find the default protocol.

You will be able to see that it has a seriesMatchingRules attribute, there you can add an object using the attribute from the dicom you need to sort with (AccessionNumber?), put a weight to it, then add a constraint for it to apply that weight.

I think you will not be able to do a direct sort in there, so you might need to get a bit creative with the way to order them.

Might be another way of sorting them though, hope someone else can help as well.

There you have the documentation for the rules. See if you can sort them using any of the DICOM attributes in some way (like if they are have an attriibute that numbers them from 1 to n, making an individual rule for each number if the instances are usually low).

@haldawe2 thanks for your response .
I am not sure I completely understand the mechanism under the getHangingProtocolModule.
my desired way of viewing series is by ‘Acquisition DateTime’ attribute ascending (old to newest).
In addition, I found that Image’s is converged into one series which is not a good behavior when ordering by acquisition datetime.

i hope i got myself clear.
Thanks a lot!,
BR Dan .

Hi - similar issue, needed to sort by Instance Number - ended up modifying getSopClassHandlerModule to change sort order of the SOPClassID specific displayset.

function getDisplaySetsFromSeries(instances) {
// If the series has no instances, stop here
if (!instances || !instances.length) {
throw new Error(‘No instances were provided’);
}

const displaySets = ;
const sopClassUids = getSopClassUids(instances);

instances.forEach(instance => {
if (!isImage(instance.SOPClassUID) && !instance.Rows) {
return;
}

let displaySet;

displaySet = makeDisplaySet([instance]);

displaySet.setAttributes({
  sopClassUids,
  instanceNumber: instance.InstanceNumber,
  acquisitionDatetime: instance.AcquisitionDateTime,
});
displaySets.push(displaySet);
displaySets.sort((a, b) => (a.InstanceNumber > b.InstanceNumber ? 1 : -1));

});

return displaySets;
}

thanks for your response! @AugmentedReporting .
looks like the code you posted here

let displaySet;

displaySet = makeDisplaySet([instance]);

displaySet.setAttributes({
  sopClassUids,
  instanceNumber: instance.InstanceNumber,
  acquisitionDatetime: instance.AcquisitionDateTime,
});
displaySets.push(displaySet);
displaySets.sort((a, b) => (a.InstanceNumber > b.InstanceNumber ? 1 : -1));

is the same as the latest version ,
as far as i saw , the issue is that the sort function accepts only integers which is not the case when sorting by acquisition datetime.

thanks we proposed the change in January but haven’t seen it pushed to master branch yet

to sort AcquisitionDateTime, suggest adding a custom function to convert from DICOM date time string to integer

Hey @AugmentedReporting ,
I tried many strategies to implement acquisition datetime sorting ,
one of the strategies were to take the instance.acquisitionDatetime - convert into a JS date ,
and sort the displaySets array by the new JS date . unfortunately it doesn’t worked properly.
In addition , I disabled the default behavior which stack all Images to one study to be ably to sort also the images along with clips by chronological order …

 let displaySet;

    let timestampStr = instance.AcquisitionDateTime ? instance.AcquisitionDateTime : ''
    let year = parseInt(timestampStr.substr(0, 4), 10);
    let month = parseInt(timestampStr.substr(4, 2), 10) - 1; // JavaScript months are 0-indexed
    let day = parseInt(timestampStr.substr(6, 2), 10);
    let hour = parseInt(timestampStr.substr(8, 2), 10);
    let minute = parseInt(timestampStr.substr(10, 2), 10);
    let second = parseInt(timestampStr.substr(12, 2), 10);
    let millisecond = parseInt(timestampStr.substr(15, 3), 10); // get the fractional seconds

    let date = new Date(year, month, day, hour, minute, second, millisecond);

    // if (isMultiFrame(instance)) {
    displaySet = makeDisplaySet([instance]);

    displaySet.setAttributes({
      sopClassUids,
      isClip: isMultiFrame(instance) === true ? true : false,
      numImageFrames: instance.NumberOfFrames,
      instanceNumber: instance.InstanceNumber,
      acquisitionDatetime: instance.AcquisitionDateTime,
      date: date
    });
    displaySets.push(displaySet);

displaySets = displaySets.sort((a, b) => a.date - b.date);
  return displaySets;

any further idea’s which I missed out ?

@AugmentedReporting @haldawe2 UPDATE :

function getDisplaySetsFromSeries(instances) {
  // If the series has no instances, stop here
  if (!instances || !instances.length) {
    throw new Error('No instances were provided');
  }

  var displaySets = [];
  const sopClassUids = getSopClassUids(instances);

  // Search through the instances (InstanceMetadata object) of this series
  // Split Multi-frame instances and Single-image modalities
  // into their own specific display sets. Place the rest of each
  // series into another display set.
  const stackableInstances = [];
  instances.forEach(instance => {
    // All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
    if (!isImage(instance.SOPClassUID) && !instance.Rows) {
      return;
    }

    let displaySet;

    //if (isMultiFrame(instance)) {
    displaySet = makeDisplaySet([instance]);

    displaySet.setAttributes({
      sopClassUids,
      isClip: isMultiFrame(instance) === true ? true : false,
      instanceNumber: instance.InstanceNumber,
      acquisitionDatetime: instance.AcquisitionDateTime,
    });
    // Case when Clip set numImagesFrames to be number of frames
    if (instance.NumberOfFrames !== undefined) {
      displaySet.setAttribute('numImageFrames', instance.NumberOfFrames)
    }
    displaySets.push(displaySet);
    //} else if (isSingleImageModality(instance.Modality)) {
    // displaySet = makeDisplaySet([instance]);
    //displaySet.setAttributes({
    // sopClassUids,
    // instanceNumber: instance.InstanceNumber,
    // acquisitionDatetime: instance.AcquisitionDateTime
    //});
    // displaySets.push(displaySet);
    // } else {
    //   stackableInstances.push(instance);
    // }
  });
  // igorned - never arrive this statement
  if (stackableInstances.length) {
    const displaySet = makeDisplaySet(stackableInstances);
    displaySet.setAttribute('studyInstanceUid', instances[0].StudyInstanceUID);
    displaySet.setAttributes({
      sopClassUids,
    });
    displaySets.push(displaySet);
  }

  function sortByAcquisitionDatetime(a, b) {
    if (a.acquisitionDatetime && b.acquisitionDatetime) {
      return a.acquisitionDatetime.localeCompare(b.acquisitionDatetime);
    }
  }
  // Sort the array
  let newDisplaySets = displaySets.sort(sortByAcquisitionDatetime)
  return newDisplaySets //displaySets;
}

I Edited getDisplaySetsFromSeries function which located in getSopClassHandlerModule.js

  • Each single frame now individually displayed as one “displaySet” and not as a stack of single frame images.
  • I created a function which orders each DisplaySet to be sorted by AcquisitionDateTime , and returns a new array

When i log the newDisplaySets , it seems that it indeed sorted the AcquisitonDateTIme correctly but when i Return it from the main function - it doesn’t really sort it properly

Any advise/ something I missed?

Follow this [Feature] Implement custom sorting of series in study panel · Issue #4083 · OHIF/Viewers · GitHub