Index: trunk/ppImage/src/ppImageDetrendPattern.c
===================================================================
--- trunk/ppImage/src/ppImageDetrendPattern.c	(revision 26694)
+++ trunk/ppImage/src/ppImageDetrendPattern.c	(revision 26895)
@@ -5,9 +5,11 @@
 #include "ppImage.h"
 
-#define ESCAPE(MESSAGE) {                               \
-        psError(PS_ERR_UNKNOWN, false, MESSAGE);        \
+#define ESCAPE(STATUS,...) {                    \
+        psError(PS_ERR_UNKNOWN, STATUS, __VA_ARGS__);   \
         psFree(view);                                   \
         return false;                                   \
     }
+
+static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipename, const char *recipevalue);
 
 bool ppImageDetrendPatternApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView,
@@ -16,65 +18,137 @@
     pmCell *cell = NULL;
 
-    assert (options->doPattern); // do not call if not needed
-    assert (inputView->chip != -1);
-    assert (inputView->cell == -1);
-    assert (inputView->readout == -1);
-    bool status;
-    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
+    assert(options->doPatternRow || options->doPatternCell); // do not call if not needed
+    assert(inputView->chip != -1);
+    assert(inputView->cell == -1);
+    assert(inputView->readout == -1);
 
-    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
-    *view = *inputView;
+    if (options->doPatternRow) {
+        bool status;
+        pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
+        pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+        *view = *inputView;
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+              ESCAPE(false, "load failure for Cell");
+            }
 
-    while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
-        if (!cell->process || !cell->file_exists) {
-            continue;
-        }
-        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-            ESCAPE("load failure for Cell");
-        }
-
-        if (!cell->data_exists) {
-            continue;
-        }
-
-        if (cell->readouts->n > 1) {
-            psWarning ("Skipping Video Cell for ppImageDetrendPatternApply");
-            continue;
-        }
-
-        psMetadataItem *doPattern = pmConfigRecipeValueByView(config, RECIPE_NAME, "PATTERN.SUBSET",
-                                                              chip->parent, view); // Do we do pattern sub?
-        if (!doPattern || doPattern->type != PS_DATA_BOOL) {
-            ESCAPE("Unable to determine whether pattern matching should be applied.");
-        }
-        if (!doPattern->data.B) {
-            continue;
-        }
-
-        psLogMsg("ppImage", PS_LOG_INFO, "Performing pattern subtraction for %d,%d\n", view->chip, view->cell);
-
-        // process each of the readouts
-        pmReadout *readout;         // Readout from cell
-        while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
-            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
-                ESCAPE("load failure for Readout");
-            }
-            if (!readout->data_exists) {
+            if (!cell->data_exists) {
                 continue;
             }
 
-            // perfore pattern correction
-            if (!pmPatternRow(readout, options->patternOrder, options->patternIter, options->patternRej,
-                              options->patternThresh, options->patternMean, options->patternStdev,
-                              options->maskValue, options->darkMask)) {
-                psFree(view);
-                return(false);
+            if (cell->readouts->n > 1) {
+                psWarning ("Skipping Video Cell for ppImageDetrendPatternApply");
+                continue;
+            }
+
+            bool doPattern = false;
+            if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.ROW.SUBSET")) {
+                ESCAPE(false, "Unable to determine whether row pattern matching should be applied.");
+            }
+            if (!doPattern) continue;
+
+            const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+            const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
+            psLogMsg("ppImage", PS_LOG_INFO, "Performing row pattern correction for %s, %s\n",
+                     chipName, cellName);
+
+            // process each of the readouts
+            pmReadout *readout;         // Readout from cell
+            while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    ESCAPE(false, "load failure for Readout");
+                }
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+                // Perform pattern correction
+                if (!pmPatternRow(readout, options->patternRowOrder, options->patternRowIter,
+                                  options->patternRowRej, options->patternRowThresh, options->patternRowMean,
+                                  options->patternRowStdev, options->maskValue, options->darkMask)) {
+                    psFree(view);
+                    return(false);
+                }
             }
         }
+        psFree(view);
     }
 
-    psFree(view);
+    if (options->doPatternCell) {
+        int numCells = chip->cells->n;       // Number of cells
+        psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell?
+        pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+        *view = *inputView;
+        for (int i = 0; i < chip->cells->n; i++) {
+            view->cell = i;
+
+            bool doPattern = false;
+            if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CELL.SUBSET")) {
+                ESCAPE(false, "Unable to determine whether row pattern matching should be applied.");
+            }
+            if (doPattern) {
+                tweak->data.U8[i] = 0xFF;
+            }
+        }
+
+        // Tweak the cells
+        if (!pmPatternCell(chip, tweak, options->patternCellBG, options->patternCellMean,
+                           options->maskValue, options->darkMask)) {
+            psFree(tweak);
+            psFree(view);
+            return false;
+        }
+        psFree(tweak);
+        psFree(view);
+    }
+
     return(true);
 }
 
+static bool doPatternForView (bool *doit, const pmConfig *config, const pmChip *chip, const pmFPAview *view, const char *recipeName, const char *recipeValue) {
 
+    *doit = false;
+
+    psMetadataItem *doPattern = pmConfigRecipeValueByView(config, recipeName, recipeValue, chip->parent, view);
+    if (!doPattern) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine whether row pattern matching should be applied.");
+        return false;
+    }
+    if (doPattern->type == PS_DATA_BOOL) {
+        *doit = doPattern->data.B;
+        return true;
+    }
+    if (doPattern->type == PS_DATA_STRING) {
+        // expect a string of the form "000110001001" with at least view->cell entries
+        char *string = doPattern->data.str;
+        if (strlen(string) < view->cell) {
+            psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string (too few elements %d)", (int) strlen(string));
+            return false;
+        }
+        switch (string[view->cell]) {
+          case '0':
+          case 'f':
+          case 'F':
+          case 'n':
+          case 'N':
+            *doit = false;
+            return true;
+          case '1':
+          case 't':
+          case 'T':
+          case 'y':
+          case 'Y':
+            *doit = true;
+            return true;
+          default:
+            psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET chip string %s (unknown value %c))", string, string[view->cell]);
+            return false;
+        }
+        psAbort("imposible to reach here");
+    }
+    psError(PS_ERR_UNKNOWN, true, "error in PATTERN.ROW.SUBSET : invalid data type");
+    return false;
+}
