<?php
// This file is part of the Huygens Remote Manager
// Copyright and license notice: see license.txt
require_once("Parameter.inc");
require_once("Database.inc");
require_once("User.inc");
require_once("Shell.inc");
require_once("hrm_config.inc");
require_once("Job.inc");
require_once("JobQueue.inc");
//!---------------------------------------------------------
// @class JobDescription
// @desc Description of the job to be processed by
// Huygens. Consisting of a parameter setting,
// a task setting and a list of image files.
// From the JobDescription a huygens parameter
// set is created.
//!---------------------------------------------------------
Class JobDescription {
public $id; // @public id String a unique id identifying the job
public $parameterSetting; // @public parameterSetting ParameterSetting the jobs parameter setting
public $taskSetting; // @public taskSetting TaskSetting the jobs task setting
public $files; // @public files Array the list of files to be processed by the job
public $owner; // @public owner User the user who created the job
public $message; // @public message String error message.
public $pass; // @public pass Integer pass 1 or 2 of step combined processing
public $credit; // @public credit String name of the credit to be used
public $group; // @public group String name of the group to be used
//public $rangeParameters; // why not use a global variable from the beginning?!
function JobDescription() {
$this->id = (string)(uniqid(''));
$this->message = "";
$this->pass = 1;
}
function message() {
return $this->message;
}
function id() {
return $this->id;
}
function setId($anId) {
$this->id = $anId;
}
function owner() {
return $this->owner;
}
function setOwner($owner) {
$this->owner = $owner;
}
function parameterSetting() {
return $this->parameterSetting;
}
function taskSetting() {
return $this->taskSetting;
}
function files() {
return $this->files;
}
function setParameterSetting($setting) {
$this->parameterSetting = $setting;
$this->owner = $setting->owner();
}
function setTaskSetting($setting) {
$this->taskSetting = $setting;
}
function setFiles($files) {
$this->files = $files;
}
function credit() {
return $this->credit;
}
function setCredit($credit) {
$this->credit = $credit;
}
function group() {
return $this->group;
}
function setGroup($group) {
$this->group = $group;
}
function addJob() {
// =========================================================================
//
// In previous versions of the HRM, the web interface would create compound
// jobs that the queue manager would then process. Now, this task has become
// responsibility of the web interface.
//
// =========================================================================
$result = True;
$lqueue = new JobQueue();
$lqueue->lock();
// createJob() function was originally called directly
$result = $result && $this->createJob();
if ( $result ) {
// Process compound jobs
$this->processCompoundJobs( );
// Assign priorities
$db = new DatabaseConnection();
$result = $db->setJobPriorities();
if ( !$result ) {
error_log( "Could not set job priorities!" );
}
}
$lqueue->unlock();
return $result;
}
function createJob() {
global $use_accounting_system;
$result = True;
$jobParameterSetting = new JobParameterSetting();
$jobParameterSetting->setOwner($this->owner);
$jobParameterSetting->setName($this->id);
$jobParameterSetting->copyParameterFrom($this->parameterSetting);
$result = $result && $jobParameterSetting->save();
$taskParameterSetting = new JobTaskSetting();
$taskParameterSetting->setOwner($this->owner);
$taskParameterSetting->setName($this->id);
$taskParameterSetting->copyParameterFrom($this->taskSetting);
$result = $result && $taskParameterSetting->save();
$db = new DatabaseConnection();
$result = $result && $db->saveJobFiles($this->id, $this->owner, $this->files);
if ($use_accounting_system) {
$result = $result && $db->saveAccountingData($this->id, $this->credit, $this->group);
}
$queue = new JobQueue();
$result = $result && $queue->queueJob($this);
if (!$result) {
$this->message = "create job - database error!";
}
return $result;
}
function processCompoundJobs() {
$queue = new JobQueue();
$compoundJobs = $queue->getCompoundJobs();
foreach ($compoundJobs as $jobDescription) {
$job = new Job($jobDescription);
$job->createSubJobsOrScript();
}
}
function load() {
global $use_accounting_system;
$db = new DatabaseConnection();
$parameterSetting = new JobParameterSetting;
$owner = new User;
$name = $db->userWhoCreatedJob($this->id);
$owner->setName($name);
$parameterSetting->setOwner($owner);
$parameterSetting->setName($this->id);
$parameterSetting = $parameterSetting->load();
$this->setParameterSetting($parameterSetting);
$taskSetting = new JobTaskSetting;
$taskSetting->setNumberOfChannels($parameterSetting->numberOfChannels());
$taskSetting->setName($this->id);
$taskSetting->setOwner($owner);
$taskSetting = $taskSetting->load();
$this->setTaskSetting($taskSetting);
$this->setFiles($db->getJobFilesFor($this->id()));
if ($use_accounting_system) {
$data = $db->loadAccountingData($this);
$this->setCredit($data['credit']);
$this->setGroup($data['group']);
}
}
function copyFrom($aJobDescription) {
$this->setParameterSetting($aJobDescription->parameterSetting());
$this->setTaskSetting($aJobDescription->taskSetting());
$this->setOwner($aJobDescription->owner());
$this->setCredit($aJobDescription->credit());
$this->setGroup($aJobDescription->group());
}
function isCompound() {
if (count($this->files)>1) {
return True;
}
if (count($this->rangeParameter())>0) {
return True;
}
return False;
}
function rangeParameter() {
//global $rangeParameters;
$result = array();
$setting = $this->taskSetting;
$rangeParameter = $setting->rangeParameter();
foreach ($rangeParameter as $parameter) {
$useParameterName = $parameter->useParameterName();
$useParameter = $setting->parameter($useParameterName);
if ($useParameter->isTrue()) {
$result[$parameter->name()] = $parameter;
//$this->rangeParameters[] = $parameter;
}
}
return $result;
}
function createSubJobsforFiles() {
$result = True;
foreach ($this->files as $file) {
// error_log("file=".$file);
$newJobDescription = new JobDescription();
$newJobDescription->copyFrom($this);
$newJobDescription->setFiles(array($file));
$result = $result && $newJobDescription->createJob();
}
return $result;
}
function createSubJobs() {
//global $rangeParameters;
$parameterSetting = $this->parameterSetting;
$numberOfChannels = $parameterSetting->numberOfChannels();
$rangeParameters = $this->rangeParameter();
$rangeParameterCombinations = $this->rangeParameterCombinations($rangeParameters);
$result = True;
if (count($rangeParameterCombinations)==0) {
return $this->createSubJobsforFiles();
}
foreach ($this->files as $file) {
// error_log("range combinations = " . count($rangeParameterCombinations));
foreach ($rangeParameterCombinations as $combination) {
// error_log("combination [" . implode(" ", $combination) . "]");
$newJobDescription = new JobDescription();
$newJobDescription->copyFrom($this);
$newJobDescription->setFiles(array($file));
$index = 0;
$setting = $newJobDescription->taskSetting();
//foreach ($this->rangeParameterNames() as $rangeParameterName) {
foreach ($rangeParameters as $rangeParameter) {
$rangeParameterName = $rangeParameter->name();
//$rangeParameter = $setting->parameter($rangeParameterName);
$realParameterName = $rangeParameter->realParameterName();
$realParameter = $setting->parameter($realParameterName);
$useParameterName = $rangeParameter->useParameterName();
$useParameter = $setting->parameter($useParameterName);
$useParameter->reset();
//error_log("range parameter " . $realParameterName . " = " . $combination[$index]);
if ($realParameterName == "SignalNoiseRatio") {
$newVal = array(null, null, null, null, null);
for ($i = 0; $i < $numberOfChannels; $i++) {
$newVal[$i] = $combination[$i];
}
}
else if ($realParameterName == "NumberOfIterations") {
if (count($combination) > 1)
$newVal = $combination[$numberOfChannels];
else
$newVal = $combination[0];
}
$realParameter->setValue($newVal);
$setting->set($realParameter);
$setting->set($useParameter);
//$index++;
}
$newJobDescription->setTaskSetting($setting);
$result = $result && $newJobDescription->createJob();
}
}
return $result;
}
function combine($firstArray, $secondArray) {
$result = array();
$i=0;
if (count($firstArray)==0) {
return $secondArray;
}
foreach ($firstArray as $firstElem) {
foreach ($secondArray as $secondElem) {
$newFirst = $firstElem;
if(is_array($newFirst)) {
$newFirst[] = $secondElem;
$row = $newFirst;
} else {
$row = array($firstElem, $secondElem);
}
$result[] = $row;
}
$i++;
}
return $result;
}
function combineAll($anArray) {
$result = array();
foreach ($anArray as $row) {
$result = $this->combine($result, $row);
}
return $result;
}
function rangeParameterCombinations($rangeParameter) {
$parameterSetting = $this->parameterSetting;
$numberOfChannels = $parameterSetting->numberOfChannels();
$jobDescriptions = array();
//$rangeParameter = $this->rangeParameter();
$matrix = array();
//if (count($rangeParameter)==1) {
$range = array_pop($rangeParameter);
if ($range == null) {
return;
}
$value = array_filter($range->value());
$snrValue = null;
$iterationValue = null;
// process SignalNoiseRatioRange, if applicable
if ($range->name() == "SignalNoiseRatioRange") {
// // error_log("SignalNoiseRatio range");
$snrRange = $range;
$snrValue = $value;
}
else if ($range->name() == "NumberOfIterationsRange") {
// error_log("NumberOfIterations range");
$iterationRange = $range;
$iterationValue = $value;
}
// process NumberOfIterationsRange, if applicable
if (count($rangeParameter) > 0) {
$range = array_pop($rangeParameter);
$value = array_filter($range->value());
if ($range->name() == "SignalNoiseRatioRange") {
// error_log("SignalNoiseRatio range");
$snrRange = $range;
$snrValue = $value;
}
else if ($range->name() == "NumberOfIterationsRange") {
// error_log("NumberOfIterations range");
$iterationRange = $range;
$iterationValue = $value;
}
}
// COMBINATORIAL combinations
// what is the form of matrix?
// compute matrix dimensions
/*$dim = 1;
if ($snrValue != null) {
for ($i = 0; $i < $numberOfChannels; $i++) {
$dim *= count($snrValue[$i]);
}
}
if ($iterationValue != null) {
$dim *= count($iterationValue);
}
error_log("dimension = " . $dim);
for ($i = 0; $i < $dim; $i++) {
$row = array();
$size = 1;
if ($snrValue != null) {
for ($j = 0; $j < $numberOfChannels; $j++) {
$val = $snrValue[$j];
if (is_array($val)) {
$val = array_filter($val);
$row[] = $val[($i/$size) % count($val)];
$size *= count($val);
}
else {
$row[] = $val;
}
}
}
if ($iterationValue != null) {
$val = $iterationValue;
$val = array_filter($val);
$row[] = $val[($i/$size) % count($val)];
error_log("index = " . ($i/$size) % count($val));
error_log("value = " . $val[($i/$size) % count($val)]);
$size *= count($val);
}
$matrix[$i] = $row;
$s = "";
for ($k = 0; $k < count($row); $k++) {
$s .= $row[$k] . " ";
}
error_log($s);
}*/
// SIMPLE combinations
if ($snrValue != null) {
$val = array_filter($snrValue);
$range = count($val[0]);
for ($i = 0; $i < $range; $i++) {
$matrix[$i] = array();
for ($j = 0; $j < $numberOfChannels; $j++) {
$matrix[$i][] = $val[$j][$i];
}
}
}
if ($iterationValue != null) {
$val = array_filter($iterationValue);
if (count($matrix) != 0) {
for ($j = 0; $j < count($iterationValue); $j++) {
for ($i = 0; $i < $range; $i++) {
if ($j == 0) {
$matrix[$i][] = $iterationValue[$j];
}
else {
$matrix[] = $matrix[$i];
$matrix[count($matrix) - 1][count($matrix[0]) - 1] = $iterationValue[$j];
}
}
}
}
else {
for ($j = 0; $j < count($iterationValue); $j++)
$matrix[] = array($iterationValue[$j]);
}
}
return $matrix;
/*} else {
foreach ($rangeParameter as $range) {
$value = array_filter($range->value());
$matrix[] = $value;
}
$combinations = $this->combineAll($matrix);
return $combinations;
}*/
}
function rangeParameterNames() {
$names = array();
foreach ($this->rangeParameters as $parameter) {
$names[] = $parameter->name();
}
return $names;
}
function putMultiChannelScriptOn($script, $isOriginal = false) {
global $useThumbnails;
global $movieMaxSize, $saveSfpPreviews, $maxComparisonSize;
$parameterSetting = $this->parameterSetting;
$numberOfChannels = $parameterSetting->numberOfChannels();
$files = $this->files();
$newScript = $script;
$loadOriginalFile = $this->loadImageString( $isOriginal );
$newScript = $newScript . $loadOriginalFile;
$newScript = $newScript . '$imageName split -mode all' . "\n";
$newScript = $newScript . '$imageName del' . "\n";
$fileBase = $this->sourceImageShortName();
$newScript = $newScript . "catch {\n";
for($channel=0; $channel < $numberOfChannels; $channel++) {
$tmpfilename = $this->id() . $fileBase . "Ch$channel";
// avoid redundant slashes in path
$newScript = $newScript . '$imageName' . ":Ch$channel save " . '{' . $this->sourceFolder() . $this->relativeSourcePath() . $tmpfilename . '} ' . '-type ics' . "\n";
$newScript = $newScript . '$imageName' . ":Ch$channel del " . "\n";
}
$newScript = $newScript . "}\n";
$dst_files = array();
for($channel=0; $channel<$numberOfChannels; $channel++) {
$newScript = $newScript . "catch {\n";
$tmpfilename = $this->id() . $fileBase . "Ch$channel";
// avoid redundant slashes in path
$this->files = array($this->relativeSourcePath() . $tmpfilename . ".ics");
// TODO refactor
// j-) removed + 1
$newScript = $newScript . $this->putScriptOnForChannel('', $channel);
$filename = $this->sourceFolder() . $this->relativeSourcePath() . $tmpfilename . ".ics";
$filename2 = $this->sourceFolder() . $this->relativeSourcePath() . $tmpfilename . ".ids";
$dst_files[] = $this->destinationImageFullName() ;
$newScript = $newScript . "}\n";
$newScript = $newScript . "catch { exec rm -f " . '"' . $filename . '"' . "}\n";
$newScript = $newScript . "catch { exec rm -f " . '"' . $filename2 . '"' . "}\n";
$newScript = $newScript . 'c clear' . "\n";
// manage measured PSF
$psfParam = $parameterSetting->parameter('PointSpreadFunction');
if ($psfParam->value() == "theoretical") {
$newScript = $newScript . 'psf clear' . "\n";
}
}
$taskSetting = $this->taskSetting;
$multichanneloutput = $taskSetting->parameter('MultiChannelOutput');
$param = $taskSetting->parameter('OutputFileFormat');
$type = $param->translatedValue();
// Use appropriate output file format
$fileFormat = $param->extension( );
if ($multichanneloutput->value()) {
$newScript = $newScript . "# join\n";
for($channel=0; $channel < $numberOfChannels; $channel++) {
if ($channel != 0) {
$newScript = $newScript . "if { ! [";
}
$newScript = $newScript . "catch { img open \"".$dst_files[$channel]. "." . $fileFormat ."\"} imageName$channel ";
if ($channel != 0) {
$newScript = $newScript . "] } {\n";
} else {
$newScript = $newScript . "\n";
}
if ($channel > 1) {
$newScript = $newScript . " catch {c join \$imageName$channel -> c}\n}\n";
} else if ($channel == 1) {
$newScript = $newScript . " catch {\$imageName0 join \$imageName1 -> c}\n}\n";
}
}
$tmpfilename = $this->id() . $fileBase; // . "Ch_all";
// avoid redundant slashes in path
$this->files = array($this->relativeSourcePath() . $tmpfilename . "." . $fileFormat );
$dstfilename = $this->destinationImageFullName();
// If the user chose CoverslipRelativePosition to be 'farthest', we z-mirror the dataset back
$coverslipParameter=$parameterSetting->parameter('CoverslipRelativePosition');
$coverslipParameterValue=$coverslipParameter->value();
if ( $coverslipParameterValue == 'farthest' ) {
$newScript = $newScript . 'catch { c mir z }' . "\n";
}
// Save
switch ($type) {
case "tiff":
case "tiff16":
$newScript = $newScript . "c save \"$dstfilename.$fileFormat\" -type $type -tiffMultiDir -cmode scale\n";
break;
default:
$newScript = $newScript . "c save \"$dstfilename.$fileFormat\" -type $type\n";
}
if ( $useThumbnails ) {
$newScript = $newScript .
'catch {
# Previews: Huygens Core 3.3.1 required for this to work.
set path "'.$dstfilename.".".$fileFormat.'"
set ddir [file dirname $path]
set fn [file tail $path]
catch { ::WebTools::savePreview c $ddir/hrm_previews $fn {preview 400} }
'."\n";
if ( $movieMaxSize > 0 ) {
$newScript = $newScript .
' catch {::WebTools::saveStackMovie c $ddir/hrm_previews ${fn}.stack '.
$movieMaxSize . '}'. "\n".
' catch {::WebTools::saveTimeSeriesMovie c $ddir/hrm_previews'.
' ${fn}.tSeries '.$movieMaxSize . '}'. "\n\n";
}
if ( $saveSfpPreviews ) {
$newScript = $newScript .
' catch {::WebTools::saveTopViewSfp c $ddir/hrm_previews ${fn}.sfp } '.
"\n";
if ( $movieMaxSize > 0 ) {
$newScript = $newScript .
' catch {::WebTools::saveTimeSeriesMovie c $ddir/hrm_previews '
. '${fn}.tSeries.sfp - SFP } '. "\n";
}
}
// Save also a preview of the original image, adopting the same
// parameters as in the result.
$newScript = $newScript . " ".
'set orig [eval img open \"$originalFile\" $originalOptions]'."\n";
$newScript = $newScript . " ". 'c adopt -> $orig' . "\n";
$newScript = $newScript . '
set path "'.$dstfilename.".".$fileFormat.".original".'"
set ddir [file dirname $path]
set ofn [file tail $path]
catch { ::WebTools::savePreview $orig $ddir/hrm_previews $ofn {preview 400} }'."\n";
if ($maxComparisonSize > 0 ) {
// Since Huygens 3.5.1p3, stacks and time series can also be
// saved in jpeg strips that can be viewed online with CSS
// tricks.
$newScript .= 'catch {
::WebTools::combineStrips [list $orig c] stack $ddir/hrm_previews ${fn} '. $maxComparisonSize .' auto
::WebTools::combineStrips [list $orig c] tSeries $ddir/hrm_previews ${fn} '. $maxComparisonSize .' auto
}
';
}
if ( $saveSfpPreviews ) {
$newScript = $newScript .
' catch {::WebTools::saveTopViewSfp $orig $ddir/hrm_previews ${ofn}.sfp }'.
"\n";
}
$newScript = $newScript . '
# Save preview of the original also in source directory
set ddir [file dirname $hrmFileName]
set fn [file tail $hrmFileName]
catch { ::WebTools::savePreview $orig $ddir/hrm_previews $fn preview }'.
"\n" ;
$newScript = $newScript . ' $orig del' . "\n";
$newScript = $newScript . '}'."\n";
}
// Delete temporaty files
foreach ($dst_files as $dst_file) {
if ( $type == "ics" ) {
$newScript .= "catch { exec rm -f \"" . $dst_file . ".ics\" }\n";
$newScript .= "catch { exec rm -f \"" . $dst_file . ".ids\" }\n";
}
else {
$newScript .= "catch { exec rm -f \"" . $dst_file . "." . $fileFormat ."\" }\n";
}
}
// Save processing history
$newScript = $newScript . "\n" . 'catch { c history -details -format txt -save "' . $dstfilename. '.history.txt" }' . "\n";
}
$this->files = $files;
return $newScript;
}
function putScriptOnForChannel($script, $channel) {
$parameterSetting = $this->parameterSetting;
$psfParam = $parameterSetting->parameter('PointSpreadFunction');
// save original excitation and emission wavelengths
$excitationWavelength = $parameterSetting->parameter('ExcitationWavelength');
$emissionWavelength = $parameterSetting->parameter('EmissionWavelength');
// DEBUG
$val = $excitationWavelength->value();
$originalExcitationWavelengthValue = array();
for ($i = 0; $i < $parameterSetting->numberOfChannels(); $i++) {
$originalExcitationWavelengthValue[$i] = $val[$i];
//error_log("GENERATING SCRIPT (JobDescription): excitation wavelength -> ".$originalExcitationWavelengthValue[$i]." [".$i."]");
}
$val = $emissionWavelength->value();
$originalEmissionWavelengthValue = array();
for ($i = 0; $i < $parameterSetting->numberOfChannels(); $i++) {
$originalEmissionWavelengthValue[$i] = $val[$i];
//error_log("GENERATING SCRIPT (JobDescription): emission wavelength -> ".$originalEmissionWavelengthValue[$i]." [".$i."]");
}
//error_log("GENERATING SCRIPT (JobDescription): channel -> ".$channel);
// save original pinhole radii
$pinholeSize = $parameterSetting->parameter('PinholeSize');
$val = $pinholeSize->value();
$originalPinholeSizeValue = array();
for ($i = 0; $i < $parameterSetting->numberOfChannels(); $i++) {
$originalPinholeSizeValue[$i] = $val[$i];
}
// save original measured PSF (file names)
if ($psfParam->value() == "measured") {
$psf = $parameterSetting->parameter('PSF');
$val = $psf->value();
$originalPSFValue = array();
for ($i = 0; $i < $parameterSetting->numberOfChannels(); $i++) {
$originalPSFValue[$i] = $val[$i];
}
}
// make single channel setting for current channel
$isMultiChannel = $parameterSetting->parameter('IsMultiChannel');
$isMultiChannel->setValue('False');
$parameterSetting->set($isMultiChannel);
$newExcitationWavelength = $parameterSetting->parameter('ExcitationWavelength');
$value = $newExcitationWavelength->value();
//error_log("GENERATING SCRIPT (JobDescription): excitation wavelength (before trimming) -> ".$value[$channel]);
$newExcitationWavelength->setValue($value[$channel]);
$val = $newExcitationWavelength->value();
//error_log("GENERATING SCRIPT (JobDescription): excitation wavelength (after trimming) -> ".$val[0]);
$parameterSetting->set($newExcitationWavelength);
$newEmissionWavelength = $parameterSetting->parameter('EmissionWavelength');
$value = $newEmissionWavelength->value();
//error_log("GENERATING SCRIPT (JobDescription): emission wavelength (before trimming) -> ".$value[$channel]);
$newEmissionWavelength->setValue($value[$channel]);
$val = $newEmissionWavelength->value();
//error_log("GENERATING SCRIPT (JobDescription): emission wavelength (after trimming) -> ".$val[0]);
$parameterSetting->set($newEmissionWavelength);
$newPinholeSize = $parameterSetting->parameter('PinholeSize');
$value = $newPinholeSize->value();
$newPinholeSize->setValue($value[$channel]);
$parameterSetting->set($newPinholeSize);
if ($psfParam->value() == "measured") {
$newPSF = $parameterSetting->parameter('PSF');
$value = $newPSF->value();
$newPSF->setValue($value[$channel]);
$parameterSetting->set($newPSF);
}
// ... and for task setting
// save original RemoveBackgroundPercent and BackgroundOffsetPercent
//WARNING do not confuse REMOVEBackgroundPercent and BackgroundOffsetPercent
$taskSetting = $this->taskSetting;
$numberOfChannels = $taskSetting->numberOfChannels();
$removeBackgroundPercent = $taskSetting->parameter('RemoveBackgroundPercent');
$val = $removeBackgroundPercent->internalValue();
$originalRemoveBackgroundPercentValue = array();
//TODO remove RemoveBackgroundPercent
for ($i = 0; $i < $numberOfChannels; $i++) {
$originalRemoveBackgroundPercentValue[$i] = $val[$i];
}
//error_log("GENERATING SCRIPT (JobDescription): remove background percent -> ".$originalRemoveBackgroundPercentValue[$i]." [".$i."]");
$backgroundOffsetPercent = $taskSetting->parameter('BackgroundOffsetPercent');
//$val = $backgroundOffsetPercent->internalValue();
//$originalBackgroundOffsetPercentValue = array();
$originalBackgroundOffsetPercentValue = $backgroundOffsetPercent->internalValue();
//for ($i = 0; $i < $numberOfChannels; $i++) {
// $originalBackgroundOffsetPercentValue[] = $val[$i];
//}
// make single channel setting for current channel
$newRemoveBackgroundPercent = $taskSetting->parameter('RemoveBackgroundPercent');
// DEBUG
//$value = $newRemoveBackgroundPercent->value();
$newRemoveBackgroundPercent->setValue($originalRemoveBackgroundPercentValue[$channel]);
$taskSetting->set($newRemoveBackgroundPercent);
// Backgriund Offset Percent
$newBackgroundOffsetPercent = $taskSetting->parameter('BackgroundOffsetPercent');
// DEBUG
//$value = $newBackgroundOffsetPercent->value();
if ($originalBackgroundOffsetPercentValue[0] == "auto" || $originalBackgroundOffsetPercentValue[0] == "object") {
$newBackgroundOffsetPercent->setValue($originalBackgroundOffsetPercentValue[0]);
$test = $newBackgroundOffsetPercent->internalValue();
//error_log("auto/object ".$test[0]);
}
else {
$newBackgroundOffsetPercent->setValue($originalBackgroundOffsetPercentValue[$channel]);
}
$taskSetting->set($newBackgroundOffsetPercent);
// Signal to noise ratio
$newSignalNoiseRatio = $taskSetting->parameter('SignalNoiseRatio');
$originalSignalNoiseRatioValue = $newSignalNoiseRatio->value();
$newSignalNoiseRatio->setValue($originalSignalNoiseRatioValue[$channel]);
$taskSetting->set($newSignalNoiseRatio);
//
$taskSetting->setNumberOfChannels(1);
// Create script
$this->setParameterSetting($parameterSetting);
$this->setTaskSetting($taskSetting);
$result = $this->putScriptOn($script);
// restore multi channel setting with original excitation and emission wavelengths
$parameterSetting = $this->parameterSetting;
$isMultiChannel->setValue('True');
$parameterSetting->set($isMultiChannel);
// DEBUG
$excitationWavelength->setValue($originalExcitationWavelengthValue);
$parameterSetting->set($excitationWavelength);
$emissionWavelength->setValue($originalEmissionWavelengthValue);
$parameterSetting->set($emissionWavelength);
// restore multi channel setting with original pinhole radii
$pinholeSize->setValue($originalPinholeSizeValue);
$parameterSetting->set($pinholeSize);
if ($psfParam->value() == "measured") {
// restore multi channel setting with original measured PSF
$psf->setValue( $originalPSFValue );
$parameterSetting->set($psf);
}
$this->setParameterSetting($parameterSetting);
// ... and for task setting
$taskSetting = $this->taskSetting;
$newSignalNoiseRatio->setValue( $originalSignalNoiseRatioValue );
$taskSetting->set($newSignalNoiseRatio);
$taskSetting->setNumberOfChannels($numberOfChannels);
// DEBUG
$removeBackgroundPercent->setValue($originalRemoveBackgroundPercentValue);
$taskSetting->set($removeBackgroundPercent);
$backgroundOffsetPercent->setValue($originalBackgroundOffsetPercentValue);
$taskSetting->set($backgroundOffsetPercent);
$this->setTaskSetting($taskSetting);
// return result
return $result;
}
function endsWithNumber($string) {
$last = $string[strlen($string)-1];
return is_numeric($last);
}
function mustReplaceSourceName() {
$parameterSetting = $this->parameterSetting;
if (!$parameterSetting->isThreeDimensional() && !$parameterSetting->isTimeSeries() && $parameterSetting->isTif()) {
if ($this->endsWithNumber($this->sourceImageShortName())) {
return True;
}
}
return False;
}
function newNameForSingleTif() {
$files = $this->files();
$name = end($files);
$path = explode("/", $name);
$shortName = array_pop($path);
$path = implode("/", $path);
$anArray = explode(".", $shortName);
$baseName = $anArray[0];
$extension = end($anArray);
$result = $this->sourceFolder() . "." . $baseName . $this->id() . "s." . $extension;
return $result;
}
// Return a single value parameters as a Tcl list for all channels. Since
// verssion 3.5 of Huygens, many parameters that where global before, can be
// now defined per channel, like the NA.
function getParamAsChannelTclList ( $setting, $param, $translate = false,
$withQuotes = false ) {
if ( $translate ) {
$values = $setting->parameter($param)->translatedValue();
} else {
$values = $setting->parameter($param)->value();
}
$chanCnt = $setting->numberOfChannels();
$retList = "";
$quote == "";
if ( $withQuotes ) {
$quote = "\"";
}
if ( is_array($values) ) {
$sep = "";
foreach ($values as $item) {
$retList .= $sep.$quote.$item.$quote;
$sep = " ";
}
} else {
for ($i = 0; $i < $chanCnt; $i ++ ) {
$retList .= $sep.$quote.$values.$quote;
$sep = " ";
}
}
$retList = trim($retList);
return "{ $retList }";
}
// Return an array of parameters from a setting as a Tcl list. Elements are
// optionally enclosed in quotes.
function getParamAsTclList ( $setting, $param, $withQuotes = false ) {
$values = $setting->parameter($param)->value();
$retList = "";
$quote == "";
if ( $withQuotes ) {
$quote = "\"";
}
if ( is_array($values) ) {
$sep = "";
foreach ($values as $item) {
$retList .= $sep.$quote.$item.$quote;
$sep = " ";
}
} else {
$retList = $values;
}
$retList = trim($retList);
return "{ $retList }";
}
function getEmptyParameterList ($parameterSetting) {
$chanCnt = $parameterSetting->numberOfChannels();
$retList = "";
$sep = "";
for ($i = 0; $i < $chanCnt; $i++ ) {
$retList .= $sep."(ignore)";
$sep = " ";
}
return "{ $retList } ";
}
function appendScriptParameters() {
global $resultImagesOwnedByUser, $huygens_group ;
global $useThumbnails;
global $movieMaxSize, $saveSfpPreviews, $maxComparisonSize;
$micr = $this->parameterSetting;
$decon = $this->taskSetting;
$data['jobID'] = $this->id;
// File parameters -------------------------------------------------
// The file may be in a subdirectory of the source folder.
$imageFileName = $this->sourceImageName();
$dirname = dirname($imageFileName);
$basename = basename($imageFileName);
$data['inputDir'] = "\"$dirname\"";
$data['inputFile'] = "\"$basename\"";
// But reporting goes always to the root of the source folder.
$data['reportDir'] = "\"".$this->sourceFolder()."\"";
$dstFileName = $this->destinationImageFullName();
$dirname = dirname($dstFileName);
$basename = basename($dstFileName);
$oFileFormat = $decon->parameter('OutputFileFormat');
$type = $oFileFormat->translatedValue();
$extension = $oFileFormat->extension( );
$data['outputDir'] = "\"$dirname\"";
$data['outputFile'] = "\"$basename.$extension\"";
$data['outputType'] = $type;
$data['seriesOption'] = "auto";
$formatParam = $micr->parameter('ImageFileFormat');
$format = $formatParam->value();
if ($format == "tiff" || $format == "tiff-single") {
// Olympus FluoView, or single XY plane: always
$data['seriesOption'] = "off";
}
$data['isTimeSeries'] = 1;
if (!$micr->isTimeSeries()) {
$data['isTimeSeries'] = 0;
if ($format == "stk") {
// Metamorph: if not time series
$data['seriesOption'] = "off";
}
}
$data['isThreeDimensional'] = 0;
if ($micr->isThreeDimensional()) {
$data['isThreeDimensional'] = 1;
}
// Microscopic parameters ------------------------------------------
// By now, force parameters from template. In the future, we can allow
// users to use some (those with value '-') or all parameters from each
// file metadata. For the latter, we can simply set this to 'metadata';
// TODO GUI 1.3
$data['parametersFrom'] = "template";
// Right now, microscope type is in the HRM a single value, but Huygens
// prefers since version 3.5 to have one value per channel.
$data['micr'] =
$this->getParamAsChannelTclList($micr, 'MicroscopeType', true);
$data['dx'] = $micr->sampleSizeX();
$data['dy'] = $micr->sampleSizeY();
$data['dz'] = $micr->sampleSizeZ();
$data['dt'] = $micr->sampleSizeT();
// Right now, NA is in the HRM a single value, but Huygens prefers since
// version 3.5 to have one value per channel. The same for the r.i.
$data['na'] = $this->getParamAsChannelTclList($micr, 'NumericalAperture');
$data['ex'] = $this->getParamAsTclList($micr, 'ExcitationWavelength');
$data['em'] = $this->getParamAsTclList($micr, 'EmissionWavelength');
$data['pr'] = $this->getParamAsTclList($micr, 'PinholeSize');
if ( $data['micr'] != "nipkow" ) {
$data['ps'] = $this->getEmptyParameterList($micr);
} else {
$data['ps'] = $this->getParamAsTclList($micr, 'PinholeSpacing');
}
// Offsets a currently not used by HRM, but may be useful in the future
// for sticthing applications.
$data['offX'] = 0;
$data['offY'] = 0;
$data['offZ'] = 0;
$data['offT'] = 0;
// Excitation beam fill is an important parameter that affects the
// effective N.A., but most people don't know it. Leave it in a
// reasonable default value by now.
// TODO GUI
$data['exBeamFill'] = 2.0;
// Leave objective quality as 'perfect' for backwards compatibility, but
// in fact something else would produce better results, as most
// objectives very rarely are ideal.
// Notice that if this is not perfect, the orientation of the sample
// matters a lot, even if we deactivate S.A. correction: the PSF is not
// symmetrical anymore for imperfect objectives.
// TODO GUI
$data['objQuality'] = "perfect";
$data['ril'] =
$this->getParamAsChannelTclList($micr, 'ObjectiveType', true);
$data['ri'] =
$this->getParamAsChannelTclList($micr, 'SampleMedium', true);
// Number of photons could very well be a multichannel parameter!
// Currently, HRM treats the two photon as a microscope type, not as a
// channel parameter. You can make this parameter just a Tcl list to make
// it multiphoton.
if ($micr->isTwoPhoton()) {
$data['pcnt'] = 2;
} else {
$data['pcnt'] = 1;
}
// Restoration parameters ------------------------------------------
// Decide what to do with multichannel images: all in a go (all), or
// split and one by one (split).
$data['channelProcessing'] = "split";
$data['method'] = $decon->parameter('DeconvolutionAlgorithm')->value();
$data['sn'] = $this->getParamAsTclList($decon, 'SignalNoiseRatio');
if ( $data['method'] == "qmle" ) {
// For QMLE, the SNR values are given by keywords: replace them.
// No automatic estimation in this case by now.
$indexValues = array (1, 2, 3, 4, 5);
$snrValues = array ("low", "fair", "good", "inf", "auto");
$data['sn'] = str_replace($indexValues, $snrValues, $data['sn']);
// In the QMLE, there's no q parameter, but iteration mode.
$data['q'] = "-";
// Currently, the mode is automatic, so the iteration limit is
// disregarded.
$data['itMode'] = "auto";
$data['it'] = "10";
} else {
$data['itMode'] = "-";
$data['it'] = $this->getParamAsTclList($decon, 'NumberOfIterations');
$data['q'] =
$decon->parameter("QualityChangeStoppingCriterion")->value();
}
// Background level
$bgParam = $decon->parameter("BackgroundOffsetPercent");
$bgValue = $bgParam->value();
$internalValue = $bgParam->internalValue();
if ($bgValue[0] == "auto" || $internalValue[0] == "auto") {
$data['bgMode'] = "auto";
$data['bg'] = 0;
} else if ($value[0] == "object" || $internalValue[0] == "object") {
$data['bgMode'] = "object";
$data['bg'] = 0;
} else {
$data['bgMode'] = "manual";
$data['bg'] = $this->getParamAsTclList($decon, 'BackgroundOffsetPercent');
}
// Bleaching correction.
// TODO GUI
$data['blMode'] = "auto";
// Doing the deconvolution with 32-bit signed float images prevents the
// use of any scaling factor. This is imporant for example ratiometric
// analysis, but maybe also for consistent between-image comparison.
// There should be an option in the interface to disable this.
// TODO GUI 1.3
$data['forceProcessAsFloat'] = 1;
// Spherical Aberration (S.A.) correction
// The various S.A. correction options result in a different value for
// the -brMode deconvolution flag.
$constant = false;
$symmetrical = false;
$depth = 0;
$brMode = 'auto';
$SAcorr = $micr->getAberractionCorrectionParameters();
if ( $SAcorr[ 'AberrationCorrectionNecessary' ] == 1 ) {
if ( $SAcorr[ 'PerformAberrationCorrection' ] == 0 ) {
$brMode = 'one';
$constant = true;
$symmetrical = true;
$coverslip = "(ignore)";
} else {
$depth = $SAcorr[ 'PSFGenerationDepth' ];
if ( $SAcorr[ 'AberrationCorrectionMode' ] == 'automatic' ) {
$brMode = 'auto';
$coverslip = $SAcorr[ 'PSFGenerationDepth' ];
} else {
if ( $SAcorr[ 'AdvancedCorrectionOptions' ] == 'user' ) {
$brMode = 'one';
$constant = true;
$coverslip = "(ignore)";
}
if ( $SAcorr[ 'AdvancedCorrectionOptions' ] == 'slice' ) {
$brMode = 'sliceBySlice';
$coverslip = $SAcorr[ 'PSFGenerationDepth' ];
}
if ( $SAcorr[ 'AdvancedCorrectionOptions' ] == 'few' ) {
$brMode = 'few';
$coverslip = $SAcorr[ 'PSFGenerationDepth' ];
}
}
}
}
$data['brMode'] = $brMode;
// S.A. correction requires knowing what is the position of the
// coverslip, and the orientation of the dataset. We don't need to mirror
// the stack, the orientation is an image parameter since Huygens 3.5.
// TODO GUI 1.3: ask the distance between the coverslip and the begining
// of the stack; since Huygens 3.5 this is considered for a good S.A.
// correction. Use the same 'PSFGenerationDepth' parameter here, but
// notice that in the HRM 1.2 this parameter is only asked when the user
// goes for a fixed-PSF. We need to know the distance to the coverslip
// for the space-variant PSF!
$data['coverslipDistanceFromStack'] = $coverslip;
// Notice: internally, Huygens uses a parameter 'iFacePrim' that sets the
// distance in micrometers, measured from the
// plane Z = 0 of the stack (the bottom one). We will translate the
// variable to make the interface easier for the user.
// Some examples:
// 1) The stack is imaged upward (plane 0 is closest to the coverlip)
// but the image acquisition started 3 um away from the coverslip (so
// that the coverslip is not visible in the image, but 3 um below the
// stack).
// Here iFacePrim should be 3, and imagingDir = 'upward'. We pass the
// parameter coverslipDistanceFromStack = 3.
// 2) The stack is imaged downward (plane 0 is farthest from the
// coverslip), but the coverslip is 3 microns inside the stack (the
// imaging started quite above the coverslip). In this case we pass
// coverslipDistanceFromStack = -3 (negative, because it is inside the
// stack) and imagingDir = 'downward'.
//
// In this case, to calculate iFacePrim, we need to know the size of the
// stack, to set the position of the coverslip relative to plane 0. The
// backend script will take care of computing iFacePrim correctly in both
// cases.
// When using a fixed PSF, the $depth is also the one to set on what
// position we want to calculate it.
// This is already asked by the current GUI.
$data['psfDepth'] = $depth;
// We don't need to mirror the image, since Huygens 3.5 the stack
// orientation is an image parameter.
$coverslipPos = $micr->parameter('CoverslipRelativePosition')->value();
if ($coverslipPos == 'farthest' ) {
$data['imagingDir'] = "downward";
} else {
$data['imagingDir'] = "upward";
}
// Point Spread Function (PSF) -------------------------------------
$psfParam = $micr->parameter('PointSpreadFunction');
$psfType = $psfParam->value();
if ($psfType == "theoretical") {
if ($symmetrical) {
// A symmetrical PSF, no S.A. correction.
$data['psf'] = "theoretical-symmetrical";
} else {
if ($constant) {
// Partial S.A. correction: asymmetrical PSF at a given
// depth.
$data['psf'] = "theoretical-fixed";
} else {
// Variant PSF: better S.A. correction.
$data['psf'] = "theoretical-variant";
}
}
$data['psfFile'] = "-";
} else {
$data['psf'] = "measured";
// A list of files, one per channel (or just one multichannel image)
$psfFiles = $micr->parameter('PSF')->value();
if ( !is_array( $psfFiles ) ) {
$data['psfFile'] = "\"". $this->sourceFolder() .$psfFiles. "\"";
} else {
$data['psfFile'] = "{";
foreach ( $psfFiles as $f ) {
$data['psfFile'] .= " \"". $this->sourceFolder() . $f. "\"";
}
$data['psfFile'] .= " }";
}
}
// HRM parameters --------------------------------------------------
if ($resultImagesOwnedByUser) {
$data['imagesOwnedBy'] = $this->owner()->name();
} else {
$data['imagesOwnedBy'] = "-";
}
$data['imagesGroup'] = $huygens_group;
if ($useThumbnails) {
$data['useThumbnails'] = 1;
} else {
$data['useThumbnails'] = 0;
}
if ($saveSfpPreviews) {
$data['saveSfpPreviews'] = 1;
} else {
$data['saveSfpPreviews'] = 0;
}
$data['maxComparisonSize'] = $maxComparisonSize;
$data['movieMaxSize'] = $movieMaxSize;
// Build settings procedure ----------------------------------------
// Dump all the $data array in a convenient Tcl form.
$s = "
proc DefineScriptParameters {} {
global hrm
set hrm_list [list \\\n";
foreach ($data as $key => $val) {
$s .= " $key $val \\\n";
}
$s .= "]
array set hrm \$hrm_list
}
# Start the job.
HRMrun deconvolution
";
return $s;
}
function putScriptOn($script, $isOriginal = false ) {
global $resultImagesOwnedByUser;
global $huygens_group;
$newScript = $script;
$newScript = $newScript . "if {![img exists a]} {img create a -logEnable} else { a keepLog }\n";
$newScript = $newScript . "if {![img exists b]} {img create b -logEnable} else { b keepLog }\n";
$newScript = $newScript . "if {![img exists c]} {img create c -logEnable} else { c keepLog }\n";
$newScript = $newScript . "if {![img exists psf]} {img create psf}\n";
if ($this->mustReplaceSourceName()) { // if it is not 3D && is not time serie && is Tif && ends with number
$newScript = $newScript . "exec cp " . '"' . $this->sourceImageName() . '" "' . $this->newNameForSingleTif() . '"' . "\n";
}
// loading and converting
$newScript = $newScript . $this->loadImageString( $isOriginal );
$parameterSetting = $this->parameterSetting;
// This test is PARTIALLY overlapping with the test a few lines below
// TODO Refactor
if ( $parameterSetting->isTimeSeries() && !$parameterSetting->isThreeDimensional() )
$newScript = $newScript . 'catch {$imageName convertZ2T}' . "\n";
$taskSetting = $this->taskSetting;
$numberOfChannels = $parameterSetting->numberOfChannels();
$taskSetting->setNumberOfChannels($numberOfChannels);
// Apply the parameters to the image in all cases:
$newScript = $parameterSetting->putScriptOn($newScript);
// manage measured PSF
$psfParam = $parameterSetting->parameter('PointSpreadFunction');
if ($psfParam->value() == "measured") {
$param = $parameterSetting->parameter('PSF');
$value = $param->value();
if ( is_array( $value ) ) {
// This is the case if the number of channel is == 1
$filename = $this->sourceFolder() . $value[0];
} else {
// This is the case if the number of channel is > 1
$filename = $this->sourceFolder() . $value;
}
$newScript = $newScript . "set psf [img open \"". $filename.
"\" -logEnable]\n";
$newScript = $newScript . "\$psf -> psf\n";
$newScript = $newScript . "\$psf del\n";
}
$z2t = False;
if (!$parameterSetting->isThreeDimensional() && $parameterSetting->isTimeSeries() && !$parameterSetting->isTif())
$z2t = True;
if ($taskSetting->isStepProcessing() && !$parameterSetting->isTimeSeries())
$z2t = True;
if ($z2t) {
$newScript = $newScript . 'catch {$imageName convertZ2T}' . "\n";
}
// manage measured PSF
if ($psfParam->value() == "theoretical") {
// We need to pass the array of parameters for the aberraction correction
// an input parameter to JobTaskSetting::putScriptOn( )
$newScript = $taskSetting->putScriptOn($newScript,
$this->parameterSetting->getAberractionCorrectionParameters( ) );
}
else if ($psfParam->value() == "measured") {
// operations
$newScript = $taskSetting->putScriptForMeasuredPointSpreadFunctionOn($newScript);
}
// if sampling sizes had been adapted, reset the original values
if ($parameterSetting->sampleSizeAdaptionFactor()!=1) {
$newScript = $newScript . 'c setp -s {' . $parameterSetting->originalSampleSizesString() . "}\n";
}
// converting and saving
$t2z = False;
if ($z2t) $t2z = True;
if (!$parameterSetting->isThreeDimensional() && $taskSetting->isOutputIms() && $parameterSetting->isTimeSeries()) $t2z = True;
if ($parameterSetting->isThreeDimensional() && $taskSetting->isOutputIms() && !$parameterSetting->isTimeSeries()) $t2z = True;
if ($t2z) {
$newScript = $newScript . 'catch {c convertT2Z}' . "\n";
$newScript = $newScript . 'c setp -s {' . $parameterSetting->originalSampleSizesString() . "}\n";
}
if (!$parameterSetting->isThreeDimensional() && !$parameterSetting->isTimeSeries() && !$taskSetting->isOutputIms()) {
$newScript = $newScript . 'c convert3d22d' . "\n";
}
$newScript = $newScript . "catch { exec mkdir -m0777 -p " . '"' . $this->destinationFolder() . '"' . " }\n";
if ($resultImagesOwnedByUser) {
$user = $this->owner();
$newScript = $newScript . "catch { exec chown -Rf " . $user->name() . ':' . $huygens_group . ' "' . $this->destinationFolderTop() . '"' . " }\n";
}
# Shouldn't we give 666 permissions to the result files, to make sure this
# works everywhere? Bad solution in general, but might be the most
# practical thing with the current code for a trusted multiuser
# environment. /// TODO ///
$newScript = $newScript . $this->saveImageString( $isOriginal );
/* TODO This is obsolete code that should be removed! isStepCombinedProcessing( ) always returns false.
if ($taskSetting->isStepCombinedProcessing()) {
$newScript = $newScript . $this->passTwoScript();
}
*/
if ($this->mustReplaceSourceName()) {
$newScript = $newScript . "catch { exec rm -f " . '"' . $this->newNameForSingleTif() . '"' . " }\n";
}
return $newScript;
}
/* OBSOLETE
function passTwoScript() {
$this->pass = 2;
$script = '';
$script = $script . 'if {[img exists $imageName]} {$imageName del}' . "\n";
$script = $script . 'c genpsf -> psf -dims auto -z -reflCorr' . "\n";
$script = $script . 'c cmle psf -> c -sn {80} -it 7 -bgMode object -bg {-100} -blMode auto -q 0 -mode fast -pad auto' . "\n";
$script = $script . $this->saveImageString();
$this->pass = 1;
return $script;
}
*/
function loadImageString( $isOriginal = false ) {
global $useThumbnails;
if ($this->mustReplaceSourceName()) {
$imageFileName = $this->newNameForSingleTif();
} else {
$imageFileName = $this->sourceImageName();
}
// TODO we should provide more convenience methods in ParameterSettings
$options = "";
if (stristr($imageFileName, ".stk")) {
// Metamorph: if not time series
if (!$this->parameterSetting->isTimeSeries()) {
$options .= " -series off";
}
}
$formatParam = $this->parameterSetting->parameter('ImageFileFormat');
$format = $formatParam->value();
if ($format == "tiff" || $format == "tiff-single") {
// Olympus FluoView, or single XY plane: always
$options .= " -series off";
}
if ( preg_match("/^(.*\.lif)\s\((.*)\)/i", $imageFileName, $match) ) {
// If a (string) comes after the file name, it is interpreted as a sub
// image. Currently this is for LIF files only.
$huygensFileName = $match[1];
$options .= " -subImage \"$match[2]\"";
} else {
$huygensFileName = $imageFileName;
}
// Turn on history
$options .= " -logEnable";
// If the user specified that the coverslip is 'farthest' we need to mirror
// the image. But this must be done only when the original dataset is
// loaded, not when the splitted channels are loaded. Therefore, by default
// this function does check for mirroring only if its input parameter
// $isOriginal is true, and this occurs only when this function is called
// from Job::createScript( ).
// We use the same hack to keep the name of the original image, in order
// to generate its preview later.
$mirror = "";
$thumbn = "";
if ( $isOriginal == true ) {
// Two additional conditions are (1) that the dataset needs correction
// and (2) that the user decided to perform correction
$absCorr=$this->parameterSetting->getAberractionCorrectionParameters( );
if ( ( $absCorr[ 'AberrationCorrectionNecessary' ] == 1 ) &&
( $absCorr[ 'PerformAberrationCorrection' ] == 1 ) &&
( $absCorr[ 'CoverslipRelativePosition' ] == 'farthest' ) ) {
$mirror = 'catch { $imageName mir z }' . "\n";
}
$thumbn = "\nset originalFile \"$huygensFileName\"".
"\nset hrmFileName \"$imageFileName\"".
"\nset originalOptions [list $options]\n";
}
return 'set imageName [img open "' . $huygensFileName . '"' . $options . ']' . "\n" . '$imageName lundo -off' . "\n" . $mirror. $thumbn;
}
// TODO refactor this part
function saveImageString( $isResult = false ) {
global $resultImagesOwnedByUser;
global $useThumbnails;
global $movieMaxSize, $saveSfpPreviews, $maxComparisonSize;
$result = '';
$setting = $this->taskSetting;
$parameterSetting = $this->parameterSetting;
$param = $setting->parameter('OutputFileFormat');
$type = $param->translatedValue();
$extension = $param->extension( );
// if ($type=='ics') {
// $result = $result . "c convert -type auto\n";
// }
$imageFileName = $this->destinationImageFullName();
// if ($type== 'tiff' || $type == 'tiff16') {
// $imageFileName = $imageFileName . '.tif';
// }
$result = $result . "c save " . '"' . $imageFileName . "." . $extension .'"' . " -type $type";
// Set -cmode to 'scale' for 8 bit datasets
if ($type == 'tiff' || $type == 'tiff16') {
$result = $result . " -tiffMultiDir -cmode scale";
}
if ( $isResult ) {
// Save preview.
if ( $useThumbnails ) {
$newScript = "\n".
'catch {
# Previews: Huygens Core 3.3.1 required for this to work.
set path "'.$imageFileName.".".$extension.'"
set ddir [file dirname $path]
set fn [file tail $path]
catch { ::WebTools::savePreview c $ddir/hrm_previews $fn {preview 400} }
'."\n";
if ( $movieMaxSize > 0 ) {
$newScript = $newScript .
' catch {::WebTools::saveStackMovie c $ddir/hrm_previews ${fn}.stack '.
$movieMaxSize . '}'. "\n".
' catch {::WebTools::saveTimeSeriesMovie c $ddir/hrm_previews'.
' ${fn}.tSeries '. $movieMaxSize . '}'. "\n\n";
}
if ( $saveSfpPreviews ) {
$newScript = $newScript .
' catch {::WebTools::saveTopViewSfp c $ddir/hrm_previews ${fn}.sfp } '.
"\n";
if ( $movieMaxSize > 0 ) {
$newScript = $newScript .
' catch {::WebTools::saveTimeSeriesMovie c $ddir/hrm_previews '
. '${fn}.tSeries.sfp - SFP } '. "\n";
}
}
// Save also a preview of the original image, adopting the same
// parameters as in the result.
$newScript = $newScript . " ".
'set orig [eval img open \"$originalFile\" $originalOptions]'."\n";
$newScript = $newScript . " ". 'c adopt -> $orig' . "\n";
$newScript = $newScript . '
set path "'.$imageFileName.".".$extension.".original".'"
set ddir [file dirname $path]
set ofn [file tail $path]
catch {::WebTools::savePreview $orig $ddir/hrm_previews $ofn {preview 400}}'.
"\n";
if ($maxComparisonSize > 0 ) {
// Since Huygens 3.5.1p3, stacks and time series can also be
// saved in jpeg strips that can be viewed online with CSS
// tricks.
$newScript .= 'catch {
::WebTools::combineStrips [list $orig c] stack $ddir/hrm_previews ${fn} '. $maxComparisonSize .' auto
::WebTools::combineStrips [list $orig c] tSeries $ddir/hrm_previews ${fn} '. $maxComparisonSize .' auto
}
';
}
if ( $saveSfpPreviews ) {
$newScript = $newScript .
' catch {::WebTools::saveTopViewSfp $orig $ddir/hrm_previews ${ofn}.sfp }'.
"\n";
}
// We keep doing things assuming a trusted environment,
// but real security would require making all directories
// accessible to the deamon only, that runs all file
// management operation after the apache queries.
// By now, grant 777 permissions to the previews subdir.
$newScript = $newScript . '
# Save preview of the original also in source directory
set sdir [file dirname $hrmFileName]
set fn [file tail $hrmFileName]
catch { ::WebTools::savePreview $orig $sdir/hrm_previews $fn {preview} }
catch { exec chmod -R a+w "$sdir/hrm_previews" }
catch { exec chmod -R a+w "$ddir/hrm_previews" }'.
"\n" ;
$newScript = $newScript . ' $orig del' . "\n";
$newScript = $newScript . '}'."\n";
$result = $result . $newScript;
}
}
// Save processing history
$result = $result . "\n" . 'catch { c history -details -format txt -save "' . $imageFileName. '.history.txt" }';
$result = $result . "\n\n";
if ($resultImagesOwnedByUser) {
$user = $this->owner();
$username = $user->name();
$result = $result . 'set modfiles [glob -nocomplain ' . '"' . $this->destinationFolder() . '*' . $this->sourceImageShortName() . '*' . '"]' . "\n";
$result = $result . 'foreach modfile $modfiles { catch { exec chown ' . "$username " . '$modfile } }' . "\n";
$result = $result .
'catch { exec chown -R '.$username.' "$ddir/hrm_previews" }'."\n";
$result = $result .
'catch { exec chown -R '.$username.' "$sdir/hrm_previews" }'."\n";
}
return $result;
}
function sourceImageName() {
$files = $this->files();
// avoid redundant slashes in path
$result = $this->sourceFolder() . ereg_replace("^/", "", end($files));
return $result;
}
function sourceImageNameWithoutPath() {
$name = $this->sourceImageName();
$pos = strrpos( $name, '/' );
if ( $pos ) {
return ( substr( $name, ( $pos + 1 ) ) );
} else {
return $name;
}
}
function relativeSourcePath() {
$files = $this->files();
$inputFile = end($files);
$inputFile = explode("/", $inputFile);
array_pop($inputFile);
$path = implode("/", $inputFile);
// avoid redundant slashes in path
if (strlen($path) > 0) $path = ereg_replace("([^/])$", "\\1/", $path);
return $path;
}
function sourceImageShortName() {
$files = $this->files();
$inputFile = end($files);
$inputFile = explode("/", $inputFile);
// remove file extension
//$inputFile = explode(".", end($inputFile));
//$inputFile = $inputFile[0];
$parameterSetting = $this->parameterSetting;
$parameter = $parameterSetting->parameter('ImageFileFormat');
$fileFormat = $parameter->value();
if ( strcasecmp( $fileFormat, 'lif' ) == 0 ) {
if ( preg_match("/^(.*)\.lif\s\((.*)\)/i", $inputFile[0], $match) ) {
$inputFile = $match[ 1 ] . '_' . $match[ 2 ];
} else {
$inputFile = substr(end($inputFile), 0, strrpos(end($inputFile), ".")); }
} else {
$inputFile = substr(end($inputFile), 0, strrpos(end($inputFile), "."));
}
return $inputFile;
}
function sourceFolder() {
global $huygens_server_image_folder;
global $image_source;
$user = $this->owner();
$result = $huygens_server_image_folder . $user->name() . "/" . $image_source . "/";
return $result;
}
function destinationImageName() {
$taskSetting = $this->taskSetting();
$files = $this->files();
$outputFile = $this->sourceImageShortName();
$outputFile = end(explode($taskSetting->name(), $this->sourceImageShortName()));
$result = $outputFile . "_" . $taskSetting->name() . "_hrm";
# Add a non-numeric string at the end: if the task name ends with a
# number, that will be removed when saving using some file formats that
# use numbers to identify Z planes. Therefore the result file won't
# be found later and an error will be generated.
if ($taskSetting->isStepCombinedProcessing()) {
if ($this->pass == 1) {
$extension = 'A';
} else {
$extension = 'B';
}
$result = $result . "_step" . $extension;
}
return $result;
}
function destinationImageNameWithoutPath() {
$name = $this->destinationImageName();
$pos = strrpos( $name, '/' );
if ( $pos ) {
$name = substr( $name, ( $pos + 1 ) );
}
// Append extension
$taskSetting = $this->taskSetting();
$param = $taskSetting->parameter('OutputFileFormat');
$fileFormat = $param->extension( );
return ( $name . "." . $fileFormat );
}
function destinationImageNameAndExtension() {
$name = $this->destinationImageName();
// Append extension
$taskSetting = $this->taskSetting();
$param = $taskSetting->parameter('OutputFileFormat');
$fileFormat = $param->extension( );
return ( $this->relativeSourcePath(). $name . "." . $fileFormat );
}
function destinationImageFullName() {
global $resultImagesRenamed;
// use source filename as destination filename
if (!$resultImagesRenamed) {
$files = $this->files();
$result = $this->destinationFolder() . ereg_replace("^/", "", end($files));
$result = substr($result, 0, strrpos($result, "."));
if ( $this->endsWithNumber($result) ) {
// Avoid output files ending in a number.
$result .= "_hrm";
}
}
else {
$result = $this->destinationFolder() . $this->destinationImageName();
}
return $result;
}
function destinationFolder() {
global $huygens_server_image_folder;
global $image_destination;
$user = $this->owner();
// avoid redundant slashes in path
$result = $huygens_server_image_folder . $user->name() . "/" . $image_destination . "/" . $this->relativeSourcePath();
return $result;
}
function destinationFolderTop() {
global $huygens_server_image_folder;
global $image_destination;
$user = $this->owner();
$path = $this->relativeSourcePath();
$pathComponents = explode("/", $path);
$top = $pathComponents[0];
$result = $huygens_server_image_folder . $user->name() . "/" . $image_destination . "/" . $top . "/";
return $result;
}
}