Example #1
0
bool Star::WasFound(void)
{
    return WasFound(m_lastFindResult);
}
Example #2
0
bool Star::Find(const usImage *pImg, int searchRegion, int base_x, int base_y, FindMode mode)
{
    FindResult Result = STAR_OK;
    double newX = base_x;
    double newY = base_y;

    try
    {
        Debug.Write(wxString::Format("Star::Find(%d, %d, %d, %d, (%d,%d,%d,%d))\n", searchRegion, base_x, base_y, mode,
            pImg->Subframe.x, pImg->Subframe.y, pImg->Subframe.width, pImg->Subframe.height));

        if (base_x < 0 || base_y < 0)
        {
            throw ERROR_INFO("coordinates are invalid");
        }

        int minx, miny, maxx, maxy;

        if (pImg->Subframe.IsEmpty())
        {
            minx = miny = 0;
            maxx = pImg->Size.GetWidth() - 1;
            maxy = pImg->Size.GetHeight() - 1;
        }
        else
        {
            minx = pImg->Subframe.GetLeft();
            maxx = pImg->Subframe.GetRight();
            miny = pImg->Subframe.GetTop();
            maxy = pImg->Subframe.GetBottom();
        }

        // search region bounds
        int start_x = wxMax(base_x - searchRegion, minx);
        int end_x   = wxMin(base_x + searchRegion, maxx);
        int start_y = wxMax(base_y - searchRegion, miny);
        int end_y   = wxMin(base_y + searchRegion, maxy);

        const unsigned short *imgdata = pImg->ImageData;
        int rowsize = pImg->Size.GetWidth();

        int peak_x = 0, peak_y = 0;
        unsigned int peak_val = 0;
        unsigned short max3[3] = { 0, 0, 0 };

        if (mode == FIND_PEAK)
        {
            for (int y = start_y; y <= end_y; y++)
            {
                for (int x = start_x; x <= end_x; x++)
                {
                    unsigned short val = imgdata[y * rowsize + x];

                    if (val > peak_val)
                    {
                        peak_val = val;
                        peak_x = x;
                        peak_y = y;
                    }
                }
            }
        }
        else
        {
            // find the peak value within the search region using a smoothing function
            // also check for saturation

            for (int y = start_y + 1; y <= end_y - 1; y++)
            {
                for (int x = start_x + 1; x <= end_x - 1; x++)
                {
                    unsigned short p = imgdata[y * rowsize + x];
                    unsigned int val =
                        2 * (unsigned int) p +
                        imgdata[(y - 1) * rowsize + (x + 0)] +
                        imgdata[(y + 0) * rowsize + (x - 1)] +
                        imgdata[(y + 0) * rowsize + (x + 1)] +
                        imgdata[(y + 1) * rowsize + (x + 0)];

                    if (val > peak_val)
                    {
                        peak_val = val;
                        peak_x = x;
                        peak_y = y;
                    }

                    if (p > max3[0])
                        std::swap(p, max3[0]);
                    if (p > max3[1])
                        std::swap(p, max3[1]);
                    if (p > max3[2])
                        std::swap(p, max3[2]);
                }
            }
        }

        // meaure noise in the annulus with inner radius A and outer radius B
        int const A = 7;   // inner radius
        int const B = 12;  // outer radius
        int const A2 = A * A;
        int const B2 = B * B;

        // find the mean and stdev of the background

        double sum = 0.0;
        double a = 0.0;
        double q = 0.0;
        int n = 0;

        const unsigned short *row = imgdata + rowsize * start_y;
        for (int y = start_y; y <= end_y; y++, row += rowsize)
        {
            int dy = y - peak_y;
            int dy2 = dy * dy;
            for (int x = start_x; x <= end_x; x++)
            {
                int dx = x - peak_x;
                int r2 = dx * dx + dy2;

                // exclude points not in annulus
                if (r2 <= A2 || r2 > B2)
                    continue;

                double const val = (double) row[x];
                sum += val;
                ++n;
                double const k = (double) n;
                double const a0 = a;
                a += (val - a) / k;
                q += (val - a0) * (val - a);
            }
        }

        double const mean_bg = sum / (double) n;
        double const sigma_bg = sqrt(q / (double) (n - 1));

        double cx = 0.0;
        double cy = 0.0;
        double mass = 0.0;

        if (mode == FIND_PEAK)
        {
            mass = peak_val;
            n = 1;
        }
        else
        {
            unsigned short const thresh = (unsigned short)(mean_bg + 2.0 * sigma_bg);

            // find pixels over threshold within aperture; compute mass and centroid

            start_x = wxMax(peak_x - A, minx);
            end_x = wxMin(peak_x + A, maxx);
            start_y = wxMax(peak_y - A, miny);
            end_y = wxMin(peak_y + A, maxy);

            n = 0;

            row = imgdata + rowsize * start_y;
            for (int y = start_y; y <= end_y; y++, row += rowsize)
            {
                int dy = y - peak_y;
                int dy2 = dy * dy;
                if (dy2 > A2)
                    continue;

                for (int x = start_x; x <= end_x; x++)
                {
                    int dx = x - peak_x;

                    // exclude points outside aperture
                    if (dx * dx + dy2 > A2)
                        continue;

                    // exclude points below threshold
                    unsigned short val = row[x];
                    if (val < thresh)
                        continue;

                    double const d = (double) val - mean_bg;

                    cx += dx * d;
                    cy += dy * d;
                    mass += d;
                    ++n;
                }
            }
        }

        Mass = mass;
        SNR = n > 0 ? mass / (sigma_bg * n) : 0.0;

        double const LOW_SNR = 3.0;

        if (mass < 10.0)
            Result = STAR_LOWMASS;
        else if (SNR < LOW_SNR)
            Result = STAR_LOWSNR;
        else
        {
            newX = peak_x + cx / mass;
            newY = peak_y + cy / mass;

            // even at saturation, the max values may vary a bit due to noise
            // Call it saturated if the the top three values are within 32 parts per 65535 of max
            if ((unsigned int)(max3[0] - max3[2]) * 65535U < 32U * (unsigned int) max3[0])
                Result = STAR_SATURATED;
        }
    }
    catch (const wxString& Msg)
    {
        POSSIBLY_UNUSED(Msg);

        if (Result == STAR_OK)
        {
            Result = STAR_ERROR;
        }
    }

    // update state
    SetXY(newX, newY);
    m_lastFindResult = Result;

    bool bReturn = WasFound(Result);

    if (!bReturn)
    {
        Mass = 0.0;
        SNR = 0.0;
    }

    Debug.AddLine(wxString::Format("Star::Find returns %d (%d), X=%.2f, Y=%.2f, Mass=%.f, SNR=%.1f",
        bReturn, Result, newX, newY, Mass, SNR));

    return bReturn;
}
Example #3
0
bool Star::Find(const usImage *pImg, int searchRegion, int base_x, int base_y, FindMode mode)
{
    FindResult Result = STAR_OK;
    double newX = base_x;
    double newY = base_y;

    try
    {
        Debug.Write(wxString::Format("Star::Find(%d, %d, %d, %d, (%d,%d,%d,%d))\n", searchRegion, base_x, base_y, mode,
            pImg->Subframe.x, pImg->Subframe.y, pImg->Subframe.width, pImg->Subframe.height));

        if (base_x < 0 || base_y < 0)
        {
            throw ERROR_INFO("coordinates are invalid");
        }

        int minx, miny, maxx, maxy;

        if (pImg->Subframe.IsEmpty())
        {
            minx = miny = 0;
            maxx = pImg->Size.GetWidth() - 1;
            maxy = pImg->Size.GetHeight() - 1;
        }
        else
        {
            minx = pImg->Subframe.GetLeft();
            maxx = pImg->Subframe.GetRight();
            miny = pImg->Subframe.GetTop();
            maxy = pImg->Subframe.GetBottom();
        }

        // search region bounds
        int start_x = wxMax(base_x - searchRegion, minx);
        int end_x   = wxMin(base_x + searchRegion, maxx);
        int start_y = wxMax(base_y - searchRegion, miny);
        int end_y   = wxMin(base_y + searchRegion, maxy);

        const unsigned short *imgdata = pImg->ImageData;
        int rowsize = pImg->Size.GetWidth();

        int peak_x = 0, peak_y = 0;
        unsigned int peak_val = 0;
        unsigned short max3[3] = { 0, 0, 0 };

        if (mode == FIND_PEAK)
        {
            for (int y = start_y; y <= end_y; y++)
            {
                for (int x = start_x; x <= end_x; x++)
                {
                    unsigned short val = imgdata[y * rowsize + x];

                    if (val > peak_val)
                    {
                        peak_val = val;
                        peak_x = x;
                        peak_y = y;
                    }
                }
            }

            PeakVal = peak_val;
        }
        else
        {
            // find the peak value within the search region using a smoothing function
            // also check for saturation

            for (int y = start_y + 1; y <= end_y - 1; y++)
            {
                for (int x = start_x + 1; x <= end_x - 1; x++)
                {
                    unsigned short p = imgdata[y * rowsize + x];
                    unsigned int val =
                        4 * (unsigned int) p +
                        imgdata[(y - 1) * rowsize + (x - 1)] +
                        imgdata[(y - 1) * rowsize + (x + 1)] +
                        imgdata[(y + 1) * rowsize + (x - 1)] +
                        imgdata[(y + 1) * rowsize + (x + 1)] +
                        2 * imgdata[(y - 1) * rowsize + (x + 0)] +
                        2 * imgdata[(y + 0) * rowsize + (x - 1)] +
                        2 * imgdata[(y + 0) * rowsize + (x + 1)] +
                        2 * imgdata[(y + 1) * rowsize + (x + 0)];

                    if (val > peak_val)
                    {
                        peak_val = val;
                        peak_x = x;
                        peak_y = y;
                    }

                    if (p > max3[0])
                        std::swap(p, max3[0]);
                    if (p > max3[1])
                        std::swap(p, max3[1]);
                    if (p > max3[2])
                        std::swap(p, max3[2]);
                }
            }

            PeakVal = max3[0];   // raw peak val
            peak_val /= 16; // smoothed peak value
        }

        // meaure noise in the annulus with inner radius A and outer radius B
        int const A = 7;   // inner radius
        int const B = 12;  // outer radius
        int const A2 = A * A;
        int const B2 = B * B;

        // center window around peak value
        start_x = wxMax(peak_x - B, minx);
        end_x = wxMin(peak_x + B, maxx);
        start_y = wxMax(peak_y - B, miny);
        end_y = wxMin(peak_y + B, maxy);

        // find the mean and stdev of the background

        double sum = 0.0;
        double a = 0.0;
        double q = 0.0;
        unsigned int nbg = 0;

        const unsigned short *row = imgdata + rowsize * start_y;
        for (int y = start_y; y <= end_y; y++, row += rowsize)
        {
            int dy = y - peak_y;
            int dy2 = dy * dy;
            for (int x = start_x; x <= end_x; x++)
            {
                int dx = x - peak_x;
                int r2 = dx * dx + dy2;

                // exclude points not in annulus
                if (r2 <= A2 || r2 > B2)
                    continue;

                double const val = (double) row[x];
                sum += val;
                ++nbg;
                double const k = (double) nbg;
                double const a0 = a;
                a += (val - a) / k;
                q += (val - a0) * (val - a);
            }
        }

        double const mean_bg = sum / (double) nbg;
        double const sigma2_bg = q / (double) (nbg - 1);
        double const sigma_bg = sqrt(sigma2_bg);
        unsigned short thresh;

        double cx = 0.0;
        double cy = 0.0;
        double mass = 0.0;
        unsigned int n;

        std::vector<R2M> hfrvec;

        if (mode == FIND_PEAK)
        {
            mass = peak_val;
            n = 1;
            thresh = 0;
        }
        else
        {
            thresh = (unsigned short)(mean_bg + 3.0 * sigma_bg + 0.5);

            // find pixels over threshold within aperture; compute mass and centroid

            start_x = wxMax(peak_x - A, minx);
            end_x = wxMin(peak_x + A, maxx);
            start_y = wxMax(peak_y - A, miny);
            end_y = wxMin(peak_y + A, maxy);

            n = 0;

            row = imgdata + rowsize * start_y;
            for (int y = start_y; y <= end_y; y++, row += rowsize)
            {
                int dy = y - peak_y;
                int dy2 = dy * dy;
                if (dy2 > A2)
                    continue;

                for (int x = start_x; x <= end_x; x++)
                {
                    int dx = x - peak_x;

                    // exclude points outside aperture
                    if (dx * dx + dy2 > A2)
                        continue;

                    // exclude points below threshold
                    unsigned short val = row[x];
                    if (val < thresh)
                        continue;

                    double const d = (double) val - mean_bg;

                    cx += dx * d;
                    cy += dy * d;
                    mass += d;
                    ++n;

                    hfrvec.push_back(R2M(x, y, d));
                }
            }
        }

        Mass = mass;

        // SNR estimate from: Measuring the Signal-to-Noise Ratio S/N of the CCD Image of a Star or Nebula, J.H.Simonetti, 2004 January 8
        //     http://www.phys.vt.edu/~jhs/phys3154/snr20040108.pdf
        double const gain = .5; // electrons per ADU, nominal
        SNR = n > 0 ? mass / sqrt(mass / gain + sigma2_bg * (double) n * (1.0 + 1.0 / (double) nbg)) : 0.0;

        double const LOW_SNR = 3.0;

        // a few scattered pixels over threshold can give a false positive
        // avoid this by requiring the smoothed peak value to be above the threshold
        if (peak_val <= thresh && SNR >= LOW_SNR)
        {
            Debug.Write(wxString::Format("Star::Find false star n=%u nbg=%u bg=%.1f sigma=%.1f thresh=%u peak=%u\n", n, nbg, mean_bg, sigma_bg, thresh, peak_val));
            SNR = LOW_SNR - 0.1;
        }

        if (mass < 10.0)
            Result = STAR_LOWMASS;
        else if (SNR < LOW_SNR)
            Result = STAR_LOWSNR;
        else
        {
            newX = peak_x + cx / mass;
            newY = peak_y + cy / mass;

            HFD = 2.0 * hfr(hfrvec, newX, newY, mass);

            // even at saturation, the max values may vary a bit due to noise
            // Call it saturated if the the top three values are within 32 parts per 65535 of max for 16-bit cameras,
            // or within 1 part per 191 for 8-bit cameras
            unsigned int d = (unsigned int) (max3[0] - max3[2]);
            unsigned int mx = (unsigned int) max3[0];

            // remove pedestal
            if (mx >= pImg->Pedestal)
                mx -= pImg->Pedestal;
            else
                mx = 0; // unlikely

            if (pImg->BitsPerPixel < 12)
            {
                if (d * 191U < 1U * mx)
                    Result = STAR_SATURATED;
            }
            else
            {
                if (d * 65535U < 32U * mx)
                    Result = STAR_SATURATED;
            }
        }
    }
    catch (const wxString& Msg)
    {
        POSSIBLY_UNUSED(Msg);

        if (Result == STAR_OK)
        {
            Result = STAR_ERROR;
        }
    }

    // update state
    SetXY(newX, newY);
    m_lastFindResult = Result;

    bool wasFound = WasFound(Result);

    if (!IsValid() || Result == STAR_ERROR)
    {
        Mass = 0.0;
        SNR = 0.0;
        HFD = 0.0;
    }

    Debug.Write(wxString::Format("Star::Find returns %d (%d), X=%.2f, Y=%.2f, Mass=%.f, SNR=%.1f, Peak=%hu HFD=%.1f\n",
        wasFound, Result, newX, newY, Mass, SNR, PeakVal, HFD));

    return wasFound;
}