IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Ignore:
Timestamp:
Dec 5, 2014, 4:44:10 PM (11 years ago)
Author:
watersc1
Message:

Sparse image map code.

File:
1 edited

Legend:

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

    r34153 r37721  
    1818
    1919#include <stdio.h>
     20#include <stdlib.h>
    2021#include "psError.h"
    2122#include "psAbort.h"
     
    3536#include "psImageStructManip.h"
    3637#include "psImageMap.h"
     38#include "psSparse.h"
    3739// #include "psImagePixelInterpolate.h"
    3840// #include "psImageUnbin.h"
     
    118120    psImageInit (A, 0.0);
    119121    psVectorInit (B, 0.0);
    120 
     122   
    121123    // we are looping over the Nx,Ny image map elements;
    122124    // the matrix equation contains Nx*Ny rows and columns
     
    262264            int I = n + Nx * m;
    263265            B->data.F32[I] = fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py;
    264 
     266           
    265267            // insert these values into their corresponding locations in A, B
    266268            // float Sum = 0.0;
     
    273275                    int J = (n + jn) + Nx * (m + jm);
    274276                    A->data.F32[J][I] = sA[jn][jm];
     277                   
    275278                    // fprintf (stderr, "A %d %d (%d %d : %d %d): %f\n", I, J, n, m, n + jn, m + jm, sA[jn][jm]);
    276279                    // Sum += sA[jn][jm];
     
    320323        return true;
    321324    }
    322 
     325   
    323326    // set bad values to NaN
    324327    for (int i = 0; i < Nx*Ny; i++) {
     
    341344    psFree (B);
    342345    psFree (Empty);
    343 
    344346    *pGoodFit = true;
    345347    return true;
     
    402404        psS32 Nkeep = 0;
    403405        if (!psImageMapFit(pGoodFit, map, mask, maskValue, x, y, f, df)) {
     406            psError(PS_ERR_UNKNOWN, false, "Could not fit image map.\n");
     407            psFree(resid);
     408            if (!inMask) psFree (mask);
     409            return false;
     410        }
     411        if (!*pGoodFit) {
     412            psWarning ("bad fit to image map, try something else");
     413            psFree(resid);
     414            if (!inMask) psFree (mask);
     415            return true;
     416        }
     417
     418        psVector *fit = psImageMapEvalVector(map, mask, maskValue, x, y);
     419        if (fit == NULL) {
     420            psError(PS_ERR_UNKNOWN, false, "Failure in psImageMapEvalVector().\n");
     421            psFree(resid);
     422            if (!inMask) psFree (mask);
     423            return false;
     424        }
     425        for (int i = 0 ; i < f->n ; i++) {
     426            resid->data.F32[i] = (f->data.F32[i] - fit->data.F32[i]);
     427        }
     428
     429        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
     430            psError(PS_ERR_UNKNOWN, false, "Failure to compute statistics on the resid vector.\n");
     431            psFree(resid);
     432            psFree(fit);
     433            if (!inMask) psFree (mask);
     434            return false;
     435        }
     436
     437        double meanValue = psStatsGetValue (stats, meanOption);
     438        double stdevValue = psStatsGetValue (stats, stdevOption);
     439
     440        psTrace("psLib.imageops", 5, "Mean is %f\n", meanValue);
     441        psTrace("psLib.imageops", 5, "Stdev is %f\n", stdevValue);
     442        psF32 minClipValue = -minClipSigma*stdevValue;
     443        psF32 maxClipValue = +maxClipSigma*stdevValue;
     444
     445        // set mask if pts are not valid
     446        // we are masking out any point which is out of range
     447        // recovery is not allowed with this scheme
     448        for (psS32 i = 0; i < resid->n; i++) {
     449            // XXX this prevents recovery of previously masked values
     450            if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue) {
     451                continue;
     452            }
     453
     454            if ((resid->data.F32[i] - meanValue > maxClipValue) || (resid->data.F32[i] - meanValue < minClipValue)) {
     455                psTrace("psLib.imageops", 6, "Masking element %d  : %f vs %f : resid is %f\n", i, f->data.F32[i], fit->data.F32[i], resid->data.F32[i]);
     456                mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
     457                continue;
     458            }
     459            Nkeep++;
     460        }
     461
     462        // We should probably exit this loop if no new elements were masked since the fit won't
     463        // change.
     464        psTrace("psLib.imageops", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
     465        stats->clippedNvalues = Nkeep;
     466        psFree(fit);
     467    }
     468
     469    // Free local temporary variables
     470    psFree(resid);
     471    if (!inMask) psFree (mask);
     472    *pGoodFit = true; // XXX probably don't need to set this (set by psImageMapFit)
     473    return true;
     474}
     475
     476// CZW: 2014-10-09
     477// Sparse versions of MapFit and MapFitClip that assume the matrices are not filled.
     478bool psImageMapFitSparse(bool *pGoodFit, psImageMap *map, const psVector *mask, psVectorMaskType maskValue,
     479                   const psVector *x, const psVector *y, const psVector *f, const psVector *df)
     480{
     481    // XXX Add Asserts
     482
     483    *pGoodFit = false;
     484
     485    // dimensions of the output map image
     486    int Nx = map->binning->nXruff;
     487    int Ny = map->binning->nYruff;
     488
     489   
     490    // no spatial information, just calculate mean & stdev
     491    if ((Nx == 1) && (Ny == 1)) {
     492        psStatsInit(map->stats);
     493
     494        // the user has supplied one of various stats option pairs,
     495        psStatsOptions mean = psStatsMeanOption(map->stats->options);
     496        psStatsOptions stdev = psStatsStdevOption(map->stats->options);
     497        if (!psStatsSingleOption(mean)) {
     498            psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
     499            return false;
     500        }
     501        if (!psStatsSingleOption(stdev)) {
     502            psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
     503            return false;
     504        }
     505
     506        // XXX does ROBUST_MEDIAN work with weight?
     507        if (!psVectorStats(map->stats, f, NULL, mask, maskValue)) {
     508            psError(PS_ERR_UNKNOWN, false, "failure to measure stats");
     509            return false;
     510        }
     511
     512        map->map->data.F32[0][0]   = psStatsGetValue(map->stats, mean);
     513        map->error->data.F32[0][0] = psStatsGetValue(map->stats, stdev);
     514        if (isfinite(map->map->data.F32[0][0]) && isfinite( map->error->data.F32[0][0])) {
     515            *pGoodFit = true;
     516        }
     517        return true;
     518    }
     519
     520    if (Nx == 1) {
     521        bool status;
     522        status = psImageMapFit1DinY (pGoodFit, map, mask, maskValue, x, y, f, df);
     523        return status;
     524    }
     525    if (Ny == 1) {
     526        bool status;
     527        status = psImageMapFit1DinX (pGoodFit, map, mask, maskValue, x, y, f, df);
     528        return status;
     529    }
     530
     531    // set up the redirection table so we can use sA[-1][-1], etc
     532    // XXX psKernel does this for you --- PAP.
     533    float SAm[3][3], *SAv[3], **sA;
     534
     535    for (int i = 0; i < 3; i++) {
     536        SAv[i] = SAm[i] + 1;
     537    }
     538    sA = SAv + 1;
     539
     540    // elements of the matrix equation Ax = B; we are solving for the vector x
     541    // psImage *A = psImageAlloc (Nx*Ny, Nx*Ny, PS_TYPE_F32);
     542    // psVector *B = psVectorAlloc (Nx*Ny, PS_TYPE_F32);
     543
     544    // psImageInit (A, 0.0);
     545    // psVectorInit (B, 0.0);
     546
     547    // CZW: call to psSparseAlloc
     548    // It should match old A, and each element of that should only touch four others.
     549    psSparse *Asparse = psSparseAlloc(Nx * Ny, 4 * Nx * Ny);
     550   
     551    // we are looping over the Nx,Ny image map elements;
     552    // the matrix equation contains Nx*Ny rows and columns
     553
     554    for (int n = 0; n < Nx; n++) {
     555        for (int m = 0; m < Ny; m++) {
     556            // define & init summing variables
     557            float rx_rx_ry_ry = 0;
     558            float rx_rx_dy_ry = 0;
     559            float dx_rx_ry_ry = 0;
     560            float dx_rx_dy_ry = 0;
     561            float fi_rx_ry    = 0;
     562            float rx_rx_py_py = 0;
     563            float rx_rx_qy_py = 0;
     564            float dx_rx_py_py = 0;
     565            float dx_rx_qy_py = 0;
     566            float fi_rx_py    = 0;
     567            float px_px_ry_ry = 0;
     568            float px_px_dy_ry = 0;
     569            float qx_px_ry_ry = 0;
     570            float qx_px_dy_ry = 0;
     571            float fi_px_ry    = 0;
     572            float px_px_py_py = 0;
     573            float px_px_qy_py = 0;
     574            float qx_px_py_py = 0;
     575            float qx_px_qy_py = 0;
     576            float fi_px_py    = 0;
     577
     578            // generate the sums for the fitting matrix element I,J
     579            // I = n + nX*m
     580            // J = (n + jn) + nX*(m + jm)
     581            for (int i = 0; i < x->n; i++) {
     582
     583                if (mask && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) continue;
     584
     585                // base coordinate offset for this point (x,y) relative to this map element (n,m)
     586                float dx = psImageBinningGetRuffX (map->binning, x->data.F32[i]) - (n + 0.5);
     587                float dy = psImageBinningGetRuffY (map->binning, y->data.F32[i]) - (m + 0.5);
     588
     589                // edge cases to include:
     590                bool edgeX = false;
     591                edgeX |= ((n == 1) && (dx < -1.0));
     592                edgeX |= ((n == Nx - 2) && (dx > +1.0));
     593
     594                bool edgeY = false;
     595                edgeY |= ((m == 1) && (dy < -1.0));
     596                edgeY |= ((m == Ny - 2) && (dy > +1.0));
     597
     598                // skip points outside of 2x2 grid centered on n,m:
     599                if (!edgeX && (fabs(dx) > 1.0)) continue;
     600                if (!edgeY && (fabs(dy) > 1.0)) continue;
     601
     602                // related offset values
     603                float rx = 1.0 - dx;
     604                float ry = 1.0 - dy;
     605                float px = 1.0 + dx;
     606                float py = 1.0 + dy;
     607                float qx = -dx;
     608                float qy = -dy;
     609
     610                // data value & weight for this point
     611                float fi = f->data.F32[i];
     612                if (!isfinite(fi)) continue;
     613
     614                float wt = 1.0;
     615                if (df != NULL) {
     616                    if (df->data.F32[i] == 0.0) {
     617                        wt = 0.0;
     618                    } else {
     619                        if (!isfinite(df->data.F32[i])) continue;
     620                        wt = 1.0 / PS_SQR(df->data.F32[i]); // XXX test for dz == NULL or dz_i = 0
     621                    }
     622                }
     623
     624                // sum the appropriate elements for the different quadrants
     625
     626                int Qx = (dx >= 0) ? 1 : 0;
     627                if (n ==      0) Qx = 1;
     628                if (n == Nx - 1) Qx = 0;
     629
     630                int Qy = (dy >= 0) ? 1 : 0;
     631                if (m ==      0) Qy = 1;
     632                if (m == Ny - 1) Qy = 0;
     633
     634                assert (isfinite(fi));
     635                assert (isfinite(wt));
     636                assert (isfinite(rx));
     637                assert (isfinite(ry));
     638
     639                // points at offset 1,1
     640                if ((Qx == 1) && (Qy == 1)) {
     641                    rx_rx_ry_ry += rx*rx*ry*ry*wt;
     642                    rx_rx_dy_ry += rx*rx*dy*ry*wt;
     643                    dx_rx_ry_ry += dx*rx*ry*ry*wt;
     644                    dx_rx_dy_ry += dx*rx*dy*ry*wt;
     645                    fi_rx_ry    += fi*rx*ry*wt;
     646                }
     647                // points at offset 1,0
     648                if ((Qx == 1) && (Qy == 0)) {
     649                    rx_rx_py_py += rx*rx*py*py*wt;
     650                    rx_rx_qy_py += rx*rx*qy*py*wt;
     651                    dx_rx_py_py += dx*rx*py*py*wt;
     652                    dx_rx_qy_py += dx*rx*qy*py*wt;
     653                    fi_rx_py    += fi*rx*py*wt;
     654                }
     655                // points at offset 0,1
     656                if ((Qx == 0) && (Qy == 1)) {
     657                    px_px_ry_ry += px*px*ry*ry*wt;
     658                    px_px_dy_ry += px*px*dy*ry*wt;
     659                    qx_px_ry_ry += qx*px*ry*ry*wt;
     660                    qx_px_dy_ry += qx*px*dy*ry*wt;
     661                    fi_px_ry    += fi*px*ry*wt;
     662                }
     663                // points at offset 0,0
     664                if ((Qx == 0) && (Qy == 0)) {
     665                    px_px_py_py += px*px*py*py*wt;
     666                    px_px_qy_py += px*px*qy*py*wt;
     667                    qx_px_py_py += qx*px*py*py*wt;
     668                    qx_px_qy_py += qx*px*qy*py*wt;
     669                    fi_px_py    += fi*px*py*wt;
     670                }
     671            }
     672
     673            // the chi-square derivatives have elements of the form g(n+jn,m+jm)*A(jn,jm),
     674            // jn,jm = -1 to +1. Convert the sums above into the correct coefficients
     675            sA[-1][-1] = qx_px_qy_py;
     676            sA[-1][ 0] = qx_px_ry_ry + qx_px_py_py;
     677            sA[-1][+1] = qx_px_dy_ry;
     678            sA[ 0][-1] = rx_rx_qy_py + px_px_qy_py;
     679            sA[ 0][ 0] = rx_rx_ry_ry + px_px_ry_ry + rx_rx_py_py + px_px_py_py;
     680            sA[ 0][+1] = rx_rx_dy_ry + px_px_dy_ry;
     681            sA[+1][-1] = dx_rx_qy_py;
     682            sA[+1][ 0] = dx_rx_ry_ry + dx_rx_py_py;
     683            sA[+1][+1] = dx_rx_dy_ry;
     684
     685            // I[ 0][ 0] = index for this n,m element:
     686            int I = n + Nx * m;
     687            //            B->data.F32[I] = fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py;
     688            // CZW: call to psSparseVector Element
     689            if (fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py == 0.0) {
     690              psSparseVectorElement(Asparse, I, 1.0);
     691            }
     692            else {
     693              psSparseVectorElement(Asparse, I, fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py);
     694            }
     695           
     696            //      printf("ADDING: %d %g \n",I, fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py);
     697            // insert these values into their corresponding locations in A, B
     698            for (int jn = -1; jn <= +1; jn++) {
     699                if (n + jn <   0) continue;
     700                if (n + jn >= Nx) continue;
     701                for (int jm = -1; jm <= +1; jm++) {
     702                    if (m + jm <   0) continue;
     703                    if (m + jm >= Ny) continue;
     704                    int J = (n + jn) + Nx * (m + jm);
     705                    //              printf("A: %d %d %g\n",J,I,sA[jn][jm]);
     706                    // A->data.F32[J][I] = sA[jn][jm];
     707                    // CZW: call to psSparseMatrixElement
     708                    if (J < I) { continue; }
     709                    psSparseMatrixElement(Asparse,J,I,sA[jn][jm]); // Ensure J < I?
     710                   
     711                }
     712            }
     713        }
     714    }
     715
     716    // test for empty diagonal elements (unconstained cells), mark, and set pivots to 1.0
     717    // CZW: I'm not totally sure how to check these in the sparse context.
     718    // Iterate over all ii pairs, and manually check the structure?
     719#if (0)
     720    psVector *Empty = psVectorAlloc (Nx*Ny, PS_TYPE_S8);
     721    psVectorInit (Empty, 0);
     722    for (int i = 0; i < Nx*Ny; i++) {
     723        if (A->data.F32[i][i] == 0.0) {
     724            Empty->data.S8[i] = 1;
     725            for (int j = 0; j < Nx*Ny; j++) {
     726                A->data.F32[i][j] = 0.0;
     727                A->data.F32[j][i] = 0.0;
     728            }
     729            A->data.F32[i][i] = 1.0;
     730            B->data.F32[i] = 0.0;
     731        }
     732    }
     733#endif
     734
     735    // CZW: call to psSparseSolve
     736    psVector *solution = psVectorAlloc(Nx*Ny, PS_TYPE_F32);
     737    psSparseConstraint Constraint;
     738    Constraint.paramDelta = 1e-3;
     739    Constraint.paramMin   = -1e5;
     740    Constraint.paramMax   = 1e5;
     741    solution = psSparseSolve(solution, Constraint, Asparse, 1000);
     742    if (!solution) {
     743      psFree(solution);
     744      psFree(Asparse);
     745      return(false);
     746    }
     747
     748#if (0)
     749    // CZW: This is a continuation of the above information
     750    // set bad values to NaN
     751    for (int i = 0; i < Nx*Ny; i++) {
     752        if (Empty->data.S8[i]) {
     753            B->data.F32[i] = NAN;
     754            A->data.F32[i][i] = 0;
     755        }
     756    }
     757#endif
     758
     759    for (int n = 0; n < Nx; n++) {
     760        for (int m = 0; m < Ny; m++) {
     761            int I = n + Nx * m;
     762            map->map->data.F32[m][n] = solution->data.F32[I];
     763            map->error->data.F32[m][n] = NAN; // sqrt(A->data.F32[I][I]); // CZW: fix this to be a real error.
     764        }
     765    }
     766
     767    //    psFree (A);
     768    //    psFree (B);
     769    //    psFree (Empty);
     770    // CZW: free things
     771    psFree(solution);
     772    psFree(Asparse);
     773   
     774    *pGoodFit = true;
     775    return true;
     776}
     777
     778// measure residuals on each pass and clip outliers based on stats
     779bool psImageMapClipFitSparse(bool *pGoodFit, psImageMap *map, psStats *stats, psVector *inMask, psVectorMaskType maskValue,
     780                             const psVector *x, const psVector *y, const psVector *f, const psVector *df)
     781{
     782    // XXX add in full PS_ASSERTS
     783    psAssert(map, "impossible");
     784    psAssert(stats, "impossible");
     785    psAssert(x, "impossible");
     786    psAssert(y, "impossible");
     787    psAssert(f, "impossible");
     788
     789    *pGoodFit = false;
     790
     791    // the user supplies one of various stats option pairs,
     792    // determine the desired mean and stdev STATS options:
     793    psStatsOptions meanOption = psStatsMeanOption(stats->options);
     794    psStatsOptions stdevOption = psStatsStdevOption(stats->options);
     795    if (!psStatsSingleOption(meanOption)) {
     796        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
     797        return false;
     798    }
     799    if (!psStatsSingleOption(stdevOption)) {
     800        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
     801        return false;
     802    }
     803
     804    // clipping range defined by min and max and/or clipSigma
     805    psF32 minClipSigma;
     806    psF32 maxClipSigma;
     807    if (isfinite(stats->max)) {
     808        maxClipSigma = fabs(stats->max);
     809    } else {
     810        maxClipSigma = fabs(stats->clipSigma);
     811    }
     812    if (isfinite(stats->min)) {
     813        minClipSigma = fabs(stats->min);
     814    } else {
     815        minClipSigma = fabs(stats->clipSigma);
     816    }
     817
     818    psVector *mask = inMask;
     819    if (!inMask) {
     820        mask = psVectorAlloc (x->n, PS_TYPE_VECTOR_MASK);
     821        psVectorInit (mask, 0);
     822    }
     823
     824    // vector to store residuals
     825    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F32);
     826
     827    psTrace("psLib.imageops", 4, "stats->clipIter is %d\n", stats->clipIter);
     828    psTrace("psLib.imageops", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
     829
     830    for (psS32 N = 0; N < stats->clipIter; N++) {
     831        psTrace("psLib.imageops", 6, "Loop iteration %d.  Calling psImageMapFit()\n", N);
     832        psS32 Nkeep = 0;
     833        if (!psImageMapFitSparse(pGoodFit, map, mask, maskValue, x, y, f, df)) {
    404834            psError(PS_ERR_UNKNOWN, false, "Could not fit image map.\n");
    405835            psFree(resid);
Note: See TracChangeset for help on using the changeset viewer.