void GetMedian(int *vx, int *vy, int vx1, int vy1, int vx2, int vy2, int vx3, int vy3) { // existant median vector (not mixed) *vx = Median3(vx1, vx2, vx3); *vy = Median3(vy1, vy2, vy3); if ( (*vx == vx1 && *vy == vy1) || (*vx == vx2 && *vy == vy2) ||(*vx == vx3 && *vy == vy3)) return; else { *vx = vx1; *vy = vy1; } }
//快速排序 void QuickSort(int A[],int Left,int Right) { int i,j; int Pivot; const int Cutoff = 3; if (Left + Cutoff <= Right) { Pivot = Median3(A,Left,Right); i = Left; j = Right - 1; while (1) { while(A[++i] < Pivot){;} while(A[--j] > Pivot){;} if (i < j) swap_int(&A[i],&A[j]); else break; } swap_int(&A[i],&A[Right - 1]); QuickSort(A,Left,i - 1); QuickSort(A,i + 1,Right); } else { InsertionSort(A+Left,Right - Left + 1); } }
void Qsort(ElementType A[],int Left,int Right) { if(Left+Cutoff<=Right) { Pivot=Median3(A,Left,Right); i = Left;j = Right - 1; for(;;) { while(A[++i]<Pivot) { } while(A[--j]>Pirot) { } if(i<j) { Swap(&A[i],&A[j]); } else { break; } Swap(&A[i],&A[Right-1]); Qsort(A,Left,i-1); Qsort(A,i+1,Right); } } else { InsertionSort(A+Left,Right-Left+1); } }
/*快速选择,选择完成之后,要求的第K小的数在数组下标为k-1上*/ void QSelect(ElementType A[], int k, int Left, int Right) { int i,j; ElementType Pivot; Pivot = Median3(A, Left, Right); if(Left + Cutoff <Right) { i = Left; j = Right-1; while(1) { while(A[++i] < Pivot); while(A[--j] > Pivot); if(i < j) Swap(&A[i], &A[j]); else break; } /*将枢纽元放回中间,此时枢纽元左边的数据都比它小 右边的数据都比它大,再对左右数据排序即可*/ Swap(&A[i], &A[Right-1]); /*k=i+1的时候,即是i=k-1的时候,代表枢纽元就已经是要求的结果了, 正好处在k-1上,这里不使用k-1进行判断是为了避免k=0的情况*/ if(k <=i) QSelect(A, k, Left, i-1); else if(k>i+1) QSelect(A, k, i+1, Right); } else/*少于3个数据就直接使用插入排序更快*/ InsertionSort(A+Left, Right-Left+1); }
void Qsort(ElementType A[], int Left, int Right) { int i,j; ElementType Pivot; Pivot = Median3(A, Left, Right); if(Left + Cutoff <Right) { i = Left; j = Right-1; while(1) { while(A[++i] < Pivot); while(A[--j] > Pivot); if(i < j) Swap(&A[i], &A[j]); else break; } /*将枢纽元放回中间,此时枢纽元左边的数据都比它小 右边的数据都比它大,再对左右数据排序即可*/ Swap(&A[i], &A[Right-1]); Qsort(A, Left, i-1); Qsort(A, i+1, Right); } else /*少于3个数据就直接使用插入排序更快*/ InsertionSort(A+Left, Right-Left+1); }
void ArraySorter::QuickSort2(int *arr, int left, int right) { if((left + 10) <= right) { int pivot = Median3(arr, left, right); int i = left, j = right-1; for(;;) { while(arr[++i] < pivot) { // Nada mucho } while(pivot < arr[--j]) { // Empty } if( i < j ) { swap(arr, i, j); } else { break; } } swap(arr, i, right-1); QuickSort2(arr, left, i-1); QuickSort2(arr, i+1, right); } else //Run insertion sort { InsertionSort(arr, right+1, left, 0); } }
void Qsort(int A[], int left, int right){ int i,j; int pivot; if(left + 10 <= right){ pivot = Median3(A,left,right); i = left; j = right - 1; for(;;){ while(A[++i] < pivot){} while(A[--j] > pivot){} if(i < j){ int t; t = A[i]; A[i] = A[j]; A[j] = t; } else break; } int t = A[i]; A[i] = A[right - 1]; A[right - 1] = t; Qsort(A,left,i - 1); Qsort(A,i + 1,right); } else select_sort(A + left, right - left + 1); }
void QuickSort(int a[],int Left,int Right) { int i; int j; int k; int Center; const int Cutoff = 3; if (Left + Cutoff <= Right) { Center = Median3(a,Left,Right); i = Left; j = Right-1; while (1)//for() { while(a[++i] < Center){;}//下标右移 while(a[--j] > Center){;}//下标左移 if (i < j) swap_int(&a[i],&a[j]); else break; } swap_int(&a[i],&a[Right-1]); QuickSort(a,Left,i-1); QuickSort(a,i+1,Right); } else { InsertionSort(a+Left,Right-Left+1); } }
void QSort(ElementType a[], int left, int right) { int i, j; ElementType pivot; if(right-left+1 > 10) { pivot = Median3(a, left, right); i = left; j = right - 1; while(1) { while(a[++i] < pivot); while(a[--j] > pivot); if(i < j) Swap(&a[i], &a[j]); else break; } Swap(&a[i], &a[right-1]); QSort(a, left, i-1); QSort(a, i+1, right); } else //对于小数组,直接采用插入排序 InsertionSort(a+left, right-left+1); }
void Qsort( ElementType A[ ], int Left, int Right ) { int i, j; ElementType Pivot; /* 1*/ if( Left + Cutoff <= Right ) { /* 2*/ Pivot = Median3( A, Left, Right ); /* 3*/ i = Left; j = Right - 1; /* 4*/ for( ; ; ) { /* 5*/ while( A[ ++i ] < Pivot ){ } /* 6*/ while( A[ --j ] > Pivot ){ } /* 7*/ if( i < j ) /* job*/ Swap( &A[ i ], &A[ j ] ); else /* 9*/ break; } /*10*/ Swap( &A[ i ], &A[ Right - 1 ] ); /* Restore pivot */ /*11*/ Qsort( A, Left, i - 1 ); /*12*/ Qsort( A, i + 1, Right ); } else /* Do an insertion sort on the subarray */ /*13*/ InsertionSort( A + Left, Right - Left + 1 ); }
void Qsort(ElementType A[], int Left, int Right) { const int SPACE = sizeof(ElementType) + 2 * sizeof(int); Resource_logSpace(SPACE); int i, j; //2 ElementType Pivot; //1 if (Left + Cutoff <= Right) { //2 Pivot = Median3(A, Left, Right); //1 i = Left; //1 j = Right - 1; //2 for (;;) { while (A[++i] < Pivot) { //4 Resource_logTime(4); } while (A[--j] > Pivot) { Resource_logTime(4); } if (i < j) { //1 Swap(&A[i], &A[j]); //4 Resource_logTime(5); } else { Resource_logTime(1); break; } } Swap(&A[i], &A[Right - 1]); /* Restore pivot *///5 Qsort(A, Left, i - 1); //1 Qsort(A, i + 1, Right); //1 Resource_logTime(11); } else { /* Do an insertion sort on the subarray */ insertionSort(A + Left, Right - Left + 1); // 3 Resource_logTime(3); } Resource_logTime(7); Resource_logSpace(-SPACE); }
/**************************************** 快速排序的Partuition算法 ****************************************/ int Partition(int A[],int Left,int Right) { int i,j; int Pivot; i = Left;j = Right; Pivot = Median3(A,Left,Right); //Pivot = A[Left]; while(i<j) { while(i<j && A[j] >= Pivot) --j; while(i<j && A[i] <= Pivot) ++i; if(i<j) Swap(&A[i],&A[j]); } Swap(&A[Left],&A[i]); return i; }
void QSort(ElementType S[], long L, long R) { long i, j, Tmp; if (Cutoff >= R - L + 1) { for (i = L+1; i <= R; i++) { Tmp = S[i]; for (j = i; j > L && S[j-1] > Tmp; j--) { S[j] = S[j-1]; } S[j] = Tmp; } } else { ElementType Pivot; i = L; j = R-2; Median3(S, L, R, Pivot); while(true) { while(S[++i] < pivot); while(S[++j] > pivot); if (i < j) { Swap(&S[i], &S[j]); } else { break; } } Swap(&S[i], &S[R-1]); QSort(S, L, i-1); QSort(S, i+1, R); } }
bool WorkerThread::HandleExpose(MyFrame::EXPOSE_REQUEST *req) { bool bError = false; try { if (WorkerThread::MilliSleep(m_pFrame->GetTimeLapse(), INT_ANY)) { throw ERROR_INFO("Time lapse interrupted"); } if (pCamera->HasNonGuiCapture()) { Debug.Write(wxString::Format("Handling exposure in thread, d=%d o=%x r=(%d,%d,%d,%d)\n", req->exposureDuration, req->options, req->subframe.x, req->subframe.y, req->subframe.width, req->subframe.height)); if (GuideCamera::Capture(pCamera, req->exposureDuration, *req->pImage, req->options, req->subframe)) { throw ERROR_INFO("Capture failed"); } } else { Debug.Write(wxString::Format("Handling exposure in myFrame, d=%d o=%x r=(%d,%d,%d,%d)\n", req->exposureDuration, req->options, req->subframe.x, req->subframe.y, req->subframe.width, req->subframe.height)); wxSemaphore semaphore; req->pSemaphore = &semaphore; wxCommandEvent evt(REQUEST_EXPOSURE_EVENT, GetId()); evt.SetClientData(req); wxQueueEvent(m_pFrame, evt.Clone()); // wait for the request to complete req->pSemaphore->Wait(); bError = req->error; req->pSemaphore = NULL; } Debug.AddLine("Exposure complete"); if (!bError) { switch (m_pFrame->GetNoiseReductionMethod()) { case NR_NONE: break; case NR_2x2MEAN: QuickLRecon(*req->pImage); break; case NR_3x3MEDIAN: Median3(*req->pImage); break; } req->pImage->CalcStats(); } } catch (wxString Msg) { POSSIBLY_UNUSED(Msg); bError = true; } return bError; }
void usImage::CalcStats() { if (!ImageData || !NPixels) return; Min = 65535; Max = 0; FiltMin = 65535; FiltMax = 0; if (Subframe.IsEmpty()) { // full frame, no subframe const unsigned short *src; src = ImageData; for (int i = 0; i < NPixels; i++) { int d = (int) *src++; if (d < Min) Min = d; if (d > Max) Max = d; } unsigned short *tmpdata = new unsigned short[NPixels]; Median3(tmpdata, ImageData, Size, wxRect(Size)); src = tmpdata; for (int i = 0; i < NPixels; i++) { int d = (int) *src++; if (d < FiltMin) FiltMin = d; if (d > FiltMax) FiltMax = d; } delete[] tmpdata; } else { // Subframe unsigned int pixcnt = Subframe.width * Subframe.height; unsigned short *tmpdata = new unsigned short[pixcnt]; unsigned short *dst; dst = tmpdata; for (int y = 0; y < Subframe.height; y++) { const unsigned short *src = ImageData + Subframe.x + (Subframe.y + y) * Size.GetWidth(); for (int x = 0; x < Subframe.width; x++) { int d = (int) *src; if (d < Min) Min = d; if (d > Max) Max = d; *dst++ = *src++; } } dst = new unsigned short[pixcnt]; Median3(dst, tmpdata, Subframe.GetSize(), wxRect(Subframe.GetSize())); const unsigned short *src = dst; for (unsigned int i = 0; i < pixcnt; i++) { int d = (int) *src++; if (d < FiltMin) FiltMin = d; if (d > FiltMax) FiltMax = d; } delete[] dst; delete[] tmpdata; } }
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; }
void test_Median3() { ElementType A[] = {8, 1, 4, 9, 0, 3, 5, 2, 7, 6}; int temp = Median3(A, 0, 9); printf("The median of {8, 1, 4, 9, 0, 3, 5, 2, 7, 6} is %4d\n",temp); }
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; }