예제 #1
0
def convert_geometry_hex1d_to_rect2d(geom, signal, key=None, add_rot=0):
    """converts the geometry object of a camera with a hexagonal grid into
    a square grid by slanting and stretching the 1D arrays of pixel x
    and y positions and signal intensities are converted to 2D
    arrays. If the signal array contains a time-dimension it is
    conserved.

    Parameters
    ----------
    geom : CameraGeometry object
        geometry object of hexagonal cameras
    signal : ndarray
        1D (no timing) or 2D (with timing) array of the pmt signals
    key : (default: None)
        arbitrary key to store the transformed geometry in a buffer
    add_rot : int/float (default: 0)
        parameter to apply an additional rotation of `add_rot` times 60°

    Returns
    -------
    new_geom : CameraGeometry object
        geometry object of the slanted picture now with a rectangular
        grid and a 2D grid for the pixel positions. contains now a 2D
        masking array signifying which of the pixels came from the
        original geometry and which are simply fillers from the
        rectangular grid
    rot_img : ndarray 2D (no timing) or 3D (with timing)
        the rectangular signal image
    """

    if key in rot_buffer:

        # if the conversion with this key was done before and stored,
        # just read it in
        (geom, new_geom, hex_to_rect_map) = rot_buffer[key]
    else:

        # otherwise, we have to do the conversion first now,
        # skew all the coordinates of the original geometry

        # extra_rot is the angle to get back to aligned hexagons with flat
        # tops. Note that the pixel rotation angle brings the camera so that
        # hexagons have a point at the top, so need to go 30deg back to
        # make them flat
        extra_rot = geom.pix_rotation - 30 * u.deg

        # total rotation angle:
        rot_angle = (add_rot * 60 * u.deg) - extra_rot

        logger.debug("geom={}".format(geom))
        logger.debug("rot={}, extra={}".format(rot_angle, extra_rot))

        rot_x, rot_y = unskew_hex_pixel_grid(geom.pix_x, geom.pix_y,
                                             cam_angle=rot_angle)

        # with all the coordinate points, we can define the bin edges
        # of a 2D histogram
        x_edges, y_edges, x_scale = get_orthogonal_grid_edges(rot_x, rot_y)

        # this histogram will introduce bins that do not correspond to
        # any pixel from the original geometry. so we create a mask to
        # remember the true camera pixels by simply throwing all pixel
        # positions into numpy.histogramdd: proper pixels contain the
        # value 1, false pixels the value 0.
        square_mask = np.histogramdd([rot_y, rot_x],
                                     bins=(y_edges, x_edges))[0].astype(bool)

        # to be consistent with the pixel intensity, instead of saving
        # only the rotated positions of the true pixels (rot_x and
        # rot_y), create 2D arrays of all x and y positions (also the
        # false ones).
        grid_x, grid_y = np.meshgrid((x_edges[:-1] + x_edges[1:]) / 2.,
                                     (y_edges[:-1] + y_edges[1:]) / 2.)

        ids = []
        # instead of blindly enumerating all pixels, let's instead
        # store a list of all valid -- i.e. picked by the mask -- 2D
        # indices
        for i, row in enumerate(square_mask):
            for j, val in enumerate(row):
                if val is True:
                    ids.append((i, j))

        # the area of the pixels (note that this is still a deformed
        # image)
        pix_area = np.ones_like(grid_x) \
            * (x_edges[1] - x_edges[0]) * (y_edges[1] - y_edges[0])

        # creating a new geometry object with the attributes we just determined
        new_geom = CameraGeometry(
            cam_id=geom.cam_id + "_rect",
            pix_id=ids,  # this is a list of all the valid coordinate pairs now
            pix_x=grid_x * u.m,
            pix_y=grid_y * u.m,
            pix_area=pix_area * u.m ** 2,
            neighbors=geom.neighbors,
            pix_type='rectangular', apply_derotation=False)

        # storing the pixel mask for later use
        new_geom.mask = square_mask

        # create a transfer map by enumerating all pixel positions in a 2D histogram
        hex_to_rect_map = np.histogramdd([rot_y, rot_x],
                                         bins=(y_edges, x_edges),
                                         weights=np.arange(len(signal)))[0].astype(int)
        # bins that do not correspond to the original image get an entry of `-1`
        hex_to_rect_map[~square_mask] = -1

        if signal.ndim > 1:
            long_map = []
            for i in range(signal.shape[-1]):
                tmp_map = hex_to_rect_map + i * (np.max(hex_to_rect_map) + 1)
                tmp_map[~square_mask] = -1
                long_map.append(tmp_map)
            hex_to_rect_map = np.array(long_map)

        if key is not None:
            # if a key is given, store the essential objects in a buffer
            rot_buffer[key] = (geom, new_geom, hex_to_rect_map)

    # done `if key in rot_buffer`

    # create the rotated rectangular image by applying `hex_to_rect_map` to the flat,
    # extended input image
    # `input_img_ext` is the flattened input image extended by one entry that contains NaN
    # since `hex_to_rect_map` contains `-1` for "fake" pixels, it maps this extra NaN
    # value at the last array position to any bin that does not correspond to a pixel of
    # the original image
    input_img_ext = np.full(np.prod(signal.shape) + 1, np.nan)

    # the way the map is produced, it has the time dimension as axis=0;
    # but `signal` has it as axis=-1, so we need to roll the axes back and forth a bit.
    # if there is no time dimension, `signal` is a 1d array and `rollaxis` has no effect.
    input_img_ext[:-1] = np.rollaxis(signal, axis=-1, start=0).ravel()

    # now apply the transfer map
    rot_img = input_img_ext[hex_to_rect_map]

    # if there is a time dimension, roll the time axis back to the last position
    try:
        rot_img = np.rollaxis(rot_img, 0, 3)
    except ValueError:
        pass

    return new_geom, rot_img
예제 #2
0
def convert_geometry_1d_to_2d(geom, signal, key=None, add_rot=0):
    """converts the geometry object of a camera with a hexagonal grid into
    a square grid by slanting and stretching the 1D arrays of pixel x
    and y positions and signal intensities are converted to 2D
    arrays. If the signal array contains a time-dimension it is
    conserved.

    Parameters
    ----------
    geom : CameraGeometry object
        geometry object of hexagonal cameras
    signal : ndarray
        1D (no timing) or 2D (with timing) array of the pmt signals
    key : (default: None)
        arbitrary key to store the transformed geometry in a buffer
    add_rot : int/float (default: 0)
        parameter to apply an additional rotation of @add_rot times 60°

    Returns
    -------
    new_geom : CameraGeometry object
        geometry object of the slanted picture now with a rectangular
        grid and a 2D grid for the pixel positions contains now a 2D
        masking array signifying which of the pixels came from the
        original geometry and which are simply fillers from the
        rectangular grid square_img : ndarray 2D (no timing) or 3D
        (with timing) array of the pmt signals

    """

    if key in rot_buffer:

        # if the conversion with this key was done and stored before,
        # just read it in
        (rot_x, rot_y, x_edges, y_edges, new_geom, x_scale) = rot_buffer[key]
    else:

        # otherwise, we have to do the conversion now first, skey all
        # the coordinates of the original geometry

        # extra_rot is the angle to get back to aligned hexagons with flat
        # tops. Note that the pixel rotation angle brings the camera so that
        # hexagons have a point at the top, so need to go 30deg past that to
        # make them flat
        extra_rot = geom.pix_rotation + 30*u.deg

        # total rotation angle:
        rot_angle =  (add_rot * 60 * u.deg) - extra_rot
        # if geom.cam_id.startswith("NectarCam")\
        #         or geom.cam_id.startswith("LSTCam"):
        #     rot_angle += geom.cam_rotation + 90 * u.deg

        logger.debug("geom={}".format(geom))
        logger.debug("rot={}, extra={}".format(rot_angle, extra_rot))


        rot_x, rot_y = unskew_hex_pixel_grid(geom.pix_x, geom.pix_y,
                                             rot_angle)

        # with all the coordinate points, we can define the bin edges
        # of a 2D histogram
        x_edges, y_edges, x_scale = get_orthogonal_grid_edges(rot_x, rot_y)

        # this histogram will introduce bins that do not correspond to
        # any pixel from the original geometry. so we create a mask to
        # remember the true camera pixels by simply throwing all pixel
        # positions into numpy.histogramdd: proper pixels contain the
        # value 1, false pixels the value 0.
        square_mask = np.histogramdd([rot_y, rot_x],
                                     bins=(y_edges, x_edges))[0].astype(bool)

        # to be consistent with the pixel intensity, instead of saving
        # only the rotated positions of the true pixels (rot_x and
        # rot_y), create 2D arrays of all x and y positions (also the
        # false ones).
        grid_x, grid_y = np.meshgrid((x_edges[:-1] + x_edges[1:]) / 2.,
                                     (y_edges[:-1] + y_edges[1:]) / 2.)

        ids = []
        # instead of blindly enumerating all pixels, let's instead
        # store a list of all valid -- i.e. picked by the mask -- 2D
        # indices
        for i, row in enumerate(square_mask):
            for j, val in enumerate(row):
                if val is True:
                    ids.append((i, j))

        # the area of the pixels (note that this is still a deformed
        # image)
        pix_area = np.ones_like(grid_x) \
                   * (x_edges[1] - x_edges[0]) * (y_edges[1] - y_edges[0])

        # creating a new geometry object with the attributes we just determined
        new_geom = CameraGeometry(
            cam_id=geom.cam_id+"_rect",
            pix_id=ids,  # this is a list of all the valid coordinate pairs now
            pix_x=grid_x * u.m,
            pix_y=grid_y * u.m,
            pix_area=pix_area * u.m ** 2,
            neighbors=geom.neighbors,
            cam_rotation=-geom.pix_rotation,
            pix_type='rectangular', apply_derotation=False)

        # storing the pixel mask and camera rotation for later use
        new_geom.mask = square_mask


        if key is not None:
            # if a key is given, store the essential objects in a buffer
            rot_buffer[key] = (rot_x, rot_y, x_edges,
                               y_edges, new_geom, x_scale)

    # resample the signal array to correspond to the square grid --
    #  for signal arrays containing time slices (ndim > 1) or not
    #  approach is the same as used for the mask only with the signal
    #  as bin-weights
    if signal.ndim > 1:
        t_dim = signal.shape[1]
        square_img = np.histogramdd([np.repeat(rot_y, t_dim),
                                     np.repeat(rot_x, t_dim),
                                     [a for a in range(t_dim)] * len(rot_x)],
                                    bins=(y_edges, x_edges, range(t_dim + 1)),
                                    weights=signal.ravel())[0]
    else:
        square_img = np.histogramdd([rot_y, rot_x],
                                    bins=(y_edges, x_edges),
                                    weights=signal)[0]

    return new_geom, square_img
예제 #3
0
def convert_geometry_1d_to_2d(geom, signal, key=None, add_rot=0):
    """converts the geometry object of a camera with a hexagonal grid into
    a square grid by slanting and stretching the 1D arrays of pixel x
    and y positions and signal intensities are converted to 2D
    arrays. If the signal array contains a time-dimension it is
    conserved.

    Parameters
    ----------
    geom : CameraGeometry object
        geometry object of hexagonal cameras
    signal : ndarray
        1D (no timing) or 2D (with timing) array of the pmt signals
    key : (default: None)
        arbitrary key to store the transformed geometry in a buffer
    add_rot : int/float (default: 0)
        parameter to apply an additional rotation of @add_rot times 60°

    Returns
    -------
    new_geom : CameraGeometry object
        geometry object of the slanted picture now with a rectangular
        grid and a 2D grid for the pixel positions contains now a 2D
        masking array signifying which of the pixels came from the
        original geometry and which are simply fillers from the
        rectangular grid square_img : ndarray 2D (no timing) or 3D
        (with timing) array of the pmt signals

    """

    if key in rot_buffer:

        # if the conversion with this key was done and stored before,
        # just read it in
        (rot_x, rot_y, x_edges, y_edges, new_geom,
         rot_angle, pix_rotation, x_scale) = rot_buffer[key]
    else:

        # otherwise, we have to do the conversion now first, skew all
        # the coordinates of the original geometry

        # extra_rot is the angle to get back to aligned hexagons with flat
        # tops. Note that the pixel rotation angle brings the camera so that
        # hexagons have a point at the top, so need to go 30deg back to
        # make them flat
        extra_rot = geom.pix_rotation - 30 * u.deg

        # total rotation angle:
        rot_angle = (add_rot * 60 * u.deg) - extra_rot
        # if geom.cam_id.startswith("NectarCam")\
        #         or geom.cam_id.startswith("LSTCam"):
        #     rot_angle += geom.cam_rotation + 90 * u.deg

        logger.debug("geom={}".format(geom))
        logger.debug("rot={}, extra={}".format(rot_angle, extra_rot))

        rot_x, rot_y = unskew_hex_pixel_grid(geom.pix_x, geom.pix_y,
                                             cam_angle=rot_angle)

        # with all the coordinate points, we can define the bin edges
        # of a 2D histogram
        x_edges, y_edges, x_scale = get_orthogonal_grid_edges(rot_x, rot_y)

        # this histogram will introduce bins that do not correspond to
        # any pixel from the original geometry. so we create a mask to
        # remember the true camera pixels by simply throwing all pixel
        # positions into numpy.histogramdd: proper pixels contain the
        # value 1, false pixels the value 0.
        square_mask = np.histogramdd([rot_y, rot_x],
                                     bins=(y_edges, x_edges))[0].astype(bool)

        # to be consistent with the pixel intensity, instead of saving
        # only the rotated positions of the true pixels (rot_x and
        # rot_y), create 2D arrays of all x and y positions (also the
        # false ones).
        grid_x, grid_y = np.meshgrid((x_edges[:-1] + x_edges[1:]) / 2.,
                                     (y_edges[:-1] + y_edges[1:]) / 2.)

        ids = []
        # instead of blindly enumerating all pixels, let's instead
        # store a list of all valid -- i.e. picked by the mask -- 2D
        # indices
        for i, row in enumerate(square_mask):
            for j, val in enumerate(row):
                if val is True:
                    ids.append((i, j))

        # the area of the pixels (note that this is still a deformed
        # image)
        pix_area = np.ones_like(grid_x) \
            * (x_edges[1] - x_edges[0]) * (y_edges[1] - y_edges[0])

        # creating a new geometry object with the attributes we just determined
        new_geom = CameraGeometry(
            cam_id=geom.cam_id + "_rect",
            pix_id=ids,  # this is a list of all the valid coordinate pairs now
            pix_x=grid_x * u.m,
            pix_y=grid_y * u.m,
            pix_area=pix_area * u.m ** 2,
            neighbors=geom.neighbors,
            pix_type='rectangular', apply_derotation=False)

        # storing the pixel mask and camera rotation for later use
        new_geom.mask = square_mask

        if key is not None:
            # if a key is given, store the essential objects in a buffer
            rot_buffer[key] = RotBuffer(rot_x, rot_y, x_edges, y_edges, new_geom,
                                        rot_angle, geom.pix_rotation, x_scale)

    # resample the signal array to correspond to the square grid --
    #  for signal arrays containing time slices (ndim > 1) or not
    #  approach is the same as used for the mask only with the signal
    #  as bin-weights
    if signal.ndim > 1:
        t_dim = signal.shape[1]
        square_img = np.histogramdd([np.repeat(rot_y, t_dim),
                                     np.repeat(rot_x, t_dim),
                                     [a for a in range(t_dim)] * len(rot_x)],
                                    bins=(y_edges, x_edges, range(t_dim + 1)),
                                    weights=signal.ravel())[0]
    else:
        square_img = np.histogramdd([rot_y, rot_x],
                                    bins=(y_edges, x_edges),
                                    weights=signal)[0]

    return new_geom, square_img
예제 #4
0
def convert_geometry_hex1d_to_rect2d(geom, signal, key=None, add_rot=0):
    """converts the geometry object of a camera with a hexagonal grid into
    a square grid by slanting and stretching the 1D arrays of pixel x
    and y positions and signal intensities are converted to 2D
    arrays. If the signal array contains a time-dimension it is
    conserved.

    Parameters
    ----------
    geom : CameraGeometry object
        geometry object of hexagonal cameras
    signal : ndarray
        1D (no timing) or 2D (with timing) array of the pmt signals
    key : (default: None)
        arbitrary key (float, string) to store the transformed geometry in a buffer
        The geometries (hex and rect) will be stored in a buffer.
        The key is necessary to make the conversion back from rect to hex.
    add_rot : int/float (default: 0)
        parameter to apply an additional rotation of `add_rot` times 60°

    Returns
    -------
    new_geom : CameraGeometry object
        geometry object of the slanted picture now with a rectangular
        grid and a 2D grid for the pixel positions. contains now a 2D
        masking array signifying which of the pixels came from the
        original geometry and which are simply fillers from the
        rectangular grid
    rot_img : ndarray 2D (no timing) or 3D (with timing)
        the rectangular signal image

    Examples
    --------
    camera = event.inst.subarray.tel[tel_id].camera
    image = event.r0.tel[tel_id].image[0]
    key = camera.camera_name
    square_geom, square_image = convert_geometry_hex1d_to_rect2d(camera, image, key=key)
    """

    if key in rot_buffer:

        # if the conversion with this key was done before and stored,
        # just read it in
        (geom, new_geom, hex_to_rect_map) = rot_buffer[key]
    else:

        # otherwise, we have to do the conversion first now,
        # skew all the coordinates of the original geometry

        # extra_rot is the angle to get back to aligned hexagons with flat
        # tops. Note that the pixel rotation angle brings the camera so that
        # hexagons have a point at the top, so need to go 30deg back to
        # make them flat
        extra_rot = geom.pix_rotation - 30 * u.deg

        # total rotation angle:
        rot_angle = (add_rot * 60 * u.deg) - extra_rot

        logger.debug(f"geom={geom}")
        logger.debug(f"rot={rot_angle}, extra={extra_rot}")

        rot_x, rot_y = unskew_hex_pixel_grid(geom.pix_x,
                                             geom.pix_y,
                                             cam_angle=rot_angle)

        # with all the coordinate points, we can define the bin edges
        # of a 2D histogram
        x_edges, y_edges, x_scale = get_orthogonal_grid_edges(
            rot_x.to_value(u.m), rot_y.to_value(u.m))

        # this histogram will introduce bins that do not correspond to
        # any pixel from the original geometry. so we create a mask to
        # remember the true camera pixels by simply throwing all pixel
        # positions into numpy.histogramdd: proper pixels contain the
        # value 1, false pixels the value 0.
        square_mask = np.histogramdd(
            [rot_y.to_value(u.m), rot_x.to_value(u.m)],
            bins=(y_edges, x_edges))[0].astype(bool)

        # to be consistent with the pixel intensity, instead of saving
        # only the rotated positions of the true pixels (rot_x and
        # rot_y), create 2D arrays of all x and y positions (also the
        # false ones).
        grid_x, grid_y = np.meshgrid((x_edges[:-1] + x_edges[1:]) / 2.0,
                                     (y_edges[:-1] + y_edges[1:]) / 2.0)

        ids = []
        # instead of blindly enumerating all pixels, let's instead
        # store a list of all valid -- i.e. picked by the mask -- 2D
        # indices
        for i, row in enumerate(square_mask):
            for j, val in enumerate(row):
                if val is True:
                    ids.append((i, j))

        # the area of the pixels (note that this is still a deformed
        # image)
        pix_area = (np.ones_like(grid_x) * (x_edges[1] - x_edges[0]) *
                    (y_edges[1] - y_edges[0]))

        # creating a new geometry object with the attributes we just determined
        new_geom = CameraGeometry(
            camera_name=geom.camera_name + "_rect",
            pix_id=ids,  # this is a list of all the valid coordinate pairs now
            pix_x=u.Quantity(grid_x.ravel(), u.meter),
            pix_y=u.Quantity(grid_y.ravel(), u.meter),
            pix_area=pix_area * u.meter**2,
            neighbors=geom.neighbors,
            pix_type="rectangular",
            apply_derotation=False,
        )

        # storing the pixel mask for later use
        new_geom.mask = square_mask

        # create a transfer map by enumerating all pixel positions in a 2D histogram
        hex_to_rect_map = np.histogramdd(
            [rot_y.to_value(u.m), rot_x.to_value(u.m)],
            bins=(y_edges, x_edges),
            weights=np.arange(len(signal)),
        )[0].astype(int)
        # bins that do not correspond to the original image get an entry of `-1`
        hex_to_rect_map[~square_mask] = -1

        if signal.ndim > 1:
            long_map = []
            for i in range(signal.shape[-1]):
                tmp_map = hex_to_rect_map + i * (np.max(hex_to_rect_map) + 1)
                tmp_map[~square_mask] = -1
                long_map.append(tmp_map)
            hex_to_rect_map = np.array(long_map)

        if key is not None:
            # if a key is given, store the essential objects in a buffer
            rot_buffer[key] = (geom, new_geom, hex_to_rect_map)

    # done `if key in rot_buffer`

    # create the rotated rectangular image by applying `hex_to_rect_map` to the flat,
    # extended input image
    # `input_img_ext` is the flattened input image extended by one entry that contains NaN
    # since `hex_to_rect_map` contains `-1` for "fake" pixels, it maps this extra NaN
    # value at the last array position to any bin that does not correspond to a pixel of
    # the original image
    input_img_ext = np.full(np.prod(signal.shape) + 1, np.nan)

    # the way the map is produced, it has the time dimension as axis=0;
    # but `signal` has it as axis=-1, so we need to roll the axes back and forth a bit.
    # if there is no time dimension, `signal` is a 1d array and `rollaxis` has no effect.
    input_img_ext[:-1] = np.rollaxis(signal, axis=-1, start=0).ravel()

    # now apply the transfer map
    rot_img = input_img_ext[hex_to_rect_map]

    # if there is a time dimension, roll the time axis back to the last position
    try:
        rot_img = np.rollaxis(rot_img, 0, 3)
    except ValueError:
        pass

    return new_geom, rot_img