// Executes mouse hotel pre-processing protocol.
// Loads each hotel into VQ and uses bounding cylinder algo to split into individual animals.
// Robert Callan <callan@invicro.com>
// updated 04/18/2013
#include "commonfuncs.vqs"
// global variables
var dm = VQ.dataManager();
var ctl = VQ.controler();
var mw = VQ.mainWin();
var sv = VQ.sliceViewer();
// input variables - check all variables before running
var pwd = ""; // password (leave empty to prompt user)
var dcmRepURL = "ipacs://test:"+pwd+"@invicro.ipacs.invicro.com"; // currently set to use invicro iPACS with user: test
var patientFilter = "*"; // filter for data to be loaded
var projectName = "/test/tumortool/customerdata_a"; // input DICOM image data project
var savePreprocessed = 0; // set as 1 to save preprocessed four-animal data to saveLoc
var saveLoc = "/test"; // location to save preprocessed four-animal data
var resample = 1; // set as 1 to resample reference image
var voxSize = 0.4; // resampling voxel size
var convert = 0; // set as 1 to convert units
var convertUnit = "uCi"; // units to convert to
var register = 1; // set as 1 to auto register images
var transform = "Translate"; // registration type (eg. Translate, Versor, Affine)
var quality = "Standard"; // registration quality
var namePerm = [1,2,4,3]; // specify renumbering of ROIs (see relabelROIs function below)
var colors = ["green", "blue", "cyan", "magenta"]; // corresponding ROI colors
var flips = [[0,0,0],[1,1,0],[0,0,0],[1,1,0]]; // each 3-element sub-array corresponds to an animal and each boolean represents a flip (ie. H/F, L/R, A/P)
var bcRadius = 18; // bounding cylinders radius
var batchSave = 0; // set as 1 to save individual split animals to saveLoc
var cropid = 1; // data id as loaded in data manager for auto-cropping
var dcmRep = VQ.dcmRep(dcmRepURL);
function getQuadrant(imgcom,roicom) {
mw.setViewMode("Slice View", "3D ROI Tool");
var c1 = imgcom.substr(1,imgcom.length-2).split(" ");
var c2 = roicom.substr(1,roicom.length-2).split(" ");
c1[0] = Number(c1[0]);
c1[1] = Number(c1[1]);
c2[0] = Number(c2[0]);
c2[1] = Number(c2[1]);
if (c2[0]<c1[0] && c2[1]<c1[1])
return 1;
else if (c2[0]>c1[0] && c2[1]<c1[1])
return 2;
else if (c2[0]<c1[0] && c2[1]>c1[1])
return 3;
else
return 4;
}
function loadAndPrep(dm,i, si) {
var series = VQ.querySeries(dcmRep, si.StudyInstanceUID); // fetch series by study id
var ref = VQ.downloadImages(dcmRep, si.StudyInstanceUID, series[1].SeriesInstanceUID);
var inp = VQ.downloadImages(dcmRep, si.StudyInstanceUID, series[0].SeriesInstanceUID);
dm.openDat(0,ref);
//mw.openZIPACS(ref);
mw.setViewMode("Slice View", "Reorientation/Registration");
VQ.sliceViewer().updateDisplay();
VQ.currentOp().resampleData(voxSize);
dm.openDat(1,inp);
//mw.openZIPACS(inp);
if (convert) {
var protocol = 'calibFactor=false|storeData=false|resample=false|convertUnit=true|convertUnitUnit='+convertUnit;
preProcessing(protocol);
}
}
function boundingCylinders() {
mw.setViewMode("Slice View", "3D ROI Tool");
var op = VQ.currentOp();
op.setRenderingQuality(2);
//VQ.getWidget("cbAutoUpdateRendering").setChecked(0); // use in stable versions to disable auto-rendering
VQ.getWidget("MagicRadius").setValue(bcRadius);
var N = op.numROIs();
if (N > 1) for (var i=1; i<N; i++) { op.deleteROIs([1],0); }
var com = op.getROICenterOfMass(0);
op.applyBoundingCylinders();
return com;
}
function autoCrop() {
mw.setViewMode("Slice View", "Cropping");
var op = VQ.currentOp();
VQ.getWidget("cbAutoCrop").setCurrentIndex(cropid);
VQ.getWidget("buttonAutoCrop").click();
// Add padding to auto-crop selection
var tempX1 = VQ.getWidget("sbFromX").value;
var tempY1 = VQ.getWidget("sbFromY").value;
var tempZ1 = VQ.getWidget("sbFromZ").value;
var tempX2 = VQ.getWidget("sbToX").value;
var tempY2 = VQ.getWidget("sbToY").value;
var tempZ2 = VQ.getWidget("sbToZ").value;
VQ.getWidget("sbFromX").setValue(tempX1-20);
VQ.getWidget("sbFromY").setValue(tempY1-20);
VQ.getWidget("sbFromZ").setValue(tempZ1-10);
VQ.getWidget("sbToX").setValue(tempX2+20);
VQ.getWidget("sbToY").setValue(tempY2+20);
VQ.getWidget("sbToZ").setValue(tempZ2+10);
op.executeCropping();
}
function flipData(fs) {
mw.setViewMode("Slice View", "Reorientation/Registration");
var op = VQ.currentOp();
VQ.getWidget("reorientTabs").setCurrentIndex(0);
VQ.getWidget("transX").setValue(0.0);
VQ.getWidget("transY").setValue(0.0);
VQ.getWidget("transZ").setValue(0.0);
// Only apply transformations to reference and input 1
op.setTransReference(1);
op.setTransInput1(1);
op.setTransInput2(0);
op.setTransInputX(0);
if (fs[0]) op.flipHeadFeet();
if (fs[1]) op.flipLeftRight();
if (fs[2]) op.flipAntPost();
op.updateGUIFromOptions();
op.dialogClosed(1);
}
function alignCylinders(idx, com_ref, com_img, flps) {
mw.setViewMode("Slice View", "3D ROI Tool");
com = VQ.currentOp().getROICenterOfMass(idx);
var trans = new Array(3);
// Center of mass coordinates of the ref animal, the target animal, and the entire image, respectively
var c1 = com_ref.substr(1,com_ref.length-2).split(" ");
var c2 = com.substr(1,com.length-2).split(" ");
var c3 = com_img.substr(1,com_img.length-2).split(" ");
// corrects CoM coordinates for flipping
if (flps[1]) c2[0] = c2[0] - 2*(c2[0] - c3[0])
if (flps[2]) c2[1] = c2[1] - 2*(c2[1] - c3[1])
if (flps[0]) c2[2] = c2[2] - 2*(c2[2] - c3[2])
for (var i = 0; i<3; ++i) {
trans[i] = (c1[i] - c2[i])*voxSize;
}
VQ.debug("Translations to be made: " + trans);
mw.setViewMode("Slice View", "Reorientation/Registration");
VQ.getWidget("transX").setValue(trans[0]);
VQ.getWidget("transY").setValue(trans[1]);
VQ.getWidget("transZ").setValue(trans[2]);
VQ.currentOp().dialogClosed(1);
}
function applyTransformations(nROIs, ref, img, flipid) {
mw.setViewMode("Slice View", "Reorientation/Registration");
var op = VQ.currentOp();
// Only apply transformations to reference and input 1
op.setTransReference(1);
op.setTransInput1(1);
op.setTransInput2(0);
// Reorientation parameters for all animals
for (var i=0; i<nROIs-1; ++i) {
dm.swapData(0,2*i);
dm.swapData(1,2*i+1);
flipData(flips[flipid[i]-1]);
alignCylinders(i+1, ref, img, flips[flipid[i]-1]);
}
}
function saveAllData() {
var dcmRep = VQ.dcmRep(dcmRepURL);
dcmRep.setProject(saveLoc);
for (var i=0; i<dm.size(); ++i) {
VQ.storeData(dcmRep, i);
}
}
function main() {
VQ.debug("* ping="+dcmRep.ping()); // Test access to DICOM repository
VQ.debug("* projects="+dcmRep.getProjectList());
dcmRep.setProject(projectName);
var studies = VQ.queryStudies(dcmRep, patientFilter); // filter studies
VQ.debug("* number of patients found: " + studies.length);
mw.setAndApplyAutoZoom(1);
if (ctl.getTo(1) < 1) ctl.setTo(1, 1); // reset upper bound of color palette for input 1
ctl.setPal(0, "gray");
ctl.setPal(1, "nih_fire2");
for (i=1; i<studies.length; ++i) {
dm.unloadData(0,dm.size());
loadAndPrep(dm,i,studies[i]);
if (savePreprocessed) { // save preprocessed images as DICOM
dcmRep.setProject(saveLoc);
VQ.storeData(dcmRep, 0);
VQ.storeData(dcmRep, 1);
}
var inps = new Array( [0,1,0] ); // apply registration to [ref, inp1, inp2]
if (register) doRegistration(transform, quality, inps);
var imgCoM = boundingCylinders(); // TODO: get imgcom another way
//relabelROIs(namePerm); // no longer needed
VQ.sliceViewer().forceUpdateDisplay();
//VQ.suspend(); uncomment if manual edits of cylinders are necessary
mw.setViewMode("Slice View", "3D ROI Tool");
op = VQ.currentOp();
var nCylinders = op.numROIs();
//var imgCoM = op.getROICenterOfMass(0);
var refCoM = op.getROICenterOfMass(1);
var cylQuad;
var roiid = new Array();
var todel = new Array();
for (var j=1; j<nCylinders; ++j) {
if (VQ.applicationName().indexOf('alpha') != -1) {
var stats = VQ.getROIStats(1,j);
if (stats.value<200) { // Removes cylinder if that region of NM contains less than 200 uCi
todel.push(j);
continue;
}
}
var roiCoM = op.getROICenterOfMass(j);
cylQuad = getQuadrant(imgCoM, roiCoM);
roiid.push(namePerm[cylQuad-1]);
op.editROI(j, "ROI #"+roiid[roiid.length-1], colors[roiid[roiid.length-1]-1], false, false);
}
op.deleteROIs(todel,0);
nCylinders = op.numROIs();
mw.setupLayoutSlice(); // switch to slices only view
mw.setViewMode("Slice View", "Navigation");
VQ.splitROI2DICOM(-1); // input value < 0 => all data sets will be split
//VQ.triggerAction("Tools|Advanced Analysis|Split ROI to DICOM"); // use for pre-1.23 versions
dm.unloadData(0,2); // unload original images
mw.setViewMode("Slice View", "3D ROI Tool");
op = VQ.currentOp();
applyTransformations(nCylinders, refCoM, imgCoM, roiid);
autoCrop(); // auto-crop using value specified above (cropid)
mw.setViewMode("Slice View", "3D ROI Tool");
op = VQ.currentOp();
if (nCylinders > 0) for (var k=1; k<nCylinders; k++) { op.deleteROIs([1],0); } // removes all ROIs
if (batchSave == 1) {
saveAllData();
VQ.debug("ROIs saved for current patient.");
}
VQ.debug(i+1 + " of " + studies.length + " studies complete.");
mw.setViewMode("Slice View", "Navigation");
//dm.unloadData(0,8);
}
}
main();
VQ.showMessage("Script complete.");
VQ.abort();