Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    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
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
    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)
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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)