IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Oct 25, 2010, 2:45:41 PM (16 years ago)
Author:
eugene
Message:

merge changes from eam_branches/ipp-20100823

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/psModules/src/imcombine/pmStack.c

    r27427 r29543  
    1919
    2020#include <stdio.h>
     21#include <string.h> // for memset
    2122#include <pslib.h>
    2223
     
    3435
    3536
    36 //#define TESTING                         // Enable test output
    37 //#define TEST_X 843-1                     // x coordinate to examine
    38 //#define TEST_Y 813-1                     // y coordinate to examine
    39 //#define TEST_RADIUS 0                    // Radius to examine
    40 
     37# if (0)
     38#define TESTING                         // Enable test output
     39#define TEST_X 4963                       // x coordinate to examine
     40#define TEST_Y 5353                       // y coordinate to examine
     41#define TEST_X 40                       // x coordinate to examine
     42#define TEST_Y 40                       // y coordinate to examine
     43#define TEST_RADIUS 0.5                 // Radius to examine
     44# endif
     45
     46# ifdef TESTING
     47# define CHECKPIX(XPIX,YPIX,MSG,...) { if (PS_SQR(XPIX - TEST_X) + PS_SQR(YPIX - TEST_Y) <= PS_SQR(TEST_RADIUS)) { fprintf(stderr,MSG,__VA_ARGS__); } }
     48# else
     49# define CHECKPIX(XPIX,YPIX,MSG,...) { }
     50# endif   
    4151
    4252// Data structure for use as a buffer when combining pixels
     
    250260                                      )
    251261{
    252 #ifdef TESTING
    253     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    254         fprintf(stderr, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
    255     }
    256 #endif
     262    CHECKPIX(x, y, "Marking image %d, pixel %d,%d for inspection\n", source, x, y);
    257263    pmStackData *data = inputs->data[source]; // Stack data of interest
    258264    if (!data) {
     
    272278                                     )
    273279{
    274 #ifdef TESTING
    275     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    276         fprintf(stderr, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
    277     }
    278 #endif
     280    CHECKPIX(x, y, "Marking pixel image %d, pixel %d,%d for rejection\n", source, x, y);
    279281    pmStackData *data = inputs->data[source]; // Stack data of interest
    280282    if (!data) {
     
    290292static void combineExtract(int *num,                        // Number of good pixels
    291293                           bool *suspect,                   // Any suspect pixels?
     294                           psImageMaskType *badMask,        // OR of all bad (masked) pixels
     295                           psImageMaskType *goodMask,       // OR of all good (unmasked) pixels
    292296                           combineBuffer *buffer, // Buffer with vectors
    293297                           psImage *image, // Combined image, for output
     
    300304                           const psVector *reject, // Indices of pixels to reject, or NULL
    301305                           int x, int y, // Coordinates of interest; frame of output image
    302                            psImageMaskType maskVal, // Value to mask
    303                            psImageMaskType maskSuspect // Value to suspect
     306                           psImageMaskType badMaskBits, // Value to mask as 'bad'
     307                           psImageMaskType suspectMaskBits // Value to mask as 'suspect'
    304308                           )
    305309{
     
    322326    }
    323327
     328    // mask values to store possible mask combinations
     329    *badMask = 0;
     330    *goodMask = 0xffff;
     331
     332    int nGoodBits[16]; // accumulate the good pixel bits here for fuzzy logic
     333    psAssert (sizeof(psImageMaskType) == 2, "psImageMaskType is not the expected size");
     334    memset (nGoodBits, 0, 16*sizeof(int));
     335
    324336    // Extract the pixel and mask data
    325337    int numGood = 0;                    // Number of good pixels
     
    328340        // should be because of how pixelMapGenerate works
    329341        if (reject && reject->data.U16[j] == i) {
     342            // pixels can be rejected because:
     343            // 1) only 1 input pixel (and 'safe' is true)
     344            // 2) only 2 input pixels were available and they were mutually inconsistent (or variance info was missing)
     345            // 3) NOTE : ifdef'ed out code for 3 inputs case
     346            // 4) outlier from sample of N pixels
     347            // 5) some of these may have been suspect.
     348            // XXX raise a specific mask bit for these (currently results in BLANK)
    330349            j++;
     350
     351            pmStackData *data = inputs->data[i]; // Stack data of interest
     352            if (data) {
     353                int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
     354                psImage *mask = data->readout->mask; // Mask of interest
     355                *badMask |= mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn]; // save the bad bits used
     356                CHECKPIX(x, y, "reject: adding bit to mask: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
     357            } else {
     358                CHECKPIX(x, y, "reject: no item in data (badMask = %x)\n", *badMask);
     359            }
    331360            continue;
    332361        }
     
    334363        pmStackData *data = inputs->data[i]; // Stack data of interest
    335364        if (!data) {
     365            CHECKPIX(x, y, "skip: no item in data (badMask = %x)\n", *badMask);
    336366            continue;
    337367        }
     
    339369        int xIn = x - data->readout->col0, yIn = y - data->readout->row0; // Coordinates on input readout
    340370        psImage *mask = data->readout->mask; // Mask of interest
    341         if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskVal) {
     371        if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits) {
     372            *badMask |= (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & badMaskBits); // save the bad bits used
     373            CHECKPIX(x, y, "skip: adding bit to mask: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
    342374            continue;
    343375        }
    344376
    345         pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & maskSuspect ?
    346             true : false;
     377        pixelSuspects->data.U8[numGood] = mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & suspectMaskBits ? true : false;
     378
     379        // *goodMask &= mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn]; // save the mask bits still used
     380        // check for set bits and increment counter as appropriate
     381        {
     382            psImageMaskType value = 0x0001;
     383            for (int nbit = 0; nbit < 16; nbit ++) {
     384                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn] & value) {
     385                    nGoodBits[nbit] ++;
     386                }
     387                value <<= 1;
     388            }
     389        }
    347390
    348391        psImage *image = data->readout->image; // Image of interest
     
    356399        pixelSources->data.U16[numGood] = i;
    357400        numGood++;
     401
     402        CHECKPIX(x, y, "keep: %d : %x (badMask = %x)\n", i, mask->data.PS_TYPE_IMAGE_MASK_DATA[yIn][xIn], *badMask);
    358403    }
    359404    pixelData->n = numGood;
     
    367412    *num = numGood;
    368413
     414    // set the mask bits if nGoodBits[i] > f*numGood
     415    {
     416        # define SUSPECT_FRACTION 0.65
     417        *goodMask = 0x0000;
     418        psImageMaskType value = 0x0001;
     419        for (int nbit = 0; nbit < 16; nbit ++) {
     420            if (nGoodBits[nbit] > SUSPECT_FRACTION*numGood) {
     421                *goodMask |= value;
     422            }
     423            value <<= 1;
     424        }
     425    }
     426
    369427#ifdef TESTING
    370428    if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    371429        for (int i = 0; i < numGood; i++) {
    372             fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d\n",
     430            fprintf(stderr, "Input %d, pixel %d,%d (%" PRIu16 "): %f %f (%f) %f %f %d %x %x -> %x %x\n",
    373431                    i, x, y, pixelSources->data.U16[i], pixelData->data.F32[i], pixelVariances->data.F32[i],
    374432                    addVariance->data.F32[i], pixelWeights->data.F32[i], pixelExps->data.F32[i],
    375                     pixelSuspects->data.U8[i]);
     433                    pixelSuspects->data.U8[i], badMaskBits, suspectMaskBits, *badMask, *goodMask);
    376434        }
    377435    }
     
    380438    return;
    381439}
    382 
    383440
    384441// Combine pixels
     
    392449                          combineBuffer *buffer, // Buffer with vectors
    393450                          int x, int y, // Coordinates of interest; frame of output image
    394                           psImageMaskType bad, // Value for bad pixels
     451                          psImageMaskType blankMask, // Value for empty pixels
     452                          psImageMaskType badMask, // Value for bad pixels
     453                          psImageMaskType goodMask, // Value for good pixels
    395454                          bool safe,           // Safe combination?
    396455                          float invTotalWeight    // Inverse of total weight for all inputs
     
    404463    // Default option is that the pixel is bad
    405464    float imageValue = NAN, varianceValue = NAN; // Value for combined image and variance map
    406     psImageMaskType maskValue = bad;    // Value for combined mask
    407465    float expValue = 0.0, expWeightValue = NAN; // Exposure value (straight, and weighted)
     466
     467    // default output mask value.  badMask is the OR of all unused input pixels. 
     468    // if there are no input pixels, this value will be 0, in which case we want to set the output pixel to BLANK.
     469    // if there are only good input pixels, and they do not result in a valid pixel, we still want to set this to BLANK.
     470    psImageMaskType maskValue = badMask ? badMask : blankMask;    // Value for combined mask
     471
     472    CHECKPIX(x, y, "bad vs good : %x %x %x\n", maskValue, badMask, blankMask);
    408473
    409474    switch (num) {
    410475      case 0: {
    411476          // Nothing to combine: it's bad
    412 #ifdef TESTING
    413           if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    414               fprintf(stderr, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
    415           }
    416 #endif
     477          CHECKPIX(x, y, "No inputs to combine, pixel %d,%d is bad.\n", x, y);
    417478          break;
    418479      }
     
    428489                  expWeightValue = pixelExps->data.F32[0];
    429490              }
    430               maskValue = 0;
    431 #ifdef TESTING
    432               if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    433                   fprintf(stderr, "Single input to combine, safety off, pixel %d,%d --> %f\n",
    434                           x, y, imageValue);
    435               }
    436 #endif
    437           }
    438 #ifdef TESTING
    439           else if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    440               fprintf(stderr, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
    441           }
    442 #endif
     491              maskValue = goodMask;
     492              CHECKPIX(x, y, "Single input to combine, safety off, pixel %d,%d --> %f\n", x, y, imageValue);
     493          } else {
     494              CHECKPIX(x, y, "Single input to combine, safety on, pixel %d,%d is bad.\n", x, y);
     495          }
    443496          break;
    444497      }
     
    446499          // Automatically accept the mean of the pixels only if we're not playing safe
    447500          if (!safe) {
    448               if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue,
    449                                           pixelData, pixelVariances, pixelExps, pixelWeights)) {
    450                   maskValue = 0;
    451 #ifdef TESTING
    452                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    453                       fprintf(stderr, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n",
    454                               x, y, imageValue, varianceValue);
    455                   }
    456 #endif
     501              if (combinationMeanVariance(&imageValue, &varianceValue, &expValue, &expWeightValue, pixelData, pixelVariances, pixelExps, pixelWeights)) {
     502                  maskValue = goodMask;
     503                  CHECKPIX(x, y, "Two inputs to combine using unsafe, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    457504              }
     505          } else {
     506              CHECKPIX(x, y, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    458507          }
    459 #ifdef TESTING
    460           else {
    461               if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    462                   fprintf(stderr, "Two inputs to combine, safety on, pixel %d,%d is bad\n", x, y);
    463               }
    464           }
    465 #endif
    466508          break;
    467509      }
     
    472514              break;
    473515          }
    474           maskValue = 0;
    475 #ifdef TESTING
    476           if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    477               fprintf(stderr, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    478           }
    479 #endif
     516          maskValue = goodMask;
     517          CHECKPIX(x, y, "Combined inputs, pixel %d,%d --> %f %f\n", x, y, imageValue, varianceValue);
    480518          break;
    481519      }
     
    522560    int numIter = PS_MAX(iter * num, 1); // Number of iterations
    523561
    524 #ifdef TESTING
    525     if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    526         fprintf(stderr, "Testing pixel %d,%d: %d %f %f %f %d %d\n",
    527                 x, y, numIter, rej, sys, olympic, useVariance, safe);
    528     }
    529 #endif
     562    CHECKPIX(x, y, "Testing pixel %d,%d: %d %f %f %f %d %d\n", x, y, numIter, rej, sys, olympic, useVariance, safe);
    530563
    531564    psVector *pixelData = buffer->pixels; // Values for the pixel of interest
     
    548581            // Systematic error contributes to the rejection level
    549582            float sysVar = PS_SQR(sys * pixelData->data.F32[i]);
    550 #ifdef TESTING
    551             // Correct variance for comparison against weighted mean including itself
    552             float compare = 1.0 - pixelWeights->data.F32[i] / sumWeights;
    553             if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    554                 fprintf(stderr, "Variance %d (%d), pixel %d,%d: %f %f %f\n", i, pixelSources->data.U16[i],
    555                         x, y, pixelVariances->data.F32[i], sysVar, compare);
    556             }
    557 #endif
     583            CHECKPIX(x, y, "Variance %d (%d), pixel %d,%d: %f %f %f\n",
     584                     i, pixelSources->data.U16[i], x, y,
     585                     pixelVariances->data.F32[i], sysVar, 1.0 - pixelWeights->data.F32[i] / sumWeights);
    558586            pixelLimits->data.F32[i] = rej2 * (pixelVariances->data.F32[i] + sysVar);
    559587        }
     
    593621                          combineMarkInspect(inputs, x, y, pixelSources->data.U16[1]);
    594622                      }
    595 #ifdef TESTING
    596                       if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    597                           fprintf(stderr, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)",
    598                                   x, y, diff, rej, sqrtf(sigma2));
    599                       }
    600 #endif
     623                      CHECKPIX(x, y, "Flagged both inputs for pixel %d,%d (%f > %f x %f\n)", x, y, diff, rej, sqrtf(sigma2));
    601624                  }
    602625              } else if (i == 0 && safe) {
     
    708731                  float median = combinationWeightedOlympic(pixelData, pixelWeights,
    709732                                                            olympic, buffer->sort); // Median for stack
    710 #ifdef TESTING
    711                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    712                       fprintf(stderr, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
    713                   }
    714 #endif
     733                  CHECKPIX(x, y, "Flag with variance pixel %d,%d: median = %f\n", x, y, median);
    715734                  float worst = -INFINITY; // Largest deviation
    716735                  for (int j = 0; j < num; j++) {
    717736                      float diff = pixelData->data.F32[j] - median; // Difference from expected
    718 #ifdef TESTING
    719                       if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    720                           fprintf(stderr, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
    721                       }
    722 #endif
     737                      CHECKPIX(x, y, "Testing input %d for pixel %d,%d: %f\n", j, x, y, diff);
    723738
    724739                      // Comparing squares --- cheaper than lots of sqrts
     
    737752                  combinationMedianStdev(&median, &stdev, pixelData, buffer->sort);
    738753                  float limit = rej * stdev; // Rejection limit
    739 #ifdef TESTING
    740                   if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    741                       fprintf(stderr,
    742                               "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n",
    743                               x, y, median, stdev, limit);
    744                   }
    745 #endif
     754                  CHECKPIX(x, y, "Flag without variance pixel %d,%d; median = %f, stdev = %f, limit = %f\n", x, y, median, stdev, limit);
    746755                  float worst = -INFINITY; // Largest deviation
    747756                  for (int j = 0; j < num; j++) {
     
    763772        if (maskIndex >= 0) {
    764773            if (suspect) {
    765 #ifdef TESTING
    766                 if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    767                     fprintf(stderr, "Throwing out all suspect pixels for %d,%d\n", x, y);
    768                 }
    769 #endif
     774                CHECKPIX(x, y, "Throwing out all suspect pixels for %d,%d\n", x, y);
    770775                // Throw out all suspect pixels
    771776                int numGood = 0;        // Number of good pixels
     
    796801            } else {
    797802                // Throw out masked pixel
    798 #ifdef TESTING
    799                 if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    800                     fprintf(stderr, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
    801                 }
    802 #endif
     803                CHECKPIX(x, y, "Throwing out input %d for pixel %d,%d\n", maskIndex, x, y);
    803804                combineMarkInspect(inputs, x, y, pixelSources->data.U16[maskIndex]);
    804805                int numGood = 0;        // Number of good pixels
     
    9991000
    10001001/// Stack input images
    1001 bool pmStackCombine(pmReadout *combined, pmReadout *expmaps, psArray *input,
    1002                     psImageMaskType maskVal, psImageMaskType maskSuspect,
    1003                     psImageMaskType bad, int kernelSize,
    1004                     float iter, float rej, float sys, float olympic,
    1005                     bool useVariance, bool safe, bool rejection)
     1002bool pmStackCombine(
     1003    pmReadout *combined,                // output stacked readout
     1004    pmReadout *expmaps,                 // output exposure map information
     1005    psArray *input,                     // input exposures
     1006    psImageMaskType badMaskBits,        // treat these bits as 'bad'
     1007    psImageMaskType suspectMaskBits,    // treat these bits as 'suspect'
     1008    psImageMaskType blankMaskBits,      // use this mask value for pixels missing input data (distinguish between Ninput = 0 and Ngood = 0?)
     1009    int kernelSize,
     1010    float iter,
     1011    float rej,
     1012    float sys,
     1013    float olympic,
     1014    bool useVariance,
     1015    bool safe,
     1016    bool rejection)
    10061017{
    10071018    bool haveVariances;                 // Do we have the variance maps?
     
    10121023    }
    10131024    PS_ASSERT_INT_NONNEGATIVE(kernelSize, false);
    1014     PS_ASSERT_INT_POSITIVE(bad, false);
     1025    PS_ASSERT_INT_POSITIVE(blankMaskBits, false);
    10151026    if (isnan(rej)) {
    10161027        PS_ASSERT_FLOAT_EQUAL(iter, 0, false);
     
    10701081    }
    10711082    psFree(stack);
    1072     pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, bad);
     1083    pmReadoutUpdateSize(combined, minInputCols, minInputRows, xSize, ySize, true, true, blankMaskBits);
    10731084    psTrace("psModules.imcombine", 1, "Have for combination [%d:%d,%d:%d] (%dx%d)\n",
    10741085            minInputCols, maxInputCols, minInputRows, maxInputRows, xSize, ySize);
     
    11311142    for (int y = minInputRows; y < maxInputRows; y++) {
    11321143        for (int x = minInputCols; x < maxInputCols; x++) {
     1144            CHECKPIX(x, y, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n", x, y, badMaskBits, blankMaskBits, iter, rej, sys, olympic, useVariance, safe, rejection);
     1145
    11331146#ifdef TESTING
    1134             if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
    1135                 fprintf(stderr, "Combining pixel %d,%d: %x %x %f %f %f %f %d %d %d\n",
    1136                         x, y, maskVal, bad, iter, rej, sys, olympic, useVariance, safe, rejection);
    1137             }
    1138 #endif
     1147            if (PS_SQR(x - TEST_X) + PS_SQR(y - TEST_Y) <= PS_SQR(TEST_RADIUS)) {
     1148                fprintf (stderr, "combine\n");
     1149            }
     1150# endif
     1151
    11391152            psVector *reject = NULL; // Images to reject for this pixel
    11401153            if (rejection) {
     
    11541167#endif
    11551168            }
    1156 
    1157             int num;                    // Number of good pixels
    1158             bool suspect;               // Suspect pixels in stack?
    1159             combineExtract(&num, &suspect, buffer, combinedImage, combinedMask, combinedVariance,
    1160                            input, weights, exps, addVariance, reject, x, y, maskVal, maskSuspect);
    1161             combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight,
    1162                           num, buffer, x, y, bad, safe, totalExpWeight);
     1169 
     1170            int num;                      // Number of good pixels
     1171            bool suspect;                 // Suspect pixels in stack?
     1172            psImageMaskType badMask = 0;  // OR of mask bits in all bad input pixels
     1173            psImageMaskType goodMask = 0; // OR of mask bits in all good input pixels
     1174            combineExtract(&num, &suspect, &badMask, &goodMask, buffer, combinedImage, combinedMask, combinedVariance, input, weights, exps, addVariance, reject, x, y, badMaskBits, suspectMaskBits);
     1175            combinePixels(combinedImage, combinedMask, combinedVariance, exp, expnum, expweight, num, buffer, x, y, blankMaskBits, badMask, goodMask, safe, totalExpWeight);
    11631176
    11641177            if (iter > 0) {
Note: See TracChangeset for help on using the changeset viewer.