package OMMosaic;
use strict;
use English;
use Carp;
use vars
qw(@ISA $VERSION $name $author $date $version @instList $numberOfStreams);
@ISA = qw(Module);
use ModuleResources;
use ModuleUtil;
# Declare identity, version, author, date, etc.
$name = __PACKAGE__;
$VERSION = '2.34';
$version = $VERSION;
$author="Duncan John Fyfe, Duncan Law-Green, Eduardo Ojero, Ed Chapin, Jose Vicente Perea, Pedro Rodriguez";
$date="2019-07-01";
#
# ChangeLog
# =========
#
# Version 2.33 - 2019-07-01 (JVP)
# ------------
#
# + In case of EPIC image > 1 deg skip only source detection, instead of skipping the whole OMMosaic module
#
# Version 2.33 - 2019-03-14 (JVP)
# ------------
#
# + Skip OMMosaic module whether the angular extent of the EPIC image (intermediate/'merged image') is larger
# than 1 deg along either axis
#
# Version 2.32 - 2016-12-19 (JVP)
# ------------
#
# + Create OM Mosaic also when only 1 image is available (per mode and per filter)
#
# Version 2.31 - 2016-03-22 (PR, JVP)
# ------------
#
# + Make source detection in stacked mosaic. Source lists from OM Mosaics
#
# Version 2.30 - 2013-06-11 (EC)
# ------------
#
# + Merge changes from SOC version.
#
# Version 2.29 - 2012-06-15 (DLG)
# ------------
#
# + Add {Partial|Full} Frane Engineering Mode PFENG2, FFENG2 to list of OMMosaic modes
#
# Version 2.28 - 2012-05-02 (DLG)
#
# + Eliminate use of GIF bitmaps
#
# ------------
#
# Version 2.27.1 - 2011-08-09 EOP
# --------------
#
# + Removed creation of GIF to avoid crashing on 64-bit. PNG file is created directly and GIF to PNG transformation is removed.
#
# Version 2.27 - 2009-05-01 (DLG)
#
# + explicitly set mincorr=0 per SRR's suggestion
#
# ------------
#
# Version 2.26 - 2009-05-01 (DLG)
#
# - Remove exposure parameter (not required in SAS 9)
#
# ------------
#
# Version 2.25 - 2005-09-13 (RGW)
#
# + shortened intermediate filenames
#
# ------------
#
# Version 2.24 - 2004-01-14 (DJF)
# ------------
#
# + Updated evaluateRules. The module was not ignoring itself properly.
#
# Version 2.23 - 2003-12-10 (DJF)
# ------------
#
# + Adapted doCommand to use anonymous lists for list parameters
#
# Version 2.22 - 2002-12-13 (DJF)
# ------------
#
# + implot parameter changes. Merged plotfile/Device
#
# Version 2.21 - 2002-12-11 (DJF)
# ------------
#
# + Change implot parameter withsrclistset to withsrclisttab (SAS 5.4)
#
# Version 2.20 - 2002-03-11 (DJF)
# ------------
#
# + Amended to look for exposure from rudi-5 side windows if we are missing the central image.
#
# Version 2.19 - 2002-02-22 (DH)
# ------------
#
# + Minor bug fix. If statement had => instead of >= .
#
# Version 2.18 - 2002-02-20 (DFJ)
# ------------
#
# + Removed 100000 second limit in module.
#
# Version 2.17 - 2002-01-24 (DFJ)
# ------------
#
# + Fixed logic for totaling duration
# + Added if/then to deal with exposure times outside of ommosaic task limits
#
#
# Version 2.20 - 2002-03-11 (DJF)
# ------------
#
# + Amended to look for exposure from rudi-5 side windows if we are missing the central image.
#
# Version 2.19 - 2002-02-22 (DH)
# ------------
#
# + Minor bug fix. If statement had => instead of >= .
#
# Version 2.18 - 2002-02-20 (DFJ)
# ------------
#
# + Removed 100000 second limit in module.
#
# Version 2.17 - 2002-01-24 (DFJ)
# ------------
#
# + Fixed logic for totaling duration
# + Added if/then to deal with exposure times outside of ommosaic task limits
#
# Version 2.20 - 2002-03-11 (DJF)
# ------------
#
# + Amended to look for exposure from rudi-5 side windows if we are missing the central image.
#
# Version 2.19 - 2002-02-22 (DH)
# ------------
#
# + Minor bug fix. If statement had => instead of >= .
#
# Version 2.18 - 2002-02-20 (DFJ)
# ------------
#
# + Removed 100000 second limit in module.
#
# Version 2.17 - 2002-01-24 (DFJ)
# ------------
#
# + Fixed logic for totaling duration
# + Added if/then to deal with exposure times outside of ommosaic task limits
#
# Version 2.16 - 2002-01-23 (DFJ)
# ------------
#
# + Added determination of total exposure and passing of it to ommosaic task
#
#
# Version 2.15 - 2001-12-17 (DFJ)
# ------------
#
# + Changed full frame sky image context strings to get around
# problem with 'too long' intermedaite file names.
#
# Version 2.14 - 2001-12-04 (DFJ)
# ------------
#
# + Added word MOSAIC to prtoduct content strings
#
# Version 2.13 - 2001-12-03 (DFJ)
# ------------
#
# + Added condition to stop mosaicing of single ENG2 or ENG4 images
#
# Version 2.12 - 2001-11-29 (DFJ)
# ------------
#
# + Fixed and changed evaluate rules. There need be no dependance on OMFastAnalyse
#
# Version 2.11 - 2001-11-29 (DFJ)
# ------------
#
# + Corrected grouping of images. The end conditions meant the
# last image got missed.
#
# Version 2.10 - 2001-11-28 (DFJ)
# ------------
#
# + Added creation of PNG images from mosaics
#
# Version 2.09 - 2001-11-28 (DFJ)
# ------------
#
# + Corrected osw to osw_id in product filename creation
#
# Version 2.08 - 2001-11-28 (DFJ)
# ------------
#
# + Removed debugging code.
# + Fixed naming of mosaic products
# + Removed some of the excessive log output
#
# Version 2.07 - 2001-11-28 (DFJ)
# ------------
#
# + added debugging code to help track a problem with the per filter processing
#
# Version 2.06 - 2001-11-28 (DFJ)
# ------------
#
# + Fixing per filter processing
#
# Version 2.05 - 2001-11-28 (DFJ)
# ------------
#
# + Fix grouping of images prior to mosaicing
#
# Version 2.04 - 2001-11-28 (DFJ)
# ------------
#
# + Correct reading of ASCII files containing image details
#
# Version 2.03 - 2001-11-27 (DFJ)
# ------------
#
# + Correct usage of readASCIIFile
#
# Version 2.02 - 2001-11-27 (DFJ)
# ------------
#
# + Fix evaluateRules dependence on OMImageAnalyse
#
# Version 2.01 - 2001-11-26 (DFJ)
# ------------
#
# + Added new product content strings
# + Changes to accomodate output from OMImageAnalyse of a single ENG2 mode
#
# Version 2.00 - 2001-11-26 (DFJ)
# ------------
#
# + Major rearangement to accomodate per filter and per mode products.
# This incorporates new rules for OM products devised enhance visibility
# of products from different OM modes
# + OMImageAnalyse now genererates ASCII lists containing Product file, mode and filter
# information. These are used too define and control what mosaiced images are produced.
#
# Version 1.12 - 2001-11-07 (DFJ)
# ------------
#
# + Changed evaluaterules to ignore if all OMImgAnalyse and
# OMFastAnalyse streams are ignored
#
# Version 1.11 - 2001-11-06 (DFJ)
# ------------
#
# + Change so module is flagged as ignored on failure of ommosaic task.
# This is a temporary 'fix' to overcome a buggy ommosaic and allow
# testing of the rest of the pipeline
#
# Version 1.10 - 2001-11-05 (DFJ)
# ------------
#
# + Changed to use skyimage which s what ommosaic requires.
#
# Version 1.09 - 2001-11-05 (DFJ)
# ------------
#
# + Added return exception for when ommosaic task fails.
#
# Version 1.08 - 2001-11-05 (DFJ)
# ------------
#
# + Fix selection of osw. Was failing to recognize 0 as a valid osw.
#
# Version 1.07 - 2001-11-02 (DJF)
# ------------
#
# + Change to logic. Generate up to three mosaiced images (Rudi5+EN2 , EN4 and FM) rather than
# + one of three
#
# Version 1.06 - 2001-11-02 (DJF)
# ------------
#
# + Added test for imInfo{'osw'}. This was not the problem.
#
# Version 1.05 - 2001-11-01 (DJF)
# ------------
#
# + More log details. Still trying to find the source of a bug.
#
# Version 1.04 - 2001-11-01 (DJF)
# ------------
#
# + Removed check for imInfo{'osw'}. Looks like it isn't propperly null.
# + Left in check for imgIngo('osw_id'}. Let's see if that works.
#
# Version 1.03 - 2001-11-01 (DJF)
# ------------
#
# + Output extra information to logs
#
# Version 1.02 - 2001-10-31 (DJF)
# ------------
#
# + Output extra information to logs
#
# Version 1.01 - 2001-10-31 (DJF)
# ------------
#
# + Change determination of osw to account for different sources (osw or osw_id)
#
# Version 1.00 - 2001-10-22 (DJF)
# ------------
#
# + Initial version
# Declare list of instruments this module is interested in
@instList=qw(om);
# Number of streams
sub numberOfStreams{ return 1; }
sub evaluateRules {
# Start conditions
return ignore() if ignored(module => 'OMgetFlat'
,stream => 1
);
return ignore() if allIgnored(module=>'OMImageAnalyse'
,instrument => thisInstrument
);
start()
if allComplete(module => 'OMImageAnalyse'
,instrument => thisInstrument
);
}
sub performAction {
info("Module version number: $version");
# OM modes are: USER , RUDI5_LR , RUDI5_HR , ENG2 , PFENG2, FFENG2, ENG4 and FAST
# We do not Mosaic FAST images
# Modes written by OMImageAnalyse
my ($USER,$RUDI5_LR,$RUDI5_HR,$ENG2,$PFENG2,$FFENG2,$ENG4) = ('USER','RUDI5_LR','RUDI5_HR','ENG2','PFENG2','FFENG2','ENG4');
# Output Modes used by OMMosaic (we don't generate High Resoultion RUDI5 mosaics
my $MosaicModes = [$USER,$RUDI5_LR,$ENG2,$PFENG2,$FFENG2,$ENG4];
info("Looking For Images to Mosaic...");
#Determine what Sky Images were written
my @SkyImgDetailsFiles = findFile(class =>'intermediate'
,instrument => thisInstrument
,format => "ASCII"
,content => "OM SKY IMAGE DETAILS"
);
unless (scalar(@SkyImgDetailsFiles)) {
info("No Sky Images to mosaic.");
return success();
}
# #---
# # Skip OMMosaic if the EPIC image is larger than ($sizeLimit degree) = 2 deg
# my $sizeLimit = 1; # (degree)
# my @mergedImages = findFile(
# class => 'intermediate'
# , content => 'merged image'
# );
# if ( fileExists( file => $mergedImages[0] ) ){
# if ( ! ModuleUtil::isSmallMap( $mergedImages[0], $sizeLimit ) ) {
# info("EPIC image $mergedImages[0] is larger than $sizeLimit. No OM Mosaic");
# return success();
# }
# }
# #---
my @SkyImgDetails = ();
foreach my $file (@SkyImgDetailsFiles) {
my @details = readASCIIFile(name=>$file);
next unless (scalar(@details));
foreach my $line (@details) {
my @tmp = split("\\|",$line);
push (@SkyImgDetails,\@tmp) if (scalar(@tmp));
}
}
my $count = scalar(@SkyImgDetails);
unless ($count) {
info("No Sky Images to mosaic.");
return success();
}
info("Found $count Images");
info ("Grouping Images by Mode then Filter");
# Sort details on: OM Mode, Filter
# 0 = Filename , 1 = Img Mode , 2 = Filter ,3 duration, 4 exp_id
my ($filename_p,$mode_p,$filter_p,$duration_p,$exp_id_p) = (0,1,2,3,4);
my @SortedSkyImgDetails = sort { ${$a}[$mode_p] cmp ${$b}[$mode_p] || ${$a}[$filter_p] cmp ${$b}[$filter_p] || ${$a}[$filename_p] cmp ${$b}[$filename_p] } @SkyImgDetails;
# separate into image groups on mode then filter
my @ImgGroups = ();
my $Img = shift (@SortedSkyImgDetails);
while (defined($Img)) {
my $oldmode = $$Img[$mode_p];
info("Mode: $oldmode");
my @mode_tmp=();
while ( (defined($Img)) && ($$Img[$mode_p] eq $oldmode) ) {
my $oldfilter = $$Img[$filter_p];
info("Filter: $oldfilter");
my (@expids,@alt_expids);
my ($duration,$alt_duration) = (0,0);
my @filter_tmp = ();
while ( (defined($Img)) && ($$Img[$filter_p] eq $oldfilter) && ($$Img[$mode_p] eq $oldmode) ) {
info("Image File : $$Img[$filename_p]");
# Add up a total for the duration here. We only want duration from one window per exposure.
# For RUDI5 we use the central image. We set the side images durations -ve in OMImageAnalyse
# to allow us to test for it here.
# For USER mopde we use the first duration from an exposure
if ( ($$Img[$duration_p] > 0) && !(grep /$$Img[$exp_id_p]/,@expids) ) {
push(@expids,$$Img[$exp_id_p]);
$duration += $$Img[$duration_p];
}
else {
# This bit is in case we are missing the central image from a Rudi-5 set.
# If that is the case we need to get the exposure time from one of the side images.
#
# The problem is image is defined across 4 exposures. The rudi5group
# returns a new exposure id of th form Rn where n the the Rudi5 group.
# The rudi5 groups are:
# Group Exposure id's
# 1 S401 - S404
# 2 S405 - S409
# etc
my $r5g = &rudi5group($$Img[$exp_id_p]);
if ( $r5g && !(grep /$r5g/,@expids) ) {
$alt_duration += abs($$Img[$duration_p]);
push(@expids,$r5g);
}
}
# Building up lists of files for each filter within each mode
push(@filter_tmp,$$Img[$filename_p]);
$Img = shift (@SortedSkyImgDetails);
}
# use $alt_duration if we have duration = 0 and a rudi5 set.
$duration = $alt_duration if (!$duration && $alt_duration);
push (@mode_tmp,[$oldfilter,$duration,\@filter_tmp]) unless ( (scalar @filter_tmp < 1) && ($oldmode ne $RUDI5_LR) );
}
push(@ImgGroups,[$oldmode,\@mode_tmp]);
}
my $listmosaicsourcelists = newFile(
class=> "intermediate"
, instrument => thisInstrument
, format => "ASCII"
, content => "MOSAIC SOURCE LISTS"
);
&per_mode($MosaicModes,\@ImgGroups, $listmosaicsourcelists);
return success();
}
sub per_mode {
my ($MosaicModes,$Groups, $listmosaicsourcelists) = @_;
my ($USER,$RUDI5_LR,$ENG2,$PFENG2,$FFENG2,$ENG4) = @$MosaicModes;
my %Content_Strings = (
$USER => "OM USER WINDOWS SKY IMAGE MOSAIC"
,$RUDI5_LR => "OM RUDI-5 SKY IMAGE MOSAIC"
,$ENG2 => "OM FULL-FRAME LORES SKY IMAGE MOSAIC"
,$PFENG2 => "OM PARTIAL-FRAME LORES SKY IMAGE MOSAIC"
,$FFENG2 => "OM FULL-FRAME LORES SKY IMAGE MOSAIC"
,$ENG4 => "OM FULL-FRAME HIRES SKY IMAGE MOSAIC"
);
info("Creating Images...");
foreach my $group (@$Groups) {
my ($mode,$filters) = @$group;
info("MODE: $mode");
if ($mode eq $USER) {
&per_filter($Content_Strings{"$USER"},$filters,$listmosaicsourcelists);
}
elsif ($mode eq $RUDI5_LR) {
&per_filter($Content_Strings{"$RUDI5_LR"},$filters,$listmosaicsourcelists);
}
elsif ($mode eq $ENG2 || $mode eq $PFENG2 || $mode eq $FFENG2) {
&per_filter($Content_Strings{"$ENG2"},$filters,$listmosaicsourcelists);
}
elsif ($mode eq $ENG4) {
&per_filter($Content_Strings{"$ENG4"},$filters,$listmosaicsourcelists);
}
else {
info("There are no rules for generating Mosaic products for this mode ($mode).");
info("No Mosaic products will be generated.");
}
info("Done: $mode");
}
}
sub per_filter {
my ($content,$Filters,$listmosaicsourcelists) = @_;
foreach my $f (@$Filters) {
my ($filter,$duration,$imgs) = @$f;
info("FILTER: $filter");
&mosaic($filter,$duration,$content,$imgs,$listmosaicsourcelists);
}
}
sub mosaic{
my ($filter,$duration,$content,$Imgs,$listmosaicsourcelists) = @_;
my $newImg=newFile(class=>'product'
,instrument => thisInstrument
,osw_id => "$filter"
,content => "$content"
);
# Determine total exposure time = duration.
info("CREATING: $newImg, Duration: $duration");
#These limits are imposed by the ommosaic task
# They may change in future
if ($duration >= 10) {
doCommand('ommosaic'
,imagesets => [@$Imgs]
,mosaicedset => $newImg
,mincorr => 0
# ,exposure => $duration ## Removed in SAS9
)
or return exception();
# Make source detection in stacked mosaic
#
my $content_srclist = $content ;
my $myold = "SKY IMAGE";
my $mynew = "SOURCE LIST";
$content_srclist =~ s/$myold/$mynew/g;
my $content_region = $content ;
$myold = "SKY IMAGE";
$mynew = "SOURCE REGION";
$content_region =~ s/$myold/$mynew/g;
# info ("This should print the filename for the mosaic source list: $content_srclist");
# my $sourceListMosaic=newFile(class=>'intermediate'
# ,instrument => thisInstrument
# ,osw_id => "$filter"
# ,content => $content_srclist
# );
my $sourceListMosaic=newFile(class=>'product'
,instrument => thisInstrument
,osw_id => "$filter"
,content => $content_srclist
);
# my $sourceListMosaicRegion=newFile(class=>'intermediate'
# ,instrument => thisInstrument
# ,osw_id => "$filter"
# ,content => $content_srclist
# ,format => "reg"
# );
my $sourceListMosaicRegion=newFile(class=>'product'
,instrument => thisInstrument
,osw_id => "$filter"
,content => "$content_region"
,format=>"ASCII"
);
# my @t = ();
# push (@t,$sourceListMosaic . "\n");
#
# info("DEBUG: writing $sourceListMosaic in file $listmosaicsourcelists");
# writeASCIIFile(
# name => $listmosaicsourcelists
# , text => \@t
# , append => 1
# );
#---
# Skip omdetect,ommag and omqualitymap if the EPIC image is larger than ($sizeLimit degree) = 1 deg
my $skip_omdetect = 1;
my $sizeLimit = 1; # (degree)
my @mergedImages = findFile(
class => 'intermediate'
, content => 'merged image'
);
if ( fileExists( file => $mergedImages[0] ) ){
if ( ! ModuleUtil::isSmallMap( $mergedImages[0], $sizeLimit ) ) {
info("EPIC image $mergedImages[0] is larger than $sizeLimit. No omdetect,ommag and omqualitymap");
$skip_omdetect = 0;
}
}
if ( $skip_omdetect ) {
doCommand('omdetect'
, set => $newImg
, nsigma => 2.0
, minsignificance => 3
, detectextended => 'Y'
, psfphotometryenabled => 'N'
, neighboursforpsfphotometry => 1
, backgroundmethod => 1
, regionfile => $sourceListMosaicRegion
, outset => $sourceListMosaic
)
or return exception();
# Write $sourceListMosaic in ascii file $listmosaicsourcelists only if run 'omdetect'
my @t = ();
push (@t,$sourceListMosaic . "\n");
info("DEBUG: writing $sourceListMosaic in file $listmosaicsourcelists");
writeASCIIFile(
name => $listmosaicsourcelists
, text => \@t
, append => 1
);
doCommand('ommag'
, set => $sourceListMosaic
)
or return exception();
doCommand('omqualitymap'
, srclistset => $sourceListMosaic
, set => $newImg
, outset => $sourceListMosaic
, mode => 'usequalityimage'
)
or return exception();
}
#---
# CREATE PNG FROM MOSAICS
my $intcnt;
($intcnt=$content)=~s/\s+//g;
my $gifFile=newFile(class => 'intermediate'
,instrument => thisInstrument
,osw_id => "$filter"
,content => "$intcnt"
,format => 'GIF'
);
my $pngFile=newFile(class => 'product'
,instrument => thisInstrument
,osw_id => "$filter"
,content => "$content"
,format => 'PNG'
);
doCommand('implot'
,set => $newImg
,device => "$pngFile/PNG"
,withsrclisttab => 'no'
) or return exception();
# # Convert to PNG format
# GIFtoPNG(source => $gifFile
# ,destination => $pngFile
# ) or return exception();
}
else {
info("The exposure time ($duration s) is outside of bounds set by the ommosaic task.");
info("No Image Mosaic will be generated.");
}
}
sub rudi5group {
my ($expid) = @_;
my $group = 0;
return 0 unless ($expid =~ /^S4/);
$expid =~ s/\D//g;
while ($expid > 0) {
$expid -= 4;
$group++;
}
return "R${group}";
}
1;