def get_scaled_cutout(self, x1, y1, x2, y2, scale_x, scale_y, method='basic', logger=None): if method in ('basic', 'view'): return self.get_scaled_cutout_basic(x1, y1, x2, y2, scale_x, scale_y, method=method) data = self._get_data() newdata, (scale_x, scale_y) = trcalc.get_scaled_cutout_basic( data, x1, y1, x2, y2, scale_x, scale_y, interpolation=method, logger=logger) res = Bunch.Bunch(data=newdata, scale_x=scale_x, scale_y=scale_y) return res
def get_scaled_cutout(self, x1, y1, x2, y2, scale_x, scale_y, method="bicubic"): newdata, (scale_x, scale_y) = trcalc.get_scaled_cutout_basic( self._get_data(), x1, y1, x2, y2, scale_x, scale_y, interpolation=method ) res = Bunch.Bunch(data=newdata, scale_x=scale_x, scale_y=scale_y) return res
def get_scaled_cutout(self, x1, y1, x2, y2, scale_x, scale_y, method='basic'): newdata, (scale_x, scale_y) = trcalc.get_scaled_cutout_basic( self._get_data(), x1, y1, x2, y2, scale_x, scale_y, interpolation=method, logger=self.logger) res = Bunch.Bunch(data=newdata, scale_x=scale_x, scale_y=scale_y) return res
def get_scaled_cutout_basic(self, x1, y1, x2, y2, scale_x, scale_y): data = self.get_data() (newdata, (scale_x, scale_y)) = trcalc.get_scaled_cutout_basic(data, x1, y1, x2, y2, scale_x, scale_y) res = Bunch.Bunch(data=newdata, scale_x=scale_x, scale_y=scale_y) return res
def get_scaled_cutout(self, x1, y1, x2, y2, scale_x, scale_y, method='basic'): if method == 'basic': return self.get_scaled_cutout_basic(x1, y1, x2, y2, scale_x, scale_y) data = self._get_data() newdata, (scale_x, scale_y) = trcalc.get_scaled_cutout_basic( data, x1, y1, x2, y2, scale_x, scale_y, interpolation=method) res = Bunch.Bunch(data=newdata, scale_x=scale_x, scale_y=scale_y) return res
def mosaic_inline(self, imagelist, bg_ref=None, trim_px=None, merge=False, allow_expand=True, expand_pad_deg=0.01, max_expand_pct=None, update_minmax=True, suppress_callback=False): """Drops new images into the current image (if there is room), relocating them according the WCS between the two images. """ # Get our own (mosaic) rotation and scale header = self.get_header() ((xrot_ref, yrot_ref), (cdelt1_ref, cdelt2_ref)) = wcs.get_xy_rotation_and_scale(header) scale_x, scale_y = math.fabs(cdelt1_ref), math.fabs(cdelt2_ref) # drop each image in the right place in the new data array mydata = self._get_data() count = 1 res = [] for image in imagelist: name = image.get('name', 'image%d' % (count)) count += 1 data_np = image._get_data() if 0 in data_np.shape: self.logger.info("Skipping image with zero length axis") continue # Calculate sky position at the center of the piece ctr_x, ctr_y = trcalc.get_center(data_np) ra, dec = image.pixtoradec(ctr_x, ctr_y) # User specified a trim? If so, trim edge pixels from each # side of the array ht, wd = data_np.shape[:2] if trim_px: xlo, xhi = trim_px, wd - trim_px ylo, yhi = trim_px, ht - trim_px data_np = data_np[ylo:yhi, xlo:xhi, ...] ht, wd = data_np.shape[:2] # If caller asked us to match background of pieces then # get the median of this piece if bg_ref is not None: bg = iqcalc.get_median(data_np) bg_inc = bg_ref - bg data_np = data_np + bg_inc # Determine max/min to update our values if update_minmax: maxval = numpy.nanmax(data_np) minval = numpy.nanmin(data_np) self.maxval = max(self.maxval, maxval) self.minval = min(self.minval, minval) # Get rotation and scale of piece header = image.get_header() ((xrot, yrot), (cdelt1, cdelt2)) = wcs.get_xy_rotation_and_scale(header) self.logger.debug("image(%s) xrot=%f yrot=%f cdelt1=%f " "cdelt2=%f" % (name, xrot, yrot, cdelt1, cdelt2)) # scale if necessary # TODO: combine with rotation? if (not numpy.isclose(math.fabs(cdelt1), scale_x) or not numpy.isclose(math.fabs(cdelt2), scale_y)): nscale_x = math.fabs(cdelt1) / scale_x nscale_y = math.fabs(cdelt2) / scale_y self.logger.debug("scaling piece by x(%f), y(%f)" % (nscale_x, nscale_y)) data_np, (ascale_x, ascale_y) = trcalc.get_scaled_cutout_basic( data_np, 0, 0, wd - 1, ht - 1, nscale_x, nscale_y, logger=self.logger) # Rotate piece into our orientation, according to wcs rot_dx, rot_dy = xrot - xrot_ref, yrot - yrot_ref flip_x = False flip_y = False # Optomization for 180 rotations if (numpy.isclose(math.fabs(rot_dx), 180.0) or numpy.isclose(math.fabs(rot_dy), 180.0)): rotdata = trcalc.transform(data_np, flip_x=True, flip_y=True) rot_dx = 0.0 rot_dy = 0.0 else: rotdata = data_np # Finish with any necessary rotation of piece if not numpy.isclose(rot_dy, 0.0): rot_deg = rot_dy self.logger.debug("rotating %s by %f deg" % (name, rot_deg)) rotdata = trcalc.rotate( rotdata, rot_deg, #rotctr_x=ctr_x, rotctr_y=ctr_y logger=self.logger) # Flip X due to negative CDELT1 if numpy.sign(cdelt1) != numpy.sign(cdelt1_ref): flip_x = True # Flip Y due to negative CDELT2 if numpy.sign(cdelt2) != numpy.sign(cdelt2_ref): flip_y = True if flip_x or flip_y: rotdata = trcalc.transform(rotdata, flip_x=flip_x, flip_y=flip_y) # Get size and data of new image ht, wd = rotdata.shape[:2] ctr_x, ctr_y = trcalc.get_center(rotdata) # Find location of image piece (center) in our array x0, y0 = self.radectopix(ra, dec) # Merge piece as closely as possible into our array # Unfortunately we lose a little precision rounding to the # nearest pixel--can't be helped with this approach x0, y0 = int(round(x0)), int(round(y0)) self.logger.debug("Fitting image '%s' into mosaic at %d,%d" % (name, x0, y0)) # This is for useful debugging info only my_ctr_x, my_ctr_y = trcalc.get_center(mydata) off_x, off_y = x0 - my_ctr_x, y0 - my_ctr_y self.logger.debug("centering offsets: %d,%d" % (off_x, off_y)) # Sanity check piece placement xlo, xhi = x0 - ctr_x, x0 + wd - ctr_x ylo, yhi = y0 - ctr_y, y0 + ht - ctr_y assert (xhi - xlo == wd), \ Exception("Width differential %d != %d" % (xhi - xlo, wd)) assert (yhi - ylo == ht), \ Exception("Height differential %d != %d" % (yhi - ylo, ht)) mywd, myht = self.get_size() if xlo < 0 or xhi > mywd or ylo < 0 or yhi > myht: if not allow_expand: raise Exception("New piece doesn't fit on image and " "allow_expand=False") # <-- Resize our data array to allow the new image # determine amount to pad expansion by expand_x = max(int(expand_pad_deg / scale_x), 0) expand_y = max(int(expand_pad_deg / scale_y), 0) nx1_off, nx2_off = 0, 0 if xlo < 0: nx1_off = abs(xlo) + expand_x if xhi > mywd: nx2_off = (xhi - mywd) + expand_x xlo, xhi = xlo + nx1_off, xhi + nx1_off ny1_off, ny2_off = 0, 0 if ylo < 0: ny1_off = abs(ylo) + expand_y if yhi > myht: ny2_off = (yhi - myht) + expand_y ylo, yhi = ylo + ny1_off, yhi + ny1_off new_wd = mywd + nx1_off + nx2_off new_ht = myht + ny1_off + ny2_off # sanity check on new mosaic size old_area = mywd * myht new_area = new_wd * new_ht expand_pct = new_area / old_area if ((max_expand_pct is not None) and (expand_pct > max_expand_pct)): raise Exception("New area exceeds current one by %.2f %%;" "increase max_expand_pct (%.2f) to allow" % (expand_pct * 100, max_expand_pct)) # go for it! new_data = numpy.zeros((new_ht, new_wd)) # place current data into new data new_data[ny1_off:ny1_off + myht, nx1_off:nx1_off + mywd] = \ mydata self._data = new_data mydata = new_data if (nx1_off > 0) or (ny1_off > 0): # Adjust our WCS for relocation of the reference pixel crpix1, crpix2 = self.get_keywords_list('CRPIX1', 'CRPIX2') kwds = dict(CRPIX1=crpix1 + nx1_off, CRPIX2=crpix2 + ny1_off) self.update_keywords(kwds) # fit image piece into our array try: if merge: mydata[ylo:yhi, xlo:xhi, ...] += rotdata[0:ht, 0:wd, ...] else: idx = (mydata[ylo:yhi, xlo:xhi, ...] == 0.0) mydata[ylo:yhi, xlo:xhi, ...][idx] = \ rotdata[0:ht, 0:wd, ...][idx] except Exception as e: self.logger.error("Error fitting tile: %s" % (str(e))) raise res.append((xlo, ylo, xhi, yhi)) # TODO: recalculate min and max values # Can't use usual techniques because it adds too much time to the # mosacing #self._set_minmax() # Notify watchers that our data has changed if not suppress_callback: self.make_callback('modified') return res
def mosaic_inline(self, imagelist, bg_ref=None, trim_px=None, merge=False): """Drops new images into the current image (if there is room), relocating them according the WCS between the two images. """ # For determining our orientation ra0, dec0 = self.pixtoradec(0, 0) ra1, dec1 = self.pixtoradec(self.width-1, self.height-1) # Get our own (mosaic) rotation and scale header = self.get_header() ((xrot_ref, yrot_ref), (cdelt1_ref, cdelt2_ref)) = wcs.get_xy_rotation_and_scale(header) ref_rot = yrot_ref scale_x, scale_y = math.fabs(cdelt1_ref), math.fabs(cdelt2_ref) # drop each image in the right place in the new data array mydata = self.get_data() count = 1 for image in imagelist: name = image.get('name', 'image%d' % (count)) count += 1 data_np = image.get_data() # Calculate sky position at the center of the piece ctr_x, ctr_y = trcalc.get_center(data_np) ra, dec = image.pixtoradec(ctr_x, ctr_y) # User specified a trim? If so, trim edge pixels from each # side of the array ht, wd = data_np.shape[:2] if trim_px: xlo, xhi = trim_px, wd - trim_px ylo, yhi = trim_px, ht - trim_px data_np = data_np[ylo:yhi, xlo:xhi, ...] ht, wd = data_np.shape[:2] # If caller asked us to match background of pieces then # get the median of this piece if bg_ref != None: bg = iqcalc.get_median(data_np) bg_inc = bg_ref - bg #print "bg=%f inc=%f" % (bg, bg_inc) data_np = data_np + bg_inc # Get rotation and scale of piece header = image.get_header() ((xrot, yrot), (cdelt1, cdelt2)) = wcs.get_xy_rotation_and_scale(header) self.logger.debug("image(%s) xrot=%f yrot=%f cdelt1=%f cdelt2=%f" % ( name, xrot, yrot, cdelt1, cdelt2)) # scale if necessary # TODO: combine with rotation? if ((math.fabs(cdelt1) != scale_x) or (math.fabs(cdelt2) != scale_y)): nscale_x = math.fabs(cdelt1) / scale_x nscale_y = math.fabs(cdelt2) / scale_y self.logger.debug("scaling piece by x(%f), y(%f)" % ( nscale_x, nscale_y)) data_np, (ascale_x, ascale_y) = trcalc.get_scaled_cutout_basic( data_np, 0, 0, wd-1, ht-1, nscale_x, nscale_y) # Rotate piece into our orientation, according to wcs rot_dx, rot_dy = xrot - xrot_ref, yrot - yrot_ref flip_x = False flip_y = False ## # Flip X due to negative CDELT1 ## if numpy.sign(cdelt1) < 0: ## flip_x = True ## # Flip Y due to negative CDELT2 ## if numpy.sign(cdelt2) < 0: ## flip_y = True # Optomization for 180 rotations if math.fabs(rot_dx) == 180.0: flip_x = not flip_x rot_dx = 0.0 if math.fabs(rot_dy) == 180.0: flip_y = not flip_y rot_dy = 0.0 self.logger.debug("flip_x=%s flip_y=%s" % (flip_x, flip_y)) rotdata = trcalc.transform(data_np, flip_x=flip_x, flip_y=flip_y) # Finish with any necessary rotation of piece if rot_dy != 0.0: rot_deg = rot_dy self.logger.debug("rotating %s by %f deg" % (name, rot_deg)) rotdata = trcalc.rotate(rotdata, rot_deg, #rotctr_x=ctr_x, rotctr_y=ctr_y ) # Get size and data of new image ht, wd = rotdata.shape[:2] ctr_x, ctr_y = trcalc.get_center(rotdata) # Find location of image piece (center) in our array x0, y0 = self.radectopix(ra, dec) # Merge piece as closely as possible into our array # Unfortunately we lose a little precision rounding to the # nearest pixel--can't be helped with this approach x0, y0 = int(round(x0)), int(round(y0)) self.logger.debug("Fitting image '%s' into mosaic at %d,%d" % ( name, x0, y0)) # Sanity check piece placement xlo, xhi = x0 - ctr_x, x0 + wd - ctr_x ylo, yhi = y0 - ctr_y, y0 + ht - ctr_y assert (xhi - xlo == wd), \ Exception("Width differential %d != %d" % (xhi - xlo, wd)) assert (yhi - ylo == ht), \ Exception("Height differential %d != %d" % (yhi - ylo, ht)) # fit image piece into our array, not overwriting any data # already written try: if merge: mydata[ylo:yhi, xlo:xhi, ...] += rotdata[0:ht, 0:wd, ...] else: idx = (mydata[ylo:yhi, xlo:xhi, ...] == 0.0) #print idx.shape, rotdata.shape mydata[ylo:yhi, xlo:xhi, ...][idx] = \ rotdata[0:ht, 0:wd, ...][idx] except Exception as e: self.logger.error("Error fitting tile: %s" % (str(e))) raise # TODO: recalculate min and max values # Can't use usual techniques because it adds too much time to the # mosacing #self._set_minmax() # Notify watchers that our data has changed self.make_callback('modified') return (xlo, ylo, xhi, yhi)
def draw_image(self, dstarr): #print "drawing image at %f,%f" % (self.x, self.y) # get extent of our data coverage in the window ((x0, y0), (x1, y1), (x2, y2), (x3, y3)) = self.fitsimage.get_pan_rect() xmin = int(min(x0, x1, x2, x3)) ymin = int(min(y0, y1, y2, y3)) xmax = int(max(x0, x1, x2, x3)) ymax = int(max(y0, y1, y2, y3)) # destination location in data_coords #dst_x, dst_y = self.x, self.y + ht dst_x, dst_y = self.x, self.y #print "actual placement at %d,%d" % (dst_x, dst_y) a1, b1, a2, b2 = 0, 0, self.image.width, self.image.height # calculate the cutout that we can make and scale to merge # onto the final image--by only cutting out what is necessary # this speeds scaling greatly at zoomed in sizes dst_x, dst_y, a1, b1, a2, b2 = \ trcalc.calc_image_merge_clip(xmin, ymin, xmax, ymax, dst_x, dst_y, a1, b1, a2, b2) #a1, b1, a2, b2 = 0, 0, self.image.width, self.image.height #print "a1,b1=%d,%d a2,b2=%d,%d" % (a1, b1, a2, b2) # is image completely off the screen? if (a2 - a1 <= 0) or (b2 - b1 <= 0): # no overlay needed #print "no overlay needed" return # scale the cutout according to the current viewer scale order = self.fitsimage.get_rgb_order() ## if 'A' in order: ## order = order.replace('A', '') srcdata = self.image.get_array(order) #print "order=%s srcdata=%s" % (order, srcdata.shape) scale_x, scale_y = self.fitsimage.get_scale_xy() (newdata, (nscale_x, nscale_y)) = \ trcalc.get_scaled_cutout_basic(srcdata, a1, b1, a2, b2, scale_x, scale_y) # calculate our offset from the pan position pan_x, pan_y = self.fitsimage.get_pan() #print "pan x,y=%f,%f" % (pan_x, pan_y) off_x, off_y = dst_x - pan_x, dst_y - pan_y # scale offset off_x *= scale_x off_y *= scale_y #print "off_x,y=%f,%f" % (off_x, off_y) # dst position in the pre-transformed array should be calculated # from the center of the array plus offsets ht, wd, dp = dstarr.shape x = int(round(wd / 2.0 + off_x)) y = int(round(ht / 2.0 + off_y)) # composite the image into the destination array at the # calculated position trcalc.overlay_image(dstarr, x, y, newdata, alpha=self.alpha, flipy=self.flipy)
def mosaic_inline(self, imagelist, bg_ref=None, trim_px=None, merge=False, allow_expand=True, expand_pad_deg=0.01, max_expand_pct=None, update_minmax=True, suppress_callback=False): """Drops new images into the current image (if there is room), relocating them according the WCS between the two images. """ # Get our own (mosaic) rotation and scale header = self.get_header() ((xrot_ref, yrot_ref), (cdelt1_ref, cdelt2_ref)) = wcs.get_xy_rotation_and_scale(header) ref_rot = yrot_ref scale_x, scale_y = math.fabs(cdelt1_ref), math.fabs(cdelt2_ref) # drop each image in the right place in the new data array mydata = self._get_data() count = 1 for image in imagelist: name = image.get('name', 'image%d' % (count)) count += 1 data_np = image._get_data() # Calculate sky position at the center of the piece ctr_x, ctr_y = trcalc.get_center(data_np) ra, dec = image.pixtoradec(ctr_x, ctr_y) # User specified a trim? If so, trim edge pixels from each # side of the array ht, wd = data_np.shape[:2] if trim_px: xlo, xhi = trim_px, wd - trim_px ylo, yhi = trim_px, ht - trim_px data_np = data_np[ylo:yhi, xlo:xhi, ...] ht, wd = data_np.shape[:2] # If caller asked us to match background of pieces then # get the median of this piece if bg_ref is not None: bg = iqcalc.get_median(data_np) bg_inc = bg_ref - bg #print "bg=%f inc=%f" % (bg, bg_inc) data_np = data_np + bg_inc # Determine max/min to update our values if update_minmax: maxval = numpy.nanmax(data_np) minval = numpy.nanmin(data_np) self.maxval = max(self.maxval, maxval) self.minval = min(self.minval, minval) # Get rotation and scale of piece header = image.get_header() ((xrot, yrot), (cdelt1, cdelt2)) = wcs.get_xy_rotation_and_scale(header) self.logger.debug("image(%s) xrot=%f yrot=%f cdelt1=%f cdelt2=%f" % ( name, xrot, yrot, cdelt1, cdelt2)) # scale if necessary # TODO: combine with rotation? if (not numpy.isclose(math.fabs(cdelt1), scale_x) or not numpy.isclose(math.fabs(cdelt2), scale_y)): nscale_x = math.fabs(cdelt1) / scale_x nscale_y = math.fabs(cdelt2) / scale_y self.logger.debug("scaling piece by x(%f), y(%f)" % ( nscale_x, nscale_y)) data_np, (ascale_x, ascale_y) = trcalc.get_scaled_cutout_basic( data_np, 0, 0, wd-1, ht-1, nscale_x, nscale_y) # Rotate piece into our orientation, according to wcs rot_dx, rot_dy = xrot - xrot_ref, yrot - yrot_ref flip_x = False flip_y = False ## # Flip X due to negative CDELT1 ## if numpy.sign(cdelt1) < 0: ## flip_x = True ## # Flip Y due to negative CDELT2 ## if numpy.sign(cdelt2) < 0: ## flip_y = True # Optomization for 180 rotations if numpy.isclose(math.fabs(rot_dx), 180.0): flip_x = not flip_x rot_dx = 0.0 if numpy.isclose(math.fabs(rot_dy), 180.0): flip_y = not flip_y rot_dy = 0.0 self.logger.debug("flip_x=%s flip_y=%s" % (flip_x, flip_y)) if flip_x or flip_y: rotdata = trcalc.transform(data_np, flip_x=flip_x, flip_y=flip_y) else: rotdata = data_np # Finish with any necessary rotation of piece if not numpy.isclose(rot_dy, 0.0): rot_deg = rot_dy self.logger.debug("rotating %s by %f deg" % (name, rot_deg)) rotdata = trcalc.rotate(rotdata, rot_deg, #rotctr_x=ctr_x, rotctr_y=ctr_y ) # Get size and data of new image ht, wd = rotdata.shape[:2] ctr_x, ctr_y = trcalc.get_center(rotdata) # Find location of image piece (center) in our array x0, y0 = self.radectopix(ra, dec) # Merge piece as closely as possible into our array # Unfortunately we lose a little precision rounding to the # nearest pixel--can't be helped with this approach x0, y0 = int(round(x0)), int(round(y0)) self.logger.debug("Fitting image '%s' into mosaic at %d,%d" % ( name, x0, y0)) # This is for useful debugging info only my_ctr_x, my_ctr_y = trcalc.get_center(mydata) off_x, off_y = x0 - my_ctr_x, y0 - my_ctr_y self.logger.debug("centering offsets: %d,%d" % (off_x, off_y)) # Sanity check piece placement xlo, xhi = x0 - ctr_x, x0 + wd - ctr_x ylo, yhi = y0 - ctr_y, y0 + ht - ctr_y assert (xhi - xlo == wd), \ Exception("Width differential %d != %d" % (xhi - xlo, wd)) assert (yhi - ylo == ht), \ Exception("Height differential %d != %d" % (yhi - ylo, ht)) mywd, myht = self.get_size() if xlo < 0 or xhi > mywd or ylo < 0 or yhi > myht: if not allow_expand: raise Exception("New piece doesn't fit on image and allow_expand=False") #<-- Resize our data array to allow the new image # determine amount to pad expansion by expand_x = max(int(expand_pad_deg / scale_x), 0) expand_y = max(int(expand_pad_deg / scale_y), 0) nx1_off, nx2_off = 0, 0 if xlo < 0: nx1_off = abs(xlo) + expand_x if xhi > mywd: nx2_off = (xhi - mywd) + expand_x xlo, xhi = xlo + nx1_off, xhi + nx1_off ny1_off, ny2_off = 0, 0 if ylo < 0: ny1_off = abs(ylo) + expand_y if yhi > myht: ny2_off = (yhi - myht) + expand_y ylo, yhi = ylo + ny1_off, yhi + ny1_off new_wd = mywd + nx1_off + nx2_off new_ht = myht + ny1_off + ny2_off # sanity check on new mosaic size old_area = mywd * myht new_area = new_wd * new_ht expand_pct = new_area / old_area if ((max_expand_pct is not None) and (expand_pct > max_expand_pct)): raise Exception("New area exceeds current one by %.2f %%;" "increase max_expand_pct (%.2f) to allow" % (expand_pct*100, max_expand_pct)) # go for it! new_data = numpy.zeros((new_ht, new_wd)) # place current data into new data new_data[ny1_off:ny1_off+myht, nx1_off:nx1_off+mywd] = \ mydata self._data = new_data mydata = new_data if (nx1_off > 0) or (ny1_off > 0): # Adjust our WCS for relocation of the reference pixel crpix1, crpix2 = self.get_keywords_list('CRPIX1', 'CRPIX2') kwds = dict(CRPIX1=crpix1 + nx1_off, CRPIX2=crpix2 + ny1_off) self.update_keywords(kwds) # fit image piece into our array try: if merge: mydata[ylo:yhi, xlo:xhi, ...] += rotdata[0:ht, 0:wd, ...] else: idx = (mydata[ylo:yhi, xlo:xhi, ...] == 0.0) mydata[ylo:yhi, xlo:xhi, ...][idx] = \ rotdata[0:ht, 0:wd, ...][idx] except Exception as e: self.logger.error("Error fitting tile: %s" % (str(e))) raise # TODO: recalculate min and max values # Can't use usual techniques because it adds too much time to the # mosacing #self._set_minmax() # Notify watchers that our data has changed if not suppress_callback: self.make_callback('modified') return (xlo, ylo, xhi, yhi)
def mosaic_inline(self, imagelist, bg_ref=None, trim_px=None, merge=False): """Drops new images into the current image (if there is room), relocating them according the WCS between the two images. """ # Get our own (mosaic) rotation and scale header = self.get_header() ((xrot_ref, yrot_ref), (cdelt1_ref, cdelt2_ref)) = wcs.get_xy_rotation_and_scale(header) ref_rot = yrot_ref scale_x, scale_y = math.fabs(cdelt1_ref), math.fabs(cdelt2_ref) # drop each image in the right place in the new data array mydata = self._get_data() count = 1 for image in imagelist: name = image.get('name', 'image%d' % (count)) count += 1 data_np = image._get_data() # Calculate sky position at the center of the piece ctr_x, ctr_y = trcalc.get_center(data_np) ra, dec = image.pixtoradec(ctr_x, ctr_y) # User specified a trim? If so, trim edge pixels from each # side of the array ht, wd = data_np.shape[:2] if trim_px: xlo, xhi = trim_px, wd - trim_px ylo, yhi = trim_px, ht - trim_px data_np = data_np[ylo:yhi, xlo:xhi, ...] ht, wd = data_np.shape[:2] # If caller asked us to match background of pieces then # get the median of this piece if bg_ref != None: bg = iqcalc.get_median(data_np) bg_inc = bg_ref - bg #print "bg=%f inc=%f" % (bg, bg_inc) data_np = data_np + bg_inc # Get rotation and scale of piece header = image.get_header() ((xrot, yrot), (cdelt1, cdelt2)) = wcs.get_xy_rotation_and_scale(header) self.logger.debug("image(%s) xrot=%f yrot=%f cdelt1=%f cdelt2=%f" % ( name, xrot, yrot, cdelt1, cdelt2)) # scale if necessary # TODO: combine with rotation? if ((math.fabs(cdelt1) != scale_x) or (math.fabs(cdelt2) != scale_y)): nscale_x = math.fabs(cdelt1) / scale_x nscale_y = math.fabs(cdelt2) / scale_y self.logger.debug("scaling piece by x(%f), y(%f)" % ( nscale_x, nscale_y)) data_np, (ascale_x, ascale_y) = trcalc.get_scaled_cutout_basic( data_np, 0, 0, wd-1, ht-1, nscale_x, nscale_y) # Rotate piece into our orientation, according to wcs rot_dx, rot_dy = xrot - xrot_ref, yrot - yrot_ref flip_x = False flip_y = False ## # Flip X due to negative CDELT1 ## if numpy.sign(cdelt1) < 0: ## flip_x = True ## # Flip Y due to negative CDELT2 ## if numpy.sign(cdelt2) < 0: ## flip_y = True # Optomization for 180 rotations if math.fabs(rot_dx) == 180.0: flip_x = not flip_x rot_dx = 0.0 if math.fabs(rot_dy) == 180.0: flip_y = not flip_y rot_dy = 0.0 self.logger.debug("flip_x=%s flip_y=%s" % (flip_x, flip_y)) rotdata = trcalc.transform(data_np, flip_x=flip_x, flip_y=flip_y) # Finish with any necessary rotation of piece if rot_dy != 0.0: rot_deg = rot_dy self.logger.debug("rotating %s by %f deg" % (name, rot_deg)) rotdata = trcalc.rotate(rotdata, rot_deg, #rotctr_x=ctr_x, rotctr_y=ctr_y ) # Get size and data of new image ht, wd = rotdata.shape[:2] ctr_x, ctr_y = trcalc.get_center(rotdata) # Find location of image piece (center) in our array x0, y0 = self.radectopix(ra, dec) # Merge piece as closely as possible into our array # Unfortunately we lose a little precision rounding to the # nearest pixel--can't be helped with this approach x0, y0 = int(round(x0)), int(round(y0)) self.logger.debug("Fitting image '%s' into mosaic at %d,%d" % ( name, x0, y0)) # This is for useful debugging info only my_ctr_x, my_ctr_y = trcalc.get_center(mydata) off_x, off_y = x0 - my_ctr_x, y0 - my_ctr_y self.logger.debug("centering offsets: %d,%d" % (off_x, off_y)) # Sanity check piece placement xlo, xhi = x0 - ctr_x, x0 + wd - ctr_x ylo, yhi = y0 - ctr_y, y0 + ht - ctr_y assert (xhi - xlo == wd), \ Exception("Width differential %d != %d" % (xhi - xlo, wd)) assert (yhi - ylo == ht), \ Exception("Height differential %d != %d" % (yhi - ylo, ht)) # fit image piece into our array, not overwriting any data # already written try: if merge: mydata[ylo:yhi, xlo:xhi, ...] += rotdata[0:ht, 0:wd, ...] else: idx = (mydata[ylo:yhi, xlo:xhi, ...] == 0.0) #print idx.shape, rotdata.shape mydata[ylo:yhi, xlo:xhi, ...][idx] = \ rotdata[0:ht, 0:wd, ...][idx] except Exception as e: self.logger.error("Error fitting tile: %s" % (str(e))) raise # TODO: recalculate min and max values # Can't use usual techniques because it adds too much time to the # mosacing #self._set_minmax() # Notify watchers that our data has changed self.make_callback('modified') return (xlo, ylo, xhi, yhi)