IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 35767


Ignore:
Timestamp:
Jul 3, 2013, 2:30:22 PM (13 years ago)
Author:
eugene
Message:

add new psMinimizeLMChi2 function with new convergence criterion; add threaded version of psImageSmoothNoMask

Location:
trunk/psLib
Files:
7 edited
3 copied

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/imageops/psImageConvolve.c

    r34724 r35767  
    12311231}
    12321232
     1233/*********************** smooth with mask, threaded version ***********************/
     1234
    12331235bool psImageSmoothMask_ScanRows_F32(psImage *calculation, psImage *calcMask, const psImage *image,
    12341236                                    const psImage *mask, psImageMaskType maskVal, psVector *gaussNorm,
     
    14841486
    14851487
     1488/*********************** smooth no-mask ***********************/
     1489
     1490bool psImageSmoothNoMask_ScanRows_F32(psImage *calculation, const psImage *image,
     1491                                      psVector *gaussNorm, float minGauss,
     1492                                      int size, int rowStart, int rowStop)
     1493{
     1494    const psF32 *gauss = &gaussNorm->data.F32[size]; // Gaussian convolution kernel
     1495
     1496    int numCols = image->numCols;
     1497    int xLast = numCols - 1; // Last index
     1498
     1499    for (int j = rowStart; j < rowStop; j++) {
     1500        for (int i = 0; i < numCols; i++) {
     1501            int xMin = PS_MAX(i - size, 0);
     1502            int xMax = PS_MIN(i + size, xLast);
     1503            const psF32 *imageData = &image->data.F32[j][xMin];
     1504            int uMin = - PS_MIN(i, size); /* Minimum kernel index */
     1505            const psF32 *gaussData = &gauss[uMin];
     1506            double sumIG = 0.0, sumG = 0.0; /* Sums for convolution */
     1507            for (int x = xMin; x <= xMax; x++, imageData++, gaussData++) {
     1508                sumIG += *imageData * *gaussData;
     1509                sumG += *gaussData;
     1510            }
     1511            if (sumG > minGauss) {
     1512                /* BW */
     1513                calculation->data.F32[i][j] = sumIG / sumG;
     1514            }
     1515        }
     1516    }
     1517    return true;
     1518}
     1519
     1520bool psImageSmoothNoMask_ScanCols_F32(psImage *output, psImage *calculation,
     1521                                      psVector *gaussNorm, float minGauss,
     1522                                      int size, int colStart, int colStop)
     1523{
     1524    const psF32 *gauss = &gaussNorm->data.F32[size]; // Gaussian convolution kernel
     1525
     1526    int numRows = output->numRows;
     1527    int yLast = numRows - 1; // Last index
     1528
     1529    for (int i = colStart; i < colStop; i++) {
     1530        for (int j = 0; j < numRows; j++) {
     1531            int yMin = PS_MAX(j - size, 0);
     1532            int yMax = PS_MIN(j + size, yLast);
     1533            const psF32 *imageData = &calculation->data.F32[i][yMin]; /* BW */
     1534            int vMin = - PS_MIN(j, size); /* Minimum kernel index */
     1535            const psF32 *gaussData = &gauss[vMin];
     1536            double sumIG = 0.0, sumG = 0.0; /* Sums for convolution */
     1537            for (int y = yMin; y <= yMax; y++, imageData++, gaussData++) {
     1538                sumIG += *imageData * *gaussData;
     1539                sumG += *gaussData;
     1540            }
     1541            output->data.F32[j][i] = (sumG > minGauss) ? sumIG / sumG : NAN;
     1542        }
     1543    }
     1544    return true;
     1545}
     1546
     1547bool psImageSmoothNoMask_ScanRows_F32_Threaded(psThreadJob *job)
     1548{
     1549    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
     1550    psAssert (job->args, "programming error: job args not alloced?");
     1551    psAssert (job->args->n == 7, "programming error: job Nargs mismatch");
     1552
     1553    psImage *calculation  = job->args->data[0]; // calculation image
     1554    const psImage *image  = job->args->data[1]; // input image
     1555
     1556    psVector *gaussNorm   = job->args->data[2]; // gauss kernel
     1557    float minGauss        = PS_SCALAR_VALUE(job->args->data[3],F32);
     1558    int size              = PS_SCALAR_VALUE(job->args->data[4],S32);
     1559    int rowStart          = PS_SCALAR_VALUE(job->args->data[5],S32);
     1560    int rowStop           = PS_SCALAR_VALUE(job->args->data[6],S32);
     1561    return psImageSmoothNoMask_ScanRows_F32(calculation, image,
     1562                                          gaussNorm, minGauss, size,
     1563                                          rowStart, rowStop);
     1564}
     1565
     1566bool psImageSmoothNoMask_ScanCols_F32_Threaded(psThreadJob *job)
     1567{
     1568    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
     1569    psAssert (job->args, "programming error: job args not alloced?");
     1570    psAssert (job->args->n == 7, "programming error: job Nargs mismatch");
     1571
     1572    psImage *output       = job->args->data[0]; // input image
     1573    psImage *calculation  = job->args->data[1]; // calculation image
     1574
     1575    psVector *gaussNorm   = job->args->data[2]; // gauss kernel
     1576    float minGauss        = PS_SCALAR_VALUE(job->args->data[3],F32);
     1577    int size              = PS_SCALAR_VALUE(job->args->data[4],S32);
     1578    int colStart          = PS_SCALAR_VALUE(job->args->data[5],S32);
     1579    int colStop           = PS_SCALAR_VALUE(job->args->data[6],S32);
     1580    return psImageSmoothNoMask_ScanCols_F32(output, calculation,
     1581                                          gaussNorm, minGauss, size,
     1582                                          colStart, colStop);
     1583}
     1584
     1585psImage *psImageSmoothNoMask_Threaded(psImage *output, const psImage *image, float sigma, float numSigma, float minGauss)
     1586{
     1587    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
     1588
     1589    int numCols = image->numCols, numRows = image->numRows; // Size of image
     1590
     1591    int nThreads = psThreadPoolSize();
     1592    int scanCols = nThreads ? (numCols / 4 / nThreads) : numCols;
     1593    int scanRows = nThreads ? (numRows / 4 / nThreads) : numRows;
     1594    int size = sigma * numSigma + 0.5;  // Half-size of kernel
     1595
     1596    // Generate normalized gaussian
     1597    psVector *gaussNorm = NULL;
     1598    IMAGE_SMOOTH_GAUSS(gaussNorm, size, sigma, F32);
     1599
     1600    switch (image->type.type) {
     1601        // Smooth an image with masked pixels
     1602        // The calculation and calcMask images are *deliberately* backwards (row,col instead of col,row or
     1603        // [col][row] instead of [row][col]) to optimise the iteration over rows; such instances are marked "BW"
     1604
     1605      case PS_TYPE_F32: {
     1606          psImage *calculation = psImageAlloc(numRows, numCols, PS_TYPE_F32); /* Calculation image; BW */
     1607
     1608          if (threaded) {
     1609              /** Smooth in X direction **/
     1610              for (int rowStart = 0; rowStart < numRows; rowStart+=scanRows) {
     1611                  int rowStop = PS_MIN (rowStart + scanRows, numRows);
     1612
     1613                  // allocate a job, construct the arguments for this job
     1614                  psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_SMOOTH_NOMASK_SCANROWS");
     1615                  psArrayAdd(job->args, 0, calculation);
     1616                  psArrayAdd(job->args, 0, (psImage *) image); // cast away const
     1617                  psArrayAdd(job->args, 0, gaussNorm);
     1618                  PS_ARRAY_ADD_SCALAR(job->args, minGauss, PS_TYPE_F32);
     1619                  PS_ARRAY_ADD_SCALAR(job->args, size,     PS_TYPE_S32);
     1620                  PS_ARRAY_ADD_SCALAR(job->args, rowStart, PS_TYPE_S32);
     1621                  PS_ARRAY_ADD_SCALAR(job->args, rowStop,  PS_TYPE_S32);
     1622                  // -> psImageSmoothNoMask_ScanRows_F32 (calculation, image, gauss, minGauss, size, rowStart, rowStop);
     1623
     1624                  // if threading is not active, we simply run the job and return
     1625                  if (!psThreadJobAddPending(job)) {
     1626                      psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1627                      psFree(calculation);
     1628                      psFree(gaussNorm);
     1629                      return false;
     1630                  }
     1631              }
     1632              // wait here for the threaded jobs to finish (NOP if threading is not active)
     1633              if (!psThreadPoolWait(true, true)) {
     1634                  psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1635                  psFree(calculation);
     1636                  psFree(gaussNorm);
     1637                  return false;
     1638              }
     1639          } else if (!psImageSmoothNoMask_ScanRows_F32(calculation, image, gaussNorm, minGauss, size, 0, numRows)) {
     1640              psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1641              psFree(calculation);
     1642              psFree(gaussNorm);
     1643              return false;
     1644          }
     1645
     1646          output = psImageRecycle(output, numCols, numRows, PS_TYPE_F32);
     1647
     1648          /** Smooth in Y direction  **/
     1649          if (threaded) {
     1650              for (int colStart = 0; colStart < numCols; colStart+=scanCols) {
     1651                  int colStop = PS_MIN (colStart + scanCols, numCols);
     1652
     1653                  // allocate a job, construct the arguments for this job
     1654                  psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_SMOOTH_NOMASK_SCANCOLS");
     1655                  psArrayAdd(job->args, 0, output);
     1656                  psArrayAdd(job->args, 0, calculation);
     1657                  psArrayAdd(job->args, 0, gaussNorm);
     1658                  PS_ARRAY_ADD_SCALAR(job->args, minGauss, PS_TYPE_F32);
     1659                  PS_ARRAY_ADD_SCALAR(job->args, size,     PS_TYPE_S32);
     1660                  PS_ARRAY_ADD_SCALAR(job->args, colStart, PS_TYPE_S32);
     1661                  PS_ARRAY_ADD_SCALAR(job->args, colStop,  PS_TYPE_S32);
     1662                  // -> psImageSmoothMask_ScanCols_F32 (output, calculation, gauss, minGauss, size, colStart, colStop);
     1663
     1664                  // if threading is not active, we simply run the job and return
     1665                  if (!psThreadJobAddPending(job)) {
     1666                      psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1667                      psFree(calculation);
     1668                      psFree(gaussNorm);
     1669                      return false;
     1670                  }
     1671              }
     1672
     1673              // wait here for the threaded jobs to finish (NOP if threading is not active)
     1674              if (!psThreadPoolWait(true, true)) {
     1675                  psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1676                  psFree(calculation);
     1677                  psFree(gaussNorm);
     1678                  return false;
     1679              }
     1680          } else if (!psImageSmoothNoMask_ScanCols_F32(output, calculation, gaussNorm, minGauss, size, 0, numCols)) {
     1681              psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
     1682              psFree(calculation);
     1683              psFree(gaussNorm);
     1684              return false;
     1685          }
     1686
     1687          psFree(calculation);
     1688          psFree(gaussNorm);
     1689
     1690          return output;
     1691      }
     1692      default:
     1693        psFree(gaussNorm);
     1694        char *typeStr;
     1695        PS_TYPE_NAME(typeStr,image->type.type);
     1696        psError(PS_ERR_BAD_PARAMETER_TYPE, true,
     1697                _("Specified psImage type, %s, is not supported."),
     1698                typeStr);
     1699        return false;
     1700    }
     1701
     1702    psAbort("Should never reach here.");
     1703    return false;
     1704}
     1705
     1706/****************************************************************************************/
     1707
    14861708bool psImageSmoothMaskF32(psImage *image, psImage *mask, psImageMaskType maskVal,
    14871709                          double  sigma, double  Nsigma)
     
    19002122        }
    19012123        {
     2124            psThreadTask *task = psThreadTaskAlloc("PSLIB_IMAGE_SMOOTH_NOMASK_SCANROWS", 7);
     2125            task->function = &psImageSmoothNoMask_ScanRows_F32_Threaded;
     2126            psThreadTaskAdd(task);
     2127            psFree(task);
     2128        }
     2129        {
     2130            psThreadTask *task = psThreadTaskAlloc("PSLIB_IMAGE_SMOOTH_NOMASK_SCANCOLS", 7);
     2131            task->function = &psImageSmoothNoMask_ScanCols_F32_Threaded;
     2132            psThreadTaskAdd(task);
     2133            psFree(task);
     2134        }
     2135        {
    19022136            psThreadTask *task = psThreadTaskAlloc("PSLIB_IMAGE_SMOOTHMASK_PIXELS", 9);
    19032137            task->function = &psImageSmoothMaskPixelsThread;
     
    19092143        psThreadTaskRemove("PSLIB_IMAGE_SMOOTHMASK_SCANROWS");
    19102144        psThreadTaskRemove("PSLIB_IMAGE_SMOOTHMASK_SCANCOLS");
     2145        psThreadTaskRemove("PSLIB_IMAGE_SMOOTH_NOMASK_SCANROWS");
     2146        psThreadTaskRemove("PSLIB_IMAGE_SMOOTH_NOMASK_SCANCOLS");
    19112147        psThreadTaskRemove("PSLIB_IMAGE_SMOOTHMASK_PIXELS");
    19122148    }
  • trunk/psLib/src/imageops/psImageConvolve.h

    r32725 r35767  
    242242    );
    243243
    244 /// Smooth an image by parts using 1D Gaussian independently in x and y, allowing for masked pixels
     244/// Smooth an imageby parts using 1D Gaussian independently in x and y, allowing for
     245/// MASKED PIXELS
    245246///
    246247/// Applies a circularly symmetric Gaussian smoothing first in x and then in y
     
    256257    );
    257258
    258 /// Smooth particular pixels on an image, allowing for masked pixels
     259/// Smooth particular pixels on an image, allowing for MASKED PIXELS
    259260///
    260261/// Applies a circularly symmetric Gaussian smoothing first in x and then in y
     
    271272    );
    272273
     274/// Smooth an image by parts using 1D Gaussian independently in x and y, allowing for
     275/// MASKED PIXELS : THREADED VERSION
     276///
     277/// Applies a circularly symmetric Gaussian smoothing first in x and then in y
     278/// directions with just a vector.  This process is 2N faster than 2D convolutions (in general).
    273279psImage *psImageSmoothMask_Threaded(psImage *output,
    274280                                    const psImage *image,
     
    279285                                    float minGauss);
    280286
     287/// Smooth an image by parts using 1D Gaussian independently in x and y, allowing for
     288/// MASKED PIXELS : THREADED VERSION
     289///
     290/// Applies a circularly symmetric Gaussian smoothing first in x and then in y
     291/// directions with just a vector.  This process is 2N faster than 2D convolutions (in general).
     292psImage *psImageSmoothNoMask_Threaded(psImage *output,
     293                                    const psImage *image,
     294                                    float sigma,
     295                                    float numSigma,
     296                                    float minGauss);
     297
     298/// Smooth an image by parts IN-SITU using 1D Gaussian independently in x and y, allowing
     299/// for MASKED PIXELS
    281300bool psImageSmoothMaskF32(
    282301    psImage *image,                    ///< the image to be smoothed
  • trunk/psLib/src/math/psMinimizeLMM.c

    r29542 r35767  
    325325        chisq += PS_SQR(delta) * dy->data.F32[i];
    326326
     327        // XXX remove this later:
     328        // psVector *tmp = x->data[i];
     329        // fprintf (stderr, "%f %f  %f %f  %f\n", tmp->data.F32[0], tmp->data.F32[1], y->data.F32[i], dy->data.F32[i], ymodel);
     330
    327331        if (isnan(dy->data.F32[i])) goto escape;
    328332        if (isnan(delta)) goto escape;
     
    360364}
    361365
     366/******************************************************************************
     367psMinimizeLMChi2():  wrapper to call either _Old or _Alt
     368  *****************************************************************************/
     369bool psMinimizeLMChi2(
     370    psMinimization *min,
     371    psImage *covar,
     372    psVector *params,
     373    psMinConstraint *constraint,
     374    const psArray *x,
     375    const psVector *y,
     376    const psVector *yWt,
     377    psMinimizeLMChi2Func func)
     378{
     379    bool status = psMinimizeLMChi2_Alt(
     380        min,
     381        covar,
     382        params,
     383        constraint,
     384        x,
     385        y,
     386        yWt,
     387        func);
     388    return status;
     389}
    362390
    363391/******************************************************************************
     
    370398XXX Make an F64 version?
    371399  *****************************************************************************/
    372 bool psMinimizeLMChi2(
     400bool psMinimizeLMChi2_Old(
    373401    psMinimization *min,
    374402    psImage *covar,
     
    507535        psF32 rho = (min->value - Chisq) / dLinear;
    508536
    509         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);
    510 
    511         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);
     537        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     538
     539        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
    512540
    513541        // dump some useful info if trace is defined
     
    580608}
    581609
     610/******************************************************************************
     611psMinimizeLMChi2(): This routine takes a function-pointer (func) which calculates an arbitrary
     612function and it's derivatives and minimizes the chi-squared match between that function at the
     613specified points and the specified value at those points.
     614
     615The original version of this function used a convergence criterion based on the change in
     616chisq.  this has problems since it depends on the choice of points used to measure the fit.
     617(consider a gaussian on a background : it 100 pixels are used -- and some or most contribute to
     618the chisq -- and the delta-chisq is 10%, then the same change in model fit will yield a
     619delta-chisq of 1% if 1000 pixels are used (all but the 100 measuring the background)).
     620
     621This implementation uses changes to the parameters and stops if they are no longer significant.
     622
     623This requires F32 input data; all internal calls use F32.
     624  *****************************************************************************/
     625bool psMinimizeLMChi2_Alt(
     626    psMinimization *min,
     627    psImage *covar,
     628    psVector *params,
     629    psMinConstraint *constraint,
     630    const psArray *x,
     631    const psVector *y,
     632    const psVector *yWt,
     633    psMinimizeLMChi2Func func)
     634{
     635    psTrace("psLib.math", 3, "---- begin ----\n");
     636    PS_ASSERT_PTR_NON_NULL(min, false);
     637    PS_ASSERT_VECTOR_NON_NULL(params, false);
     638    PS_ASSERT_VECTOR_NON_EMPTY(params, false);
     639    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, false);
     640    psVector *paramMask = NULL;
     641    if (constraint != NULL) {
     642        paramMask = constraint->paramMask;
     643        if (paramMask != NULL) {
     644            PS_ASSERT_VECTOR_TYPE(paramMask, PS_TYPE_VECTOR_MASK, false);
     645            PS_ASSERT_VECTORS_SIZE_EQUAL(params, paramMask, false);
     646        }
     647    }
     648    PS_ASSERT_PTR_NON_NULL(x, false);
     649    for (psS32 i = 0 ; i < x->n ; i++) {
     650        psVector *coord = (psVector *) (x->data[i]);
     651        PS_ASSERT_VECTOR_NON_NULL(coord, false);
     652        PS_ASSERT_VECTOR_TYPE(coord, PS_TYPE_F32, false);
     653    }
     654    PS_ASSERT_VECTOR_NON_NULL(y, false);
     655    PS_ASSERT_VECTOR_NON_EMPTY(y, false);
     656    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F32, false);
     657    PS_ASSERT_VECTORS_SIZE_EQUAL(x, y, false);
     658    if (yWt != NULL) {
     659        PS_ASSERT_VECTOR_TYPE(yWt, PS_TYPE_F32, false);
     660        PS_ASSERT_VECTORS_SIZE_EQUAL(y, yWt, false);
     661    }
     662    PS_ASSERT_PTR_NON_NULL(func, false);
     663
     664    psMinimizeLMLimitFunc checkLimits = NULL;
     665    if (constraint) {
     666        checkLimits = constraint->checkLimits;
     667    }
     668
     669    // this function has test and current values for several things
     670    // the current best value is in lower case
     671    // the next guess value is in upper case
     672
     673    // allocate internal arrays (current vs Guess)
     674    psImage *Alpha = NULL;
     675    psVector *Beta = NULL;
     676
     677    // Alpha & Beta only contain elements to represent the unmasked parameters
     678    if (!psMinLM_AllocAB (&Alpha, &Beta, params, paramMask)) {
     679        psAbort ("programming error: no unmasked parameters to be fit\n");
     680    }
     681
     682    int nFitParams = Beta->n;
     683    psImage *alpha   = psImageAlloc(nFitParams, nFitParams, PS_TYPE_F32);
     684    psVector *beta   = psVectorAlloc(nFitParams, PS_TYPE_F32);
     685    psVector *Params = psVectorAlloc(params->n, PS_TYPE_F32);
     686
     687    psVector *dy     = NULL;
     688    psF32 Chisq = 0.0;
     689    psF32 lambda = 0.001;
     690    psF32 dLinear = 0.0;
     691    psF32 nu = 2.0;
     692
     693    // the user provides the error or NULL.  we need to convert
     694    // to appropriate weights
     695    if (yWt != NULL) {
     696        dy = (psVector *) yWt;
     697    } else {
     698        dy = psVectorAlloc(y->n, PS_TYPE_F32);
     699        psVectorInit(dy, 1.0);
     700    }
     701
     702    // number of degrees of freedom for this fit
     703    int nDOF = dy->n - nFitParams;
     704
     705    // calculate initial alpha and beta, set chisq (min->value)
     706    min->value = psMinLM_SetABX(alpha, beta, params, paramMask, x, y, dy, func);
     707    if (isnan(min->value)) {
     708        min->iter = min->maxIter;
     709        psFree(alpha);
     710        psFree(Alpha);
     711        psFree(beta);
     712        psFree(Beta);
     713        psFree(Params);
     714        return(false);
     715    }
     716    // dump some useful info if trace is defined
     717    if (psTraceGetLevel("psLib.math") >= 6) {
     718        p_psImagePrint(psTraceGetDestination(), alpha, "alpha guess (0)");
     719        p_psVectorPrint(psTraceGetDestination(), beta, "beta guess (0)");
     720    }
     721    if (psTraceGetLevel("psLib.math") >= 5) {
     722        p_psVectorPrint(psTraceGetDestination(), params, "params guess (0)");
     723    }
     724
     725    bool done = (min->iter >= min->maxIter);
     726    while (!done) {
     727        psTrace("psLib.math", 5, "Iteration number %d.  (max iterations is %d).\n", min->iter, min->maxIter);
     728
     729        if (min->chisqConvergence) {
     730          psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->lastDelta, min->minTol, min->maxTol);
     731        } else {
     732          psTrace("psLib.math", 5, "Last delta is %f.  stop if < %f, accept if < %f\n", min->rParSigma, min->minTol*nFitParams, min->maxTol*nFitParams);
     733        }
     734
     735        // set a new guess for Alpha, Beta, Params
     736        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, checkLimits, lambda, &dLinear)) {
     737            min->iter ++;
     738            if (min->iter >=  min->maxIter) break;
     739            lambda *= 10.0;
     740            // ALT? lambda *= 2.0;
     741            continue;
     742        }
     743
     744        // dump some useful info if trace is defined
     745        if (psTraceGetLevel("psLib.math") >= 6) {
     746            p_psImagePrint(psTraceGetDestination(), Alpha, "Alpha guess (1)");
     747            p_psVectorPrint(psTraceGetDestination(), Beta, "Beta guess (1)");
     748            p_psVectorPrint(psTraceGetDestination(), beta, "beta current (1)");
     749        }
     750        if (psTraceGetLevel("psLib.math") >= 5) {
     751            p_psVectorPrint(psTraceGetDestination(), Params, "params guess (1)");
     752        }
     753
     754        // calculate the parameter change (rParDelta) and error radius (rParSigma)
     755        //    rParDelta : radius of parameter change;
     756        //    rParSigma : radius of parameter error
     757       
     758        // note that (before SetABX) Alpha[i][i] is the covariance matrix and
     759        // Beta is the actual parameter change for this pass
     760
     761        // note that Alpha & Beta only represent unmasked parameters, while params and Params have all
     762
     763        // dParSigma = Alpha[i][i] : error (squared) on parameter i
     764        // dParDelta = Params->data.F32[i] - params->data.F32[i]     : change on parameter i
     765        float rParSigma = 0.0;
     766        for (int j = 0, J = 0; j < Params->n; j++) {
     767            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
     768                continue;
     769            }
     770            rParSigma += PS_SQR(Params->data.F32[j] - params->data.F32[j]) / Alpha->data.F32[J][J];
     771            J++;
     772        }
     773        rParSigma = sqrt(rParSigma);
     774        psTrace("psLib.math", 5, "rParSigma: %f, Niter: %d\n", rParSigma, min->iter);
     775        // fprintf (stderr, "rParSigma: %f, Niter: %d\n", rParSigma, min->iter);
     776
     777        // calculate Chisq for new guess, update Alpha & Beta
     778        Chisq = psMinLM_SetABX(Alpha, Beta, Params, paramMask, x, y, dy, func);
     779        if (isnan(Chisq)) {
     780            min->iter ++;
     781            if (min->iter >= min->maxIter) break;
     782            lambda *= 10.0;
     783            // ALT lambda *= 2.0;
     784            continue;
     785        }
     786
     787        // convergence criterion:
     788        // compare the delta (min->value - Chisq) with the
     789        // expected delta from the linear model (dLinear)
     790        // accept new guess if it is an improvement (rho > 0), or else increase lambda
     791        psF32 rho = (min->value - Chisq) / dLinear;
     792
     793        psTrace("psLib.math", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g, nDOF: %d\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda, nDOF);
     794
     795        psTrace("psLib.math.dLinear", 5, "last chisq: %f, new chisq %f, delta: %f, dLinear: %f, rho: %f, lambda: %g\n", min->value, Chisq, min->lastDelta, dLinear, rho, lambda);
     796
     797        // dump some useful info if trace is defined
     798        if (psTraceGetLevel("psLib.math") >= 6) {
     799            p_psImagePrint(psTraceGetDestination(), Alpha, "alpha guess (2)");
     800            p_psVectorPrint(psTraceGetDestination(), Beta, "beta guess (2)");
     801        }
     802
     803        // change in chisq/nDOF since last minimum
     804        min->lastDelta = (min->value - Chisq) / nDOF;
     805
     806        // rho is positive if the new chisq is smaller; allow for some insignificant change (slight negative rho)
     807
     808        // XXX the old version of lambda changes:
     809        // XXX : Madsen gives suggestion for better use of rho
     810        // rho is positive if the new chisq is smaller
     811        if (rho >= -1e-6) {
     812            min->value = Chisq;
     813            alpha  = psImageCopy(alpha, Alpha, PS_TYPE_F32);
     814            beta   = psVectorCopy(beta, Beta, PS_TYPE_F32);
     815            params = psVectorCopy(params, Params, PS_TYPE_F32);
     816        }
     817        switch (min->gainFactorMode) {
     818          case 0:
     819            if (rho >= -1e-6) {
     820              lambda *= 0.25;
     821            } else {
     822              lambda *= 10.0;
     823            }
     824            break;
     825
     826          case 1:
     827            // adjust the gain ratio (lambda) based on rho
     828            if (rho < 0.25) {
     829              lambda *= 2.0;
     830            }
     831            if (rho > 0.75) {
     832              lambda *= 0.333;
     833            }
     834            break;
     835
     836          case 2:
     837            if (rho > 0.0) {
     838              lambda *= PS_MAX(0.33, (1.0 - pow(2.0*rho - 1.0, 3.0)));
     839              nu = 2.0;
     840            } else {
     841              lambda *= nu;
     842              nu *= 2.0;
     843            }
     844            break;
     845        }
     846        min->iter++;
     847
     848        // ending conditions:
     849        // 1) hard limit : too many iterations
     850        done = (min->iter >= min->maxIter);
     851       
     852        // 2) require deltaChi > 1e-6 (ie, chisq is decreasing, but accept an insignificant change)
     853        if (min->lastDelta < -1e-6) {
     854            continue;
     855        }
     856
     857        // save this value in case we stop iterating
     858        min->rParSigma = rParSigma;
     859
     860        // 2) require chisqDOF < maxChisqDOF (if maxChisqDOF is not NAN)
     861        // keep iterating regardless of rParSigma in this case
     862        float chisqDOF = Chisq / nDOF;
     863        if (isfinite(min->maxChisqDOF) && (chisqDOF > min->maxChisqDOF)) {
     864            continue;
     865        }
     866
     867        // delta-chisq or rParSigma ?
     868        if (min->chisqConvergence) {
     869          done |= (min->lastDelta < min->minTol);
     870        } else {
     871          done |= (rParSigma < min->minTol*nFitParams);
     872        }
     873    }
     874    psTrace("psLib.math", 5, "chisq: %f, last delta: %f, rParSigma: %f, Niter: %d\n", min->value, min->lastDelta, min->rParSigma, min->iter);
     875
     876    // construct & return the covariance matrix (if requested)
     877    if (covar != NULL) {
     878        if (!psMinLM_GuessABP(Alpha, Beta, Params, alpha, beta, params, paramMask, NULL, 0.0, NULL)) {
     879            psTrace ("psLib.math", 5, "failure to calculate covariance matrix\n");
     880        }
     881        // set covar values which are not masked
     882        psImageInit (covar, 0.0);
     883        for (int j = 0, J = 0; j < params->n; j++) {
     884            if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[j])) {
     885                covar->data.F32[j][j] = 1.0;
     886                continue;
     887            }
     888            for (int k = 0, K = 0; k < params->n; k++) {
     889                if (paramMask && (paramMask->data.PS_TYPE_VECTOR_MASK_DATA[k])) continue;
     890                covar->data.F32[j][k] = Alpha->data.F32[J][K];
     891                K++;
     892            }
     893            J++;
     894        }
     895    }
     896
     897    // free the internal temporary data
     898    psFree(alpha);
     899    psFree(Alpha);
     900    psFree(beta);
     901    psFree(Beta);
     902    psFree(Params);
     903    if (yWt == NULL) {
     904        psFree(dy);
     905    }
     906
     907    // if the last improvement was at least as good as maxTol, accept the fit:
     908    if (min->chisqConvergence) {
     909      if (min->lastDelta <= min->maxTol) {
     910        psTrace("psLib.math", 6, "---- end (true) ----\n");
     911        return(true);
     912      }
     913    } else {
     914      if (min->rParSigma <= min->maxTol*nFitParams) {
     915        psTrace("psLib.math", 6, "---- end (true) ----\n");
     916        return(true);
     917      }
     918    }
     919    psTrace("psLib.math", 6, "---- end (false) ----\n");
     920    return(false);
     921}
     922
    582923bool psMinLM_AllocAB (psImage **Alpha, psVector **Beta, const psVector *params, const psVector *paramMask) {
    583924
     
    625966    min->iter = 0;
    626967    min->lastDelta = NAN;
     968    min->rParSigma = NAN;
    627969    min->maxChisqDOF = NAN;
    628970
     971    // we default to the old algorithm for convergence
     972    min->chisqConvergence = true;
     973    min->gainFactorMode = 0;
     974   
    629975    return(min);
    630976}
  • trunk/psLib/src/math/psMinimizeLMM.h

    r28998 r35767  
    3030#include "psConstants.h"
    3131
     32# define PS_MINIMIZE_LMM_GAIN_FACTOR_MODE 0
     33# define PS_MINIMIZE_LMM_CHISQ_CONVERGENCE 1
     34
    3235#define PS_DETERMINE_BRACKET_STEP_SIZE 0.10
    3336#define PS_MAX_LMM_ITERATIONS 100
     
    8285    int iter;                          ///< Number of iterations to date
    8386    float lastDelta;                   ///< The last difference for the fit
     87    float rParSigma;                   ///< last fractional change in the parameters
    8488    float maxChisqDOF;                 ///< for Chisq minimization, require that we reach here before checking tolerance
     89    int gainFactorMode;
     90    bool chisqConvergence;
    8591}
    8692psMinimization;
     
    124130 */
    125131bool psMinimizeLMChi2(
     132    psMinimization *min,               ///< Minimization specification
     133    psImage *covar,                    ///< Covariance matrix
     134    psVector *params,                  ///< "Best Guess" for the parameters that minimize func
     135    psMinConstraint *constraint, ///< Constraints on the parameters
     136    const psArray *x,                  ///< Measurement ordinates of multiple vectors
     137    const psVector *y,                 ///< Measurement coordinates
     138    const psVector *yWt,               ///< Errors in the measurement coordinates
     139    psMinimizeLMChi2Func func          ///< Specified function
     140);
     141
     142/** Minimizes a specified function based on the Levenberg-Marquardt method.
     143 *
     144 *  @return bool:   True if successful.
     145 */
     146bool psMinimizeLMChi2_Old(
     147    psMinimization *min,               ///< Minimization specification
     148    psImage *covar,                    ///< Covariance matrix
     149    psVector *params,                  ///< "Best Guess" for the parameters that minimize func
     150    psMinConstraint *constraint, ///< Constraints on the parameters
     151    const psArray *x,                  ///< Measurement ordinates of multiple vectors
     152    const psVector *y,                 ///< Measurement coordinates
     153    const psVector *yWt,               ///< Errors in the measurement coordinates
     154    psMinimizeLMChi2Func func          ///< Specified function
     155);
     156
     157/** Minimizes a specified function based on the Levenberg-Marquardt method
     158    Uses alternative convergence criterion.
     159 *
     160 *  @return bool:   True if successful.
     161 */
     162bool psMinimizeLMChi2_Alt(
    126163    psMinimization *min,               ///< Minimization specification
    127164    psImage *covar,                    ///< Covariance matrix
  • trunk/psLib/test/imageops/Makefile.am

    r32725 r35767  
    1717        tap_psImagePixelManip \
    1818        tap_psImageSmooth \
     19        tap_psImageSmoothMask_Threaded \
    1920        tap_psImageSmooth_PreAlloc \
    2021        tap_psImageStructManip \
  • trunk/psLib/test/math/Makefile.am

    r28998 r35767  
    4848        tap_psMatrixVectorArithmetic04 \
    4949        tap_psMinimizePowell \
     50        tap_psMinimizeLMM \
     51        tap_psMinimizeLMM_Alt \
     52        tap_psMinimizeLMM_Trail \
    5053        tap_psSpline1D \
    5154        tap_psPolynomialMD \
  • trunk/psLib/test/pstap/src/pstap.h

    r12738 r35767  
    144144    } \
    145145}
     146
     147# define ok_float      is_float     
     148# define ok_float_tol  is_float_tol
     149# define ok_double     is_double   
     150# define ok_double_tol is_double_tol
     151# define ok_str        is_str         
     152# define ok_strn       is_strn     
     153# define ok_int        is_int         
     154# define ok_long       is_long     
     155# define ok_bool       is_bool     
Note: See TracChangeset for help on using the changeset viewer.