IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 28998


Ignore:
Timestamp:
Aug 20, 2010, 11:47:48 AM (16 years ago)
Author:
eugene
Message:

added pre-ffted kernel convolutions, changed minimization to accept maxTol and minTol

Location:
trunk/psLib
Files:
8 edited
3 copied

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/fft/psImageFFT.c

    r21183 r28998  
    532532    return out;
    533533}
     534
     535void psKernelFFTFree(psKernelFFT *kernel) {
     536
     537    if (!kernel->fft) return;
     538   
     539    bool threaded = psThreadPoolSize(); // Are we running threaded?
     540
     541    FFTW_LOCK;
     542    fftwf_free(kernel->fft);
     543    FFTW_UNLOCK;
     544
     545    return;
     546}
     547
     548psKernelFFT *psKernelFFTAlloc(const psKernel *input) {
     549
     550    psKernelFFT *kernel = psAlloc(sizeof(psKernelFFT));
     551    psMemSetDeallocator(kernel, (psFreeFunc)psKernelFFTFree);
     552
     553    kernel->fft = NULL;
     554
     555    kernel->xMin = input->xMin;
     556    kernel->xMax = input->xMax;
     557    kernel->yMin = input->yMin;
     558    kernel->yMax = input->yMax;
     559
     560    kernel->paddedCols = 0;
     561    kernel->paddedRows = 0;
     562
     563    return kernel;
     564}
     565
     566// generate the FFTed kernel image appropriate to be used for convolution of the given input image
     567psKernelFFT *psImageConvolveKernelInit(const psImage *in, const psKernel *kernel)
     568{
     569    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
     570    PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL);
     571    PS_ASSERT_KERNEL_NON_NULL(kernel, NULL);
     572
     573    bool threaded = psThreadPoolSize(); // Are we running threaded?
     574
     575    int numCols = in->numCols, numRows = in->numRows; // Size of image
     576    int xMin = kernel->xMin, xMax = kernel->xMax, yMin = kernel->yMin, yMax = kernel->yMax; // Kernel sizes
     577
     578    // Need to pad the kernel (and later the input image) to protect from wrap-around effects
     579    if (xMax - xMin > numCols || yMax - yMin > numRows) {
     580        // Cannot pad the image if the kernel is larger.
     581        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
     582                _("Kernel cannot extend further than input image size (%dx%d vs %dx%d)."),
     583                xMax, yMax, numCols, numRows);
     584        return NULL;
     585    }
     586
     587    int paddedCols = numCols + PS_MAX(-xMin, xMax); // Number of columns in padded image
     588    int paddedRows = numRows + PS_MAX(-yMin, yMax); // Number of rows in padded image
     589
     590#if CONVOLVE_FFT_BINARY_SIZE
     591    // Make the size an integer power of two
     592    {
     593        int twoCols, twoRows;           // Size that is a factor of two
     594        for (twoCols = 1; twoCols <= paddedCols && twoCols < INT_MAX - 1; twoCols <<= 1); // No action
     595        for (twoRows = 1; twoRows <= paddedRows && twoRows < INT_MAX - 1; twoRows <<= 1); // No action
     596        if (paddedCols > twoCols || paddedRows > twoRows) {
     597            psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Unable to scale size (%dx%d) up to factor of two",
     598                    paddedCols, paddedRows);
     599            return NULL;
     600        }
     601        paddedCols = twoCols;
     602        paddedRows = twoRows;
     603    }
     604#endif
     605
     606    int numPadded = paddedCols * paddedRows; // Number of pixels in padded image
     607
     608    // Create data array containing the padded kernel
     609    FFTW_LOCK;
     610    psF32 *data = fftwf_malloc(numPadded * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); // Data for FFTW
     611    FFTW_UNLOCK;
     612
     613    // Generate the padded kernel image.  We could generate the padded kernel image using
     614    // memcpy, but by going pixel by pixel we can apply the normalisation that corrects for the
     615    // FFT renormalisation.  By applying it to the kernel here, we save applying it to the
     616    // output image(s).
     617
     618    // copy kernel into data array
     619    psF32 *dataPtr = data;              // Pointer into FFTW data
     620    float norm = 1.0 / (float)(paddedRows * paddedCols); // Normalisation to correct for FFT
     621
     622    int xNegMin = PS_MIN(-1, xMin), xNegMax = PS_MIN(-1, xMax); // Min and max for x when negative
     623    int xPosMin = PS_MAX(0, xMin), xPosMax = PS_MAX(0, xMax); // Min and max for x when positive
     624    int yNegMin = PS_MIN(-1, yMin), yNegMax = PS_MIN(-1, yMax); // Min and max for x when negative
     625    int yPosMin = PS_MAX(0, yMin), yPosMax = PS_MAX(0, yMax); // Min and max for x when positive
     626    int blankCols = xNegMin + paddedCols - xPosMax - 1; // Number of columns between kernel extrema
     627    int blankRows = (yNegMin + paddedRows - yPosMax - 1) * paddedCols; // Rows between kernel extrema
     628    size_t blankColBytes = blankCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes in blankCols
     629    for (int y = yPosMin; y <= yPosMax; y++) {
     630        // y is positive
     631        for (int x = xPosMin; x <= xPosMax; x++, dataPtr++) {
     632            // x is positive
     633            *dataPtr = kernel->kernel[y][x] * norm;
     634        }
     635        // Columns between kernel extrema
     636        memset(dataPtr, 0, blankColBytes);
     637        dataPtr += blankCols;
     638        for (int x = xNegMin; x <= xNegMax; x++, dataPtr++) {
     639            // x is negative
     640            *dataPtr = kernel->kernel[y][x] * norm;
     641        }
     642    }
     643    // Rows between kernel extrema
     644    memset(dataPtr, 0, blankRows * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
     645    dataPtr += blankRows;
     646    for (int y = yNegMin; y <= yNegMax; y++) {
     647        // y is negative
     648        for (int x = xPosMin; x <= xPosMax; x++, dataPtr++) {
     649            // x is positive
     650            *dataPtr = kernel->kernel[y][x] * norm;
     651        }
     652        // Columns between kernel extrema
     653        memset(dataPtr, 0, blankColBytes);
     654        dataPtr += blankCols;
     655        for (int x = xNegMin; x <= xNegMax; x++, dataPtr++) {
     656            // x is negative
     657            *dataPtr = kernel->kernel[y][x] * norm;
     658        }
     659    }
     660
     661#if 0
     662    {
     663        // Use this for inspecting the result of copying the kernel
     664        psImage *test = psImageAlloc(paddedCols, paddedRows, PS_TYPE_F32);
     665        psFree(test->p_rawDataBuffer);
     666        test->p_rawDataBuffer = data;
     667        test->data.V[0] = test->p_rawDataBuffer;
     668        for (int y = 1; y < paddedRows; y++) {
     669            test->data.V[y] = (psPtr)((int8_t *)test->data.V[y - 1] + paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
     670        }
     671        // View image here or write to disk
     672        test->p_rawDataBuffer = NULL;
     673        psFree(test);
     674    }
     675#endif
     676
     677    // Do the forward FFT
     678    // Note that the FFT images have different size from the input
     679    FFTW_LOCK;
     680    fftwf_complex *fft = fftwf_malloc((paddedCols/2 + 1) * paddedRows * sizeof(fftwf_complex)); // FFT
     681    FFTW_UNLOCK;
     682
     683    FFTW_LOCK;
     684    fftwf_plan plan = fftwf_plan_dft_r2c_2d(paddedRows, paddedCols, data, fft, FFTW_PLAN_RIGOR);
     685    FFTW_UNLOCK;
     686
     687    fftwf_execute(plan);
     688
     689    FFTW_LOCK;
     690    fftwf_destroy_plan(plan);
     691    fftwf_free(data);
     692    FFTW_UNLOCK;
     693
     694    psKernelFFT *output = psKernelFFTAlloc(kernel);
     695    output->fft = fft;
     696    output->paddedCols = paddedCols;
     697    output->paddedRows = paddedRows;
     698
     699    return output;
     700}
     701
     702psImage *psImageConvolveKernel(psImage *out, const psImage *in, const psImage *mask, psImageMaskType maskVal, const psKernelFFT *kernel)
     703{
     704    PS_ASSERT_IMAGE_NON_NULL(in, NULL);
     705    PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL);
     706    PS_ASSERT_PTR_NON_NULL(kernel, NULL);
     707    PS_ASSERT_PTR_NON_NULL(kernel->fft, NULL);
     708    if (mask) {
     709        PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
     710        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_IMAGE_MASK, NULL);
     711        PS_ASSERT_IMAGES_SIZE_EQUAL(mask, in, NULL);
     712    }
     713
     714    bool threaded = psThreadPoolSize(); // Are we running threaded?
     715
     716    int numCols = in->numCols, numRows = in->numRows; // Size of image
     717    int xMin = kernel->xMin, xMax = kernel->xMax, yMin = kernel->yMin, yMax = kernel->yMax; // Kernel sizes
     718
     719    // Need to pad the input image to protect from wrap-around effects
     720    if (xMax - xMin > numCols || yMax - yMin > numRows) {
     721        // Cannot pad the image if the kernel is larger.
     722        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Kernel cannot extend further than input image size (%dx%d vs %dx%d)."), xMax, yMax, numCols, numRows);
     723        return NULL;
     724    }
     725
     726    int paddedCols = numCols + PS_MAX(-xMin, xMax); // Number of columns in padded image
     727    int paddedRows = numRows + PS_MAX(-yMin, yMax); // Number of rows in padded image
     728
     729#if CONVOLVE_FFT_BINARY_SIZE
     730    // Make the size an integer power of two
     731    {
     732        int twoCols, twoRows;           // Size that is a factor of two
     733        for (twoCols = 1; twoCols <= paddedCols && twoCols < INT_MAX - 1; twoCols <<= 1); // No action
     734        for (twoRows = 1; twoRows <= paddedRows && twoRows < INT_MAX - 1; twoRows <<= 1); // No action
     735        if (paddedCols > twoCols || paddedRows > twoRows) {
     736            psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Unable to scale size (%dx%d) up to factor of two",
     737                    paddedCols, paddedRows);
     738            return NULL;
     739        }
     740        paddedCols = twoCols;
     741        paddedRows = twoRows;
     742    }
     743#endif
     744
     745    if (paddedCols != kernel->paddedCols) {
     746        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Image is inconsistent with allocated kernel"));
     747        return NULL;
     748    }   
     749    if (paddedRows != kernel->paddedRows) {
     750        psError(PS_ERR_BAD_PARAMETER_SIZE, true, _("Image is inconsistent with allocated kernel"));
     751        return NULL;
     752    }   
     753
     754    int numPadded = paddedCols * paddedRows; // Number of pixels in padded image
     755
     756    // Create data array containing the padded image
     757    FFTW_LOCK;
     758    psF32 *data = fftwf_malloc(numPadded * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); // Data for FFTW
     759    FFTW_UNLOCK;
     760    psF32 *dataPtr = data;              // Pointer into FFTW data
     761    psF32 **imageData = in->data.F32;   // Pointer into image data
     762
     763    // Copy image into data array
     764    size_t goodBytes = numCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes per image row
     765    size_t padBytes = (paddedCols - numCols) * PSELEMTYPE_SIZEOF(PS_TYPE_F32); // Number of bytes to pad
     766    for (int y = 0; y < numRows; y++, dataPtr += paddedCols, imageData++) {
     767        memcpy(dataPtr, *imageData, goodBytes);
     768        memset(dataPtr + numCols, 0, padBytes);
     769    }
     770    memset(dataPtr, 0, (paddedRows - numRows) * paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
     771
     772#if 0
     773    {
     774        // Use this for inspecting the result of copying the image
     775        psImage *test = psImageAlloc(paddedCols, paddedRows, PS_TYPE_F32);
     776        psFree(test->p_rawDataBuffer);
     777        test->p_rawDataBuffer = data;
     778        test->data.V[0] = test->p_rawDataBuffer;
     779        for (int y = 1; y < paddedRows; y++) {
     780            test->data.V[y] = (psPtr)((int8_t *)test->data.V[y - 1] +
     781                                      paddedCols * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
     782        }
     783        // View image here
     784        test->p_rawDataBuffer = NULL;
     785        psFree(test);
     786    }
     787#endif
     788
     789    // Mask bad pixels (which may be NANs), lest they infect everything
     790    if (mask && maskVal) {
     791        for (int y = 0; y < numRows; y++) {
     792            for (int x = 0; x < numCols; x++) {
     793                if (mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal) {
     794                    data[x + paddedCols * y] = 0;
     795                }
     796            }
     797        }
     798    }
     799
     800    // Do the forward FFT
     801    // Note that the FFT images have different size from the input
     802    FFTW_LOCK;
     803    fftwf_complex *fft = fftwf_malloc((paddedCols/2 + 1) * paddedRows * sizeof(fftwf_complex)); // FFT
     804    FFTW_UNLOCK;
     805
     806    int fftCols = paddedCols/2 + 1, fftRows = paddedRows; // Size of FFT images
     807    int fftPixels = fftCols * fftRows;  // Number of pixels in FFT image
     808
     809    FFTW_LOCK;
     810    fftwf_plan forward = fftwf_plan_dft_r2c_2d(paddedRows, paddedCols, data, fft, FFTW_PLAN_RIGOR);
     811    FFTW_UNLOCK;
     812
     813    fftwf_execute(forward);
     814
     815    FFTW_LOCK;
     816    fftwf_destroy_plan(forward);
     817    FFTW_UNLOCK;
     818
     819    fftwf_complex *kernelFFT = kernel->fft;
     820
     821    // Multiply the two transforms
     822    for (int i = 0; i < fftPixels; i++) {
     823        // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i
     824#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
     825        // C99 complex support
     826        fft[i] *= kernelFFT[i];
     827#else
     828        // FFTW's backup complex support
     829        float imageReal = fft[i][0], imageImag = fft[i][1];
     830        float kernelReal = kernelFFT[i][0], kernelImag = kernelFFT[i][1];
     831        fft[i][0] = imageReal * kernelReal - imageImag * kernelImag;
     832        fft[i][1] = imageImag * kernelReal + imageReal * kernelImag;
     833#endif
     834    }
     835
     836    // Do the backward FFT
     837    FFTW_LOCK;
     838    fftwf_plan backward = fftwf_plan_dft_c2r_2d(paddedRows, paddedCols, fft, data, FFTW_PLAN_RIGOR);
     839    FFTW_UNLOCK;
     840
     841    fftwf_execute(backward);
     842
     843    FFTW_LOCK;
     844    fftwf_destroy_plan(backward);
     845    fftwf_free(fft);
     846    FFTW_UNLOCK;
     847
     848    // Copy into the target, without the padding
     849    out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32);
     850    psF32 **outData = out->data.F32;    // Pointer into output
     851    dataPtr = data;                     // Reset to start
     852    for (int y = 0; y < numRows; y++, outData++, dataPtr += paddedCols) {
     853        memcpy(*outData, dataPtr, goodBytes);
     854    }
     855
     856    FFTW_LOCK;
     857    fftwf_free(data);
     858    FFTW_UNLOCK;
     859
     860    return out;
     861}
  • trunk/psLib/src/fft/psImageFFT.h

    r21183 r28998  
    1515#include "psImage.h"
    1616#include "psImageConvolve.h"
     17
     18typedef struct {
     19    void *fft;
     20    int xMin;                          ///< Most negative x index
     21    int yMin;                          ///< Most negative y index
     22    int xMax;                          ///< Most positive x index
     23    int yMax;                          ///< Most positive y index
     24    int paddedCols;
     25    int paddedRows;
     26} psKernelFFT;
    1727
    1828/// @addtogroup MathOps Mathematical Operations
     
    7181    const psImage *mask,                ///< Corresponding mask
    7282    psImageMaskType maskVal,            ///< Value to mask
    73     const psKernel *kernel              ///< kernel to colvolve with
     83    const psKernel *kernel              ///< kernel to convolve with
     84    );
     85
     86/// Allocate an the psKernelFFT structure
     87psKernelFFT *psKernelFFTAlloc(
     88    const psKernel *input               ///< kernel to convolve with
    7489);
     90
     91/// Generate an FFT'ed kernel suitable for convolution with the given image
     92psKernelFFT *psImageConvolveKernelInit(
     93    const psImage *in,                  ///< representative image to convolve
     94    const psKernel *kernel              ///< kernel to convolve with
     95    );
     96
     97/// Convolve the given image with the pre-FFT'ed kernel
     98psImage *psImageConvolveKernel(
     99    psImage *out,                       ///< Output image, or NULL
     100    const psImage *in,                  ///< Image to convolve
     101    const psImage *mask,                ///< Corresponding mask
     102    psImageMaskType maskVal,            ///< Value to mask
     103    const psKernelFFT *kernel           ///< kernel to convolve with
     104    );
    75105
    76106/// @}
  • trunk/psLib/src/jpeg/psImageJpeg.c

    r27145 r28998  
    77#include <string.h>
    88
     9#include <kapa.h>
    910#include "psMemory.h"
    1011#include "psImage.h"
     
    134135    return psImageJpegColormapSet (map, "greyscale");
    135136}
     137
     138// XXX need to fix library references for this (psLib does not depend on libkapa)
     139# if (0)
     140// XXX Add colormap bar with scale (min -> max)
     141// XXX Add option to plot the source overlay (pass in bDrawBuffer populated with points?)
     142// XXX need to update bDraw APIs to pass in/out structure and avoid the local static
     143bool psImageJpegNew(const psImageJpegColormap *map, const psImage *image, const char *filename,
     144                 float min, float max)
     145{
     146    PS_ASSERT_PTR_NON_NULL(map, false);
     147    PS_ASSERT_IMAGE_NON_NULL(image, false);
     148    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
     149    PS_ASSERT_VECTOR_NON_NULL(map->red, false);
     150    PS_ASSERT_VECTOR_NON_NULL(map->green, false);
     151    PS_ASSERT_VECTOR_NON_NULL(map->blue, false);
     152    PS_ASSERT_PTR_NON_NULL(filename, false);
     153    PS_ASSERT_INT_POSITIVE(strlen(filename), false);
     154    PS_ASSERT_FLOAT_REAL(min, false);
     155    PS_ASSERT_FLOAT_REAL(max, false);
     156
     157    float zero, scale;
     158    struct jpeg_compress_struct cinfo;
     159    struct jpeg_error_mgr jerr;
     160
     161    long pixel;
     162    JSAMPLE *jpegLine;   // Points to data for current line
     163    JSAMPROW jpegLineList[1];  // pointer to JSAMPLE row[s]
     164    JSAMPLE *jpegImage;
     165    JSAMPLE *outPix;
     166
     167    /* JPEG init calls */
     168    cinfo.err = jpeg_std_error (&jerr);
     169    jpeg_create_compress (&cinfo);
     170
     171    /* open file, prep for jpeg */
     172    FILE *f = fopen(filename, "w");
     173    if (!f) {
     174        psError(PS_ERR_IO, true, "failed to open %s for output\n", filename);
     175        return false;
     176    }
     177    jpeg_stdio_dest(&cinfo, f);
     178
     179    /* set up color jpeg buffers */
     180    int quality = 75;
     181    cinfo.image_width = image->numCols; // image width and height, in pixels
     182    cinfo.image_height = image->numRows;
     183    cinfo.input_components = 3;
     184    cinfo.in_color_space = JCS_RGB;
     185    jpeg_set_defaults (&cinfo);
     186    jpeg_set_quality (&cinfo, quality, true); // limit to baseline-JPEG values
     187    jpeg_start_compress (&cinfo, true);
     188
     189    psU8 *Rpix = map->red->data.U8;
     190    psU8 *Gpix = map->green->data.U8;
     191    psU8 *Bpix = map->blue->data.U8;
     192
     193    if (max == min) {
     194        zero = min - 0.1;
     195        scale = 256.0/0.2;
     196    } else {
     197        zero = min;
     198        scale = 256.0/(max - min);
     199    }
     200
     201    int dx = image->numCols;
     202    int dy = image->numRows;
     203
     204    // output image buffer and line buffer
     205    jpegLine = psAlloc (3*dx*sizeof(JSAMPLE));
     206    jpegImage = psAlloc (3*dx*dy*sizeof(JSAMPLE));
     207
     208    // first copy the image data into the output buffer
     209    for (int j = 0; j < dy; j++) {
     210        psF32 *row = image->data.F32[j];
     211
     212        outPix = jpegLine;
     213        for (int i = 0; i < dx; i++, outPix += 3) {
     214            if (isfinite(row[i])) {
     215                pixel = PS_JPEG_SCALEVALUE(row[i],zero,scale);
     216                outPix[0] = Rpix[pixel];
     217                outPix[1] = Gpix[pixel];
     218                outPix[2] = Bpix[pixel];
     219            } else {
     220                // XXX NAN value should be set per-color map
     221                outPix[0] = 0x00;
     222                outPix[1] = 0xff;
     223                outPix[2] = 0x00;
     224            }
     225        }
     226        memcpy (&jpegImage[j*3*dx], jpegLine, 3*dx);
     227    }
     228
     229    bDrawBuffer *bdbuf = bDrawBufferCreate(dx, dy);
     230    bDrawSetBuffer(bdbuf);
     231    bDrawColor red = KapaColorByName("red");
     232    bDrawSetStyle (red, 1, 0);
     233    bDrawCircle(40.0, 20.0, 3.0);
     234
     235    {
     236        int Npalette;
     237        png_color *palette = KapaPNGPalette (&Npalette);
     238        bDrawColor white = KapaColorByName ("white");
     239        for (int j = 0; j < dy; j++) {
     240            for (int i = 0; i < dx; i++) {
     241                bDrawColor color = bdbuf[0].pixels[j][i];
     242                if (color == white) continue;
     243                jpegImage[j*3*dx + 3*i + 0] = palette[color].red;
     244                jpegImage[j*3*dx + 3*i + 1] = palette[color].green;
     245                jpegImage[j*3*dx + 3*i + 2] = palette[color].blue;
     246            }
     247        }
     248    }
     249    bDrawBufferFree (bdbuf);
     250
     251    // write out the image buffer
     252    for (int j = 0; j < image->numRows; j++) {
     253        jpegLineList[0] = &jpegImage[j*3*dx];
     254        if (jpeg_write_scanlines(&cinfo, jpegLineList, 1) == 0) {
     255            psError(PS_ERR_IO, true, "Unable to write line %d to JPEG", j);
     256            psFree(jpegLine);
     257            psFree(jpegImage);
     258            fclose(f);
     259            return false;
     260        }
     261    }
     262
     263    jpeg_finish_compress(&cinfo);
     264    if (fclose(f) == EOF) {
     265        psError(PS_ERR_IO, true, "Failed to close %s", filename);
     266        psFree(jpegLine);
     267        psFree(jpegImage);
     268        return false;
     269    }
     270    jpeg_destroy_compress(&cinfo);
     271
     272    psFree(jpegLine);
     273    psFree(jpegImage);
     274    return true;
     275}
     276# endif
    136277
    137278bool psImageJpeg(const psImageJpegColormap *map, const psImage *image, const char *filename,
  • trunk/psLib/src/math/psMinimizeLMM.c

    r26951 r28998  
    441441    }
    442442
     443    // number of degrees of freedom for this fit
     444    int nDOF = dy->n - params->n;
     445
    443446    // calculate initial alpha and beta, set chisq (min->value)
    444447    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
     
    456459    }
    457460
    458     // iterate until the tolerance is reached, or give up
    459     while ((min->iter < min->maxIter) && ((min->lastDelta > min->tol) || !isfinite(min->lastDelta))) {
     461    // iterate until: (a) nIter = min->iter or (b) (chisq / ndof) < maxChisq and deltaChisq < minTol (but don't stop unless Chisq is finite)
     462    bool done = (min->iter >= min->maxIter);
     463    while (!done) {
    460464        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
    461         psTrace("psLib.math", 5, "Last delta is %f.  Min->tol is %f.\n", min->lastDelta, min->tol);
     465        psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->lastDelta, min->minTol, min->maxTol);
    462466
    463467        // set a new guess for Alpha, Beta, Params
    464468        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
    465469            min->iter ++;
     470            if (min->iter >=  min->maxIter) break;
    466471            lambda *= 10.0;
    467472            continue;
     
    482487        if (isnan(Chisq)) {
    483488            min->iter ++;
     489            if (min->iter >= min->maxIter) break;
    484490            lambda *= 10.0;
    485491            continue;
     
    492498        psF32 rho = (min->value - Chisq) / dLinear;
    493499
    494         psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value,
    495                 Chisq, min->lastDelta, dLinear, rho, lambda);
    496 
    497         psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value,
    498                 Chisq, min->lastDelta, dLinear, rho, lambda);
     500        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     501
     502        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %f\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
    499503
    500504        // dump some useful info if trace is defined
     
    506510        /* rho is positive if the new chisq is smaller; allow for some insignificant change (slight negative rho) */
    507511        if (rho >= -1e-6) {
    508             min->lastDelta = (min->value - Chisq) / (dy->n - params->n);
     512            min->lastDelta = (min->value - Chisq) / nDOF;
    509513            min->value = Chisq;
    510514            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
     
    516520        }
    517521        min->iter++;
     522
     523        done = (min->iter >= min->maxIter);
     524       
     525        // check for convergence:
     526        float chisqDOF = Chisq / nDOF;
     527        if (!isfinite(min->maxChisqDOF) || ((chisqDOF < min->maxChisqDOF) && isfinite(min->lastDelta))) {
     528            done |= (min->lastDelta < min->minTol);
     529        }
    518530    }
    519531    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, Niter: %d\n", min->value, min->lastDelta, min->iter);
     
    549561        psFree(dy);
    550562    }
    551     if (min->iter == min->maxIter) {
    552         psTrace("psLib.math", 3, "---- end (false) ----\n");
    553         return(false);
    554     }
    555     psTrace("psLib.math", 3, "---- end (true) ----\n");
    556     return(true);
     563
     564    // if the last improvement was at least as good as maxTol, accept the fit:
     565    if (min->lastDelta <= min->maxTol) {
     566        psTrace("psLib.math", 6, "---- end (true) ----\n");
     567        return(true);
     568    }
     569    psTrace("psLib.math", 6, "---- end (false) ----\n");
     570    return(false);
    557571}
    558572
     
    588602}
    589603
    590 psMinimization *psMinimizationAlloc(int maxIter,
    591                                     float tol)
     604psMinimization *psMinimizationAlloc(int maxIter, float minTol, float maxTol)
    592605{
    593606    PS_ASSERT_INT_NONNEGATIVE(maxIter, NULL);
     
    595608    psMinimization *min = psAlloc(sizeof(psMinimization));
    596609    psMemSetDeallocator(min, (psFreeFunc)minimizationFree);
     610
    597611    P_PSMINIMIZATION_SET_MAXITER(min,maxIter);
    598     P_PSMINIMIZATION_SET_TOL(min,tol);
     612    P_PSMINIMIZATION_SET_MIN_TOL(min,minTol);
     613    P_PSMINIMIZATION_SET_MAX_TOL(min,maxTol);
     614
    599615    min->value = 0.0;
    600616    min->iter = 0;
    601617    min->lastDelta = NAN;
     618    min->maxChisqDOF = NAN;
    602619
    603620    return(min);
  • trunk/psLib/src/math/psMinimizeLMM.h

    r23486 r28998  
    3333#define PS_MAX_LMM_ITERATIONS 100
    3434#define PS_MAX_MINIMIZE_ITERATIONS 100
    35 #define P_PSMINIMIZATION_SET_MAXITER(m,val) *(int*)&m->maxIter = val
    36         #define P_PSMINIMIZATION_SET_TOL(m,val) *(float*)&m->tol = val
     35#define P_PSMINIMIZATION_SET_MAXITER(m,val) { *(int*)&m->maxIter  = val; }
     36#define P_PSMINIMIZATION_SET_MIN_TOL(m,val) { *(float*)&m->minTol = val; }
     37#define P_PSMINIMIZATION_SET_MAX_TOL(m,val) { *(float*)&m->maxTol = val; }
    3738
    3839                typedef enum {
     
    7576typedef struct
    7677{
    77     const int maxIter;                 ///< Convergence limit
    78     const float tol;                   ///< Error Tolerance
     78    const int maxIter;                  ///< Convergence limit
     79    const float minTol;                 ///< Convergence Tolerance (stop if we reach this value)
     80    const float maxTol;                 ///< Max Tolerance (accept fit if last improvement was this good)
    7981    float value;                       ///< Value of function at minimum
    8082    int iter;                          ///< Number of iterations to date
    8183    float lastDelta;                   ///< The last difference for the fit
     84    float maxChisqDOF;                 ///< for Chisq minimization, require that we reach here before checking tolerance
    8285}
    8386psMinimization;
     
    102105psMinimization *psMinimizationAlloc(
    103106    int maxIter,                       ///< Number of minimization iterations to perform.
    104     float tol                          ///< Requested error tolerance
     107    float minTol,                      ///< stop if tolerance is less than this
     108    float maxTol                       ///< accept fit if tolerance is less than this
    105109) PS_ATTR_MALLOC;
    106110
  • trunk/psLib/src/math/psMinimizePowell.c

    r28635 r28998  
    457457
    458458        mul = bracket->data.F32[1];
    459         if ((fabs(a-b) < min->tol) && (fabs(b-c) < min->tol)) {
     459        if ((fabs(a-b) < min->minTol) && (fabs(b-c) < min->minTol)) {
    460460            PS_VECTOR_ADD_MULTIPLE(params, paramMask, line, params, mul);
    461461            min->value = func(params, coords);
     
    524524    psTrace("psLib.math", 4, "---- psMinimizePowell() begin ----\n");
    525525    psTrace("psLib.math", 6, "min->maxIter is %d\n", min->maxIter);
    526     psTrace("psLib.math", 6, "min->tol is %f\n", min->tol);
     526    psTrace("psLib.math", 6, "min->minTol is %f\n", min->minTol);
    527527
    528528    if (paramMask == NULL) {
     
    573573        for (i=0;i<numDims;i++) {
    574574            if (myParamMask->data.PS_TYPE_VECTOR_MASK_DATA[i] == 0) {
     575
    575576                P_PSMINIMIZATION_SET_MAXITER((&dummyMin),PS_MINIMIZE_POWELL_LINEMIN_MAX_ITERATIONS);
    576                 *(float*)&dummyMin.tol = PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE;
     577                P_PSMINIMIZATION_SET_MIN_TOL((&dummyMin),PS_MINIMIZE_POWELL_LINEMIN_ERROR_TOLERANCE);
     578
    577579                mul = LineMin(&dummyMin, Q, ((psVector *) v->data[i]),
    578580                              myParamMask, coords, func);
     
    677679
    678680        // 8: Go to step 3 until the change is less than some tolerance.
    679         if (fabs(baseFuncVal - currFuncVal) <= min->tol) {
     681        if (fabs(baseFuncVal - currFuncVal) <= min->minTol) {
    680682            psFree(v);
    681683            psFree(pQP);
  • trunk/psLib/src/math/psStats.c

    r27334 r28998  
    12111211
    12121212        // Fit a Gaussian to the data.
    1213         psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
     1213        psMinimization *minimizer = psMinimizationAlloc(100, 0.01, 1.0); // The minimizer information
    12141214        psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
    12151215        // Initial guess for the mean (index 0) and var (index 1).
  • trunk/psLib/test/math/Makefile.am

    r24321 r28998  
    4949        tap_psMinimizePowell \
    5050        tap_psSpline1D \
    51         tap_psPolynomialMD
     51        tap_psPolynomialMD \
     52        tap_psPolynomialMD_sampleDark
    5253
    5354#       tap_psRandom
Note: See TracChangeset for help on using the changeset viewer.