iPACS VivoScript Integration

New object-oriented iPACS VivoScript Integration

With the release of VivoQuant 1.23, new functionality has been added to facilitate iPACS communication within VivoScript. Users are now able to easily filter though entire iPACS projects easily and pick out the desired data sets in clean, simple scripts. The functionality has been written in VivoScript and included in every new install of VivoQuant 1.23 as ipacs.vqs, so implementing it within your VivoScrips is very easy.

To use ipacs.vqs, it must first be included in a VivoScript by adding the following line to the first line of your script:

#include "ipacs.vqs"

This will allow the script to access the iPACS object and all of its methods.

iPACS VivoScript Basics

Communication between VivoScript and the iPACS is implemented in an object-oriented fashion. Queries to the iPACS are returned as objects or lists of objects, particularly in dealing with studies and series.  The study and series objects returned by the query methods are in turn used by subsequent methods to access the data on the iPACS.

To interact with the iPACS, an iPACS object must be created. To do so, the following constructor should be used:

var repo = new iPACS(ipacs_url,project_location);

The iPACS, study and series objects have several properties associated with them. To access object properties, use the following convention:

var project = repo.project;

The following are accessible properties of the iPACS object:

  • dcmRep: DICOM repository object
  • repository_url: Address of repository (eg ipacss://invicro.ipacs.invicro.com)
  • project: Project path

The study object has the following properties associated with it:

  • PatientComments
  • PatientID
  • PatientsName
  • StudyDate
  • StudyDescription
  • StudyInstanceUID
  • StudyTime

The series object has the following properties associated with it:

  • ManufacturersModelName
  • Modality
  • SeriesDate
  • SeriesDescription
  • SeriesInstanceUID
  • SeriesNumber
  • SeriesTime
  • StudyInstanceUID

Series and studies properties can be used in the following methods to filter results or to grab data from the iPACS data

Overview of Available Methods

The following methods can be used by the iPACS object to query, retrieve, and store data to the iPACS

getStudies()

Returns an array of all study objects within a project. Filters can be used to limit the output so as to only return a certain subset of a project, such as time point or group.

Optional Arguments:

  • -namefilter: A filter for PatientsName
  • -idfilter: A filter for PatientID
  • -sort: Option to sort the output data. Can sort on PatientsName, PatientID, or Study Date
  • -datestart: A filter that only gets projects more recent that the date requested. Must be in the format YYYYMMDD.
  •  -dateend: A filter that only gets studies older than date submitted. Must be in the format YYYYMMDD.

All arguments are optional and can be added in any order.

Example Usage:

//Assume iPACS object repo has already been created.
var studies = repo.getStudies(); // Gets all studies in repo
var studies = repo.getStudies('-namefilter','*S1*') // Gets all studies with the pattern "S1" in repo
var studies = repo.getStudies('-datestart','20140101') // Only gets studies with a 2014 study date
var studies = repo.getStudies('-datestart','20140101','-dateend','20140131') // Only gets studies with a January 2014 study date
var studies = repo.getStudies('-idfilter','A1007','sort','StudyDate','-datestart','20130901'); // Only gets studies with PatientID A1007, from September 1,2013 on and sorted by date.

getSeries(study)

Returns an array of all series object within study ‘study.’ Filters can be used to limit the output based on modality or series description.

Required Arguments

  • study: Study Object to get the series objects from

Optional Arguments

  • -modality: DICOM modality code. Usually used are CT, PT, and NM. A full list can be found here
  • -seriesdescription: A filter that only returns series that match the series description

The optional arguments can be added in any order, but the required argument must always come first

Example Usage:

//Assume iPACS object repo and array of study objects studies have been created
var ct = repo.getSeries(series[0],'-modaltiy','CT');
var nm = repo.getSeries(series[0],'-modality','NM','-seriesdescription','*focused*');

getImages(study,series,input)

Downloads and opens the image located at study ‘study’ and series ‘series.’ It will load the image into input ‘input’ in the Data Manager. If series has more than one image associated with it, all images will be loaded.

Required Arguments

  • study: Study Object image is located in
  • series: Series Object image is located in
  • input: Input in data manager to load image into

Example Usage:

// Assumes iPACS object repo, study object studies and series objects ct and nm
repo.getImages(studies[0],ct[0],0);
repo.getImages(studies[0],nm[0],1);

submitData(input)

Stores data located at input ‘input’ to the iPACS

Required Arguments:

  • input: Input of data to store to the iPACS repository

Example Usage:

// Assumes iPACS repository repo
repo.submitData(0);
repo.submitData(1);

 getROIs()

Retrieves and loads most recent ROI meeting criteria specified in optional arguments

Optional Arguments:

  • -creator: name of person who submitted ROI (must match quantification data point). Exact match only
  • -names: List of ROIs to get. Will return most recent ROI that contains at least all of the ROIs found in names array.
  • -savepath: Optional save location of temporary rmha file. Necessary if computer permissions prohibit saving to VQ root directory.

Example Usage

// Assumes iPACS object repo and images are loaded that have ROI data points
repo.getROIs('-creator','anovicki') // Last ROI submitted by anovicki
repo.getROIs('-names',['Muscle','Brain']) // Last ROI submitted containing Muscle and Brain ROIs (and any other ROIs included in the .rmha files)
repo.getROIs('-names',['Liver'],'-creator','jtierney','-savepath','C:/tmp.rmha')

 mergeROIs()

Similar to getROIs() but supports merging of ROIs from separate rmha files. Also supports removal of unwanted ROIs once loaded.

Required Arguments:

  • -names: List of ROIs to get. Will get the most recent ROI file for each of the ROIs in roilist and merge them into VivoQuant

Optional Arguments:

  • -creator: name of person who submitted ROI (must match quantification data point). Exact match only
  • -savepath: Optional save location of temporary rmha file. Necessary if computer permissions prohibit saving to VQ root directory.
  • -exactlist: Boolean on whether or not to delete ROIs not located on the -names list. If true, end result will ONLY be the ROIs in -names (Default: TRUE)

Example Usage

// Assumes iPACS object repo, and image with quantification data points is loaded
repo.mergeROIs('-names',['Heart','Muscle'],'-exactlist',true) // Merges most recent version of Heart and Muscle. Removes other ROIs
repo.mergeROIs('-names',['Heart','Muscle'],'-exactlist',false) // Merges most recent version of Heart and Muscle. Leaves any other ROIs located in rmha folder

 submitROIs()

Submits all ROIs as a single rmha file to the iPACS. Submits data points for all inputs loaded. (Note: Inputs must be turned on in the Visual controller in order for them to be submitted. Inputs higher than Input 2 will be submitted if Input 2 is turned on).

Example Usage

// Assumes ROIs are loaded in 3D ROI Tool and iPACS object repo exists
repo.submitROIs()

 getInjectedDose()

Returns injected dose found in the iPACS subject information data point. Will return 0 if no information is entered on the data point

Required Arguments

  • input: Input number to check injected dose

Example Usage:

// Assumes iPACS object repo and images with subject information data points loaded
var inj = repo.getInjectedDose(1); // Gets injected dose of input 1


getQuantification()

Returns list of all quantification data points associated with a given input. Useful for grabbing specific data about ROIs without loading them. Each item in the list is a quantification data point object that has the same properties as the actual data points visible on the iPACS. For example, to get the mean value of the data in the ROI, type quant[0].mean , where quant[0] is the first item in the list returned by the method.

Required Arguments

  • input: Index of input to get quantification data points

Example Usage:

// Assumes iPACS Object repo and images loaded into VQ
var quant = repo.getQuantification(1);
// Lists names of all quantification data points (ROI names) for Input 1
for (i=0; i<quant.length;i++){
    VQ.debug(quant[i].name);
}

 

 

Example VivoScript

This example VivoScript iterates through each study within a project and performs the following

  1. Downloads the image
  2. Converts to %ID/g
  3. Sets min and max range
  4. Downloads the ROIs associated with the image
  5. Builds file name using Patient Name and Patient ID from Study Object
  6. Makes each ROI invisible
  7. Iterates through each ROI and takes an image of subject centered at the ROI center of mass with the ROI visible

The script assumes that injected doses (in uCi) are entered for each subject on the Subject Information data point on the iPACS (for conversion to %ID/g)

#include "ipacs.vqs"

// Location to save images
var obase = 'C:/path/to/local/folder/'

// Minimum and Maximum %ID/g values to scale to
var pidgmin = 0;
var pidgmax = 60;

// VivoQuant Variables
var dm = VQ.dataManager();
var mw = VQ.mainWin();
var mm = VQ.minMaxTool();
var ctl = VQ.controler();

// Instantiate a new iPACS object
var repo = new iPACS('ipacss://ipacsname.ipacs.invicro.com','/path/to/remote/folder/');

var studies = repo.getStudies();

for (var i = 0; i < studies.length; i++){

        // Get CT and NM series
        var ct = repo.getSeries(studies[i],'-modality','CT')
        var nm = repo.getSeries(studies[i],'-modality','NM','-seriesdescription','*In-111 *'); // Only gets nm series that have In-111 in the description
        // Test to make sure ct and nm are both one object long (for this project they should be)
        if (ct.length != 1 || nm.length != 1)
            VQ.suspend('ERROR');

        // Load images
        repo.getImages(studies[i],ct[0],0);
        repo.getImages(studies[i],nm[0],1);

        // Get injected dose and voxel size
        var inj = repo.getInjectedDose(1);
        var vox_sizex = dm.voxelSizeX(0);
        var vox_sizey = dm.voxelSizeY(0);
        var vox_sizez = dm.voxelSizeZ(0);
        var vox_size = vox_sizex * vox_sizey * vox_sizez;
        // Conversion
        mw.setViewMode("Slice View",'Arithmetics');
        // Convert to %ID
        VQ.currentOp().multScalar(1,100/inj);
        // Convert to %ID/g (assume 1 g = 1 cm^3)
        VQ.currentOp().multScalar(1,1000/vox_size);

        // Set %ID/g Range
        dm.setMinMaxCache(1,pidgmin,pidgmax);
        mm.resetValues();
        mm.applyValues();
        mm.updateDisplay();

        // Download ROIs. The method will ONLY get the ROIs listed in the '-names' list
        repo.mergeROIs('-names',['Heart','Lungs','Liver','Spleen','LeftKidney','RightKidney','Bladder','Brain','Tumor','Bone'],'-exactlist',true);

        // Make file name
        pname = studies[i].PatientsName;
        pid = studies[i].PatientID;
        pname = pname.replace('+','_');
        pid = pid.replace(' ','_');
        fname = pname + '-' + pid;

        mw.setViewMode('Slice View','3D ROI Tool');
        var roi = VQ.currentOp();

        // Make all ROIs invisible
        for (var j = 1; j < roi.numROIs(); j++){
            var det = roi.getROIDetails(j);
            roi.editROI(j,det[0],det[1],det[2],true,det[4],det[5]);
        }

        // Loop through each ROI and take a picture centered on it with ROI on/off
        for (var j = 1; j < roi.numROIs(); j++){
            var det = roi.getROIDetails(j);
            roi.editROI(j,det[0],det[1],det[2],false,det[4],det[5]);
            fname_out = fname + '-' + det[0];
            // Set COM
            var centers = roi.getROICenterOfMass(j);
            centers = centers.substr(1,centers.length-2).split(" ");
            ctl.setPos(0,centers[0]);
            ctl.setPos(1,centers[1]);
            ctl.setPos(2,centers[2]);
            ctl.setRange(0,0,0.6);
            // Take picture
            mw.saveImage(obase + fname_out + '-roi',5);
            // Make ROI invisible
            roi.editROI(j,det[0],det[1],det[2],true,det[4],det[5]);
            // Take picture
            mw.saveImage(obase + fname_out + '-noroi',5);
        }
        // Remove all ROIs
        while(roi.numROIs() > 1)
            roi.deleteROI(1,0);
    }