// Copyright (C) 1999-2018 // Smithsonian Astrophysical Observatory, Cambridge, MA, USA // For conditions of distribution and use, see copyright notice in "copyright" #include "context.h" #include "base.h" #include "fitsimage.h" #include "fvcontour.h" #include "sigbus.h" #ifdef INTERP #undef INTERP #endif #define INTERP (((Base*)parent_)->interp) static const char* methodName_[] = { "smooth", "block" }; // It is a modified version of contour code found in Fv 2.4 // Fv may be obtained from the HEASARC (High Energy Astrophysics Science // Archive Research Center) FTOOLS Web site at: // http://heasarc.gsfc.nasa.gov/ftools/fv.html // The original author is unknown. FVContour::FVContour() { parent_ =NULL; colorName_ = dupstr("green"); lineWidth_ =1; dash_ =0; dlist_[0] =8; dlist_[1] =3; method_ = BLOCK; smooth_ =4; numLevel_ =5; level_ =NULL; scale_ =NULL; } FVContour::~FVContour() { if (colorName_) delete [] colorName_; if (level_) delete [] level_; if (scale_) delete scale_; } void FVContour::create(Base* pp, FitsImage* fits, FrScale* fr, const char* cc, int ww, int dd, Method mm, int nn, int rr, const char* ll) { lcontourlevel_.deleteAll(); parent_ = pp; colorName_ = dupstr(cc); lineWidth_ = ww; dash_ = dd; method_ = mm; smooth_ = rr; numLevel_ = nn; frScale_ = *fr; level_ = dupstr(ll); if (level_ && strlen(level_)>0) { int cnt = 0; double levels[100]; string x(level_); istringstream str(x); while ((cnt<100) && (str >> levels[cnt])) cnt++; scale_ = new InverseScale(cnt, levels); } else buildScale(fits); append(fits); } void FVContour::buildScale(FitsImage* fits) { switch (frScale_.colorScaleType()) { case FrScale::LINEARSCALE: scale_ = new LinearInverseScale(numLevel_, frScale_.low(), frScale_.high()); break; case FrScale::LOGSCALE: scale_ = new LogInverseScale(numLevel_, frScale_.low(), frScale_.high(), frScale_.expo()); break; case FrScale::POWSCALE: scale_ = new PowInverseScale(numLevel_, frScale_.low(), frScale_.high(), frScale_.expo()); break; case FrScale::SQRTSCALE: scale_ = new SqrtInverseScale(numLevel_, frScale_.low(), frScale_.high()); break; case FrScale::SQUAREDSCALE: scale_ = new SquaredInverseScale(numLevel_, frScale_.low(), frScale_.high()); break; case FrScale::ASINHSCALE: scale_ = new AsinhInverseScale(numLevel_, frScale_.low(), frScale_.high()); break; case FrScale::SINHSCALE: scale_ = new SinhInverseScale(numLevel_, frScale_.low(), frScale_.high()); break; case FrScale::HISTEQUSCALE: scale_ = new HistEquInverseScale(numLevel_, frScale_.low(), frScale_.high(), frScale_.histequ(fits), HISTEQUSIZE); break; case FrScale::IISSCALE: scale_ = new IISInverseScale(numLevel_, frScale_.low(), frScale_.high(), fits->iisz()); break; } } void FVContour::update(FitsImage* fits) { lcontourlevel_.deleteAll(); switch (frScale_.clipScope()) { case FrScale::GLOBAL: break; case FrScale::LOCAL: if (scale_) delete scale_; buildScale(fits); if (level_) delete [] level_; { ostringstream str; str << *scale_ << ends; level_ = dupstr(str.str().c_str()); } break; } append(fits); } const char* FVContour::methodName() { return methodName_[method_]; } void FVContour::append(FitsImage* fits) { if (smooth_ == 1) unity(fits); else switch (method_) { case SMOOTH: nobin(fits); break; case BLOCK: bin(fits); break; } } void FVContour::unity(FitsImage* fits) { FitsBound* params = fits->getDataParams(((Base*)parent_)->currentContext->secMode()); long width = fits->width(); long height = fits->height(); // blank img long size = width*height; double* img = new double[size]; if (!img) { internalError("FVContour could not allocate enough memory"); return; } for (long ii=0; ii<size; ii++) img[ii] = FLT_MIN; // fill img SETSIGBUS for(long jj=params->ymin; jj<params->ymax; jj++) { for(long ii=params->xmin; ii<params->xmax; ii++) { long kk = jj*width + ii; double vv = fits->getValueDouble(kk); if (isfinite(vv)) img[kk] = vv; } } CLEARSIGBUS // do contours build(width, height, img, fits->dataToRef); // clean up delete [] img; } void FVContour::nobin(FitsImage* fits) { long width = fits->width(); long height = fits->height(); // blank img long size = width*height; double* img = new double[size]; if (!img) { internalError("FVContour could not allocate enough memory"); return; } for (long ii=0; ii<size; ii++) img[ii] = FLT_MIN; // generate kernal int r = smooth_-1; double* kernal = gaussian(r); // convolve convolve(fits,kernal,img,r); // now, do contours build(width, height, img, fits->dataToRef); // cleanup delete kernal; delete [] img; } void FVContour::convolve(FitsImage* fits, double* kernal, double* dest, int r) { FitsBound* params = fits->getDataParams(((Base*)parent_)->currentContext->secMode()); long width = fits->width(); int rr = 2*r+1; SETSIGBUS for (long jj=params->ymin; jj<params->ymax; jj++) { for (long ii=params->xmin; ii<params->xmax; ii++) { long ir = ii-r; long irr = ii+r; long jr = jj-r; long jrr = jj+r; for (long n=jr, nn=0; n<=jrr; n++, nn++) { if (n>=params->ymin && n<params->ymax) { for (long m=ir, mm=0; m<=irr; m++, mm++) { if (m>=params->xmin && m<params->xmax) { double vv = fits->getValueDouble(n*width+m); if (isfinite(vv)) { double kk = kernal[nn*rr+mm]; double* ptr = dest+(jj*width+ii); if (*ptr == FLT_MIN) *ptr = vv*kk; else *ptr += vv*kk; } } } } } } } CLEARSIGBUS } double* FVContour::tophat(int r) { int rr = 2*r+1; int ksz = rr*rr; double* kernal = new double[ksz]; memset(kernal, 0, ksz*sizeof(double)); double kt = 0; for (int yy=-r; yy<=r; yy++) { for (int xx=-r; xx<=r; xx++) { if ((xx*xx + yy*yy) <= r*r) { kernal[(yy+r)*rr+(xx+r)] = 1; kt++; } } } // normalize kernal for (int aa=0; aa<ksz; aa++) kernal[aa] /= kt; return kernal; } double* FVContour::gaussian(int r) { int rr = 2*r+1; int ksz = rr*rr; double sigma = r/2.; double* kernal = new double[ksz]; memset(kernal, 0, ksz*sizeof(double)); double kt = 0; double aa = 1./(sigma*sigma); double cc = 1./(sigma*sigma); for (int yy=-r; yy<=r; yy++) { for (int xx=-r; xx<=r; xx++) { if ((xx*xx + yy*yy) <= r*r) { double vv = exp(-.5*(aa*xx*xx + cc*yy*yy)); kernal[(yy+r)*rr+(xx+r)] = vv; kt += vv; } } } // normalize kernal for (int aa=0; aa<ksz; aa++) kernal[aa] /= kt; return kernal; } void FVContour::bin(FitsImage* fits) { FitsBound* params = fits->getDataParams(((Base*)parent_)->currentContext->secMode()); long width = fits->width(); long height = fits->height(); int rr = smooth_; long w2 = (long)(width/rr); long h2 = (long)(height/rr); Matrix m = Translate((Vector(-width,-height)/2).floor()) * Scale(1./rr) * Translate((Vector(w2,h2)/2).floor()); Matrix n = m.invert(); double* mm = m.mm(); double* img = new double[w2 * h2]; { for (long jj=0; jj<h2; jj++) for (long ii=0; ii<w2; ii++) img[jj*w2 + ii] = FLT_MIN; } short* count = new short[w2 * h2]; memset(count, 0, w2*h2*sizeof(short)); SETSIGBUS for (long jj=params->ymin; jj<params->ymax; jj++) { for (long ii=params->xmin; ii<params->xmax; ii++) { double xx = ii*mm[0] + jj*mm[3] + mm[6]; double yy = ii*mm[1] + jj*mm[4] + mm[7]; if (xx >= 0 && xx < w2 && yy >= 0 && yy < h2) { long kk = (long(yy)*w2 + long(xx)); double v = fits->getValueDouble(jj*width + ii); if (isfinite(v)) { if (count[kk]) img[kk] += v; else img[kk] = v; count[kk]++; } } } } for (long kk=0; kk<w2*h2; kk++) if (count[kk]) img[kk] /= count[kk]; CLEARSIGBUS delete [] count; Matrix w = n * fits->dataToRef; build(w2, h2, img, w); delete [] img; } void FVContour::build(long xdim, long ydim, double *image, Matrix& mx) { long nelem = xdim*ydim; char* usedGrid = new char[nelem]; double** rows = new double*[ydim]; for (long jj=0; jj<ydim; jj++) rows[jj] = image + jj*xdim; for (long c=0; c<scale_->size(); c++) { double cntour = scale_->level(c); ContourLevel* cl =new ContourLevel(parent_, cntour, colorName_, lineWidth_, dash_, dlist_); memset(usedGrid,0,nelem); // Search outer edge long ii,jj; // Search top for (jj=0, ii=0; ii<xdim-1; ii++) if (rows[jj][ii]<cntour && cntour<=rows[jj][ii+1]) trace(xdim, ydim, cntour, ii, jj, top, rows, usedGrid, mx, cl); // Search right for (jj=0; jj<ydim-1; jj++) if (rows[jj][ii]<cntour && cntour<=rows[jj+1][ii]) trace(xdim, ydim, cntour, ii-1, jj, right, rows, usedGrid, mx, cl); // Search Bottom for (ii--; ii>=0; ii--) if (rows[jj][ii+1]<cntour && cntour<=rows[jj][ii]) trace(xdim, ydim, cntour, ii, jj-1, bottom, rows, usedGrid, mx, cl); // Search Left for (ii=0, jj--; jj>=0; jj--) if (rows[jj+1][ii]<cntour && cntour<=rows[jj][ii]) trace(xdim, ydim, cntour, ii, jj, left, rows, usedGrid, mx, cl); // Search each row of the image for (jj=1; jj<ydim-1; jj++) for (ii=0; ii<xdim-1; ii++) if (!usedGrid[jj*xdim + ii] && rows[jj][ii]<cntour && cntour<=rows[jj][ii+1]) trace(xdim, ydim, cntour, ii, jj, top, rows, usedGrid, mx, cl); if (!cl->lcontour().isEmpty()) lcontourlevel_.append(cl); } delete [] usedGrid; delete [] rows; } void FVContour::trace(long xdim, long ydim, double cntr, long xCell, long yCell, int side, double** rows, char* usedGrid, Matrix& mx, ContourLevel* cl) { long ii = xCell; long jj = yCell; int origSide = side; int init = 1; int done = (ii<0 || ii>=xdim-1 || (jj<0 && jj>=ydim-1)); Contour* cc = new Contour(cl); while (!done) { int flag = 0; double a = rows[jj][ii]; double b = rows[jj][ii+1]; double c = rows[jj+1][ii+1]; double d = rows[jj+1][ii]; double X, Y; if (init) { init = 0; switch (side) { case top: X = (cntr-a) / (b-a) + ii; Y = jj; break; case right: X = ii+1; Y = (cntr-b) / (c-b) + jj; break; case bottom: X = (cntr-c) / (d-c) + ii; Y = jj+1; break; case left: X = ii; Y = (cntr-a) / (d-a) + jj; break; } } else { if (side==top) usedGrid[jj*xdim + ii] = 1; do { if (++side == none) side = top; switch (side) { case top: if (a>=cntr && cntr>b) { flag = 1; X = (cntr-a) / (b-a) + ii; Y = jj; jj--; } break; case right: if( b>=cntr && cntr>c ) { flag = 1; X = ii+1; Y = (cntr-b) / (c-b) + jj; ii++; } break; case bottom: if( c>=cntr && cntr>d ) { flag = 1; X = (cntr-d) / (c-d) + ii; Y = jj+1; jj++; } break; case left: if( d>=cntr && cntr>a ) { flag = 1; X = ii; Y = (cntr-a) / (d-a) + jj; ii--; } break; } } while (!flag); if (++side == none) side = top; if (++side == none) side = top; if (ii==xCell && jj==yCell && side==origSide) done = 1; if (ii<0 || ii>=xdim-1 || jj<0 || jj>=ydim-1) done = 1; } cc->lvertex().append(new Vertex(Vector(X+.5,Y+.5)*mx)); } if (!cc->lvertex().isEmpty()) cl->lcontour().append(cc); else delete cc; }