示例#1
0
 def visible_tiles(self,
                   visible_geom,
                   stride=None,
                   extra_tiles_box=None) -> Box:
     """
     given a visible world geometry and sampling, return (sampling-state, [Box-of-tiles-to-draw])
     sampling state is WELLSAMPLED/OVERSAMPLED/UNDERSAMPLED
     returned Box should be iterated per standard start:stop style
     tiles are specified as (iy,ix) integer pairs
     extra_box value says how many extra tiles to include around each edge
     """
     if stride is None:
         stride = Point(1, 1)
     if extra_tiles_box is None:
         extra_tiles_box = Box(0, 0, 0, 0)
     v = visible_geom
     e = extra_tiles_box
     return visible_tiles(
         float(self.pixel_rez[0]), float(self.pixel_rez[1]),
         float(self.tile_size[0]), float(self.tile_size[1]),
         float(self.image_center[0]), float(self.image_center[1]),
         int(self.image_shape[0]), int(self.image_shape[1]),
         int(self.tile_shape[0]), int(self.tile_shape[1]), float(v[0]),
         float(v[1]), float(v[2]), float(v[3]), float(v[4]), float(v[5]),
         int(stride[0]), int(stride[1]), int(e[0]), int(e[1]), int(e[2]),
         int(e[3]))
示例#2
0
    def init_overview(self, data_arrays):
        """Create and add a low resolution version of the data that is always
        shown behind the higher resolution image tiles.
        """
        # FUTURE: Actually use this data attribute. For now let the base
        #         think there is data (not None)
        self._data = ArrayProxy(self.ndim, self.shape)
        self.overview_info = nfo = {}
        y_slice, x_slice = self.calc.overview_stride
        # Update kwargs to reflect the new spatial resolution of the overview image
        nfo["cell_width"] = self.cell_width * x_slice.step
        nfo["cell_height"] = self.cell_height * y_slice.step
        # Tell the texture state that we are adding a tile that should never expire and should always exist
        nfo["texture_tile_index"] = ttile_idx = self.texture_state.add_tile((0, 0, 0), expires=False)
        for idx, data in enumerate(data_arrays):
            if data is not None:
                _y_slice, _x_slice = self.calc.calc_overview_stride(image_shape=data.shape)
                overview_data = data[_y_slice, _x_slice]
            else:
                overview_data = None
            self._textures[idx].set_tile_data(ttile_idx, self._normalize_data(overview_data))

        # Handle wrapping around the anti-meridian so there is a -180/180 continuous image
        num_tiles = 1 if not self.wrap_lon else 2
        tl = TESS_LEVEL * TESS_LEVEL
        nfo["texture_coordinates"] = np.empty((6 * num_tiles * tl, 2), dtype=np.float32)
        nfo["vertex_coordinates"] = np.empty((6 * num_tiles * tl, 2), dtype=np.float32)
        factor_rez, offset_rez = self.calc.calc_tile_fraction(
            0, 0, Point(np.int64(y_slice.step), np.int64(x_slice.step)))
        nfo["texture_coordinates"][:6 * tl, :2] = self.calc.calc_texture_coordinates(ttile_idx, factor_rez, offset_rez,
                                                                                     tessellation_level=TESS_LEVEL)
        nfo["vertex_coordinates"][:6 * tl, :2] = self.calc.calc_vertex_coordinates(0, 0, y_slice.step, x_slice.step,
                                                                                   factor_rez, offset_rez,
                                                                                   tessellation_level=TESS_LEVEL)
        self._set_vertex_tiles(nfo["vertex_coordinates"], nfo["texture_coordinates"])
示例#3
0
def calc_stride(v_dx, v_dy, t_dx, t_dy, overview_stride_y, overview_stride_x):
    # screen dy,dx in world distance per pixel
    # world distance per pixel for our data
    # compute texture pixels per screen pixels
    tsy = min(overview_stride_y,
              max(1, np.ceil(v_dy * PREFERRED_SCREEN_TO_TEXTURE_RATIO / t_dy)))
    tsx = min(overview_stride_x,
              max(1, np.ceil(v_dx * PREFERRED_SCREEN_TO_TEXTURE_RATIO / t_dx)))

    return Point(np.int64(tsy), np.int64(tsx))
示例#4
0
文件: visuals.py 项目: rayg-ssec/sift
    def set_channels(self,
                     data_arrays,
                     shape=None,
                     cell_width=None,
                     cell_height=None,
                     origin_x=None,
                     origin_y=None):
        assert (shape or data_arrays
                is not None), "`data` or `shape` must be provided"
        if cell_width is not None:
            self.cell_width = cell_width
        if cell_height:
            self.cell_height = cell_height  # Note: cell_height is usually negative
        if origin_x:
            self.origin_x = origin_x
        if origin_y:
            self.origin_y = origin_y
        self.shape = self._compute_shape(shape, data_arrays)
        assert None not in (self.cell_width, self.cell_height, self.origin_x,
                            self.origin_y, self.shape)
        # how many of the higher resolution channel tiles (smaller geographic area) make
        # up a low resolution channel tile
        self._channel_factors = tuple(
            self.shape[0] / float(chn.shape[0]) if chn is not None else 1.
            for chn in data_arrays)
        self._lowest_factor = max(self._channel_factors)
        self._lowest_rez = Resolution(
            abs(self.cell_height * self._lowest_factor),
            abs(self.cell_width * self._lowest_factor))

        # Where does this image lie in this lonely world
        self.calc = TileCalculator(self.name,
                                   self.shape,
                                   Point(x=self.origin_x, y=self.origin_y),
                                   Resolution(dy=abs(self.cell_height),
                                              dx=abs(self.cell_width)),
                                   self.tile_shape,
                                   self.texture_shape,
                                   wrap_lon=self.wrap_lon)

        # Reset texture state, if we change things to know which texture
        # don't need to be updated then this can be removed/changed
        self.texture_state.reset()
        self._need_texture_upload = True
        self._need_vertex_update = True
        # Reset the tiling logic to force a retile
        # even though we might be looking at the exact same spot
        self._latest_tile_box = None
示例#5
0
文件: visuals.py 项目: rayg-ssec/sift
    def _init_geo_parameters(self, origin_x, origin_y, cell_width, cell_height,
                             projection, texture_shape, tile_shape, wrap_lon,
                             shape, data):
        self._viewable_mesh_mask = None
        self._ref1 = None
        self._ref2 = None

        self.origin_x = origin_x
        self.origin_y = origin_y
        self.cell_width = cell_width
        self.cell_height = cell_height  # Note: cell_height is usually negative
        self.texture_shape = texture_shape
        self.tile_shape = tile_shape
        self.num_tex_tiles = self.texture_shape[0] * self.texture_shape[1]
        self._stride = (0, 0)
        self._latest_tile_box = None
        self.wrap_lon = wrap_lon
        self._tiles = {}
        assert (shape
                or data is not None), "`data` or `shape` must be provided"
        self.shape = shape or data.shape
        self.ndim = len(self.shape) or data.ndim

        # Where does this image lie in this lonely world
        self.calc = TileCalculator(
            self.name,
            self.shape,
            Point(x=self.origin_x, y=self.origin_y),
            Resolution(dy=abs(self.cell_height), dx=abs(self.cell_width)),
            self.tile_shape,
            self.texture_shape,
            wrap_lon=self.wrap_lon,
            projection=projection,
        )
        # What tiles have we used and can we use
        self.texture_state = TextureTileState(self.num_tex_tiles)
示例#6
0
        monkeypatch.setattr('uwsift.view.tile_calculator.calc_stride',
                            calc_stride.py_func)
        monkeypatch.setattr('uwsift.view.tile_calculator.calc_overview_stride',
                            calc_overview_stride.py_func)
        monkeypatch.setattr(
            'uwsift.view.tile_calculator.calc_vertex_coordinates',
            calc_vertex_coordinates.py_func)
        monkeypatch.setattr(
            'uwsift.view.tile_calculator.calc_texture_coordinates',
            calc_texture_coordinates.py_func)


@pytest.mark.parametrize("tc_params,vg,etiles,stride,exp",
                         [([
                             "test", (500, 500),
                             Point(500000, -500000),
                             Resolution(200, 200), (50, 50)
                         ], ViewBox(200000, -300000, 500000, -6000, 500, 400),
                           (1, 1, 1, 1),
                           (2, 2), Box(bottom=3, left=7, top=-2, right=3))])
def test_visible_tiles(tc_params, vg, etiles, stride, exp):
    """Test returned box of tiles to draw is correct given a visible world geometry and sampling."""
    tile_calc = TileCalculator(*tc_params)
    res = tile_calc.visible_tiles(vg, stride, etiles)
    assert res == exp


@pytest.mark.parametrize(
    "cp,ip,cs,exp",
    [([[10.0, 10.0], [20.0, 20.0]], [[10.0, 10.0], [20.0, 20.0]], (1, 1),
      (2.0, 2.0))])
示例#7
0
    def __init__(self,
                 name,
                 image_shape,
                 ul_origin,
                 pixel_rez,
                 tile_shape=(DEFAULT_TILE_HEIGHT, DEFAULT_TILE_WIDTH),
                 texture_shape=(DEFAULT_TEXTURE_HEIGHT, DEFAULT_TEXTURE_WIDTH),
                 projection=DEFAULT_PROJECTION,
                 wrap_lon=False):
        """Initialize numbers used by multiple calculations.

        Args:
            name (str): the 'name' of the tile, typically the path of the file it represents
            image_shape (int, int): (height, width) in pixels
            ul_origin (float, float): (y, x) in world coords specifies upper-left coordinate of the image
            pixel_rez (float, float): (dy, dx) in world coords per pixel ascending from corner [0,0],
                as measured near zero_point
            tile_shape (int, int): the pixel dimensions (h:int, w:int) of the GPU tiling we want to use
            texture_shape (int, int): the size of the texture being used (h, w) in number of tiles

        Notes:

            - Tiling is aligned to pixels, not world
            - World coordinates are eqm such that 0,0 matches 0°N 0°E, going north/south +-90° and west/east +-180°
            - Data coordinates are pixels with b l or b r corner being 0,0

        """
        super(TileCalculator, self).__init__()
        self.name = name
        self.image_shape = Point(np.int64(image_shape[0]),
                                 np.int64(image_shape[1]))
        self.ul_origin = Point(*ul_origin)
        self.pixel_rez = Resolution(np.float64(pixel_rez[0]),
                                    np.float64(pixel_rez[1]))
        self.tile_shape = Point(np.int64(tile_shape[0]),
                                np.int64(tile_shape[1]))
        # in units of tiles:
        self.texture_shape = texture_shape
        # in units of data elements (float32):
        self.texture_size = (self.texture_shape[0] * self.tile_shape[0],
                             self.texture_shape[1] * self.tile_shape[1])
        self.image_tiles_avail = (self.image_shape[0] / self.tile_shape[0],
                                  self.image_shape[1] / self.tile_shape[1])
        self.wrap_lon = wrap_lon

        self.proj = Proj(projection)
        self.image_extents_box = e = Box(
            bottom=np.float64(self.ul_origin[0] -
                              self.image_shape[0] * self.pixel_rez.dy),
            top=np.float64(self.ul_origin[0]),
            left=np.float64(self.ul_origin[1]),
            right=np.float64(self.ul_origin[1] +
                             self.image_shape[1] * self.pixel_rez.dx),
        )
        # Array of points across the image space to be used as an estimate of image coverage
        # Used when checking if the image is viewable on the current canvas's projection
        self.image_mesh = np.meshgrid(
            np.linspace(e.left, e.right, IMAGE_MESH_SIZE),
            np.linspace(e.bottom, e.top, IMAGE_MESH_SIZE))
        self.image_mesh = np.column_stack((
            self.image_mesh[0].ravel(),
            self.image_mesh[1].ravel(),
        ))
        self.image_center = Point(
            self.ul_origin.y - self.image_shape[0] / 2. * self.pixel_rez.dy,
            self.ul_origin.x + self.image_shape[1] / 2. * self.pixel_rez.dx)
        # size of tile in image projection
        self.tile_size = Resolution(self.pixel_rez.dy * self.tile_shape[0],
                                    self.pixel_rez.dx * self.tile_shape[1])
        self.overview_stride = self.calc_overview_stride()
示例#8
0
def visible_tiles(z_dy, z_dx, tile_size_dy, tile_size_dx, image_center_y,
                  image_center_x, image_shape_y, image_shape_x, tile_shape_y,
                  tile_shape_x, v_bottom, v_left, v_top, v_right, v_dy, v_dx,
                  stride_y, stride_x, x_bottom, x_left, x_top, x_right):
    tile_size = Resolution(tile_size_dy * stride_y, tile_size_dx * stride_x)
    # should be the upper-left corner of the tile centered on the center of the image
    to = Point(image_center_y + tile_size.dy / 2.,
               image_center_x - tile_size.dx / 2.)  # tile origin

    # number of data pixels between view edge and originpoint
    pv = Box(bottom=(v_bottom - to.y) / -(z_dy * stride_y),
             top=(v_top - to.y) / -(z_dy * stride_y),
             left=(v_left - to.x) / (z_dx * stride_x),
             right=(v_right - to.x) / (z_dx * stride_x))

    th = tile_shape_y
    tw = tile_shape_x
    # first tile we'll need is (tiy0, tix0)
    # floor to make sure we get the upper-left of the theoretical tile
    tiy0 = np.floor(pv.top / th)
    tix0 = np.floor(pv.left / tw)
    # number of tiles wide and high we'll absolutely need
    # add 0.5 and ceil to make sure we include all possible tiles
    # NOTE: output r and b values are exclusive, l and t are inclusive
    nth = np.ceil((pv.bottom - tiy0 * th) / th + 0.5)
    ntw = np.ceil((pv.right - tix0 * tw) / tw + 0.5)

    # now add the extras
    if x_bottom > 0:
        nth += int(x_bottom)
    if x_left > 0:
        tix0 -= int(x_left)
        ntw += int(x_left)
    if x_top > 0:
        tiy0 -= int(x_top)
        nth += int(x_top)
    if x_right > 0:
        ntw += int(x_right)

    # Total number of tiles in this image at this stride (could be fractional)
    ath, atw = max_tiles_available(image_shape_y, image_shape_x, tile_shape_y,
                                   tile_shape_x, stride_y, stride_x)
    # truncate to the available tiles
    hw = atw / 2.
    hh = ath / 2.
    # center tile is half pixel off because we want center of the center
    # tile to be at the center of the image
    if tix0 < -hw + 0.5:
        ntw += hw - 0.5 + tix0
        tix0 = -hw + 0.5
    if tiy0 < -hh + 0.5:
        nth += hh - 0.5 + tiy0
        tiy0 = -hh + 0.5
    # add 0.5 to include the "end of the tile" since the r and b are exclusive
    if tix0 + ntw > hw + 0.5:
        ntw = hw + 0.5 - tix0
    if tiy0 + nth > hh + 0.5:
        nth = hh + 0.5 - tiy0

    tilebox = Box(
        bottom=np.int64(np.ceil(tiy0 + nth)),
        left=np.int64(np.floor(tix0)),
        top=np.int64(np.floor(tiy0)),
        right=np.int64(np.ceil(tix0 + ntw)),
    )
    return tilebox
示例#9
0
    def __init__(self, data, origin_x, origin_y, cell_width, cell_height,
                 shape=None,
                 tile_shape=(DEFAULT_TILE_HEIGHT, DEFAULT_TILE_WIDTH),
                 texture_shape=(DEFAULT_TEXTURE_HEIGHT, DEFAULT_TEXTURE_WIDTH),
                 wrap_lon=False, projection=DEFAULT_PROJECTION,
                 cmap='viridis', method='tiled', clim='auto', gamma=1.,
                 interpolation='nearest', **kwargs):
        if method != 'tiled':
            raise ValueError("Only 'tiled' method is currently supported")
        method = 'subdivide'
        grid = (1, 1)

        # visual nodes already have names, so be careful
        if not hasattr(self, "name"):
            self.name = kwargs.get("name", None)
        self._viewable_mesh_mask = None
        self._ref1 = None
        self._ref2 = None

        self.origin_x = origin_x
        self.origin_y = origin_y
        self.cell_width = cell_width
        self.cell_height = cell_height  # Note: cell_height is usually negative
        self.texture_shape = texture_shape
        self.tile_shape = tile_shape
        self.num_tex_tiles = self.texture_shape[0] * self.texture_shape[1]
        self._stride = (0, 0)  # Current stride is None when we are showing the overview
        self._latest_tile_box = None
        self.wrap_lon = wrap_lon
        self._tiles = {}
        assert (shape or data is not None), "`data` or `shape` must be provided"
        self.shape = shape or data.shape
        self.ndim = len(self.shape) or data.ndim

        # Where does this image lie in this lonely world
        self.calc = TileCalculator(
            self.name,
            self.shape,
            Point(x=self.origin_x, y=self.origin_y),
            Resolution(dy=abs(self.cell_height), dx=abs(self.cell_width)),
            self.tile_shape,
            self.texture_shape,
            wrap_lon=self.wrap_lon,
            projection=projection,
        )
        # What tiles have we used and can we use
        self.texture_state = TextureTileState(self.num_tex_tiles)

        # load 'float packed rgba8' interpolation kernel
        # to load float interpolation kernel use
        # `load_spatial_filters(packed=False)`
        kernel, self._interpolation_names = load_spatial_filters()

        self._kerneltex = Texture2D(kernel, interpolation='nearest')
        # The unpacking can be debugged by changing "spatial-filters.frag"
        # to have the "unpack" function just return the .r component. That
        # combined with using the below as the _kerneltex allows debugging
        # of the pipeline
        # self._kerneltex = Texture2D(kernel, interpolation='linear',
        #                             internalformat='r32f')

        # create interpolation shader functions for available
        # interpolations
        fun = [Function(_interpolation_template % n)
               for n in self._interpolation_names]
        self._interpolation_names = [n.lower()
                                     for n in self._interpolation_names]

        self._interpolation_fun = dict(zip(self._interpolation_names, fun))
        self._interpolation_names.sort()
        self._interpolation_names = tuple(self._interpolation_names)

        # overwrite "nearest" and "bilinear" spatial-filters
        # with  "hardware" interpolation _data_lookup_fn
        self._interpolation_fun['nearest'] = Function(_texture_lookup)
        self._interpolation_fun['bilinear'] = Function(_texture_lookup)

        if interpolation not in self._interpolation_names:
            raise ValueError("interpolation must be one of %s" %
                             ', '.join(self._interpolation_names))

        self._interpolation = interpolation

        # check texture interpolation
        if self._interpolation == 'bilinear':
            texture_interpolation = 'linear'
        else:
            texture_interpolation = 'nearest'

        self._method = method
        self._grid = grid
        self._need_texture_upload = True
        self._need_vertex_update = True
        self._need_colortransform_update = True
        self._need_interpolation_update = True
        self._texture = TextureAtlas2D(self.texture_shape, tile_shape=self.tile_shape,
                                       interpolation=texture_interpolation,
                                       format="LUMINANCE", internalformat="R32F",
                                       )
        self._subdiv_position = VertexBuffer()
        self._subdiv_texcoord = VertexBuffer()

        # impostor quad covers entire viewport
        vertices = np.array([[-1, -1], [1, -1], [1, 1],
                             [-1, -1], [1, 1], [-1, 1]],
                            dtype=np.float32)
        self._impostor_coords = VertexBuffer(vertices)
        self._null_tr = NullTransform()

        self._init_view(self)
        super(ImageVisual, self).__init__(vcode=VERT_SHADER, fcode=FRAG_SHADER)
        self.set_gl_state('translucent', cull_face=False)
        self._draw_mode = 'triangles'

        # define _data_lookup_fn as None, will be setup in
        # self._build_interpolation()
        self._data_lookup_fn = None

        self.gamma = gamma
        self.clim = clim if clim != 'auto' else (np.nanmin(data), np.nanmax(data))
        self._texture_LUT = None
        self.cmap = cmap

        self.overview_info = None
        self.init_overview(data)
        # self.transform = PROJ4Transform(projection, inverse=True)

        self.freeze()
示例#10
0
 def _get_stride(self, view_box):
     s = self.calc.calc_stride(view_box, texture=self._lowest_rez)
     return Point(np.int64(s[0] * self._lowest_factor), np.int64(s[1] * self._lowest_factor))