lsst.pipe.tasks gcf790cdeb6+1ce96500e5
Loading...
Searching...
No Matches
_utils.py
Go to the documentation of this file.
1# This file is part of pipe_tasks.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
21
22from __future__ import annotations
23
24import numpy as np
25from typing import TYPE_CHECKING
26
27if TYPE_CHECKING:
28 from numpy.typing import NDArray
29 from lsst.geom import Box2I
30
31
33 """Create feathering masks for seamless image patch blending.
34
35 This class generates feathering masks used to smoothly blend image patches
36 into a larger mosaic. The feathering gradually transitions from full opacity
37 to zero opacity across the patch boundaries, reducing visible seams.
38
39 Parameters
40 ----------
41 patch_grow : `int`
42 Number of pixels to grow the patch boundaries for feathering.
43 bin_factor : `int`, optional
44 Binning factor for the feathering calculation. Reduces resolution
45 of feathering masks for faster computation. Default is 1.
46
47 Notes
48 -----
49 The feathering masks are created as linear ramps from 0 to 1 (or 1 to 0)
50 over the `patch_grow` distance. The masks are stored in `self.featherings`
51 as a list of arrays in the order: [top, bottom, left, right].
52 """
53
54 def __init__(self, patch_grow: int, bin_factor: int = 1) -> None:
55 self.patch_grow = patch_grow
56 self.bin_factor = bin_factor
57 self.featherings = None
58
59 def _make_featherings(self, dimensions: tuple[int, int]) -> None:
60 """Create feathering masks for all edges of the patch.
61
62 Parameters
63 ----------
64 dimensions : `tuple` of `int`
65 Shape of the patch (height, width) for which to create feathering masks.
66
67 Notes
68 -----
69 This method creates four feathering masks (top, bottom, left, right) using
70 linear ramps from 0 to 1 (or 1 to 0) over the `patch_grow` distance. The
71 featherings are stored in `self.featherings` as a list of arrays.
72 """
73 extent = self.patch_grow * 2
74 if self.bin_factor != 1:
75 extent = int(np.floor(extent / self.bin_factor))
76 ramp = np.linspace(0, 1, extent)
77 ramp[0] = 1e-17
78 top = np.ones(dimensions)
79 top[:extent, :] = np.repeat(np.expand_dims(ramp, 1), top.shape[1], axis=1)
80
81 bottom = np.ones(dimensions)
82 bottom[-1 * extent :, :] = np.repeat( # noqa: E203
83 np.expand_dims(1 - ramp, 1), bottom.shape[1], axis=1
84 )
85
86 left = np.ones(dimensions)
87 left[:, :extent] = np.repeat(np.expand_dims(ramp, 0), left.shape[0], axis=0)
88
89 right = np.ones(dimensions)
90 right[:, -1 * extent :] = np.repeat( # noqa: E203
91 np.expand_dims(1 - ramp, 0), right.shape[0], axis=0
92 )
93 self.featherings = [
94 top,
95 bottom,
96 left,
97 right,
98 ]
99
101 self, image: NDArray, patch: NDArray, newBox: Box2I, box: Box2I, reverse: bool = True
102 ) -> None:
103 """Add a patch to an image with feathering at the edges.
104
105 Parameters
106 ----------
107 image : `NDArray`
108 Target image to which the patch will be added. Modified in-place.
109 patch : `NDArray`
110 Patch array to be added to the image.
111 newBox : `Box2I`
112 New bounding box position of the patch.
113 box : `Box2I`
114 Original bounding box position of the patch.
115 reverse : `bool`, optional
116 If True, reverse the patch along the first axis before adding.
117 Default is True.
118
119 Notes
120 -----
121 This method applies feathering to smoothly blend the patch into the image
122 at the edges where the patch overlaps with existing image content. The
123 feathering is applied based on which edges of the patch differ between
124 `box` and `newBox`. The patch is multiplied by a mixer array that gradually
125 transitions from 0 to 1 across the feathering region.
126 """
127 base_shape = patch.shape if patch.ndim == 2 else patch.shape[:2]
128 mixer = np.ones(base_shape)
129 if self.featherings is None:
130 self._make_featherings(base_shape)
131 if box.getBeginY() != newBox.getBeginY():
132 mixer *= self.featherings[0]
133 if box.getEndY() != newBox.getEndY():
134 mixer *= self.featherings[1]
135 if box.getBeginX() != newBox.getBeginX():
136 mixer *= self.featherings[2]
137 if box.getEndX() != newBox.getEndX():
138 mixer *= self.featherings[3]
139
140 if image.ndim > 2:
141 mixer = np.repeat(np.expand_dims(mixer, 2), 3, axis=2)
142
143 patch = mixer * patch
144
145 image[*box.slices] += patch[::-1, :, :] if reverse else patch
None add_to_image(self, NDArray image, NDArray patch, Box2I newBox, Box2I box, bool reverse=True)
Definition _utils.py:102
None __init__(self, int patch_grow, int bin_factor=1)
Definition _utils.py:54
None _make_featherings(self, tuple[int, int] dimensions)
Definition _utils.py:59