/*! * 2D wavelet noise * @param[in] nx,ny 解像度 */ void rxWaveletNoise::GenerateNoiseTile2(int n) { // MARK:GenerateNoiseTile2 if(g_pNoiseTileData != NULL && n == g_iNoiseTileSize) return; if(n%2) n++; // tile size must be even int sz = n*n; RXREAL *temp1 = new RXREAL[sz]; RXREAL *temp2 = new RXREAL[sz]; RXREAL *noise = new RXREAL[sz]; // Step 1. Fill the tile with random numbers in the range -1 to 1. for(int i = 0; i < n*n; ++i){ noise[i] = GaussianNoise(); temp1[i] = temp2[i] = 0; } // Steps 2 and 3. Downsample and Upsample the tile for(int iy = 0; iy < n; ++iy){ // each x row int idx = iy*n; Downsample(&noise[idx], &temp1[idx], n, 1); Upsample( &temp1[idx], &temp2[idx], n, 1); } for(int ix = 0; ix < n; ++ix){ // each y row int idx = ix; Downsample(&temp2[idx], &temp1[idx], n, n); Upsample( &temp1[idx], &temp2[idx], n, n); } // Step 4. Subtract out the coarse-scale contribution for(int i = 0; i < n*n; ++i){ noise[i] -= temp2[i]; } // Avoid even/odd variance difference by adding odd-offset version of noise to itself. int offset = n/2; if(offset%2 == 0) offset++; for(int i = 0, ix = 0; ix < n; ++ix){ for(int iy = 0; iy < n; ++iy){ temp1[i++] = noise[ModW(ix+offset, n)+ModW(iy+offset, n)*n]; } } for(int i = 0; i < n*n; ++i){ noise[i] += temp1[i]; } g_pNoiseTileData = noise; g_iNoiseTileSize = n; delete [] temp1; delete [] temp2; }
/// Downsampler::Measure double Downsampler::Measure(class ImageLayout *src,class ImageLayout *dest,double in) { src->TestIfCompatible(dest); Downsample(m_ppucSource,src); Downsample(m_ppucDestination,dest); return in; }
static void DownsampleY(float* to, const float* from, int sx,int sy, int sz) { for (int ix = 0; ix < sx; ix++) for (int iz = 0; iz < sz; iz++) { const int i = ix + iz*sx*sy; Downsample(&from[i], &to[i], sy, sx); } }
// some convenience functions for an nxn image static void DownsampleX(float* to, const float* from, int sx,int sy, int sz) { for (int iy = 0; iy < sy; iy++) for (int iz = 0; iz < sz; iz++) { const int i = iy * sx + iz*sx*sy; Downsample(&from[i], &to[i], sx, 1); } }
static void DownsampleZ(float* to, const float* from, int sx,int sy, int sz) { for (int ix = 0; ix < sx; ix++) for (int iy = 0; iy < sy; iy++) { const int i = ix + iy*sx; Downsample(&from[i], &to[i], sz, sx*sy); } }
void train(const TemplateList &data) { if (!transform || !transform->trainable) return; TemplateList downsampled = Downsample(data, classes, instances, fraction, inputVariable, gallery, subjects); transform->train(downsampled); }
void* _Paint( void *ithr ) { CThrdat &me = vthr[(long)ithr]; for( int i = me.i0; i < me.ilim; ++i ) { vector<uint8> msk; uint8* src; TAffine inv; uint32 w, h; int x0, xL, y0, yL, wL, hL, wi, hi; src = Raster8FromAny( GP->vTile[i].name.c_str(), w, h, GP->flog ); if( GP->resmask ) ResinMask8( msk, src, w, h, false ); if( GP->sdnorm > 0 ) NormRas( src, w, h, GP->lgord, GP->sdnorm ); if( GP->resmask ) { int n = w * h; for( int j = 0; j < n; ++j ) { if( !msk[j] ) src[j] = GP->bkval; } } ScanLims( x0, xL, y0, yL, GP->ws, GP->hs, GP->vTile[i].t2g, w, h ); wi = w; hi = h; inv.InverseOf( GP->vTile[i].t2g ); if( GP->iscl > 1 ) { // Scaling down // actually downsample src image Downsample( src, wi, hi, GP->iscl ); // and point at the new pixels TAffine A; A.NUSetScl( 1.0/GP->iscl ); inv = A * inv; } wL = wi - 1; hL = hi - 1; for( int iy = y0; iy < yL; ++iy ) { for( int ix = x0; ix < xL; ++ix ) { Point p( ix, iy ); inv.Transform( p ); if( p.x >= 0 && p.x < wL && p.y >= 0 && p.y < hL ) { int pix = (int)SafeInterp( p.x, p.y, src, wi, hi ); if( pix != GP->bkval ) GP->scp[ix+GP->ws*iy] = pix; } } } RasterFree( src ); } return NULL; }
bool PixPair::Load( const char *apath, const char *bpath, int order, int bDoG, int r1, int r2, FILE* flog ) { printf( "\n---- Image loading ----\n" ); clock_t t0 = StartTiming(); /* ----------------------------- */ /* Load and sanity check rasters */ /* ----------------------------- */ uint8 *aras, *bras; uint32 wa, ha, wb, hb; int ok = false; aras = Raster8FromAny( apath, wa, ha, flog ); bras = Raster8FromAny( bpath, wb, hb, flog ); if( !aras || !bras ) { fprintf( flog, "PixPair: Picture load failure.\n" ); goto exit; } if( wa != wb || ha != hb ) { fprintf( flog, "PixPair: Nonmatching picture dimensions.\n" ); goto exit; } ok = true; wf = wa; hf = ha; ws = wa; hs = ha; scl = 1; /* -------------- */ /* Resin removal? */ /* -------------- */ //if( dbgCor ) { // ZeroResin( "bloba.tif", aras ); // ZeroResin( "blobb.tif", bras ); //} //else { // ZeroResin( NULL, aras ); // ZeroResin( NULL, bras ); //} //StopTiming( flog, "Resin removal", t0 ); /* ------- */ /* Flatten */ /* ------- */ LegPolyFlatten( _avf, aras, wf, hf, order ); RasterFree( aras ); LegPolyFlatten( _bvf, bras, wf, hf, order ); RasterFree( bras ); avs_vfy = avs_aln = avf_vfy = avf_aln = &_avf; bvs_vfy = bvs_aln = bvf_vfy = bvf_aln = &_bvf; /* ------------- */ /* Apply filters */ /* ------------- */ //{ // vector<CD> kfft; // double K[] = { // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; // Convolve( _avf, _avf, wf, hf, K, 11, 11, true, true, kfft ); // Normalize( _avf ); // Convolve( _bvf, _bvf, wf, hf, K, 11, 11, true, true, kfft ); // Normalize( _bvf ); //} #if 0 { vector<CD> kfft; double K[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; Convolve( _avfflt, _avf, wf, hf, K, 11, 11, true, true, kfft ); Normalize( _avfflt ); Convolve( _bvfflt, _bvf, wf, hf, K, 11, 11, true, true, kfft ); Normalize( _bvfflt ); avs_aln = avf_aln = &_avfflt; bvs_aln = bvf_aln = &_bvfflt; bDoG = true; } #endif if( bDoG ) { vector<double> DoG; vector<CD> kfft; int dim = MakeDoGKernel( DoG, r1, r2, flog ); Convolve( _avfflt, _avf, wf, hf, &DoG[0], dim, dim, true, true, kfft ); Normalize( _avfflt ); Convolve( _bvfflt, _bvf, wf, hf, &DoG[0], dim, dim, true, true, kfft ); Normalize( _bvfflt ); avs_aln = avf_aln = &_avfflt; bvs_aln = bvf_aln = &_bvfflt; } /* --------------------- */ /* Downsample all images */ /* --------------------- */ if( ws > 2048 || hs >= 2048 ) { do { ws /= 2; hs /= 2; scl *= 2; } while( ws > 2048 || hs > 2048 ); fprintf( flog, "PixPair: Scaling by %d\n", scl ); if( ws * scl != wf || hs * scl != hf ) { fprintf( flog, "PixPair: Dimensions not multiple of scale!\n" ); goto exit; } Downsample( _avs, _avf ); Downsample( _bvs, _bvf ); avs_vfy = avs_aln = &_avs; bvs_vfy = bvs_aln = &_bvs; if( bDoG ) { if( _avfflt.size() ) { Downsample( _avsflt, _avfflt ); avs_aln = &_avsflt; } if( _bvfflt.size() ) { Downsample( _bvsflt, _bvfflt ); bvs_aln = &_bvsflt; } } } else fprintf( flog, "PixPair: Using image scale=1.\n" ); /* ------------------------------ */ /* Write DoG images for debugging */ /* ------------------------------ */ #if 0 if( bDoG ) { VectorDblToTif8( "DoGa.tif", avs_aln, ws, hs ); VectorDblToTif8( "DoGb.tif", bvs_aln, ws, hs ); } #endif /* -------- */ /* Clean up */ /* -------- */ exit: if( aras ) RasterFree( aras ); if( bras ) RasterFree( bras ); StopTiming( flog, "Image conditioning", t0 ); return ok; }
bool SiftGPU::BuildGaussPyramid(IplImage* base) { float k; int intvlsSum = intvls + 3; float sig_total, sig_prev; printf("\n ----------- BuildGaussPyramid inside --------------- \n"); imgArray = (IplImage**)calloc(octvs, sizeof(IplImage*)); sigmaList[0] = SIFT_SIGMA; k = pow( 2.0, 1.0 / intvls ); for(int i = 1; i < intvlsSum; i++ ) { sig_prev = pow( k, i - 1 ) * SIFT_SIGMA; sig_total = sig_prev * k; sigmaList[i] = sqrt( sig_total * sig_total - sig_prev * sig_prev ); } imgArray[0] = cvCloneImage(base); sizeOfImages[0] = imgArray[0]->imageSize; SizeOfPyramid += imgArray[0]->imageSize * intvlsSum; imageHeightInPyramid[0] = imgArray[0]->height; imageWidthInPyramid[0] = imgArray[0]->width; for(int o = 1; o < octvs; o++ ) { imgArray[o] = Downsample( imgArray[o-1] ); SizeOfPyramid += imgArray[o]->imageSize * intvlsSum; sizeOfImages[o] = imgArray[o]->imageSize; imageHeightInPyramid[o] = imgArray[o]->height; imageWidthInPyramid[o] = imgArray[o]->width; } gaussFilterGPU->CreateBufferForPyramid(SizeOfPyramid); subtractGPU->CreateBufferForPyramid(SizeOfPyramid); int offset = 0; offset = 0; int OffsetAct = 0; int OffsetPrev = 0; for(int o = 0; o < octvs; o++ ) { for(int i = 0; i < intvlsSum; i++ ) { if( o == 0 && i == 0 ) { gaussFilterGPU->SendImageToPyramid(imgArray[o], OffsetAct); } else if(i == 0) { gaussFilterGPU->ReceiveImageFromPyramid(imgArray[o-1], OffsetPrev); imgArray[o] = Downsample( imgArray[o-1] ); gaussFilterGPU->SendImageToPyramid(imgArray[o], OffsetAct); } if(i > 0 ) { gaussFilterGPU->Process( sigmaList[i], imgArray[o]->width, imgArray[o]->height, OffsetPrev, OffsetAct); subtractGPU->Process(gaussFilterGPU->cmBufPyramid, imageWidthInPyramid[o], imageHeightInPyramid[o], OffsetPrev, OffsetAct); } OffsetPrev = OffsetAct; OffsetAct += sizeOfImages[o]; } } //free( sigmaList ); return true; }
/*! * 3Dノイズタイル生成 * @param[in] n タイルグリッド数 */ RXREAL* rxWaveletNoise::GenerateNoiseTile4r(int &n, int &nt) { if(n%2) n++; // tile size must be even long sz = n*n*n*nt; RXREAL *temp1 = new RXREAL[sz]; RXREAL *temp2 = new RXREAL[sz]; RXREAL *noise = new RXREAL[sz]; // Step 1. Fill the tile with random numbers in the range -1 to 1. for(int i = 0; i < sz; ++i){ noise[i] = GaussianNoise(); } // Steps 2 and 3. Downsample and Upsample the tile for(int iy = 0; iy < n; ++iy){ for(int iz = 0; iz < n; ++iz){ for(int it = 0; it < nt; ++it){ // each x row long idx = iy*n+iz*n*n+it*n*n*n; Downsample(&noise[idx], &temp1[idx], n, 1); Upsample( &temp1[idx], &temp2[idx], n, 1); } } } for(int ix = 0; ix < n; ++ix){ for(int iz = 0; iz < n; ++iz){ for(int it = 0; it < nt; ++it){ // each y row long idx = ix+iz*n*n+it*n*n*n; Downsample(&temp2[idx], &temp1[idx], n, n); Upsample( &temp1[idx], &temp2[idx], n, n); } } } for(int ix = 0; ix < n; ++ix){ for(int iy = 0; iy < n; ++iy){ for(int it = 0; it < nt; ++it){ // each z row long idx = ix+iy*n+it*n*n*n; Downsample(&temp2[idx], &temp1[idx], n, n*n); Upsample( &temp1[idx], &temp2[idx], n, n*n); } } } for(int ix = 0; ix < n; ++ix){ for(int iy = 0; iy < n; ++iy){ for(int iz = 0; iz < n; ++iz){ // each t row long idx = ix+iy*n+iz*n*n; Downsample(&temp2[idx], &temp1[idx], nt, n*n*n); Upsample( &temp1[idx], &temp2[idx], nt, n*n*n); } } } // Step 4. Subtract out the coarse-scale contribution for(int i = 0; i < sz; ++i){ noise[i] -= temp2[i]; } // Avoid even/odd variance difference by adding odd-offset version of noise to itself. int offset = n/2; if(offset%2 == 0) offset++; for(int i = 0, ix = 0; ix < n; ++ix){ for(int iy = 0; iy < n; ++iy){ for(int iz = 0; iz < n; ++iz){ for(int it = 0; it < nt; ++it){ temp1[i++] = noise[ModW(ix+offset, n)+ModW(iy+offset, n)*n+ModW(iz+offset, n)*n*n+ModW(it+offset, nt)*n*n*n]; } } } } for(int i = 0; i < sz; ++i){ noise[i] += temp1[i]; } delete [] temp1; delete [] temp2; return noise; }
/*! * * @param[in] * @return */ RXREAL* rxWaveletNoise::GenerateNoiseTile3r(int &nx, int &ny, int &nz) { if(nx%2) nx++; // tile size must be even if(ny%2) ny++; // tile size must be even if(nz%2) nz++; // tile size must be even int sz = nx*ny*nz; RXREAL *temp1 = new RXREAL[sz]; RXREAL *temp2 = new RXREAL[sz]; RXREAL *noise = new RXREAL[sz]; init_genrand((unsigned)time(NULL)); //init_genrand(1234); // Step 1. Fill the tile with random numbers in the range -1 to 1. for(int i = 0; i < sz; ++i){ noise[i] = GaussianNoise(); } // Steps 2 and 3. Downsample and Upsample the tile for(int iy = 0; iy < ny; ++iy){ for(int iz = 0; iz < nz; ++iz){ // each x row int idx = iy*nx+iz*nx*ny; Downsample(&noise[idx], &temp1[idx], nx, 1); Upsample( &temp1[idx], &temp2[idx], nx, 1); } } for(int ix = 0; ix < nx; ++ix){ for(int iz = 0; iz < nz; ++iz){ // each y row int idx = ix+iz*nx*ny; Downsample(&temp2[idx], &temp1[idx], ny, nx); Upsample( &temp1[idx], &temp2[idx], ny, nx); } } for(int ix = 0; ix < nx; ++ix){ for(int iy = 0; iy < ny; ++iy){ // each z row int idx = ix+iy*nx; Downsample(&temp2[idx], &temp1[idx], nz, nx*ny); Upsample( &temp1[idx], &temp2[idx], nz, nx*ny); } } // Step 4. Subtract out the coarse-scale contribution for(int i = 0; i < sz; ++i){ noise[i] -= temp2[i]; } // Avoid even/odd variance difference by adding odd-offset version of noise to itself. int offset = nx/2; if(offset%2 == 0) offset++; for(int i = 0, ix = 0; ix < nx; ++ix){ for(int iy = 0; iy < ny; ++iy){ for(int iz = 0; iz < nz; ++iz){ temp1[i++] = noise[ModW(ix+offset, nx)+ModW(iy+offset, ny)*nx+ModW(iz+offset, nz)*nx*ny]; } } } for(int i = 0; i < sz; ++i){ noise[i] += temp1[i]; } delete [] temp1; delete [] temp2; return noise; }
bool Star::AutoFind(const usImage& image, int extraEdgeAllowance, int searchRegion) { if (!image.Subframe.IsEmpty()) { Debug.AddLine("Autofind called on subframe, returning error"); return false; // not found } wxBusyCursor busy; Debug.AddLine(wxString::Format("Star::AutoFind called with edgeAllowance = %d searchRegion = %d", extraEdgeAllowance, searchRegion)); // run a 3x3 median first to eliminate hot pixels usImage smoothed; smoothed.CopyFrom(image); Median3(smoothed); // convert to floating point FloatImg conv(smoothed); // downsample the source image const int downsample = 1; if (downsample > 1) { FloatImg tmp; Downsample(tmp, conv, downsample); conv.Swap(tmp); } // run the PSF convolution { FloatImg tmp; psf_conv(tmp, conv); conv.Swap(tmp); } enum { CONV_RADIUS = 4 }; int dw = conv.Size.GetWidth(); // width of the downsampled image int dh = conv.Size.GetHeight(); // height of the downsampled image wxRect convRect(CONV_RADIUS, CONV_RADIUS, dw - 2 * CONV_RADIUS, dh - 2 * CONV_RADIUS); // region containing valid data SaveImage(conv, "PHD2_AutoFind.fit"); enum { TOP_N = 100 }; // keep track of the brightest stars std::set<Peak> stars; // sorted by ascending intensity double global_mean, global_stdev; GetStats(&global_mean, &global_stdev, conv, convRect); Debug.AddLine("AutoFind: global mean = %.1f, stdev %.1f", global_mean, global_stdev); const double threshold = 0.1; Debug.AddLine("AutoFind: using threshold = %.1f", threshold); // find each local maximum int srch = 4; for (int y = convRect.GetTop() + srch; y <= convRect.GetBottom() - srch; y++) { for (int x = convRect.GetLeft() + srch; x <= convRect.GetRight() - srch; x++) { float val = conv.px[dw * y + x]; bool ismax = false; if (val > 0.0) { ismax = true; for (int j = -srch; j <= srch; j++) { for (int i = -srch; i <= srch; i++) { if (i == 0 && j == 0) continue; if (conv.px[dw * (y + j) + (x + i)] > val) { ismax = false; break; } } } } if (!ismax) continue; // compare local maximum to mean value of surrounding pixels const int local = 7; double local_mean, local_stdev; wxRect localRect(x - local, y - local, 2 * local + 1, 2 * local + 1); localRect.Intersect(convRect); GetStats(&local_mean, &local_stdev, conv, localRect); // this is our measure of star intensity double h = (val - local_mean) / global_stdev; if (h < threshold) { // Debug.AddLine(wxString::Format("AG: local max REJECT [%d, %d] PSF %.1f SNR %.1f", imgx, imgy, val, SNR)); continue; } // coordinates on the original image int imgx = x * downsample + downsample / 2; int imgy = y * downsample + downsample / 2; stars.insert(Peak(imgx, imgy, h)); if (stars.size() > TOP_N) stars.erase(stars.begin()); } } for (std::set<Peak>::const_reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) Debug.AddLine("AutoFind: local max [%d, %d] %.1f", it->x, it->y, it->val); // merge stars that are very close into a single star { const int minlimitsq = 5 * 5; repeat: for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set<Peak>::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = a->x - b->x; int dy = a->y - b->y; int d2 = dx * dx + dy * dy; if (d2 < minlimitsq) { // very close, treat as single star Debug.AddLine("AutoFind: merge [%d, %d] %.1f - [%d, %d] %.1f", a->x, a->y, a->val, b->x, b->y, b->val); // erase the dimmer one stars.erase(a); goto repeat; } } } } // exclude stars that would fit within a single searchRegion box { // build a list of stars to be excluded std::set<int> to_erase; const int extra = 5; // extra safety margin const int fullw = searchRegion + extra; for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set<Peak>::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = abs(a->x - b->x); int dy = abs(a->y - b->y); if (dx <= fullw && dy <= fullw) { // stars closer than search region, exclude them both // but do not let a very dim star eliminate a very bright star if (b->val / a->val >= 5.0) { Debug.AddLine("AutoFind: close dim-bright [%d, %d] %.1f - [%d, %d] %.1f", a->x, a->y, a->val, b->x, b->y, b->val); } else { Debug.AddLine("AutoFind: too close [%d, %d] %.1f - [%d, %d] %.1f", a->x, a->y, a->val, b->x, b->y, b->val); to_erase.insert(std::distance(stars.begin(), a)); to_erase.insert(std::distance(stars.begin(), b)); } } } } RemoveItems(stars, to_erase); } // exclude stars too close to the edge { enum { MIN_EDGE_DIST = 40 }; int edgeDist = MIN_EDGE_DIST + extraEdgeAllowance; std::set<Peak>::iterator it = stars.begin(); while (it != stars.end()) { std::set<Peak>::iterator next = it; ++next; if (it->x <= edgeDist || it->x >= image.Size.GetWidth() - edgeDist || it->y <= edgeDist || it->y >= image.Size.GetHeight() - edgeDist) { Debug.AddLine("AutoFind: too close to edge [%d, %d] %.1f", it->x, it->y, it->val); stars.erase(it); } it = next; } } // At first I tried running Star::Find on the survivors to find the best // star. This had the unfortunate effect of locating hot pixels which // the psf convolution so nicely avoids. So, don't do that! -ag // find the brightest non-saturated star. If no non-saturated stars, settle for a saturated star. bool allowSaturated = false; while (true) { Debug.AddLine("AutoSelect: finding best star allowSaturated = %d", allowSaturated); for (std::set<Peak>::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) { Star tmp; tmp.Find(&image, searchRegion, it->x, it->y, FIND_CENTROID); if (tmp.WasFound()) { if (tmp.GetError() == STAR_SATURATED && !allowSaturated) { Debug.AddLine("Autofind: star saturated [%d, %d] %.1f Mass %.f SNR %.1f", it->x, it->y, it->val, tmp.Mass, tmp.SNR); continue; } SetXY(it->x, it->y); Debug.AddLine("Autofind returns star at [%d, %d] %.1f Mass %.f SNR %.1f", it->x, it->y, it->val, tmp.Mass, tmp.SNR); return true; } } if (allowSaturated) break; // no stars found Debug.AddLine("AutoFind: could not find a non-saturated star!"); allowSaturated = true; } Debug.AddLine("Autofind: no star found"); return false; }
bool Star::AutoFind(const usImage& image, int extraEdgeAllowance, int searchRegion) { if (!image.Subframe.IsEmpty()) { Debug.AddLine("Autofind called on subframe, returning error"); return false; // not found } wxBusyCursor busy; Debug.Write(wxString::Format("Star::AutoFind called with edgeAllowance = %d searchRegion = %d\n", extraEdgeAllowance, searchRegion)); // run a 3x3 median first to eliminate hot pixels usImage smoothed; smoothed.CopyFrom(image); Median3(smoothed); // convert to floating point FloatImg conv(smoothed); // downsample the source image const int downsample = 1; if (downsample > 1) { FloatImg tmp; Downsample(tmp, conv, downsample); conv.Swap(tmp); } // run the PSF convolution { FloatImg tmp; psf_conv(tmp, conv); conv.Swap(tmp); } enum { CONV_RADIUS = 4 }; int dw = conv.Size.GetWidth(); // width of the downsampled image int dh = conv.Size.GetHeight(); // height of the downsampled image wxRect convRect(CONV_RADIUS, CONV_RADIUS, dw - 2 * CONV_RADIUS, dh - 2 * CONV_RADIUS); // region containing valid data SaveImage(conv, "PHD2_AutoFind.fit"); enum { TOP_N = 100 }; // keep track of the brightest stars std::set<Peak> stars; // sorted by ascending intensity double global_mean, global_stdev; GetStats(&global_mean, &global_stdev, conv, convRect); Debug.Write(wxString::Format("AutoFind: global mean = %.1f, stdev %.1f\n", global_mean, global_stdev)); const double threshold = 0.1; Debug.Write(wxString::Format("AutoFind: using threshold = %.1f\n", threshold)); // find each local maximum int srch = 4; for (int y = convRect.GetTop() + srch; y <= convRect.GetBottom() - srch; y++) { for (int x = convRect.GetLeft() + srch; x <= convRect.GetRight() - srch; x++) { float val = conv.px[dw * y + x]; bool ismax = false; if (val > 0.0) { ismax = true; for (int j = -srch; j <= srch; j++) { for (int i = -srch; i <= srch; i++) { if (i == 0 && j == 0) continue; if (conv.px[dw * (y + j) + (x + i)] > val) { ismax = false; break; } } } } if (!ismax) continue; // compare local maximum to mean value of surrounding pixels const int local = 7; double local_mean, local_stdev; wxRect localRect(x - local, y - local, 2 * local + 1, 2 * local + 1); localRect.Intersect(convRect); GetStats(&local_mean, &local_stdev, conv, localRect); // this is our measure of star intensity double h = (val - local_mean) / global_stdev; if (h < threshold) { // Debug.Write(wxString::Format("AG: local max REJECT [%d, %d] PSF %.1f SNR %.1f\n", imgx, imgy, val, SNR)); continue; } // coordinates on the original image int imgx = x * downsample + downsample / 2; int imgy = y * downsample + downsample / 2; stars.insert(Peak(imgx, imgy, h)); if (stars.size() > TOP_N) stars.erase(stars.begin()); } } for (std::set<Peak>::const_reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) Debug.Write(wxString::Format("AutoFind: local max [%d, %d] %.1f\n", it->x, it->y, it->val)); // merge stars that are very close into a single star { const int minlimitsq = 5 * 5; repeat: for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set<Peak>::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = a->x - b->x; int dy = a->y - b->y; int d2 = dx * dx + dy * dy; if (d2 < minlimitsq) { // very close, treat as single star Debug.Write(wxString::Format("AutoFind: merge [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); // erase the dimmer one stars.erase(a); goto repeat; } } } } // exclude stars that would fit within a single searchRegion box { // build a list of stars to be excluded std::set<int> to_erase; const int extra = 5; // extra safety margin const int fullw = searchRegion + extra; for (std::set<Peak>::const_iterator a = stars.begin(); a != stars.end(); ++a) { std::set<Peak>::const_iterator b = a; ++b; for (; b != stars.end(); ++b) { int dx = abs(a->x - b->x); int dy = abs(a->y - b->y); if (dx <= fullw && dy <= fullw) { // stars closer than search region, exclude them both // but do not let a very dim star eliminate a very bright star if (b->val / a->val >= 5.0) { Debug.Write(wxString::Format("AutoFind: close dim-bright [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); } else { Debug.Write(wxString::Format("AutoFind: too close [%d, %d] %.1f - [%d, %d] %.1f\n", a->x, a->y, a->val, b->x, b->y, b->val)); to_erase.insert(std::distance(stars.begin(), a)); to_erase.insert(std::distance(stars.begin(), b)); } } } } RemoveItems(stars, to_erase); } // exclude stars too close to the edge { enum { MIN_EDGE_DIST = 40 }; int edgeDist = MIN_EDGE_DIST + extraEdgeAllowance; std::set<Peak>::iterator it = stars.begin(); while (it != stars.end()) { std::set<Peak>::iterator next = it; ++next; if (it->x <= edgeDist || it->x >= image.Size.GetWidth() - edgeDist || it->y <= edgeDist || it->y >= image.Size.GetHeight() - edgeDist) { Debug.Write(wxString::Format("AutoFind: too close to edge [%d, %d] %.1f\n", it->x, it->y, it->val)); stars.erase(it); } it = next; } } // At first I tried running Star::Find on the survivors to find the best // star. This had the unfortunate effect of locating hot pixels which // the psf convolution so nicely avoids. So, don't do that! -ag // try to identify the saturation point // first, find the peak pixel overall unsigned short maxVal = 0; for (unsigned int i = 0; i < image.NPixels; i++) if (image.ImageData[i] > maxVal) maxVal = image.ImageData[i]; // next see if any of the stars has a flat-top bool foundSaturated = false; for (std::set<Peak>::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) { Star tmp; tmp.Find(&image, searchRegion, it->x, it->y, FIND_CENTROID); if (tmp.WasFound() && tmp.GetError() == STAR_SATURATED) { if ((maxVal - tmp.PeakVal) * 255U > maxVal) { // false positive saturation, flat top but below maxVal Debug.Write(wxString::Format("AutoSelect: false positive saturation peak = %hu, max = %hu\n", tmp.PeakVal, maxVal)); } else { // a saturated star was found foundSaturated = true; break; } } } unsigned int sat_level; // saturation level, including pedestal if (foundSaturated) { // use the peak overall pixel value as the saturation limit Debug.Write(wxString::Format("AutoSelect: using saturation level peakVal = %hu\n", maxVal)); sat_level = maxVal; // includes pedestal } else { // no staurated stars found, can't make any assumption about whether the max val is saturated Debug.Write(wxString::Format("AutoSelect: using saturation level from BPP %u and pedestal %hu\n", image.BitsPerPixel, image.Pedestal)); sat_level = ((1U << image.BitsPerPixel) - 1) + image.Pedestal; if (sat_level > 65535) sat_level = 65535; } unsigned int diff = sat_level > image.Pedestal ? sat_level - image.Pedestal : 0U; // "near-saturation" threshold at 90% saturation unsigned short sat_thresh = (unsigned short)((unsigned int) image.Pedestal + 9 * diff / 10); Debug.Write(wxString::Format("AutoSelect: BPP = %u, saturation at %u, pedestal %hu, thresh = %hu\n", image.BitsPerPixel, sat_level, image.Pedestal, sat_thresh)); // Final star selection // pass 1: find brightest star with peak value < 90% saturation AND SNR > 6 // this pass will reject saturated and nearly-saturated stars // pass 2: find brightest non-saturated star // pass 3: find brightest star, even if saturated for (int pass = 1; pass <= 3; pass++) { Debug.Write(wxString::Format("AutoSelect: finding best star pass %d\n", pass)); for (std::set<Peak>::reverse_iterator it = stars.rbegin(); it != stars.rend(); ++it) { Star tmp; tmp.Find(&image, searchRegion, it->x, it->y, FIND_CENTROID); if (tmp.WasFound()) { if (pass == 1) { if (tmp.PeakVal > sat_thresh) { Debug.Write(wxString::Format("Autofind: near-saturated [%d, %d] %.1f Mass %.f SNR %.1f Peak %hu\n", it->x, it->y, it->val, tmp.Mass, tmp.SNR, tmp.PeakVal)); continue; } if (tmp.GetError() == STAR_SATURATED || tmp.SNR < 6.0) continue; } else if (pass == 2) { if (tmp.GetError() == STAR_SATURATED) { Debug.Write(wxString::Format("Autofind: star saturated [%d, %d] %.1f Mass %.f SNR %.1f\n", it->x, it->y, it->val, tmp.Mass, tmp.SNR)); continue; } } // star accepted SetXY(it->x, it->y); Debug.Write(wxString::Format("Autofind returns star at [%d, %d] %.1f Mass %.f SNR %.1f\n", it->x, it->y, it->val, tmp.Mass, tmp.SNR)); return true; } } if (pass == 1) Debug.Write("AutoFind: could not find a star on Pass 1\n"); else if (pass == 2) Debug.Write("AutoFind: could not find a non-saturated star!\n"); } Debug.Write("Autofind: no star found\n"); return false; }