Changeset 37721 for trunk/psLib/src/imageops/psImageMapFit.c
- Timestamp:
- Dec 5, 2014, 4:44:10 PM (11 years ago)
- File:
-
- 1 edited
-
trunk/psLib/src/imageops/psImageMapFit.c (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/imageops/psImageMapFit.c
r34153 r37721 18 18 19 19 #include <stdio.h> 20 #include <stdlib.h> 20 21 #include "psError.h" 21 22 #include "psAbort.h" … … 35 36 #include "psImageStructManip.h" 36 37 #include "psImageMap.h" 38 #include "psSparse.h" 37 39 // #include "psImagePixelInterpolate.h" 38 40 // #include "psImageUnbin.h" … … 118 120 psImageInit (A, 0.0); 119 121 psVectorInit (B, 0.0); 120 122 121 123 // we are looping over the Nx,Ny image map elements; 122 124 // the matrix equation contains Nx*Ny rows and columns … … 262 264 int I = n + Nx * m; 263 265 B->data.F32[I] = fi_rx_ry + fi_rx_py + fi_px_ry + fi_px_py; 264 266 265 267 // insert these values into their corresponding locations in A, B 266 268 // float Sum = 0.0; … … 273 275 int J = (n + jn) + Nx * (m + jm); 274 276 A->data.F32[J][I] = sA[jn][jm]; 277 275 278 // fprintf (stderr, "A %d %d (%d %d : %d %d): %f\n", I, J, n, m, n + jn, m + jm, sA[jn][jm]); 276 279 // Sum += sA[jn][jm]; … … 320 323 return true; 321 324 } 322 325 323 326 // set bad values to NaN 324 327 for (int i = 0; i < Nx*Ny; i++) { … … 341 344 psFree (B); 342 345 psFree (Empty); 343 344 346 *pGoodFit = true; 345 347 return true; … … 402 404 psS32 Nkeep = 0; 403 405 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. 478 bool 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 779 bool 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)) { 404 834 psError(PS_ERR_UNKNOWN, false, "Could not fit image map.\n"); 405 835 psFree(resid);
Note:
See TracChangeset
for help on using the changeset viewer.
