167 def run(self, input_images: Iterable[tuple[NDArray, SkyWcs, Box2I]], healpix_id) -> Struct:
168 """Main execution method for generating HealPix tiles.
172 input_images : Iterable[tuple[NDArray, SkyWcs, Box2I]]
173 Iterable of tuples containing image data, WCS, and bounding box information.
175 The HealPix order 8 ID to process.
180 Output structure containing the processed HealPix order 8 tile.
181 This has been downsampled to 256x256 corresponding to a quarter of a healpix
187 target_wcs = makeHpxWcs(8, healpix_id, 12,
False)
192 output_array_hpx = np.zeros((4096, 4096, 3), dtype=np.float32)
193 output_array_hpx[:, :, :] = np.nan
195 self.log.info(
"Warping input exposures and populating hpx8 super tile.")
198 for input_image, in_wcs, in_box
in input_images:
199 tmp_image = ImageF(in_box)
200 in_image: NDArray = input_image
203 match in_image.dtype:
205 in_image = in_image.astype(np.float32) / 255.0
207 in_image = in_image.astype(np.float32) / 65535
209 in_image = in_image.astype(np.float32)
212 for channel
in ColorChannel:
214 existing = output_array_hpx[..., channel.value]
217 channel_array = in_image[..., channel.value]
218 tmp_image.array[:, :] = channel_array
221 warpped = self.
warper.warpImage(target_wcs, tmp_image, in_wcs, maxBBox=exp_bbox)
222 warpped_box_slices = warpped.getBBox().slices
225 are_warpped = np.isfinite(warpped.array)
226 existing[warpped_box_slices][are_warpped] = warpped.array[are_warpped]
229 output_array_hpx[np.isnan(output_array_hpx)] = 0
232 output_array_hpx = output_array_hpx[::-1, :, :]
245 for zoom, hips_level, factor
in zip((0, 2, 4, 8), (11, 10, 9, 8), (3, 2, 1, 0)):
246 self.log.info(
"Generating tiles for hxp level %d", hips_level)
249 binned_array = cv2.resize(output_array_hpx, (size, size), interpolation=cv2.INTER_LANCZOS4)
251 binned_array = output_array_hpx
261 tmp_pixels = np.array([[healpix_id]])
262 for _
in range(factor):
263 tmp_array = np.zeros(np.array(tmp_pixels.shape) * 2)
264 for ii
in range(tmp_pixels.shape[0]):
265 for jj
in range(tmp_pixels.shape[1]):
266 tmp_array_view = tmp_array[ii * 2 : ii * 2 + 2, jj * 2 : jj * 2 + 2]
267 tmp_range_set = RangeSet(int(tmp_pixels[ii, jj]))
268 tmp_array_view[:, :] = (
269 np.array([x
for x
in range(*tmp_range_set.scaled(4)[0])], dtype=int)[[0, 2, 1, 3]]
271 tmp_pixels = tmp_array
274 hpx_id_array = tmp_pixels
275 for i
in range(binned_array.shape[0] // 512):
276 for j
in range(binned_array.shape[1] // 512):
277 pixel_id = int(hpx_id_array[i, j])
278 sub_pixel = binned_array[i * 512 : i * 512 + 512, j * 512 : j * 512 + 512, :]
279 self.log.info(f
"writing sub_pixel {pixel_id}")
285 self.config.file_extension,
286 self.config.array_type,
291 zoomed = cv2.resize(output_array_hpx, (256, 256), interpolation=cv2.INTER_LANCZOS4)
293 return Struct(output_hpx=zoomed)
296 """Create a feathered box to use in blending images together.
298 This is used when mixing patches together into a larger contiguous region
299 such that overlaps are blended by a ramp function. This will be 50%
300 for each area at exactly halfway through the overlap, and 1/0 at the
305 shape : `tuple` of `int`, `int`
306 Shape of the output weighting mask.
308 Amount to grow bounding boxes.
313 The output weighting mask to use in blending, repeated along third
314 axis to use with RGB images.
318 yind, xind = np.mgrid[: patch_grow * 4, : patch_grow * 4]
319 dis = ((yind - 2 * patch_grow) ** 2 + (xind - 2 * patch_grow) ** 2) ** 0.5
320 radial = 1 - np.clip(dis / (2 * patch_grow), 0, 1)
322 ramp = np.linspace(0, 1, patch_grow * 2)
323 tmpImg = np.zeros(shape)
324 tmpImg[: patch_grow * 2, :] = np.repeat(np.expand_dims(ramp, 1), tmpImg.shape[1], axis=1)
326 tmpImg[-1 * patch_grow * 2 :, :] = np.repeat(
327 np.expand_dims(1 - ramp, 1), tmpImg.shape[1], axis=1
329 tmpImg[:, : patch_grow * 2] = np.repeat(np.expand_dims(ramp, 0), tmpImg.shape[0], axis=0)
331 tmpImg[:, -1 * patch_grow * 2 :] = np.repeat(
332 np.expand_dims(1 - ramp, 0), tmpImg.shape[0], axis=0
335 tmpImg[: 2 * patch_grow, : 2 * patch_grow] = radial[: 2 * patch_grow, : 2 * patch_grow]
336 tmpImg[: 2 * patch_grow, -2 * patch_grow :] = radial[: 2 * patch_grow, -2 * patch_grow :]
337 tmpImg[-2 * patch_grow :, : 2 * patch_grow] = radial[-2 * patch_grow :, : 2 * patch_grow]
338 tmpImg[-2 * patch_grow :, -2 * patch_grow :] = radial[-2 * patch_grow :, -2 * patch_grow :]
339 tmpImg = np.repeat(np.expand_dims(tmpImg, 2), 3, axis=2)
343 self, tract_patch: dict[int, Iterable[tuple[DeferredDatasetHandle, SkyWcs, Box2I]]], patch_grow: int
344 ) -> list[tuple[NDArray, SkyWcs, Box2I]]:
345 """Assemble all the patches in each tract into images.
347 This function takes in an input keyed by tract, with values
348 corresponding the patches in that tract that overlap the quatum's
349 healpix value. It assembles each of these into a single image such
350 that the return values is a list of images (and metadata) one element
351 for each input tract.
355 tract_patch : `dict` of `int` to `iterable` of `tuple` of
356 `DeferredDatasetHandle`, `SkyWcs` and `Box2I`
357 Input images and metadata organized into corresponding tracts.
359 Amount to grow patches by
363 output_list : `list` of `tuple` of `NDArray` `SkyWcs` and `Box2I`
364 List of assembled images and metadata, one element for each tract
370 for _, iterable
in tract_patch.items():
372 for _, _, bbox
in iterable:
373 new_box.include(bbox)
375 new_array = np.zeros((new_box.getHeight(), new_box.getWidth(), 3), dtype=np.float32)
376 for handle, skyWcs, box
in iterable:
379 localOrigin = box.getBegin() - new_box.getBegin()
381 x=int(np.floor(localOrigin.x)),
382 y=int(np.floor(localOrigin.y)),
386 x=int(np.floor(box.getWidth())),
387 y=int(np.floor(box.getHeight())),
389 tmpBox =
Box2I(localOrigin, localExtent)
396 existing = ~np.all(new_array[*tmpBox.slices] == 0, axis=2)
399 new_array[*tmpBox.slices][~existing, :] = image[~existing, :]
403 new_array[*tmpBox.slices][existing, :] = (
404 tmpImg[existing] * image[existing]
405 + (1 - tmpImg[existing]) * new_array[*tmpBox.slices][existing, :]
407 boxes.append((new_array, skyWcs, new_box))
412 butlerQC: QuantumContext,
413 inputRefs: InputQuantizedConnection,
414 outputRefs: OutputQuantizedConnection,
417 healpix_id = butlerQC.quantum.dataId[
"healpix8"]
420 skymap: BaseSkyMap = butlerQC.get(inputRefs.skymap)
425 for input_image_ref
in inputRefs.input_images:
426 tract = input_image_ref.dataId[
"tract"]
427 patch = input_image_ref.dataId[
"patch"]
430 imageWcs = skymap[tract][patch].getWcs()
431 box = skymap[tract][patch].getInnerBBox()
432 patch_grow = skymap[tract][patch].getCellInnerDimensions().getX()
433 box = box.dilatedBy(patch_grow)
434 imageHandle = butlerQC.get(input_image_ref)
435 container = inputs_by_tract.setdefault(tract, list())
436 container.append((imageHandle, imageWcs, box))
440 outputs = self.
run(input_images, healpix_id)
441 butlerQC.put(outputs, outputRefs)