LSST Applications g0265f82a02+d6b5cd48b5,g02d81e74bb+80768bd682,g04242d3e92+8eaa23c173,g06b2ea86fd+734f9505a2,g2079a07aa2+14824f138e,g212a7c68fe+5f4fc2ea00,g2305ad1205+293ab1327e,g2bbee38e9b+d6b5cd48b5,g337abbeb29+d6b5cd48b5,g3ddfee87b4+8eaa23c173,g487adcacf7+abec5a19c5,g50ff169b8f+5929b3527e,g52b1c1532d+a6fc98d2e7,g591dd9f2cf+97ef3b4495,g5a732f18d5+66d966b544,g5d7b63bc56+636c3c3fd8,g64a986408d+80768bd682,g858d7b2824+80768bd682,g8a8a8dda67+a6fc98d2e7,g99cad8db69+6282a5f541,g9ddcbc5298+d4bad12328,ga1e77700b3+246acaaf9c,ga8c6da7877+9e3c062e8e,gb0e22166c9+3863383f4c,gb6a65358fc+d6b5cd48b5,gba4ed39666+9664299f35,gbb8dafda3b+60f904e7bc,gc120e1dc64+1bf26d0180,gc28159a63d+d6b5cd48b5,gcf0d15dbbd+8eaa23c173,gd2a12a3803+f8351bc914,gdaeeff99f8+a38ce5ea23,ge79ae78c31+d6b5cd48b5,gee10cc3b42+a6fc98d2e7,gf1cff7945b+80768bd682,v24.1.5.rc1
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.pipe.tasks.maskStreaks.MaskStreaksTask Class Reference
Inheritance diagram for lsst.pipe.tasks.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 518 of file maskStreaks.py.

Member Function Documentation

◆ _cannyFilter()

lsst.pipe.tasks.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 623 of file maskStreaks.py.

623 def _cannyFilter(self, image):
624 """Apply a canny filter to the data in order to detect edges.
625
626 Parameters
627 ----------
628 image : `np.ndarray`
629 2-d image data on which to run filter.
630
631 Returns
632 -------
633 cannyData : `np.ndarray`
634 2-d image of edges found in input image.
635 """
636 # Ensure that the pixels are zero or one. Change the datatype to
637 # np.float64 to be compatible with the Canny filter routine.
638 filterData = (image > 0).astype(np.float64)
639 return canny(filterData, use_quantiles=True, sigma=0.1)
640

◆ _findClusters()

lsst.pipe.tasks.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 663 of file maskStreaks.py.

663 def _findClusters(self, lines):
664 """Group lines that are close in parameter space and likely describe
665 the same streak.
666
667 Parameters
668 ----------
669 lines : `LineCollection`
670 Collection of lines to group into clusters.
671
672 Returns
673 -------
674 result : `LineCollection`
675 Average `Line` for each cluster of `Line`s in the input
676 `LineCollection`.
677 """
678 # Scale variables by threshold bin-size variable so that rho and theta
679 # are on the same scale. Since the clustering algorithm below stops when
680 # the standard deviation <= 1, after rescaling each cluster will have a
681 # standard deviation at or below the bin-size.
682 x = lines.rhos / self.config.rhoBinSize
683 y = lines.thetas / self.config.thetaBinSize
684 X = np.array([x, y]).T
685 nClusters = 1
686
687 # Put line parameters in clusters by starting with all in one, then
688 # subdividing until the parameters of each cluster have std dev=1.
689 # If nClusters == len(lines), each line will have its own 'cluster', so
690 # the standard deviations of each cluster must be zero and the loop
691 # is guaranteed to stop.
692 while True:
693 kmeans = KMeans(n_clusters=nClusters, n_init='auto').fit(X)
694 clusterStandardDeviations = np.zeros((nClusters, 2))
695 for c in range(nClusters):
696 inCluster = X[kmeans.labels_ == c]
697 clusterStandardDeviations[c] = np.std(inCluster, axis=0)
698 # Are the rhos and thetas in each cluster all below the threshold?
699 if (clusterStandardDeviations <= 1).all():
700 break
701 nClusters += 1
702
703 # The cluster centers are final line estimates
704 finalClusters = kmeans.cluster_centers_.T
705
706 # Rescale variables:
707 finalRhos = finalClusters[0] * self.config.rhoBinSize
708 finalThetas = finalClusters[1] * self.config.thetaBinSize
709 result = LineCollection(finalRhos, finalThetas)
710 self.log.info("Lines were grouped into %s potential streak(s)", len(finalRhos))
711
712 return result
713

◆ _fitProfile()

lsst.pipe.tasks.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 714 of file maskStreaks.py.

714 def _fitProfile(self, lines, maskedImage):
715 """Fit the profile of the streak.
716
717 Given the initial parameters of detected lines, fit a model for the
718 streak to the original (non-binary image). The assumed model is a
719 straight line with a Moffat profile.
720
721 Parameters
722 ----------
723 lines : `LineCollection`
724 Collection of guesses for `Line`s detected in the image.
725 maskedImage : `lsst.afw.image.maskedImage`
726 Original image to be used to fit profile of streak.
727
728 Returns
729 -------
730 lineFits : `LineCollection`
731 Collection of `Line` profiles fit to the data.
732 finalMask : `np.ndarray`
733 2d mask array with detected streaks=1.
734 """
735 data = maskedImage.image.array
736 weights = maskedImage.variance.array**-1
737 # Mask out any pixels with non-finite weights
738 weights[~np.isfinite(weights) | ~np.isfinite(data)] = 0
739
740 lineFits = LineCollection([], [])
741 finalLineMasks = [np.zeros(data.shape, dtype=bool)]
742 nFinalLines = 0
743 for line in lines:
744 line.sigma = self.config.invSigma**-1
745 lineModel = LineProfile(data, weights, line=line)
746 # Skip any lines that do not cover any data (sometimes happens because of chip gaps)
747 if lineModel.lineMaskSize == 0:
748 continue
749
750 fit, chi2, fitFailure = lineModel.fit(dChi2Tol=self.config.dChi2Tolerance, log=self.log)
751 if fitFailure:
752 self.log.warning("Streak fit failed.")
753
754 # Initial estimate should be quite close: fit is deemed unsuccessful if rho or theta
755 # change more than the allowed bin in rho or theta:
756 if ((abs(fit.rho - line.rho) > 2 * self.config.rhoBinSize)
757 or (abs(fit.theta - line.theta) > 2 * self.config.thetaBinSize)):
758 fitFailure = True
759 self.log.warning("Streak fit moved too far from initial estimate. Line will be dropped.")
760
761 if fitFailure:
762 continue
763
764 self.log.debug("Best fit streak parameters are rho=%.2f, theta=%.2f, and sigma=%.2f", fit.rho,
765 fit.theta, fit.sigma)
766
767 # Make mask
768 lineModel.setLineMask(fit)
769 finalModel = lineModel.makeProfile(fit)
770 # Take absolute value, as streaks are allowed to be negative
771 finalModelMax = abs(finalModel).max()
772 finalLineMask = abs(finalModel) > self.config.footprintThreshold
773 # Drop this line if the model profile is below the footprint threshold
774 if not finalLineMask.any():
775 continue
776 fit.chi2 = chi2
777 fit.finalModelMax = finalModelMax
778 lineFits.append(fit)
779 finalLineMasks.append(finalLineMask)
780 nFinalLines += 1
781
782 finalMask = np.array(finalLineMasks).any(axis=0)
783 nMaskedPixels = finalMask.sum()
784 percentMasked = (nMaskedPixels / finalMask.size) * 100
785 self.log.info("%d streak(s) fit, with %d pixels masked (%0.2f%% of image)", nFinalLines,
786 nMaskedPixels, percentMasked)
787
788 return lineFits, finalMask
int max

◆ _runKHT()

lsst.pipe.tasks.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 641 of file maskStreaks.py.

641 def _runKHT(self, image):
642 """Run Kernel Hough Transform on image.
643
644 Parameters
645 ----------
646 image : `np.ndarray`
647 2-d image data on which to detect lines.
648
649 Returns
650 -------
651 result : `LineCollection`
652 Collection of detected lines, with their detected rho and theta
653 coordinates.
654 """
655 lines = lsst.kht.find_lines(image, self.config.clusterMinimumSize,
656 self.config.clusterMinimumDeviation, self.config.delta,
657 self.config.minimumKernelHeight, self.config.nSigma,
658 self.config.absMinimumKernelHeight)
659 self.log.info("The Kernel Hough Transform detected %s line(s)", len(lines))
660
661 return LineCollection(lines.rho, lines.theta)
662

◆ find()

lsst.pipe.tasks.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 540 of file maskStreaks.py.

540 def find(self, maskedImage):
541 """Find streaks in a masked image.
542
543 Parameters
544 ----------
545 maskedImage : `lsst.afw.image.maskedImage`
546 The image in which to search for streaks.
547
548 Returns
549 -------
550 result : `lsst.pipe.base.Struct`
551 Results as a struct with attributes:
552
553 ``originalLines``
554 Lines identified by kernel hough transform.
555 ``lineClusters``
556 Lines grouped into clusters in rho-theta space.
557 ``lines``
558 Final result for lines after line-profile fit.
559 ``mask``
560 2-d boolean mask where detected lines are True.
561 """
562 mask = maskedImage.getMask()
563 detectionMask = (mask.array & mask.getPlaneBitMask(self.config.detectedMaskPlane))
564
565 self.edges = self._cannyFilter(detectionMask)
566 self.lines = self._runKHT(self.edges)
567
568 if len(self.lines) == 0:
569 lineMask = np.zeros(detectionMask.shape, dtype=bool)
570 fitLines = LineCollection([], [])
571 clusters = LineCollection([], [])
572 else:
573 clusters = self._findClusters(self.lines)
574 fitLines, lineMask = self._fitProfile(clusters, maskedImage)
575
576 # The output mask is the intersection of the fit streaks and the image detections
577 outputMask = lineMask & detectionMask.astype(bool)
578
579 return pipeBase.Struct(
580 lines=fitLines,
581 lineClusters=clusters,
582 originalLines=self.lines,
583 mask=outputMask,
584 )
585

◆ run()

lsst.pipe.tasks.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.maskedImage`
    The image in which to search for streaks. The mask detection plane
    corresponding to `config.detectedMaskPlane` must be set with the
    detected pixels.

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 587 of file maskStreaks.py.

587 def run(self, maskedImage):
588 """Find and mask streaks in a masked image.
589
590 Finds streaks in the image and modifies maskedImage in place by adding a
591 mask plane with any identified streaks.
592
593 Parameters
594 ----------
595 maskedImage : `lsst.afw.image.maskedImage`
596 The image in which to search for streaks. The mask detection plane
597 corresponding to `config.detectedMaskPlane` must be set with the
598 detected pixels.
599
600 Returns
601 -------
602 result : `lsst.pipe.base.Struct`
603 Results as a struct with attributes:
604
605 ``originalLines``
606 Lines identified by kernel hough transform.
607 ``lineClusters``
608 Lines grouped into clusters in rho-theta space.
609 ``lines``
610 Final result for lines after line-profile fit.
611 """
612 streaks = self.find(maskedImage)
613
614 maskedImage.mask.addMaskPlane(self.config.streaksMaskPlane)
615 maskedImage.mask.array[streaks.mask] |= maskedImage.mask.getPlaneBitMask(self.config.streaksMaskPlane)
616
617 return pipeBase.Struct(
618 lines=streaks.lines,
619 lineClusters=streaks.lineClusters,
620 originalLines=streaks.originalLines,
621 )
622

Member Data Documentation

◆ _DefaultName

str lsst.pipe.tasks.maskStreaks.MaskStreaksTask._DefaultName = "maskStreaks"
staticprotected

Definition at line 537 of file maskStreaks.py.

◆ ConfigClass

lsst.pipe.tasks.maskStreaks.MaskStreaksTask.ConfigClass = MaskStreaksConfig
static

Definition at line 536 of file maskStreaks.py.

◆ edges

lsst.pipe.tasks.maskStreaks.MaskStreaksTask.edges

Definition at line 565 of file maskStreaks.py.

◆ lines

lsst.pipe.tasks.maskStreaks.MaskStreaksTask.lines

Definition at line 566 of file maskStreaks.py.


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