Beispiel #1
0
def FindSpot(image, sensitivity_limit=100):
    """
    This function detects the spot and calculates and returns the coordinates of
    its center. The algorithms for spot detection and center calculation are
    similar to the ones that are used in Fine alignment.
    image (model.DataArray): Optical image
    sensitivity_limit (int): Limit of sensitivity in spot detection
    returns (tuple of floats):    Position of the spot center in px (from the
       left-top corner of the image), possibly with sub-pixel resolution.
    raises:
            LookupError() if spot was not found
    """
    subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(image, (1, 1), 20, sensitivity_limit)
    if not subimages:
        raise LookupError("No spot detected")

    spot_coordinates = [FindCenterCoordinates(i) for i in subimages]
    optical_coordinates = coordinates.ReconstructCoordinates(subimage_coordinates, spot_coordinates)

    # Too many spots detected
    if len(optical_coordinates) > 10:
        logging.info("Found %d potential spots on image with data %s -> %s",
                     len(optical_coordinates), image.min(), image.max())
        raise LookupError("Too many spots detected")

    # Pick the brightest one
    max_intensity = 0
    max_pos = optical_coordinates[0]
    for i in optical_coordinates:
        x, y = int(round(i[1])), int(round(i[0]))
        if image[x, y] >= max_intensity:
            max_pos = i
            max_intensity = image[x, y]
    return max_pos
Beispiel #2
0
    def test_devide_and_find_center_grid(self):
        """
        Test DivideInNeighborhoods combined with FindCenterCoordinates
        """
        grid_data = hdf5.read_data("grid_10x10.h5")
        C, T, Z, Y, X = grid_data[0].shape
        grid_data[0].shape = Y, X

        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(
            grid_data[0], (10, 10), 40)

        spot_coordinates = coordinates.FindCenterCoordinates(subimages)
        optical_coordinates = coordinates.ReconstructCoordinates(
            subimage_coordinates, spot_coordinates)

        self.assertEqual(len(subimages), 100)
Beispiel #3
0
    def test_divide_and_find_center_grid_noise(self):
        """
        Test DivideInNeighborhoods combined with FindCenterCoordinates for noisy input
        """
        grid_data = hdf5.read_data("grid_10x10.h5")
        C, T, Z, Y, X = grid_data[0].shape
        grid_data[0].shape = Y, X

        # Add Gaussian noise
        noise = random.normal(0, 40, grid_data[0].size)
        noise_array = noise.reshape(grid_data[0].shape[0],
                                    grid_data[0].shape[1])
        noisy_grid_data = grid_data[0] + noise_array

        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(
            noisy_grid_data, (10, 10), 40)

        spot_coordinates = [spot.FindCenterCoordinates(i) for i in subimages]
        optical_coordinates = coordinates.ReconstructCoordinates(
            subimage_coordinates, spot_coordinates)

        self.assertEqual(len(subimages), 100)
Beispiel #4
0
    def test_devide_and_find_center_grid_missing_point(self):
        """
        Test DivideInNeighborhoods combined with FindCenterCoordinates for grid that misses one point
        """
        grid_data = hdf5.read_data("grid_missing_point.h5")
        C, T, Z, Y, X = grid_data[0].shape
        grid_data[0].shape = Y, X

        # Add Gaussian noise
        noise = random.normal(0, 40, grid_data[0].size)
        noise_array = noise.reshape(grid_data[0].shape[0],
                                    grid_data[0].shape[1])
        noisy_grid_data = grid_data[0] + noise_array

        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(
            noisy_grid_data, (10, 10), 40)

        spot_coordinates = coordinates.FindCenterCoordinates(subimages)
        optical_coordinates = coordinates.ReconstructCoordinates(
            subimage_coordinates, spot_coordinates)

        self.assertEqual(len(subimages), 99)
Beispiel #5
0
def main(args):
    """
    Handles the command line arguments
    args is the list of arguments passed
    return (int): value to return to the OS as program exit code
    """

    # arguments handling
    parser = argparse.ArgumentParser(
        description="Automated AR acquisition at multiple spot locations")

    parser.add_argument(
        "--repetitions_x",
        "-x",
        dest="repetitions_x",
        required=True,
        help=
        "repetitions defines the number of CL spots in the grid (x dimension)")
    parser.add_argument(
        "--repetitions_y",
        "-y",
        dest="repetitions_y",
        required=True,
        help=
        "repetitions defines the number of CL spots in the grid (y dimension)")
    parser.add_argument(
        "--dwell_time",
        "-t",
        dest="dwell_time",
        required=True,
        help="dwell_time indicates the time to scan each spot (unit: s)")
    parser.add_argument(
        "--max_allowed_diff",
        "-d",
        dest="max_allowed_diff",
        required=True,
        help=
        "max_allowed_diff indicates the maximum allowed difference in electron coordinates (unit: m)"
    )

    options = parser.parse_args(args[1:])
    repetitions = (int(options.repetitions_x), int(options.repetitions_y))
    dwell_time = float(options.dwell_time)
    max_allowed_diff = float(options.max_allowed_diff)

    try:
        escan = None
        detector = None
        ccd = None
        # find components by their role
        for c in model.getComponents():
            if c.role == "e-beam":
                escan = c
            elif c.role == "se-detector":
                detector = c
            elif c.role == "ccd":
                ccd = c
        if not all([escan, detector, ccd]):
            logging.error("Failed to find all the components")
            raise KeyError("Not all components found")

        # ccd.data.get()
        gscanner = GridScanner(repetitions, dwell_time, escan, ccd, detector)

        # Wait for ScanGrid to finish
        optical_image, electron_coordinates, electron_scale = gscanner.DoAcquisition(
        )
        hdf5.export("scanned_image.h5", optical_image)
        logging.debug("electron coord = %s", electron_coordinates)

        ############## TO BE REMOVED ON TESTING##############
        #        grid_data = hdf5.read_data("scanned_image.h5")
        #        C, T, Z, Y, X = grid_data[0].shape
        #        grid_data[0].shape = Y, X
        #        optical_image = grid_data[0]
        #####################################################

        logging.debug("Isolating spots...")
        opxs = optical_image.metadata[model.MD_PIXEL_SIZE]
        optical_dist = escan.pixelSize.value[0] * electron_scale[0] / opxs[0]
        subimages, subimage_coordinates = coordinates.DivideInNeighborhoods(
            optical_image, repetitions, optical_dist)
        logging.debug("Number of spots found: %d", len(subimages))

        hdf5.export("spot_found.h5", subimages, thumbnail=None)
        logging.debug("Finding spot centers...")
        spot_coordinates = spot.FindCenterCoordinates(subimages)
        logging.debug("center coord = %s", spot_coordinates)
        optical_coordinates = coordinates.ReconstructCoordinates(
            subimage_coordinates, spot_coordinates)
        logging.debug(optical_coordinates)
        rgb_optical = img.DataArray2RGB(optical_image)

        for ta in optical_coordinates:
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 0] = 255
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 1] *= 0.5
            rgb_optical[ta[1] - 1:ta[1] + 1, ta[0] - 1:ta[0] + 1, 2] *= 0.5

        misc.imsave('spots_image.png', rgb_optical)

        # TODO: Make function for scale calculation
        sorted_coordinates = sorted(optical_coordinates,
                                    key=lambda tup: tup[1])
        tab = tuple(
            map(operator.sub, sorted_coordinates[0], sorted_coordinates[1]))
        optical_scale = math.hypot(tab[0], tab[1])
        scale = electron_scale[0] / optical_scale
        print(scale)

        # max_allowed_diff in pixels
        max_allowed_diff_px = max_allowed_diff / escan.pixelSize.value[0]

        logging.debug("Matching coordinates...")
        known_electron_coordinates, known_optical_coordinates, max_diff = coordinates.MatchCoordinates(
            optical_coordinates, electron_coordinates, scale,
            max_allowed_diff_px)

        logging.debug("Calculating transformation...")
        (calc_translation_x, calc_translation_y), (
            calc_scaling_x,
            calc_scaling_y), calc_rotation = transform.CalculateTransform(
                known_electron_coordinates, known_optical_coordinates)
        logging.debug("Electron->Optical: ")
        print(calc_translation_x, calc_translation_y, calc_scaling_x,
              calc_scaling_y, calc_rotation)
        final_electron = coordinates._TransformCoordinates(
            known_optical_coordinates,
            (calc_translation_x, calc_translation_y), calc_rotation,
            (calc_scaling_x, calc_scaling_y))

        logging.debug("Overlay done.")

        # Calculate distance between the expected and found electron coordinates
        coord_diff = []
        for ta, tb in zip(final_electron, known_electron_coordinates):
            tab = tuple(map(operator.sub, ta, tb))
            coord_diff.append(math.hypot(tab[0], tab[1]))

        mean_difference = numpy.mean(coord_diff) * escan.pixelSize.value[0]

        variance_sum = 0
        for i in range(0, len(coord_diff)):
            variance_sum += (mean_difference - coord_diff[i])**2
        variance = (variance_sum / len(coord_diff)) * escan.pixelSize.value[0]

        not_found_spots = len(electron_coordinates) - len(final_electron)

        # Generate overlay image
        logging.debug("Generating images...")
        (calc_translation_x, calc_translation_y), (
            calc_scaling_x,
            calc_scaling_y), calc_rotation = transform.CalculateTransform(
                known_optical_coordinates, known_electron_coordinates)
        logging.debug("Optical->Electron: ")
        print(calc_translation_x, calc_translation_y, calc_scaling_x,
              calc_scaling_y, calc_rotation)
        overlay_coordinates = coordinates._TransformCoordinates(
            known_electron_coordinates,
            (calc_translation_y, calc_translation_x), -calc_rotation,
            (calc_scaling_x, calc_scaling_y))

        for ta in overlay_coordinates:
            rgb_optical[ta[0] - 1:ta[0] + 1, ta[1] - 1:ta[1] + 1, 1] = 255

        misc.imsave('overlay_image.png', rgb_optical)
        misc.imsave('optical_image.png', optical_image)
        logging.debug(
            "Done. Check electron_image.png, optical_image.png and overlay_image.png."
        )

    except:
        logging.exception("Unexpected error while performing action.")
        return 127

    logging.info(
        "\n**Overlay precision stats (Resulted to expected electron coordinates comparison)**\n Mean distance: %f (unit: m)\n Variance: %f (unit: m)\n Not found spots: %d",
        mean_difference, variance, not_found_spots)
    return 0