LSST Applications g0603fd7c41+f1f8eaba91,g124d44cf3d+ce19972735,g180d380827+c1373eaf06,g1afd7665f7+eb25d4c773,g2079a07aa2+86d27d4dc4,g2305ad1205+aa3c8c93b6,g2bbee38e9b+44a02a0554,g337abbeb29+44a02a0554,g33d1c0ed96+44a02a0554,g3a166c0a6a+44a02a0554,g3d1719c13e+a4710a6d26,g487adcacf7+e387efc8c5,g50ff169b8f+96c6868917,g52b1c1532d+585e252eca,g591dd9f2cf+7f57e6be76,g858d7b2824+a4710a6d26,g991b906543+a4710a6d26,g99cad8db69+832a1c95fd,g9b9dfce982+e7b986f76c,g9ddcbc5298+9a081db1e4,ga1e77700b3+03d07e1c1f,gb0e22166c9+60f28cb32d,gb23b769143+a4710a6d26,gb3a676b8dc+e2510deafe,gba4ed39666+c2a2e4ac27,gbb8dafda3b+201573ceae,gbd998247f1+585e252eca,gc120e1dc64+7fb97cd961,gc28159a63d+44a02a0554,gc3e9b769f7+20d5ea8805,gcf0d15dbbd+e7b986f76c,gdaeeff99f8+f9a426f77a,ge79ae78c31+44a02a0554,ged0e8a7f67+8df1cf93fe,gee10cc3b42+585e252eca,w.2024.18
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Member Functions | Public Attributes | Static Public Attributes | Protected Member Functions | Static Protected Attributes | List of all members
lsst.meas.algorithms.maskStreaks.MaskStreaksTask Class Reference
Inheritance diagram for lsst.meas.algorithms.maskStreaks.MaskStreaksTask:

Public Member Functions

 find (self, maskedImage)
 
 run (self, maskedImage)
 

Public Attributes

 edges
 
 lines
 

Static Public Attributes

 ConfigClass = MaskStreaksConfig
 

Protected Member Functions

 _cannyFilter (self, image)
 
 _runKHT (self, image)
 
 _findClusters (self, lines)
 
 _fitProfile (self, lines, maskedImage)
 

Static Protected Attributes

str _DefaultName = "maskStreaks"
 

Detailed Description

Find streaks or other straight lines in image data.

Nearby objects passing through the field of view of the telescope leave a
bright trail in images. This class uses the Kernel Hough Transform (KHT)
(Fernandes and Oliveira, 2007), implemented in `lsst.houghtransform`. The
procedure works by taking a binary image, either provided as put or produced
from the input data image, using a Canny filter to make an image of the
edges in the original image, then running the KHT on the edge image. The KHT
identifies clusters of non-zero points, breaks those clusters of points into
straight lines, keeps clusters with a size greater than the user-set
threshold, then performs a voting procedure to find the best-fit coordinates
of any straight lines. Given the results of the KHT algorithm, clusters of
lines are identified and grouped (generally these correspond to the two
edges of a strea) and a profile is fit to the streak in the original
(non-binary) image.

Definition at line 435 of file maskStreaks.py.

Member Function Documentation

◆ _cannyFilter()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask._cannyFilter ( self,
image )
protected
Apply a canny filter to the data in order to detect edges.

Parameters
----------
image : `np.ndarray`
    2-d image data on which to run filter.

Returns
-------
cannyData : `np.ndarray`
    2-d image of edges found in input image.

Definition at line 542 of file maskStreaks.py.

542 def _cannyFilter(self, image):
543 """Apply a canny filter to the data in order to detect edges.
544
545 Parameters
546 ----------
547 image : `np.ndarray`
548 2-d image data on which to run filter.
549
550 Returns
551 -------
552 cannyData : `np.ndarray`
553 2-d image of edges found in input image.
554 """
555 # Ensure that the pixels are zero or one. Change the datatype to
556 # np.float64 to be compatible with the Canny filter routine.
557 filterData = (image > 0).astype(np.float64)
558 return canny(filterData, use_quantiles=True, sigma=0.1)
559

◆ _findClusters()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask._findClusters ( self,
lines )
protected
Group lines that are close in parameter space and likely describe
the same streak.

Parameters
----------
lines : `LineCollection`
    Collection of lines to group into clusters.

Returns
-------
result : `LineCollection`
    Average `Line` for each cluster of `Line`s in the input
    `LineCollection`.

Definition at line 582 of file maskStreaks.py.

582 def _findClusters(self, lines):
583 """Group lines that are close in parameter space and likely describe
584 the same streak.
585
586 Parameters
587 ----------
588 lines : `LineCollection`
589 Collection of lines to group into clusters.
590
591 Returns
592 -------
593 result : `LineCollection`
594 Average `Line` for each cluster of `Line`s in the input
595 `LineCollection`.
596 """
597 # Scale variables by threshold bin-size variable so that rho and theta
598 # are on the same scale. Since the clustering algorithm below stops when
599 # the standard deviation <= 1, after rescaling each cluster will have a
600 # standard deviation at or below the bin-size.
601 x = lines.rhos / self.config.rhoBinSize
602 y = lines.thetas / self.config.thetaBinSize
603 X = np.array([x, y]).T
604 nClusters = 1
605
606 # Put line parameters in clusters by starting with all in one, then
607 # subdividing until the parameters of each cluster have std dev=1.
608 # If nClusters == len(lines), each line will have its own 'cluster', so
609 # the standard deviations of each cluster must be zero and the loop
610 # is guaranteed to stop.
611 while True:
612 kmeans = KMeans(n_clusters=nClusters, n_init='auto').fit(X)
613 clusterStandardDeviations = np.zeros((nClusters, 2))
614 for c in range(nClusters):
615 inCluster = X[kmeans.labels_ == c]
616 clusterStandardDeviations[c] = np.std(inCluster, axis=0)
617 # Are the rhos and thetas in each cluster all below the threshold?
618 if (clusterStandardDeviations <= 1).all():
619 break
620 nClusters += 1
621
622 # The cluster centers are final line estimates
623 finalClusters = kmeans.cluster_centers_.T
624
625 # Rescale variables:
626 finalRhos = finalClusters[0] * self.config.rhoBinSize
627 finalThetas = finalClusters[1] * self.config.thetaBinSize
628 result = LineCollection(finalRhos, finalThetas)
629 self.log.info("Lines were grouped into %s potential streak(s)", len(finalRhos))
630
631 return result
632

◆ _fitProfile()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask._fitProfile ( self,
lines,
maskedImage )
protected
Fit the profile of the streak.

Given the initial parameters of detected lines, fit a model for the
streak to the original (non-binary image). The assumed model is a
straight line with a Moffat profile.

Parameters
----------
lines : `LineCollection`
    Collection of guesses for `Line`s detected in the image.
maskedImage : `lsst.afw.image.maskedImage`
    Original image to be used to fit profile of streak.

Returns
-------
lineFits : `LineCollection`
    Collection of `Line` profiles fit to the data.
finalMask : `np.ndarray`
    2d mask array with detected streaks=1.

Definition at line 633 of file maskStreaks.py.

633 def _fitProfile(self, lines, maskedImage):
634 """Fit the profile of the streak.
635
636 Given the initial parameters of detected lines, fit a model for the
637 streak to the original (non-binary image). The assumed model is a
638 straight line with a Moffat profile.
639
640 Parameters
641 ----------
642 lines : `LineCollection`
643 Collection of guesses for `Line`s detected in the image.
644 maskedImage : `lsst.afw.image.maskedImage`
645 Original image to be used to fit profile of streak.
646
647 Returns
648 -------
649 lineFits : `LineCollection`
650 Collection of `Line` profiles fit to the data.
651 finalMask : `np.ndarray`
652 2d mask array with detected streaks=1.
653 """
654 data = maskedImage.image.array
655 weights = maskedImage.variance.array**-1
656 # Mask out any pixels with non-finite weights
657 weights[~np.isfinite(weights) | ~np.isfinite(data)] = 0
658
659 lineFits = LineCollection([], [])
660 finalLineMasks = [np.zeros(data.shape, dtype=bool)]
661 nFinalLines = 0
662 nFitFailures = 0
663 for line in lines:
664 line.sigma = self.config.invSigma**-1
665 lineModel = LineProfile(data, weights, line=line)
666 # Skip any lines that do not cover any data (sometimes happens because of chip gaps)
667 if lineModel.lineMaskSize == 0:
668 continue
669
670 fit, chi2, fitFailure = lineModel.fit(dChi2Tol=self.config.dChi2Tolerance, log=self.log)
671
672 # Initial estimate should be quite close: fit is deemed unsuccessful if rho or theta
673 # change more than the allowed bin in rho or theta:
674 if ((abs(fit.rho - line.rho) > 2 * self.config.rhoBinSize)
675 or (abs(fit.theta - line.theta) > 2 * self.config.thetaBinSize)):
676 fitFailure = True
677 self.log.debug("Streak fit moved too far from initial estimate. Line will be dropped.")
678
679 if fitFailure:
680 nFitFailures += 1
681 continue
682
683 # Make mask
684 lineModel.setLineMask(fit)
685 finalModel = lineModel.makeProfile(fit)
686 # Take absolute value, as streaks are allowed to be negative
687 finalModelMax = abs(finalModel).max()
688 finalLineMask = abs(finalModel) > self.config.footprintThreshold
689 # Drop this line if the model profile is below the footprint threshold
690 if not finalLineMask.any():
691 continue
692 fit.chi2 = chi2
693 fit.finalModelMax = finalModelMax
694 lineFits.append(fit)
695 finalLineMasks.append(finalLineMask)
696 nFinalLines += 1
697
698 if nFitFailures > 0:
699 self.log.info("Streak profile could not be fit for %d out of %d detected lines.", nFitFailures,
700 len(lines))
701 finalMask = np.array(finalLineMasks).any(axis=0)
702
703 return lineFits, finalMask
int max

◆ _runKHT()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask._runKHT ( self,
image )
protected
Run Kernel Hough Transform on image.

Parameters
----------
image : `np.ndarray`
    2-d image data on which to detect lines.

Returns
-------
result : `LineCollection`
    Collection of detected lines, with their detected rho and theta
    coordinates.

Definition at line 560 of file maskStreaks.py.

560 def _runKHT(self, image):
561 """Run Kernel Hough Transform on image.
562
563 Parameters
564 ----------
565 image : `np.ndarray`
566 2-d image data on which to detect lines.
567
568 Returns
569 -------
570 result : `LineCollection`
571 Collection of detected lines, with their detected rho and theta
572 coordinates.
573 """
574 lines = lsst.kht.find_lines(image, self.config.clusterMinimumSize,
575 self.config.clusterMinimumDeviation, self.config.delta,
576 self.config.minimumKernelHeight, self.config.nSigma,
577 self.config.absMinimumKernelHeight)
578 self.log.info("The Kernel Hough Transform detected %s line(s)", len(lines))
579
580 return LineCollection(lines.rho, lines.theta)
581

◆ find()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask.find ( self,
maskedImage )
Find streaks in a masked image.

Parameters
----------
maskedImage : `lsst.afw.image.maskedImage`
    The image in which to search for streaks.

Returns
-------
result : `lsst.pipe.base.Struct`
    Results as a struct with attributes:

    ``originalLines``
        Lines identified by kernel hough transform.
    ``lineClusters``
        Lines grouped into clusters in rho-theta space.
    ``lines``
        Final result for lines after line-profile fit.
    ``mask``
        2-d boolean mask where detected lines are True.

Definition at line 457 of file maskStreaks.py.

457 def find(self, maskedImage):
458 """Find streaks in a masked image.
459
460 Parameters
461 ----------
462 maskedImage : `lsst.afw.image.maskedImage`
463 The image in which to search for streaks.
464
465 Returns
466 -------
467 result : `lsst.pipe.base.Struct`
468 Results as a struct with attributes:
469
470 ``originalLines``
471 Lines identified by kernel hough transform.
472 ``lineClusters``
473 Lines grouped into clusters in rho-theta space.
474 ``lines``
475 Final result for lines after line-profile fit.
476 ``mask``
477 2-d boolean mask where detected lines are True.
478 """
479 mask = maskedImage.mask
480 detectionMask = (mask.array & mask.getPlaneBitMask(self.config.detectedMaskPlane))
481
482 self.edges = self._cannyFilter(detectionMask)
483 self.lines = self._runKHT(self.edges)
484
485 if len(self.lines) == 0:
486 lineMask = np.zeros(detectionMask.shape, dtype=bool)
487 fitLines = LineCollection([], [])
488 clusters = LineCollection([], [])
489 else:
490 clusters = self._findClusters(self.lines)
491 fitLines, lineMask = self._fitProfile(clusters, maskedImage)
492
493 # The output mask is the intersection of the fit streaks and the image detections
494 outputMask = lineMask & detectionMask.astype(bool)
495
496 return pipeBase.Struct(
497 lines=fitLines,
498 lineClusters=clusters,
499 originalLines=self.lines,
500 mask=outputMask,
501 )
502

◆ run()

lsst.meas.algorithms.maskStreaks.MaskStreaksTask.run ( self,
maskedImage )
Find and mask streaks in a masked image.

Finds streaks in the image and modifies maskedImage in place by adding a
mask plane with any identified streaks.

Parameters
----------
maskedImage : `lsst.afw.image.Exposure` or `lsst.afw.image.maskedImage`
    The image in which to search for streaks. The mask detection plane
    corresponding to `config.detectedMaskPlane` must be set with the
    detected pixels. The mask will have a plane added with any detected
    streaks, and with the mask plane name set by
    self.config.streaksMaskPlane.

Returns
-------
result : `lsst.pipe.base.Struct`
    Results as a struct with attributes:

    ``originalLines``
        Lines identified by kernel hough transform.
    ``lineClusters``
        Lines grouped into clusters in rho-theta space.
    ``lines``
        Final result for lines after line-profile fit.

Definition at line 504 of file maskStreaks.py.

504 def run(self, maskedImage):
505 """Find and mask streaks in a masked image.
506
507 Finds streaks in the image and modifies maskedImage in place by adding a
508 mask plane with any identified streaks.
509
510 Parameters
511 ----------
512 maskedImage : `lsst.afw.image.Exposure` or `lsst.afw.image.maskedImage`
513 The image in which to search for streaks. The mask detection plane
514 corresponding to `config.detectedMaskPlane` must be set with the
515 detected pixels. The mask will have a plane added with any detected
516 streaks, and with the mask plane name set by
517 self.config.streaksMaskPlane.
518
519 Returns
520 -------
521 result : `lsst.pipe.base.Struct`
522 Results as a struct with attributes:
523
524 ``originalLines``
525 Lines identified by kernel hough transform.
526 ``lineClusters``
527 Lines grouped into clusters in rho-theta space.
528 ``lines``
529 Final result for lines after line-profile fit.
530 """
531 streaks = self.find(maskedImage)
532
533 maskedImage.mask.addMaskPlane(self.config.streaksMaskPlane)
534 maskedImage.mask.array[streaks.mask] |= maskedImage.mask.getPlaneBitMask(self.config.streaksMaskPlane)
535
536 return pipeBase.Struct(
537 lines=streaks.lines,
538 lineClusters=streaks.lineClusters,
539 originalLines=streaks.originalLines,
540 )
541

Member Data Documentation

◆ _DefaultName

str lsst.meas.algorithms.maskStreaks.MaskStreaksTask._DefaultName = "maskStreaks"
staticprotected

Definition at line 454 of file maskStreaks.py.

◆ ConfigClass

lsst.meas.algorithms.maskStreaks.MaskStreaksTask.ConfigClass = MaskStreaksConfig
static

Definition at line 453 of file maskStreaks.py.

◆ edges

lsst.meas.algorithms.maskStreaks.MaskStreaksTask.edges

Definition at line 482 of file maskStreaks.py.

◆ lines

lsst.meas.algorithms.maskStreaks.MaskStreaksTask.lines

Definition at line 483 of file maskStreaks.py.


The documentation for this class was generated from the following file: