Index: /trunk/ippScripts/scripts/magic_destreak.pl
===================================================================
--- /trunk/ippScripts/scripts/magic_destreak.pl	(revision 21084)
+++ /trunk/ippScripts/scripts/magic_destreak.pl	(revision 21085)
@@ -176,10 +176,13 @@
 my $image = $uri;
 
-my ($mask, $weight, $astrom);
+my ($mask, $ch_mask, $weight, $astrom);
 
 if ($stage eq "raw") {
     $astrom = $ipprc->filename("PSASTRO.OUTPUT", $cam_path_base);
 } elsif ($stage eq "chip") {
-    $mask   = $ipprc->filename("PPIMAGE.CHIP.MASK", $path_base, $class_id);
+    # we use the mask output from the camera stage for input and replace
+    # the output of the chip stage with that mask as well.
+    $mask   = $ipprc->filename("PSASTRO.OUTPUT.MASK", $cam_path_base, $class_id);
+    $ch_mask= $ipprc->filename("PPIMAGE.CHIP.MASK", $path_base, $class_id);
     $weight = $ipprc->filename("PPIMAGE.CHIP.WEIGHT", $path_base, $class_id);
     $astrom = $ipprc->filename("PSASTRO.OUTPUT", $cam_path_base);
@@ -198,4 +201,5 @@
     $command .= " -astrom $astrom" if defined $astrom;
     $command .= " -mask $mask" if defined $mask;
+    $command .= " -chip_mask $ch_mask" if defined $ch_mask;
     $command .= " -weight $weight" if defined $weight;
     $command .= " -skycelllist $skycell_list" if defined $skycell_list;
Index: /trunk/magic/remove/src/Makefile.simple
===================================================================
--- /trunk/magic/remove/src/Makefile.simple	(revision 21084)
+++ /trunk/magic/remove/src/Makefile.simple	(revision 21085)
@@ -16,18 +16,23 @@
     Line.o
 
-REPLACE_OBJECTS=    \
+REPLACE_OBJECTS=      \
     ${COMMON_OBJECTS} \
     streaksreplace.o
 
-COMPARE_OBJECTS=    \
+COMPARE_OBJECTS=      \
     ${COMMON_OBJECTS} \
     streakscompare.o
 
+RELEASE_OBJECTS=      \
+    ${COMMON_OBJECTS} \
+    streaksrelease.o
+
 STREAKSFLAGS=-DSTREAKS_COMPRESS_OUTPUT=1
 OPTFLAGS= -g
+#CFLAGS=`psmodules-config --cflags` -std=gnu99 ${OPTFLAGS} ${STREAKSFLAGS}
 CFLAGS=`psmodules-config --cflags` -std=gnu99 ${OPTFLAGS}
 LDFLAGS=`psmodules-config --libs`
 
-PROGRAMS= streaksremove streaksreplace streakscompare
+PROGRAMS= streaksremove streaksreplace streakscompare streaksrelease
 
 all:	${PROGRAMS}
@@ -39,8 +44,11 @@
 streakscompare:  ${COMPARE_OBJECTS}
 
+streaksrelease:  ${RELEASE_OBJECTS}
+
 install:	${PROGRAMS}
 	install -t  $(PSCONFDIR)/$(PSCONFIG)/bin streaksremove
 	install -t  $(PSCONFDIR)/$(PSCONFIG)/bin streaksreplace
 	install -t  $(PSCONFDIR)/$(PSCONFIG)/bin streakscompare
+	install -t  $(PSCONFDIR)/$(PSCONFIG)/bin streaksrelease
 
 clean:
Index: /trunk/magic/remove/src/streaksio.c
===================================================================
--- /trunk/magic/remove/src/streaksio.c	(revision 21084)
+++ /trunk/magic/remove/src/streaksio.c	(revision 21085)
@@ -5,4 +5,7 @@
 static nebServer *ourNebServer = NULL;
 
+// open the files required for streaks procesing.
+// if remove is true the calling program is streaksremove and the recovery files are outputs
+// if false the recovery files are inputs
 streakFiles *openFiles(pmConfig *config, bool remove)
 {
@@ -30,4 +33,6 @@
 
     if (remove) {
+        // XXX: the recovery file should be required if stage is raw and the replace flag is set
+        // that is if the input raw image is to be destroyed
         sf->recImage = sFileOpen(config, stage, "RECOVERY", inputBasename, false);
     } else {
@@ -45,4 +50,14 @@
         }
     }
+    // If we're in streaksremove and operating on a chip stage image
+    // if we are passed a mask it is camera stage mask and we also
+    // need to destreak the chip level mask file as well
+    if (remove && sf->inMask && (stage == IPP_STAGE_CHIP)) {
+        sf->inChMask = sFileOpen(config, stage, "INPUT.CHMASK", NULL, true);
+        inputBasename = basename(sf->inChMask->name);
+        sf->outChMask = sFileOpen(config, stage, "OUTPUT", inputBasename, true);
+        sf->recChMask = sFileOpen(config, stage, "RECOVERY", inputBasename, false);
+    }
+
     sf->inWeight = sFileOpen(config, stage, "INPUT.WEIGHT", NULL, false);
     if (sf->inWeight) {
@@ -421,6 +436,16 @@
     // XXX: This function assumes that it is only used for a single
     // chip single cell FPA (i.e. a skycell)
+    assert(sf->inImage->pmfile->fpa->chips->n == 1);
+
     pmFPAview *view = sf->view;
-    assert(view->chip == 0);
+    if (view) {
+        // The view was allocated and the chip set by setupAstrometry()
+        assert(view->chip == 0);
+    } else {
+        sf->view = pmFPAviewAlloc(0);
+        view = sf->view;
+        sf->chip = pmFPAviewNextChip(view, sf->inImage->pmfile->fpa, 1);
+        assert(sf->chip != NULL);
+    }
     view->cell = 0;
     sf->cell =  pmFPAviewThisCell(view, sf->inImage->pmfile->fpa);
@@ -747,4 +772,9 @@
         closeImage(sf->recMask);
     }
+    if (sf->inChMask) {
+        closeImage(sf->inChMask);
+        closeImage(sf->outChMask);
+        closeImage(sf->recChMask);
+    }
     if (sf->inWeight) {
         closeImage(sf->inWeight);
Index: /trunk/magic/remove/src/streaksrelease.c
===================================================================
--- /trunk/magic/remove/src/streaksrelease.c	(revision 21085)
+++ /trunk/magic/remove/src/streaksrelease.c	(revision 21085)
@@ -0,0 +1,481 @@
+#include "streaksremove.h"
+
+static pmConfig *parseArguments(int argc, char **argv);
+static bool readAndCopyToOutput(streakFiles *sf, bool exciseAll);
+static StreakPixels * getStreakPixels(streakFiles *sfiles, Streaks *streaks);
+static void exciseNonWarpedPixels(streakFiles *sfiles, double newMaskValue);
+static bool warpedPixel(streakFiles *sfiles, PixelPos *cellCoord);
+static void excisePixel(streakFiles *sfiles, unsigned int x, unsigned int y, bool streak, double newMaskValue);
+static void writeImages(streakFiles *sf, bool exciseImageCube);
+static bool replicateOutputs(streakFiles *sfiles);
+
+int
+main(int argc, char *argv[])
+{
+    bool status;
+
+    psLibInit(NULL);
+    psTimerStart("STREAKSREMOVE");
+
+    pmConfig *config = parseArguments(argc, argv);
+    if (!config) {
+        psError(PS_ERR_UNKNOWN, false, "failed to parse arguments\n");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    psMetadata *masks = psMetadataLookupMetadata(&status, config->recipes, "MASKS");
+    if (!status) {
+        psError(PM_ERR_CONFIG, false, "failed to lookup MASKS in recipes\n");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+    psU8 poorWarp = (double) psMetadataLookupU8(&status, masks, "POOR.WARP");
+    if (!status) {
+        psError(PM_ERR_CONFIG, false, "failed to lookup mask value for POOR.WARP in recipes\n");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+    // we're setting pixels with any mask bits execpt POOR.WARP to NAN
+    psU8 maskMask = ~poorWarp;
+
+    // Does true work here?
+    streakFiles *sfiles = openFiles(config, true);
+
+    if (sfiles->stage == IPP_STAGE_RAW) {
+        // copy PHU to output files
+        copyPHU(sfiles, true);
+
+        // advance to the first image extension
+        if (!streakFilesNextExtension(sfiles)) {
+            psErrorStackPrint(stderr, "failed to advance to first extension of input images");
+            exit (PS_EXIT_PROG_ERROR);
+        }
+    }
+
+    int totalPixels = 0;
+    int totalMaskedPixels = 0;
+    int totalNANdPixels = 0;
+    int totalNANdWeights = 0;
+
+    // Iterate through each component of the input (there is only one except for raw images)
+    do {
+        psTimerStart("PROCESS_COMPONENT");
+
+        // read the images and copy the data from the inputs to the outputs
+        // This also sets up the astrometry and initializes the recovery image(s)
+
+        if (!readAndCopyToOutput(sfiles, false)) {
+            // false return value indicates that this extension is not an image and
+            // the contents has already been copied to the output file.
+            // we don't need to process this extesion for streaks.
+            continue;
+        }
+
+        int maskedPixels = 0;
+        int nandPixels = 0;
+        int nandWeights = 0;
+
+        psImage *image = sfiles->outImage->image;
+        psImage *mask = sfiles->outMask->image;
+        psImage *weight= sfiles->outWeight->image;
+
+        for (int y=0; y < image->numRows; y++) {
+            for (int x=0; x < image->numCols; x++) {
+                // these gets are not necessary, we could just set the pixels to nan
+                // but I want to get the counts
+                double imageVal  = psImageGet(image, x, y);
+                double weightVal = psImageGet(weight, x, y);
+                psU8 maskVal   = psImageGet(mask, x, y);
+                if (maskVal & maskMask) {
+                    ++maskedPixels;
+                    if (!isnan(imageVal)) {
+                        ++nandPixels;
+                        psImageSet(image, x, y, NAN);
+                    }
+                    if (!isnan(weightVal)) {
+                        ++nandWeights;
+                        psImageSet(weight, x, y, NAN);
+                    }
+                }
+            }
+        }
+        totalPixels += sfiles->inImage->numRows * sfiles->inImage->numCols;
+        totalMaskedPixels += maskedPixels;
+        totalNANdPixels += nandPixels;
+        totalNANdWeights += nandWeights;
+
+        // write out the destreaked temporary images and the recovery images
+        writeImages(sfiles, false);
+
+        printf("time to process component %d: %f\n", sfiles->extnum, psTimerClear("PROCESS_COMPONENT"));
+    } while (streakFilesNextExtension(sfiles));
+
+    printf("pixels:        %10ld\n", totalPixels);
+    printf("masked pixels: %10ld %4.2f%%\n", totalMaskedPixels, 100. * totalMaskedPixels / totalPixels);
+    printf("nand pixels:   %10ld %4.2f%%\n", totalNANdPixels, 100. * totalNANdPixels / totalPixels);
+    printf("nand weights:  %10ld %4.2f%%\n", totalNANdWeights, 100. * totalNANdWeights / totalPixels);
+
+    psTimerStart("CLOSE_IMAGES");
+    // close all files
+    closeImages(sfiles);
+    printf("time to close images: %f\n", psTimerClear("CLOSE_IMAGES"));
+
+#ifdef NOTYET
+    if (!replicateOutputs(sfiles)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to replicate output files");
+        psErrorStackPrint(stderr, "");
+        exit(PS_EXIT_UNKNOWN_ERROR);
+    }
+
+    if (psMetadataLookupBool(&status, config->arguments, "REPLACE")) {
+        //     swap the instances for the input and output
+        //     Note this is a database operation. No file I/O is performed
+        if (!swapOutputsToInputs(sfiles)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to swap files");
+
+            // XXX: Now what? I guess swapOutputsToInputs will need to undo anything that
+            // it has done and give a detailed report of what happened
+
+            psErrorStackPrint(stderr, "");
+            exit(PS_EXIT_UNKNOWN_ERROR);
+        }
+
+        if (psMetadataLookupBool(&status, config->arguments, "REMOVE")) {
+            //      delete the temporary storage objects (which now points to the original image(s)
+            if (!deleteTemps(sfiles)) {
+                psError(PS_ERR_UNKNOWN, false, "failed to delete temporary files");
+                // XXX: Now what? At this point the output files have been swapped, so we can't
+                // repeat the operation.
+
+                // Returning error status here is problematic. The inputs have been streak removed
+                // but they're still lying around
+                // Maybe just print an error message and
+                // let other system tools clean up
+                psErrorStackPrint(stderr, "");
+                exit(PS_EXIT_UNKNOWN_ERROR);
+            }
+        }
+    }
+#endif  // REPLACE, REMOVE
+    printf("time to run streaksrelease: %f\n", psTimerClear("STREAKSREMOVE"));
+
+    // nebServerFree(ourNebServer);
+    psFree(config);
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+
+    //  PAU
+    fprintf(stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "streaksrelease");
+
+
+    return 0;
+}
+
+static void usage()
+{
+    psErrorStackPrint(stderr, "\n");
+    fprintf(stderr, "USAGE: streaksrelease -stage raw|chip|warp|diff -image IMAGE.fits -outroot directory\n\n");
+    fprintf(stderr, "Other arguments:\n");
+    fprintf(stderr, "\t-class_id class_id: class_id (required for raw and chip stages)\n");
+    fprintf(stderr, "\t-mask MASK.fits mask file to de-streak\n");
+    fprintf(stderr, "\t-weight WEIGHT.fits: weight file to de-streak\n");
+    fprintf(stderr, "\t-chip_mask MASK.fits: name of mask for chip stage (camera stage mask is passed tih -mask)\n");
+
+    exit(2);
+}
+
+static pmConfig *parseArguments(int argc, char **argv)
+{
+
+    pmConfig *config = pmConfigRead(&argc, argv, NULL);
+    if (!config) {
+        streaksExit("failed to parse arguments", PS_EXIT_CONFIG_ERROR);
+    }
+
+    int argnum;
+    ippStage stage = IPP_STAGE_NONE;
+    if ((argnum = psArgumentGet(argc, argv, "-stage"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        stage = parseStage(argv[argnum]);
+        psMetadataAddS32(config->arguments, PS_LIST_TAIL, "STAGE", 0,
+                "pipeline stage for streak removal", stage);
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "-stage is required\n");
+        usage();
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-class_id"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "CLASS_ID", 0,
+                "class_id", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    } else if ((stage == IPP_STAGE_RAW) || (stage == IPP_STAGE_CHIP)) {
+        psError(PS_ERR_UNKNOWN, true, "-class_id is required\n");
+        usage();
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-image"))) {
+        // for raw and chip level images we use psFits for I/O and ...
+        if (CHIP_LEVEL_INPUT(stage)) {
+            psArgumentRemove(argnum, &argc, argv);
+            psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT", 0,
+                    "name of input image", argv[argnum]);
+            psArgumentRemove(argnum, &argc, argv);
+        } else  {
+            // ... for warped images we use pmFPAfiles
+            if (!pmConfigFileSetsMD(config->arguments, &argc, argv, "INPUT", "-image", NULL)) { ;
+                psError(PS_ERR_UNKNOWN, false, "failed to process -image");
+                usage();
+            }
+        }
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "-image is required\n");
+        usage();
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-mask"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT.MASK", 0,
+                "name of input mask image", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-chip_mask"))) {
+        if (stage != IPP_STAGE_CHIP) {
+            psError(PS_ERR_UNKNOWN, true, "-chip_mask only allowed at chip stage");
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT.CHMASK", 0,
+                "name of input chip mask image", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-weight"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT.WEIGHT", 0,
+                "name of input weight image", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-outroot"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psString dir = pathToDirectory(argv[argnum]);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "directory for output files",
+            dir);
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "-outroot is required\n");
+        usage();
+    }
+
+    if (argc != 1) {
+        psString unexpectedArguments = NULL;
+        for (int i=1; i<argc; i++) {
+            psStringAppend(&unexpectedArguments, "%s ", argv[i]);
+        }
+        psError(PS_ERR_UNKNOWN, true, "unexpected arguments: %s", unexpectedArguments);
+        psFree(unexpectedArguments);
+        usage();
+    }
+
+    return config;
+}
+
+// set the image for the output files to the contents of the corresponding input file.
+static bool
+readAndCopyToOutput(streakFiles *sf, bool exciseAll)
+{
+    if (sf->inImage->pmfile) {
+        // image data from pmFPAfile (diff or warp file)
+        readImageFrom_pmFile(sf);
+    } else {
+        // image data directly from psFits
+        readImage(sf->inImage, sf->extnum, sf->stage);
+    }
+    sf->outImage->header = (psMetadata*) psMemIncrRefCounter(sf->inImage->header);
+
+    if (!SFILE_IS_IMAGE(sf->inImage)) {
+        // fprintf(stderr, "skipping extnum: %d not an image\n", sf->extnum);
+        // XXX: This extension is probably a video table.
+        copyTable(sf->outImage, sf->inImage, sf->extnum);
+        return false;
+    }
+
+    if (sf->inImage->image) {
+        setupImageRefs(sf->outImage, sf->recImage, sf->inImage, sf->extnum, exciseAll);
+    }  else {
+        // Image cubes are handeled specially
+    }
+
+    // set up the compression parameters
+#ifdef STREAKS_COMPRESS_OUTPUT
+    // compression of the image pixels is disabled for now. Some consortium members
+    // have problems reading them
+    copyFitsOptions(sf->outImage, sf->recImage, sf->inImage);
+
+    // XXX: TODO: can we derive these values from the input header?
+    // psFitsCompressionGet(sf->inImage->image) gives compression none
+    // perhaps we should just use the definition of COMP_IMG in the configuration 
+    psFitsSetCompression(sf->outImage->fits, PS_FITS_COMPRESS_RICE, sf->tiles, 8, 0, 0);
+    if (sf->recImage) {
+        psFitsSetCompression(sf->recImage->fits, PS_FITS_COMPRESS_RICE, sf->tiles, 8, 0, 0);
+    }
+#endif
+
+    if (sf->inMask) {
+        readImage(sf->inMask, sf->extnum, sf->stage);
+        sf->outMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
+        if (sf->recMask) {
+            sf->recMask->header = (psMetadata*) psMemIncrRefCounter(sf->inMask->header);
+        }
+        setupImageRefs(sf->outMask, sf->recMask, sf->inMask, sf->extnum, exciseAll);
+        if (sf->outChMask) {
+            sf->outChMask->header = (psMetadata *) psMemIncrRefCounter(sf->outMask->header);
+            sf->outChMask->image = (psImage *) psMemIncrRefCounter(sf->outMask->image);
+            if (sf->recChMask) {
+                sf->recChMask->header = (psMetadata *) psMemIncrRefCounter(sf->recMask->header);
+                sf->recChMask->image = (psImage *) psMemIncrRefCounter(sf->recMask->image);
+            }
+        }
+
+#ifdef STREAKS_COMPRESS_OUTPUT
+        // XXX: see note above
+        copyFitsOptions(sf->outMask, sf->recMask, sf->inMask);
+        psFitsSetCompression(sf->outMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+        if (sf->recMask) {
+            psFitsSetCompression(sf->recMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+        }
+        if (sf->outChMask) {
+            copyFitsOptions(sf->outChMask, sf->recChMask, sf->inMask);
+            psFitsSetCompression(sf->outChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            if (sf->recChMask) {
+                psFitsSetCompression(sf->recChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            }
+        }
+#endif
+    }
+
+    if (sf->inWeight) {
+        readImage(sf->inWeight, sf->extnum, sf->stage);
+        sf->outWeight->header = (psMetadata*) psMemIncrRefCounter(sf->inWeight->header);
+        if (sf->recWeight) {
+            sf->recWeight->header = (psMetadata*) psMemIncrRefCounter(sf->inWeight->header);
+        }
+        setupImageRefs(sf->outWeight, sf->recWeight, sf->inWeight, sf->extnum, exciseAll);
+
+#ifdef STREAKS_COMPRESS_OUTPUT
+        copyFitsOptions(sf->outWeight, sf->recWeight, sf->inWeight);
+        // XXX: see note above
+        psFitsSetCompression(sf->outWeight->fits, PS_FITS_COMPRESS_RICE, sf->tiles, 8, 0, 0);
+        if (sf->recWeight) {
+            psFitsSetCompression(sf->recWeight->fits, PS_FITS_COMPRESS_RICE, sf->tiles, 8, 0, 0);
+        }
+#endif
+    }
+    // and for raw images, create sub images that represent the actual image
+    // area (no overscan)
+
+    return true;
+}
+
+
+
+static void
+writeImages(streakFiles *sf, bool exciseImageCube)
+{
+    psString extname = NULL;
+    if (sf->nHDU > 1) {
+        bool mdok;
+        extname = psMetadataLookupStr(&mdok, sf->inImage->header, "EXTNAME");
+    }
+    if (sf->inImage->numZPlanes == 0)  {
+        // note exciseing complete images is handled in readAndCopyToOutput
+        writeImage(sf->outImage, extname, sf->extnum);
+        writeImage(sf->recImage, extname, sf->extnum);
+    } else {
+        // we have an image cube
+        double initValue;
+        if (exciseImageCube) {
+            // copy the entire input image to the recovery image
+            writeImageCube(sf->recImage, sf->inImage->imagecube, extname, sf->extnum);
+            initValue = NAN;
+        } else {
+            // otherwise write it to the output 
+            writeImageCube(sf->outImage, sf->inImage->imagecube, extname, sf->extnum);
+            initValue = 0;
+        }
+
+        // borrow one of the images from the imagecube and set it to init value
+        psImage *image = psArrayGet (sf->inImage->imagecube, 0);
+        psMemIncrRefCounter(image);
+        psImageInit(image, initValue);
+        if (exciseImageCube) {
+            sf->outImage->image = image;
+            writeImage(sf->outImage, extname, sf->extnum);
+        } else {
+            // write zero valued image to reccovery
+            if (sf->recImage) {
+                sf->recImage->image = image;
+                writeImage(sf->recImage, extname, sf->extnum);
+            }
+        }
+        // Assumption: there are no mask and weight images for video cells
+        return;
+    }
+    if (sf->outMask) {
+        writeImage(sf->outMask, extname, sf->extnum);
+        writeImage(sf->recMask, extname, sf->extnum);
+        if (sf->stage == IPP_STAGE_CHIP) {
+            writeImage(sf->outChMask, extname, sf->extnum);
+            writeImage(sf->recChMask, extname, sf->extnum);
+        }
+    }
+    if (sf->outWeight) {
+        writeImage(sf->outWeight, extname, sf->extnum);
+        writeImage(sf->recWeight, extname, sf->extnum);
+    }
+}
+
+// for any of the outputs that are stored in Nebulous set their extended attributes
+// and replicate the files
+
+static bool
+replicateOutputs(streakFiles *sfiles)
+{
+    bool status = false;
+
+    // XXX: TODO: need a nebGetXatrr function, but there isn't one
+    // another option would be to take the number of copies to be
+    // created as an option. That way the system could decide
+    // whether to replicate anything other than raw Image files
+    void *xattr = NULL;
+
+    if (!replicate(sfiles->outImage, xattr)) {
+        psError(PM_ERR_SYS, false, "failed to replicate outImage.");
+        return false;
+    }
+
+#ifdef notyet
+    // XXX: don't replicate mask and weight images until we can look up
+    // the input's xattr. There may be a perl program that can getXattr
+    if (sfiles->outMask) {
+        // get xattr from input to see if we need to replicate
+        if (!replicate(sfiles->outMask, xattr)) {
+            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
+            return false;
+        }
+    }
+    if (sfiles->outWeight) {
+        // get xattr from input to see if we need to replicate
+        if (!replicate(sfiles->outWeight, xattr)) {
+            psError(PM_ERR_SYS, false, "failed to replicate outImage.");
+            return false;
+        }
+    }
+#endif
+
+    return true;
+}
+
+
Index: /trunk/magic/remove/src/streaksremove.c
===================================================================
--- /trunk/magic/remove/src/streaksremove.c	(revision 21084)
+++ /trunk/magic/remove/src/streaksremove.c	(revision 21085)
@@ -85,5 +85,5 @@
     int totalStreakPixels = 0;
 
-    // Iterate through each component of the input (only one except for raw stage)
+    // Iterate through each component of the input (there is only one except for raw images)
     do {
         bool exciseImageCube = false;
@@ -223,5 +223,6 @@
     fprintf(stderr, "\t-remove: remove the original image after processing (requires -replace)\n");
     fprintf(stderr, "\t-keepnonwarped: do not exise pixels that were not part of difference processing\n");
-    fprintf(stderr, "\t-transparent val: instead of setting exicsed pixel to NAN add val\n");
+    fprintf(stderr, "\t-transparent val: instead of setting excicsed pixel to NAN add val\n");
+    fprintf(stderr, "\t-chip_mask MASK.fits: name of mask for chip stage (camera stage mask is passed tih -mask)\n");
 
     exit(2);
@@ -333,4 +334,5 @@
         psArgumentRemove(argnum, &argc, argv);
         bool nebulousMask = IN_NEBULOUS(argv[argnum]);
+        // This isn't a strict requirement if stage == IPP_STAGE_CHIP
         if (nebulousMask != nebulousImage) {
             psError(PS_ERR_UNKNOWN, true, "mask image must be %snebulous path with %s image path\n",
@@ -340,4 +342,21 @@
         psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT.MASK", 0,
                 "name of input mask image", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-chip_mask"))) {
+        if (stage != IPP_STAGE_CHIP) {
+            psError(PS_ERR_UNKNOWN, true, "-chip_mask only allowed at chip stage");
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        bool nebulousMask = IN_NEBULOUS(argv[argnum]);
+        if (nebulousMask != nebulousImage) {
+            psError(PS_ERR_UNKNOWN, true, "chip mask image must be %snebulous path with %s image path\n",
+                nebulousImage ? "" : "non ", nebulousImage ? "nebulous" : "regular");
+            usage();
+        }
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "INPUT.CHMASK", 0,
+                "name of input chip mask image", argv[argnum]);
         psArgumentRemove(argnum, &argc, argv);
     }
@@ -429,6 +448,7 @@
         streaksExit("", PS_EXIT_UNKNOWN_ERROR);
     }
+    // For the chip level files, copy the WCS from the astrometry file to the header
+    // XXX: do we want to do this for raw images as well?
     if (sf->stage == IPP_STAGE_CHIP) {
-        // For the chip level files, copy the WCS from the astrometry file to the header
         if (!sf->bilevelAstrometry) {
             updateAstrometry = true;
@@ -481,6 +501,13 @@
             pmAstromWriteWCS(sf->outMask->header, sf->inAstrom->fpa, sf->chip, 0.001);
         }
-
         setupImageRefs(sf->outMask, sf->recMask, sf->inMask, sf->extnum, exciseAll);
+        if (sf->outChMask) {
+            sf->outChMask->header = (psMetadata *) psMemIncrRefCounter(sf->outMask->header);
+            sf->outChMask->image = (psImage *) psMemIncrRefCounter(sf->outMask->image);
+            if (sf->recChMask) {
+                sf->recChMask->header = (psMetadata *) psMemIncrRefCounter(sf->recMask->header);
+                sf->recChMask->image = (psImage *) psMemIncrRefCounter(sf->recMask->image);
+            }
+        }
 
 #ifdef STREAKS_COMPRESS_OUTPUT
@@ -490,4 +517,11 @@
         if (sf->recMask) {
             psFitsSetCompression(sf->recMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+        }
+        if (sf->outChMask) {
+            copyFitsOptions(sf->outChMask, sf->recChMask, sf->inMask);
+            psFitsSetCompression(sf->outChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            if (sf->recChMask) {
+                psFitsSetCompression(sf->recChMask->fits, PS_FITS_COMPRESS_PLIO, sf->tiles, 8, 0, 0);
+            }
         }
 #endif
@@ -567,4 +601,8 @@
         writeImage(sf->outMask, extname, sf->extnum);
         writeImage(sf->recMask, extname, sf->extnum);
+        if (sf->stage == IPP_STAGE_CHIP) {
+            writeImage(sf->outChMask, extname, sf->extnum);
+            writeImage(sf->recChMask, extname, sf->extnum);
+        }
     }
     if (sf->outWeight) {
Index: /trunk/magic/remove/src/streaksremove.h
===================================================================
--- /trunk/magic/remove/src/streaksremove.h	(revision 21084)
+++ /trunk/magic/remove/src/streaksremove.h	(revision 21085)
@@ -54,4 +54,7 @@
     sFile *recMask;
     sFile *recWeight;
+    sFile *inChMask;    // for chip stage input mask is camera mask and output goes to both chip and cam masks
+    sFile *outChMask;
+    sFile *recChMask;
     psString class_id;
     pmFPAfile  *inAstrom;
