void InlineFlowBoxPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& cullRect) { ASSERT(paintInfo.phase == PaintPhaseForeground); if (m_inlineFlowBox.lineLayoutItem().style()->visibility() != VISIBLE) return; // You can use p::first-line to specify a background. If so, the root line boxes for // a line may actually have to paint a background. LayoutObject* inlineFlowBoxLayoutObject = LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.lineLayoutItem()); const ComputedStyle* styleToUse = m_inlineFlowBox.lineLayoutItem().style(m_inlineFlowBox.isFirstLineStyle()); bool shouldPaintBoxDecorationBackground; if (m_inlineFlowBox.parent()) shouldPaintBoxDecorationBackground = inlineFlowBoxLayoutObject->hasBoxDecorationBackground(); else shouldPaintBoxDecorationBackground = m_inlineFlowBox.isFirstLineStyle() && styleToUse != m_inlineFlowBox.lineLayoutItem().style(); if (!shouldPaintBoxDecorationBackground) return; if (DrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground)) return; DrawingRecorder recorder(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(cullRect)); LayoutRect frameRect = frameRectClampedToLineTopAndBottomIfNeeded(); // Move x/y to our coordinates. LayoutRect localRect(frameRect); m_inlineFlowBox.flipForWritingMode(localRect); LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); LayoutRect adjustedFrameRect = LayoutRect(adjustedPaintOffset, frameRect.size()); IntRect adjustedClipRect; BorderPaintingType borderPaintingType = getBorderPaintType(adjustedFrameRect, adjustedClipRect); // Shadow comes first and is behind the background and border. if (!m_inlineFlowBox.boxModelObject().boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, &m_inlineFlowBox)) paintBoxShadow(paintInfo, *styleToUse, Normal, adjustedFrameRect); Color backgroundColor = inlineFlowBoxLayoutObject->resolveColor(*styleToUse, CSSPropertyBackgroundColor); paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), adjustedFrameRect); paintBoxShadow(paintInfo, *styleToUse, Inset, adjustedFrameRect); switch (borderPaintingType) { case DontPaintBorders: break; case PaintBordersWithoutClip: BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, adjustedFrameRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle()), BackgroundBleedNone, m_inlineFlowBox.includeLogicalLeftEdge(), m_inlineFlowBox.includeLogicalRightEdge()); break; case PaintBordersWithClip: // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, // but it isn't even clear how this should work at all. LayoutRect imageStripPaintRect = paintRectForImageStrip(adjustedPaintOffset, frameRect.size(), LTR); GraphicsContextStateSaver stateSaver(paintInfo.context); paintInfo.context.clip(adjustedClipRect); BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, imageStripPaintRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle())); break; } }
void TwButton::paintBackground(TwPainter* painter) { TwColor fillColor; TwColor boundColor; if (isEnabled()) { switch (frameState()) { case Tw::State_Normal: { fillColor = backgroundColor(); boundColor = k_DefBoundColor; } break; case Tw::State_Active: { fillColor = m_activeColor; boundColor = m_activeColor; boundColor.setAlpha(255); } break; case Tw::State_Pressed: { fillColor = m_pressedColor; boundColor = m_pressedColor; boundColor.setAlpha(255); } break; } } else { fillColor = k_DefDisableFillColor; boundColor = k_DefBoundColor; } if (fillColor.alpha() != 0) { painter->fillRect(localRect(), fillColor); } if (boundColor.alpha() != 0) { painter->drawRect(localRect(), boundColor, 1.0f, true); } }
bool Button::onMouseDrag(int2 start, int2 current, int key, int is_final) { m_mouse_press = key == 0 && !is_final && m_is_enabled; if(key == 0 && m_is_enabled) { if(is_final == 1 && localRect().isInside(current)) sendEvent(this, Event::button_clicked, m_id); return true; } return false; }
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const { LayoutBlock* caretPainter = caretLayoutObject(node); if (!caretPainter) return IntRect(); LayoutRect localRect(rect); caretPainter->flipForWritingMode(localRect); return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox(); }
void Timer::draw() { glBegin( GL_QUADS ); // top Color(0.333).scaleValue( 0.75 ).withAlpha( 0.75 ).set(); glVertex2i( 0, 0 ); glVertex2i( bounds.w, 0 ); // bottom Color(0.333).scaleValue( 0.25 ).withAlpha( 0.5 ).set(); glVertex2i( bounds.w, bounds.h ); glVertex2i( 0, bounds.h ); glEnd(); if ( enabled()) { // draw pulsing Pause symbol Color(0.333).scaleValue( m_ticks & 1 ? 0.8 : 1.0 ).withAlpha(0.5).set(); glBegin( GL_QUADS ); glVertex2i( 7, 7 ); glVertex2i( 13, 7 ); glVertex2i( 13, 23 ); glVertex2i( 7, 23 ); // prime numbers are good! glVertex2i( 17, 7 ); glVertex2i( 23, 7 ); glVertex2i( 23, 23 ); glVertex2i( 17, 23 ); glEnd(); } else { // draw Run symbol Color(0.333).withAlpha(0.5).set(); glBegin( GL_TRIANGLES ); glVertex2i( 8, 5 ); glVertex2i( 23, 15 ); glVertex2i( 8, 24 ); glEnd(); Color(0.333).set(); glEnable( GL_LINE_SMOOTH ); glBegin( GL_LINE_LOOP ); glVertex2i( 8, 5 ); glVertex2i( 23, 15 ); glVertex2i( 8, 24 ); glEnd(); glDisable( GL_LINE_SMOOTH ); } glColor4f( 0, 0, 0, 0.5 ); localRect().stroke(); }
void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) { for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { FloatRect localRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()); quads.append(localToAbsoluteQuad(localRect)); } for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { if (!curr->isText()) curr->absoluteQuads(quads, false); } if (continuation() && topLevel) continuation()->absoluteQuads(quads, topLevel); }
bool ImageButton::onMouseDrag(int2 start, int2 current, int key, int is_final) { if(key == 0 && !m_mouse_press && !m_proto.sound_name.empty() && !(m_mode == mode_toggle_on && m_is_pressed)) audio::playSound(m_proto.sound_name.c_str(), 1.0f); m_mouse_press = key == 0 && !is_final && m_is_enabled; if(key == 0 && m_is_enabled) { if(is_final == 1 && localRect().isInside(current)) { if(m_mode == mode_toggle) m_is_pressed ^= 1; else if(m_mode == mode_toggle_on) m_is_pressed = true; sendEvent(this, Event::button_clicked, m_id); } return true; } return false; }
void PaintLayerClipper::mapLocalToRootWithGeometryMapper( const ClipRectsContext& context, LayoutRect& layoutRect) const { DCHECK(m_geometryMapper); bool success; const ObjectPaintProperties::PropertyTreeStateWithOffset* layerBorderBoxProperties = m_layer.layoutObject()->paintProperties()->localBorderBoxProperties(); FloatRect localRect(layoutRect); localRect.moveBy(FloatPoint(layerBorderBoxProperties->paintOffset)); layoutRect = LayoutRect(m_geometryMapper->mapRectToDestinationSpace( localRect, layerBorderBoxProperties->propertyTreeState, context.rootLayer->layoutObject() ->paintProperties() ->localBorderBoxProperties() ->propertyTreeState, success)); DCHECK(success); }
QRectF ShaderNodeUI::boundingRect() const { QRectF localRect(QPointF(), QSizeF(m_nodeSize.width() + (kNodePortRadius * 2.0f), m_nodeSize.height())); return localRect; }
bool View::containsPoint(SkPoint point, View * reference) { SkPoint p = convertToLocal(point, reference); return localRect().intersects(p.x(), p.y(), p.x() + 1, p.y() + 1); }
void InlineFlowBoxPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_inlineFlowBox.lineLayoutItem().style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; LayoutRect frameRect = frameRectClampedToLineTopAndBottomIfNeeded(); // Move x/y to our coordinates. LayoutRect localRect(frameRect); m_inlineFlowBox.flipForWritingMode(localRect); LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); const NinePieceImage& maskNinePieceImage = m_inlineFlowBox.lineLayoutItem().style()->maskBoxImage(); StyleImage* maskBoxImage = m_inlineFlowBox.lineLayoutItem().style()->maskBoxImage().image(); // Figure out if we need to push a transparency layer to render our mask. bool pushTransparencyLayer = false; bool compositedMask = m_inlineFlowBox.lineLayoutItem().hasLayer() && m_inlineFlowBox.boxModelObject().layer()->hasCompositedMask(); bool flattenCompositingLayers = paintInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers; SkXfermode::Mode compositeOp = SkXfermode::kSrcOver_Mode; if (!compositedMask || flattenCompositingLayers) { if ((maskBoxImage && m_inlineFlowBox.lineLayoutItem().style()->maskLayers().hasImage()) || m_inlineFlowBox.lineLayoutItem().style()->maskLayers().next()) { pushTransparencyLayer = true; paintInfo.context.beginLayer(1.0f, SkXfermode::kDstIn_Mode); } else { // TODO(fmalita): passing a dst-in xfer mode down to paintFillLayers/paintNinePieceImage // seems dangerous: it is only correct if applied atomically (single draw call). While // the heuristic above presumably ensures that is the case, this approach seems super // fragile. We should investigate dropping this optimization in favour of the more // robust layer branch above. compositeOp = SkXfermode::kDstIn_Mode; } } LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size()); paintFillLayers(paintInfo, Color::transparent, m_inlineFlowBox.lineLayoutItem().style()->maskLayers(), paintRect, compositeOp); bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(); if (!hasBoxImage || !maskBoxImage->isLoaded()) { if (pushTransparencyLayer) paintInfo.context.endLayer(); return; // Don't paint anything while we wait for the image to load. } LayoutBoxModelObject* boxModel = toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())); // The simple case is where we are the only box for this object. In those // cases only a single call to draw is required. if (!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) { BoxPainter::paintNinePieceImage(*boxModel, paintInfo.context, paintRect, m_inlineFlowBox.lineLayoutItem().styleRef(), maskNinePieceImage, compositeOp); } else { // We have a mask image that spans multiple lines. // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, // but it isn't even clear how this should work at all. LayoutRect imageStripPaintRect = paintRectForImageStrip(adjustedPaintOffset, frameRect.size(), LTR); FloatRect clipRect(clipRectForNinePieceImageStrip(m_inlineFlowBox, maskNinePieceImage, paintRect)); GraphicsContextStateSaver stateSaver(paintInfo.context); // TODO(chrishtr): this should be pixel-snapped. paintInfo.context.clip(clipRect); BoxPainter::paintNinePieceImage(*boxModel, paintInfo.context, imageStripPaintRect, m_inlineFlowBox.lineLayoutItem().styleRef(), maskNinePieceImage, compositeOp); } if (pushTransparencyLayer) paintInfo.context.endLayer(); }
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; }