void DataSignal :: WriteSmartPeakInfoToXML (RGTextOutput& text, const RGString& indent, const RGString& bracketTag, const RGString& locationTag) { int peak; Endl endLine; RGString suffix; // if (HasCrossChannelSignalLink ()) { if (HasAlleleName () && (!mDoNotCall)) { peak = (int) floor (Peak () + 0.5); if (mOffGrid) suffix = " OL"; text << indent << "<" << bracketTag << ">" << endLine; text << indent << "\t<mean>" << GetMean () << "</mean>" << endLine; text << indent << "\t<height>" << peak << "</height>" << endLine; text << indent << "\t<BPS>" << GetBioID () << "</BPS>" << endLine; // text << indent << "\t<" << locationTag << ">" << (int) floor (GetApproximateBioID () + 0.5) << "</" << locationTag << ">" << endLine; text << indent << "\t<" << locationTag << ">" << GetApproximateBioID () << "</" << locationTag << ">" << endLine; text << indent << "\t<PeakArea>" << TheoreticalArea () << "</PeakArea>" << endLine; text << indent << "\t<allele>" << GetAlleleName () << suffix << "</allele>" << endLine; text << indent << "\t<width>" << 4.0 * GetStandardDeviation () << "</width>" << endLine; text << indent << "\t<fit>" << GetCurveFit () << "</fit>" << endLine; text << indent << "</" + bracketTag << ">" << endLine; } // } }
bool DataSignal :: ReportSmartNoticeObjects (RGTextOutput& text, const RGString& indent, const RGString& delim) { if (NumberOfSmartNoticeObjects () > 0) { int msgLevel = GetHighestMessageLevelWithRestrictionSM (); RGDListIterator it (*mSmartMessageReporters); SmartMessageReporter* nextNotice; text.SetOutputLevel (msgLevel); if (!text.TestCurrentLevel ()) { text.ResetOutputLevel (); return false; } Endl endLine; text << endLine; text << indent << "Notices for curve with (Mean, Sigma, Peak, 2Content, Fit) = " << delim << delim << delim << delim << delim << delim; text << GetMean () << delim << GetStandardDeviation () << delim << Peak () << delim << GetScale (2) << delim << Fit << endLine; while (nextNotice = (SmartMessageReporter*) it ()) text << indent << nextNotice->GetMessage () << nextNotice->GetMessageData () << endLine; text.ResetOutputLevel (); text.Write (1, "\n"); } else return false; return true; }
/** Add a peak to the list * @param ipeak :: Peak object to add (copy) into this. */ void PeaksWorkspace::addPeak(const API::IPeak& ipeak) { if (dynamic_cast<const Peak*>(&ipeak)) { peaks.push_back((const Peak&) ipeak); } else { peaks.push_back(Peak(ipeak)); } }
/** Execute the algorithm. */ void CreatePeaksWorkspace::exec() { MatrixWorkspace_sptr instWS = getProperty("InstrumentWorkspace"); PeaksWorkspace_sptr out(new PeaksWorkspace()); setProperty("OutputWorkspace", out); int NumberOfPeaks = getProperty("NumberOfPeaks"); if (instWS) { out->setInstrument(instWS->getInstrument()); // Create some default peaks for (int i=0; i < NumberOfPeaks; i++) { out->addPeak( Peak(out->getInstrument(), out->getInstrument()->getDetectorIDs(true)[0], 1.0) ); } } }
double SuperGaussian :: GetPullupToleranceInBP (double noise) const { //pullUpToleranceFactor double P = Peak (); if (P <= 0.0) return (mPullupTolerance + (2.0 * sin (0.5 * acos (Fit)) / 4.47)); double localFit = Fit; if (localFit < 0.1) localFit = 0.1; double localNoise = noise; double LN = 0.95 * P; if (localNoise > LN) localNoise = LN; double temp1 = 1.0 / localFit; double temp = 2.0 * (temp1 * temp1 - 1.0) * StandardDeviation * log (P / (P - localNoise)); return (mPullupTolerance + pullUpToleranceFactor * mApproxBioIDPrime * sqrt (temp)); }
void PeakDetectorNaiveImpl::findPeaks(const FrequencyData& fd, peakdata::Scan& result) const { result.scanNumber = fd.scanNumber(); result.retentionTime = fd.retentionTime(); result.observationDuration = fd.observationDuration(); result.calibrationParameters = fd.calibrationParameters(); result.peakFamilies.clear(); const double noiseLevel = sqrt(fd.variance()); const double threshold = noiseLevel * noiseFactor_; for (FrequencyData::const_iterator it=fd.data().begin(); it!=fd.data().end(); ++it) if (isPeak(it, fd.data(), threshold, detectionRadius_)) { result.peakFamilies.push_back(PeakFamily()); PeakFamily& peakFamily = result.peakFamilies.back(); peakFamily.peaks.push_back(Peak()); Peak& peak = peakFamily.peaks.back(); peak.frequency = it->x; peak.intensity = it->y.real(); peak.phase = it->y.imag(); } }
void DataSignal :: WriteSmartTableArtifactInfoToXML (RGTextOutput& text, RGTextOutput& tempText, const RGString& indent, const RGString& bracketTag, const RGString& locationTag) { int peak; Endl endLine; RGString suffix; RGString label; SmartMessageReporter* notice; int i; RGString virtualAllele; SmartMessageReporter* nextNotice; text.SetOutputLevel (1); int msgNum; smAcceptedOLLeft acceptedOLLeft; smAcceptedOLRight acceptedOLRight; int reportedMessageLevel = GetHighestMessageLevelWithRestrictionSM (); //bool hasThreeLoci; //bool needLocus0; if ((!DontLook ()) && (NumberOfSmartNoticeObjects () != 0)) { bool firstNotice = true; peak = (int) floor (Peak () + 0.5); virtualAllele = GetVirtualAlleleName (); text << indent << "<" << bracketTag << ">" << endLine; // Should be <Artifact> text << indent << "\t<Id>" << GetSignalID () << "</Id>" << endLine; text << indent << "\t<Level>" << reportedMessageLevel << "</Level>" << endLine; text << indent << "\t<RFU>" << peak << "</RFU>" << endLine; text << indent << "\t<" << locationTag << ">" << GetApproximateBioID () << "</" << locationTag << ">" << endLine; text << indent << "\t<PeakArea>" << TheoreticalArea () << "</PeakArea>" << endLine; text << indent << "\t<Time>" << GetMean () << "</Time>" << endLine; text << indent << "\t<Fit>" << GetCurveFit () << "</Fit>" << endLine; if (!mAllowPeakEdit) text << indent << "\t<AllowPeakEdit>false</AllowPeakEdit>" << endLine; RGDListIterator it (*mSmartMessageReporters); i = 0; while (notice = (SmartMessageReporter*) it ()) { if (!notice->GetDisplayOsirisInfo ()) continue; if (firstNotice) { label = indent + "\t<Label>"; firstNotice = false; } if (i > 0) label << " "; label += notice->GetMessage (); label += notice->GetMessageData (); i++; } if (i > 0) { label << "</Label>"; text << label << endLine; } // Now add list of notices... it.Reset (); while (nextNotice = (SmartMessageReporter*) it ()) { msgNum = Notice::GetNextMessageNumber (); nextNotice->SetMessageCount (msgNum); text << indent << "\t<MessageNumber>" << msgNum << "</MessageNumber>" << endLine; tempText << "\t\t<Message>\n"; tempText << "\t\t\t<MessageNumber>" << msgNum << "</MessageNumber>\n"; tempText << "\t\t\t<Text>" << nextNotice->GetMessage () << nextNotice->GetMessageData () << "</Text>\n"; if (nextNotice->HasViableExportInfo ()) { if (nextNotice->IsEnabled ()) tempText << "\t\t\t<Hidden>false</Hidden>\n"; else tempText << "\t\t\t<Hidden>true</Hidden>\n"; if (!nextNotice->IsCritical ()) tempText << "\t\t\t<Critical>false</Critical>\n"; if (nextNotice->IsEnabled ()) tempText << "\t\t\t<Enabled>true</Enabled>\n"; else tempText << "\t\t\t<Enabled>false</Enabled>\n"; if (!nextNotice->IsEditable ()) tempText << "\t\t\t<Editable>false</Editable>\n"; if (nextNotice->GetDisplayExportInfo ()) tempText << "\t\t\t<DisplayExportInfo>true</DisplayExportInfo>\n"; else tempText << "\t\t\t<DisplayExportInfo>false</DisplayExportInfo>\n"; if (!nextNotice->GetDisplayOsirisInfo ()) tempText << "\t\t\t<DisplayOsirisInfo>false</DisplayOsirisInfo>\n"; tempText << "\t\t\t<MsgName>" << nextNotice->GetMessageName () << "</MsgName>\n"; //tempText << "\t\t\t<ExportProtocolList>"; //tempText << "\t\t\t" << nextNotice->GetExportProtocolInformation (); //tempText << "\t\t\t</ExportProtocolList>\n"; } tempText << "\t\t</Message>\n"; } // Now add list of alleles //hasThreeLoci = (mLocus != NULL) && (mLeftLocus != NULL) && (mRightLocus != NULL); //if (mLocus != NULL) { // if ((mLeftLocus == NULL) && (mRightLocus == NULL)) // needLocus0 = true; // else // needLocus0 = false; //} //else // needLocus0 = false; //needLocus0 = (!hasThreeLoci) && ((mLocus != mLeftLocus) || (mLocus != mRightLocus)); if (mLocus != NULL) { //testing RGString locusName = mLocus->GetLocusName (); suffix = GetAlleleName (0); if ((suffix.Length () > 0) || (virtualAllele.Length () > 0)) { text << indent << "\t<Allele>" << endLine; if (suffix.Length () > 0) text << indent << "\t\t<Name>" << suffix << "</Name>" << endLine; else text << indent << "\t\t<Name>" << virtualAllele << "</Name>" << endLine; if (mOffGrid) suffix = "true"; else if (mAcceptedOffGrid) suffix = "accepted"; else suffix = "false"; text << indent << "\t\t<OffLadder>" << suffix << "</OffLadder>" << endLine; text << indent << "\t\t<BPS>" << GetBioID (0) << "</BPS>" << endLine; text << indent << "\t\t<Locus>" << mLocus->GetLocusName () << "</Locus>" << endLine; text << indent << "\t\t<Location>0</Location>" << endLine; text << indent << "\t</Allele>" << endLine; } } if ((mLeftLocus != NULL) && (mLeftLocus != mLocus)) { if (mAlleleNameLeft.Length () > 0) { text << indent << "\t<Allele>" << endLine; text << indent << "\t\t<Name>" << mAlleleNameLeft << "</Name>" << endLine; if (mIsOffGridLeft) suffix = "true"; else if (GetMessageValue (acceptedOLLeft)) suffix = "accepted"; else suffix = "false"; text << indent << "\t\t<OffLadder>" << suffix << "</OffLadder>" << endLine; text << indent << "\t\t<BPS>" << GetBioID (-1) << "</BPS>" << endLine; text << indent << "\t\t<Locus>" << mLeftLocus->GetLocusName () << "</Locus>" << endLine; text << indent << "\t\t<Location>-1</Location>" << endLine; text << indent << "\t</Allele>" << endLine; } } if ((mRightLocus != NULL) && (mRightLocus != mLocus)) { if (mAlleleNameRight.Length () > 0) { text << indent << "\t<Allele>" << endLine; text << indent << "\t\t<Name>" << mAlleleNameRight << "</Name>" << endLine; if (mIsOffGridRight) suffix = "true"; else if (GetMessageValue (acceptedOLRight)) suffix = "accepted"; else suffix = "false"; text << indent << "\t\t<OffLadder>" << suffix << "</OffLadder>" << endLine; text << indent << "\t\t<BPS>" << GetBioID (1) << "</BPS>" << endLine; text << indent << "\t\t<Locus>" << mRightLocus->GetLocusName () << "</Locus>" << endLine; text << indent << "\t\t<Location>1</Location>" << endLine; text << indent << "\t</Allele>" << endLine; } } text << indent << "</" + bracketTag << ">" << endLine; } text.ResetOutputLevel (); }
void DataSignal :: WriteSmartArtifactInfoToXML (RGTextOutput& text, const RGString& indent, const RGString& bracketTag, const RGString& locationTag) { int peak; Endl endLine; RGString suffix; RGString label; SmartMessageReporter* notice; int i; RGString virtualAllele; int reportedMessageLevel; reportedMessageLevel = GetHighestMessageLevelWithRestrictionSM (); bool thisIsFirstNotice = true; if ((!DontLook ()) && (NumberOfSmartNoticeObjects () != 0)) { peak = (int) floor (Peak () + 0.5); if (mOffGrid) suffix = " OL"; virtualAllele = GetVirtualAlleleName (); RGDListIterator it (*mSmartMessageReporters); i = 0; while (notice = (SmartMessageReporter*) it ()) { if (!notice->GetDisplayOsirisInfo ()) continue; if (thisIsFirstNotice) { text << indent << "<" << bracketTag << ">" << endLine; text << indent << "\t<level>" << reportedMessageLevel << "</level>" << endLine; text << indent << "\t<mean>" << GetMean () << "</mean>" << endLine; text << indent << "\t<height>" << peak << "</height>" << endLine; // text << indent << "\t<" << locationTag << ">" << (int) floor (GetApproximateBioID () + 0.5) << "</" << locationTag << ">" << endLine; text << indent << "\t<" << locationTag << ">" << GetApproximateBioID () << "</" << locationTag << ">" << endLine; text << indent << "\t<PeakArea>" << TheoreticalArea () << "</PeakArea>" << endLine; if (virtualAllele.Length () > 0) text << indent << "\t<equivAllele>" << virtualAllele << suffix << "</equivAllele>" << endLine; text << indent << "\t<width>" << 4.0 * GetStandardDeviation () << "</width>" << endLine; text << indent << "\t<fit>" << GetCurveFit () << "</fit>" << endLine; label = indent + "\t<label>"; thisIsFirstNotice = false; } if (i > 0) label << " "; label += notice->GetMessage (); label += notice->GetMessageData (); i++; } RGString temp = GetVirtualAlleleName () + suffix; if (temp.Length () > 0) { if (i > 0) label << " "; label += "Allele " + temp; } if (i > 0) { label << "</label>"; text << label << endLine; text << indent << "</" + bracketTag << ">" << endLine; } /*if ((signalLink != NULL) && (!signalLink->xmlArtifactInfoWritten)) { signalLink->xmlArtifactInfoWritten = true; peak = signalLink->height; text << indent << "<" << bracketTag << ">" << endLine; text << indent << "\t<mean>" << signalLink->mean << "</mean>" << endLine; text << indent << "\t<height>" << peak << "</height>" << endLine; text << indent << "\t<" << locationTag << ">" << signalLink->bioID << "</" << locationTag << ">" << endLine; text << indent << "\t<fit>" << GetCurveFit () << "</fit>" << endLine; label = indent + "\t<label>"; mNoticeObjectIterator.Reset (); i = 0; while (notice = (Notice*) mNoticeObjectIterator ()) { if (i > 0) label << " "; label += notice->GetLabel (); i++; } if (temp.Length () > 0) { if (i > 0) label << " "; label += "Allele " + temp; } label << "</label>"; text << label << endLine; text << indent << "</" + bracketTag << ">" << endLine; }*/ } }
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; }