コード例 #1
0
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;
    }
}
コード例 #2
0
ファイル: k-merge_sort.c プロジェクト: RanLee/algorithm
//快速排序  
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);  
	}  
}  
コード例 #3
0
ファイル: sort.c プロジェクト: ChaoMu/myDS
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);
	}
} 
コード例 #4
0
ファイル: sort.cpp プロジェクト: YinWenAtBIT/Data-Structure
/*快速选择,选择完成之后,要求的第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);
}
コード例 #5
0
ファイル: sort.cpp プロジェクト: YinWenAtBIT/Data-Structure
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);
}
コード例 #6
0
ファイル: ArraySorter.cpp プロジェクト: konorati/223Projects
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);
	}
}
コード例 #7
0
ファイル: 1807.cpp プロジェクト: halftan/DataStructure
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);
}
コード例 #8
0
ファイル: sort.c プロジェクト: Lumi-liu/newbie_training
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);
	}
}
コード例 #9
0
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);
}
コード例 #10
0
ファイル: sort.c プロジェクト: unlessbamboo/grocery-shop
        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 );
        }
コード例 #11
0
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);
}
コード例 #12
0
ファイル: QuickSort.cpp プロジェクト: KoenChiu/Sort
/****************************************
快速排序的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;
}
コード例 #13
0
ファイル: DS_05_01_Sort.c プロジェクト: LuoYY/DataStructures
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);
    }
}
コード例 #14
0
ファイル: worker_thread.cpp プロジェクト: simonct/phd2
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;
}
コード例 #15
0
ファイル: usImage.cpp プロジェクト: xeqtr1982/phd2
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;
    }
}
コード例 #16
0
ファイル: star.cpp プロジェクト: Saharac/open-phd-guiding
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;
}
コード例 #17
0
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);
}
コード例 #18
0
ファイル: star.cpp プロジェクト: AndresPozo/phd2
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;
}