IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Aug 9, 2007, 10:25:52 AM (19 years ago)
Author:
Paul Price
Message:

Extensive changes to the way spatial variation of the kernel is implemented. I was doing it the stupid way, rather than the smart way outlined in Alard 2000 (A&ASS, 144, 363). Fixed up the normalisation of the kernels (essential for proper flux conservation with spatial variation). Put in support for dividing an image up into regions.

File:
1 edited

Legend:

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

    r14420 r14455  
    1919    psFree(kernels->uStop);
    2020    psFree(kernels->vStop);
    21     psFree(kernels->xOrder);
    22     psFree(kernels->yOrder);
    2321    psFree(kernels->preCalc);
    2422}
     
    5048
    5149    kernels->type = type;
     50    kernels->num = numBasisFunctions;
    5251    kernels->u = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    5352    kernels->v = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
     
    5554    kernels->uStop = NULL;
    5655    kernels->vStop = NULL;
    57     kernels->xOrder = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    58     kernels->yOrder = psVectorAlloc(numBasisFunctions, PS_TYPE_S32);
    5956    kernels->subIndex = 0;
    6057    kernels->preCalc = NULL;
     
    6259    kernels->inner = 0;
    6360    kernels->spatialOrder = spatialOrder;
     61    kernels->bgOrder = 0;
    6462
    6563    return kernels;
     
    7169    PS_ASSERT_INT_NONNEGATIVE(spatialOrder, NULL);
    7270
    73     int num = PS_SQR(2 * size + 1) * (spatialOrder + 1) * (spatialOrder + 2) / 2; // Number of basis functions
     71    int num = PS_SQR(2 * size + 1); // Number of basis functions
    7472
    7573    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_POIS,
     
    8179    // Generate a set of kernels for each (u,v)
    8280    for (int v = - size, index = 0; v <= size; v++) {
    83         for (int u = - size; u <= size; u++) {
    84             // Iterate over spatial order.  This loop creates the terms for
    85             // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    86             for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    87                 for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    88                     kernels->u->data.S32[index] = u;
    89                     kernels->v->data.S32[index] = v;
    90                     kernels->xOrder->data.S32[index] = xOrder;
    91                     kernels->yOrder->data.S32[index] = yOrder;
    92 
    93                     psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
    94                             u, v, xOrder, yOrder);
    95                 }
    96             }
    97         }
    98     }
    99 
    100     kernels->subIndex = (num - (spatialOrder + 1) * (spatialOrder + 2) / 2) / 2;
     81        for (int u = - size; u <= size; u++, index++) {
     82            kernels->u->data.S32[index] = u;
     83            kernels->v->data.S32[index] = v;
     84
     85            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
     86        }
     87    }
     88
     89    kernels->subIndex = num / 2;
    10190    assert(kernels->u->data.S32[kernels->subIndex] == 0 &&
    102            kernels->v->data.S32[kernels->subIndex] == 0 &&
    103            kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    104            kernels->yOrder->data.S32[kernels->subIndex] == 0);
     91           kernels->v->data.S32[kernels->subIndex] == 0);
    10592
    10693    return kernels;
     
    127114        num += (gaussOrder + 1) * (gaussOrder + 2) / 2;
    128115    }
    129     num *= (spatialOrder + 1) * (spatialOrder + 2) / 2;
    130116
    131117    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_ISIS,
     
    138124    kernels->widths = psVectorAlloc(num, PS_TYPE_F32);
    139125    kernels->preCalc = psArrayAlloc(num);
     126    psKernel *subtract = NULL;          // Kernel to subtract to maintain flux scaling
    140127
    141128    // Set the kernel parameters
    142129    for (int i = 0, index = 0; i < numGaussians; i++) {
     130        float norm = 1.0 / (M_2_PI * sqrtf(sigmas->data.F32[i])); // Normalisation for Gaussian
    143131        // Iterate over (u,v) order
    144132        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
    145             for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++) {
     133            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
    146134
    147135                // Set the pre-calculated kernel
     
    150138                for (int v = -size; v <= size; v++) {
    151139                    for (int u = -size; u <= size; u++) {
    152                         sum += preCalc->kernel[v][u] = power(u, uOrder) * power(v, vOrder) *
     140                        sum += preCalc->kernel[v][u] = norm * power(u, uOrder) * power(v, vOrder) *
    153141                            expf(-0.5 * (PS_SQR(u) + PS_SQR(v)) / PS_SQR(sigmas->data.F32[i]));
    154142                    }
    155143                }
    156                 // Normalise sum of kernel component to unity
    157                 psBinaryOp(preCalc->image, preCalc->image, "*", psScalarAlloc(1.0/sum, PS_TYPE_F32));
    158 
    159                 // Iterate over spatial order.  This loop creates the terms for
    160                 // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    161                 for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    162                     for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    163                         kernels->widths->data.F32[index] = sigmas->data.F32[i];
    164                         kernels->u->data.S32[index] = uOrder;
    165                         kernels->v->data.S32[index] = vOrder;
    166                         kernels->xOrder->data.S32[index] = xOrder;
    167                         kernels->yOrder->data.S32[index] = yOrder;
    168                         kernels->preCalc->data[index] = psMemIncrRefCounter(preCalc);
    169 
    170                         psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %d %d\n", index,
    171                                 sigmas->data.F32[i], uOrder, vOrder, xOrder, yOrder);
     144                if (index == 0) {
     145                    subtract = preCalc;
     146                    for (int v = -size; v <= size; v++) {
     147                        for (int u = -size; u <= size; u++) {
     148                            preCalc->kernel[v][u] /= sum;
     149                        }
     150                    }
     151                } else if (uOrder % 2 == 0 && vOrder % 2 == 0) {
     152                    // Normalise sum of kernel component to unity for even functions
     153                    for (int v = -size; v <= size; v++) {
     154                        for (int u = -size; u <= size; u++) {
     155                            preCalc->kernel[v][u] = preCalc->kernel[v][u] / sum - subtract->kernel[v][u];
     156                        }
    172157                    }
    173158                }
    174159
    175                 psFree(preCalc);        // Drop reference
     160                kernels->widths->data.F32[index] = sigmas->data.F32[i];
     161                kernels->u->data.S32[index] = uOrder;
     162                kernels->v->data.S32[index] = vOrder;
     163                kernels->preCalc->data[index] = preCalc;
     164
     165                psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d\n", index,
     166                        sigmas->data.F32[i], uOrder, vOrder);
    176167            }
    177168        }
    178169    }
    179170
    180     kernels->subIndex = 0;
    181     assert(kernels->u->data.S32[kernels->subIndex] == 0 &&
    182            kernels->v->data.S32[kernels->subIndex] == 0 &&
    183            kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    184            kernels->yOrder->data.S32[kernels->subIndex] == 0);
     171    kernels->subIndex = -1;
    185172
    186173    if (psTraceGetLevel("psModules.imcombine.kernel") >= 10) {
     
    220207    psTrace("psModules.imcombine", 3, "Inner: %d Outer: %d\n", numInner, numOuter);
    221208
    222     int num = PS_SQR(2 * numTotal + 1) *
    223         (spatialOrder + 1) * (spatialOrder + 2) / 2; // Number of basis functions
     209    int num = PS_SQR(2 * numTotal + 1); // Number of basis functions
    224210
    225211    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
     
    264250        int uStop = u + widths->data.S32[numTotal + i]; // Width of pixel
    265251
    266         for (int j = - numTotal; j <= numTotal; j++) {
     252        for (int j = - numTotal; j <= numTotal; j++, index++) {
    267253            int v = locations->data.S32[numTotal + j]; // Location of pixel
    268254            int vStop = v + widths->data.S32[numTotal + j]; // Width of pixel
    269255
    270             // Iterate over spatial order.  This loop creates the terms for
    271             // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    272             for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    273                 for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    274                     kernels->u->data.S32[index] = u;
    275                     kernels->v->data.S32[index] = v;
    276                     kernels->uStop->data.S32[index] = uStop;
    277                     kernels->vStop->data.S32[index] = vStop;
    278                     kernels->xOrder->data.S32[index] = xOrder;
    279                     kernels->yOrder->data.S32[index] = yOrder;
    280 
    281                     psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d %d %d\n", index,
    282                             u, uStop, v, vStop, xOrder, yOrder);
    283                 }
    284             }
    285         }
    286     }
    287 
    288     kernels->subIndex = (num - (spatialOrder + 1) * (spatialOrder + 2) / 2) / 2;
     256            kernels->u->data.S32[index] = u;
     257            kernels->v->data.S32[index] = v;
     258            kernels->uStop->data.S32[index] = uStop;
     259            kernels->vStop->data.S32[index] = vStop;
     260
     261            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
     262                    u, uStop, v, vStop);
     263        }
     264    }
     265
     266    kernels->subIndex = num / 2;
    289267    assert(kernels->u->data.S32[kernels->subIndex] == 0 &&
    290268           kernels->v->data.S32[kernels->subIndex] == 0 &&
    291269           kernels->uStop->data.S32[kernels->subIndex] == 0 &&
    292            kernels->vStop->data.S32[kernels->subIndex] == 0 &&
    293            kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    294            kernels->yOrder->data.S32[kernels->subIndex] == 0);
     270           kernels->vStop->data.S32[kernels->subIndex] == 0);
    295271
    296272    psFree(locations);
     
    327303    psTrace("psModules.imcombine", 3, "Inner: %d Outer: %d\n", numInner, numOuter);
    328304
    329     int num = PS_SQR(2 * numTotal + 1) *
    330         (spatialOrder + 1) * (spatialOrder + 2) / 2; // Number of basis functions
     305    int num = PS_SQR(2 * numTotal + 1); // Number of basis functions
    331306
    332307    psTrace("psModules.imcombine", 3, "Number of basis functions: %d\n", num);
     
    369344        int u = start->data.S32[numTotal + i]; // Location of pixel
    370345        int uStop = stop->data.S32[numTotal + i]; // Width of pixel
    371         for (int j = - numTotal; j <= numTotal; j++) {
     346        for (int j = - numTotal; j <= numTotal; j++, index++) {
    372347            int v = start->data.S32[numTotal + j]; // Location of pixel
    373348            int vStop = stop->data.S32[numTotal + j]; // Width of pixel
    374349
    375             // Iterate over spatial order.  This loop creates the terms for
    376             // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    377             for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    378                 for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    379                     kernels->u->data.S32[index] = u;
    380                     kernels->v->data.S32[index] = v;
    381                     kernels->uStop->data.S32[index] = uStop;
    382                     kernels->vStop->data.S32[index] = vStop;
    383                     kernels->xOrder->data.S32[index] = xOrder;
    384                     kernels->yOrder->data.S32[index] = yOrder;
    385 
    386                     psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d %d %d\n", index,
    387                             u, uStop, v, vStop, xOrder, yOrder);
    388                 }
    389             }
    390         }
    391     }
    392 
    393     kernels->subIndex = (num - (spatialOrder + 1) * (spatialOrder + 2) / 2) / 2;
     350            kernels->u->data.S32[index] = u;
     351            kernels->v->data.S32[index] = v;
     352            kernels->uStop->data.S32[index] = uStop;
     353            kernels->vStop->data.S32[index] = vStop;
     354
     355            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
     356                    u, uStop, v, vStop);
     357        }
     358    }
     359
     360    kernels->subIndex = num / 2;
    394361    assert(kernels->u->data.S32[kernels->subIndex] == 0 &&
    395362           kernels->v->data.S32[kernels->subIndex] == 0 &&
    396363           kernels->uStop->data.S32[kernels->subIndex] == 0 &&
    397            kernels->vStop->data.S32[kernels->subIndex] == 0 &&
    398            kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    399            kernels->yOrder->data.S32[kernels->subIndex] == 0);
     364           kernels->vStop->data.S32[kernels->subIndex] == 0);
    400365
    401366    psFree(start);
     
    429394
    430395    int numInner = PS_SQR(2 * inner + 1); // Number of inner kernel elements
    431     int numSpatial = (spatialOrder + 1) * (spatialOrder + 2) / 2; // Number of spatial variations of a kernel
    432 
    433     int num = (numGaussianVars + numInner) * numSpatial; // Total number of basis functions
     396
     397    int num = numGaussianVars + numInner; // Total number of basis functions
    434398
    435399    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_GUNK,
     
    440404    psFree(params);
    441405
    442     kernels->widths = psVectorAlloc(numGaussianVars * numSpatial, PS_TYPE_F32);
    443     kernels->preCalc = psArrayAlloc(numGaussianVars * numSpatial);
    444     kernels->inner = numGaussianVars * numSpatial;
     406    kernels->widths = psVectorAlloc(numGaussianVars, PS_TYPE_F32);
     407    kernels->preCalc = psArrayAlloc(numGaussianVars);
     408    kernels->inner = numGaussianVars;
     409    psKernel *subtract = NULL;          // Kernel to subtract to maintain flux scaling
    445410
    446411    // Set the Gaussian kernel parameters
    447412    for (int i = 0, index = 0; i < numGaussians; i++) {
     413        float norm = 1.0 / (M_2_PI * sqrtf(sigmas->data.F32[i])); // Normalisation for Gaussian
    448414        // Iterate over (u,v) order
    449415        for (int uOrder = 0; uOrder <= orders->data.S32[i]; uOrder++) {
    450             for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++) {
    451 
    452 
     416            for (int vOrder = 0; vOrder <= orders->data.S32[i] - uOrder; vOrder++, index++) {
    453417                // Set the pre-calculated kernel
    454418                psKernel *preCalc = psKernelAlloc(-size, size, -size, size);
     
    456420                for (int v = -size; v <= size; v++) {
    457421                    for (int u = -size; u <= size; u++) {
    458                         sum += preCalc->kernel[v][u] = power(u, uOrder) * power(v, vOrder) *
     422                        sum += preCalc->kernel[v][u] = norm * power(u, uOrder) * power(v, vOrder) *
    459423                            expf(-0.5 * (PS_SQR(u) + PS_SQR(v)) / PS_SQR(sigmas->data.F32[i]));
    460424                    }
    461425                }
    462                 // Normalise sum of kernel component to unity
    463                 psBinaryOp(preCalc->image, preCalc->image, "*", psScalarAlloc(1.0/sum, PS_TYPE_F32));
    464 
    465                 // Iterate over spatial order.  This loop creates the terms for
    466                 // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    467                 for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    468                     for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    469                         kernels->widths->data.F32[index] = sigmas->data.F32[i];
    470                         kernels->u->data.S32[index] = uOrder;
    471                         kernels->v->data.S32[index] = vOrder;
    472                         kernels->xOrder->data.S32[index] = xOrder;
    473                         kernels->yOrder->data.S32[index] = yOrder;
    474                         kernels->preCalc->data[index] = psMemIncrRefCounter(preCalc);
    475 
    476                         psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d %d %d\n", index,
    477                                 sigmas->data.F32[i], uOrder, vOrder, xOrder, yOrder);
     426                if (index == 0) {
     427                    subtract = preCalc;
     428                    for (int v = -size; v <= size; v++) {
     429                        for (int u = -size; u <= size; u++) {
     430                            preCalc->kernel[v][u] /= sum;
     431                        }
     432                    }
     433                } else if (uOrder % 2 == 0 && vOrder % 2 == 0) {
     434                    // Normalise sum of kernel component to unity for even functions
     435                    for (int v = -size; v <= size; v++) {
     436                        for (int u = -size; u <= size; u++) {
     437                            preCalc->kernel[v][u] = preCalc->kernel[v][u] / sum - subtract->kernel[v][u];
     438                        }
    478439                    }
    479440                }
    480441
    481                 psFree(preCalc);        // Drop reference
     442                kernels->widths->data.F32[index] = sigmas->data.F32[i];
     443                kernels->u->data.S32[index] = uOrder;
     444                kernels->v->data.S32[index] = vOrder;
     445                kernels->preCalc->data[index] = preCalc;
     446
     447                psTrace("psModules.imcombine", 7, "Kernel %d: %f %d %d\n", index,
     448                        sigmas->data.F32[i], uOrder, vOrder);
    482449            }
    483450        }
    484451    }
    485 
    486452
    487453    // Generate a grid set of kernels for each (u,v)
    488454    for (int v = - inner, index = kernels->inner; v <= inner; v++) {
    489         for (int u = - inner; u <= inner; u++) {
    490             // Iterate over spatial order.  This loop creates the terms for
    491             // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    492             for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    493                 for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    494                     kernels->u->data.S32[index] = u;
    495                     kernels->v->data.S32[index] = v;
    496                     kernels->xOrder->data.S32[index] = xOrder;
    497                     kernels->yOrder->data.S32[index] = yOrder;
    498 
    499                     psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d\n", index,
    500                             u, v, xOrder, yOrder);
    501                 }
    502             }
    503         }
    504     }
    505 
    506     kernels->subIndex = kernels->inner + (numInner - 1) * numSpatial / 2;
     455        for (int u = - inner; u <= inner; u++, index++) {
     456            kernels->u->data.S32[index] = u;
     457            kernels->v->data.S32[index] = v;
     458
     459            psTrace("psModules.imcombine", 7, "Kernel %d: %d %d\n", index, u, v);
     460        }
     461    }
     462
     463    kernels->subIndex = inner + num / 2;
    507464    assert(kernels->u->data.S32[kernels->subIndex] == 0 &&
    508            kernels->v->data.S32[kernels->subIndex] == 0 &&
    509            kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    510            kernels->yOrder->data.S32[kernels->subIndex] == 0);
     465           kernels->v->data.S32[kernels->subIndex] == 0);
    511466
    512467    return kernels;
     
    543498    int numRings = numOuter + numInner; // Number of rings (not including the central pixel)
    544499    int numPoly = (ringsOrder + 1) * (ringsOrder + 2) / 2; // Number of polynomial variants of each ring
    545     int numSpatial = (spatialOrder + 1) * (spatialOrder + 2) / 2; // Number of spatial variations of a kernel
    546 
    547     int num = (numRings * numPoly + 1) * numSpatial; // Total number of basis functions
     500
     501    int num = numRings * numPoly + 1; // Total number of basis functions
    548502
    549503    pmSubtractionKernels *kernels = pmSubtractionKernelsAlloc(num, PM_SUBTRACTION_KERNEL_RINGS,
     
    589543        // Iterate over (u,v) order
    590544        for (int uOrder = 0; uOrder <= (i == 0 ? 0 : ringsOrder); uOrder++) {
    591             for (int vOrder = 0; vOrder <= (i == 0 ? 0 : ringsOrder) - uOrder; vOrder++) {
     545            for (int vOrder = 0; vOrder <= (i == 0 ? 0 : ringsOrder) - uOrder; vOrder++, index++) {
    592546
    593547                psArray *data = psArrayAlloc(3); // Container for data
     
    604558                } else {
    605559                    int j = 0;          // Index for data
     560                    double norm = 0.0;  // Normalisation
    606561                    for (int v = -size; v <= size; v++) {
    607562                        int v2 = PS_SQR(v);   // Square of v
     
    616571                                uCoords->data.S32[j] = u;
    617572                                vCoords->data.S32[j] = v;
    618                                 poly->data.F32[j] = uPoly * vPoly;
     573                                norm += poly->data.F32[j] = uPoly * vPoly;
    619574
    620575                                psVectorExtend(uCoords, RINGS_BUFFER, 1);
     
    629584                        }
    630585                    }
     586                    // Normalise kernel component to unit sum
     587                    psBinaryOp(poly, poly, "*", psScalarAlloc(1.0 / norm, PS_TYPE_F32));
     588
    631589                }
    632590
    633591                psTrace("psModules.imcombine", 8, "%ld pixels in ring\n", uCoords->n);
    634592
    635                 // Iterate over spatial order.  This loop creates the terms for
    636                 // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
    637                 for (int xOrder = 0; xOrder <= spatialOrder; xOrder++) {
    638                     for (int yOrder = 0; yOrder <= spatialOrder - xOrder; yOrder++, index++) {
    639                         kernels->preCalc->data[index] = psMemIncrRefCounter(data);
    640                         kernels->u->data.S32[index] = uOrder;
    641                         kernels->v->data.S32[index] = vOrder;
    642                         kernels->xOrder->data.S32[index] = xOrder;
    643                         kernels->yOrder->data.S32[index] = yOrder;
    644 
    645                         psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d %d %d\n", index,
    646                                 i, uOrder, vOrder, xOrder, yOrder);
    647                     }
    648                 }
    649                 psFree(data);
     593                kernels->preCalc->data[index] = data;
     594                kernels->u->data.S32[index] = uOrder;
     595                kernels->v->data.S32[index] = vOrder;
     596
     597                psTrace("psModules.imcombine", 7, "Kernel %d: %d %d %d\n", index,
     598                        i, uOrder, vOrder);
    650599            }
    651600        }
     
    653602
    654603    kernels->subIndex = 0;
    655     assert(kernels->xOrder->data.S32[kernels->subIndex] == 0 &&
    656            kernels->yOrder->data.S32[kernels->subIndex] == 0);
    657604
    658605    return kernels;
Note: See TracChangeset for help on using the changeset viewer.