Esempio n. 1
0
def _execute(data, air_region: SensibleROI, cores=None, chunksize=None, progress=None):
    log = getLogger(__name__)

    with progress:
        progress.update(msg="Normalization by air region")
        if isinstance(air_region, list):
            air_region = SensibleROI.from_list(air_region)

        # initialise same number of air sums
        img_num = data.shape[0]
        with pu.temp_shared_array((img_num, 1, 1), data.dtype) as air_sums:
            # turn into a 1D array, from the 3D that is returned
            air_sums = air_sums.reshape(img_num)

            calc_sums_partial = ptsm.create_partial(_calc_sum,
                                                    fwd_function=ptsm.return_to_second,
                                                    air_left=air_region.left,
                                                    air_top=air_region.top,
                                                    air_right=air_region.right,
                                                    air_bottom=air_region.bottom)

            data, air_sums = ptsm.execute(data, air_sums, calc_sums_partial, cores, chunksize, progress=progress)

            air_sums_partial = ptsm.create_partial(_divide_by_air_sum, fwd_function=ptsm.inplace)

            data, air_sums = ptsm.execute(data, air_sums, air_sums_partial, cores, chunksize, progress=progress)

            avg = np.average(air_sums)
            max_avg = np.max(air_sums) / avg
            min_avg = np.min(air_sums) / avg

            log.info(f"Normalization by air region. " f"Average: {avg}, max ratio: {max_avg}, min ratio: {min_avg}.")
Esempio n. 2
0
def create_factors(data: np.ndarray, roi=None, cores=None, chunksize=None, progress=None):
    """
    Calculate the scale factors as the mean of the ROI
    :param data: The data stack from which the scale factors will be calculated
    :param roi: Region of interest for which the scale factors will be calculated
    :param cores: Number of cores that will perform the calculation
    :param chunksize: How many chunks of work each core will receive
    :return: The scale factor for each image.
    """

    progress = Progress.ensure_instance(progress, num_steps=data.shape[0])
    with progress:
        img_num = data.shape[0]
        # make sure to clean up if for some reason the scale factor array still exists
        with pu.temp_shared_array((img_num, 1, 1)) as scale_factors:
            # turn into a 1D array, from the 3D that is returned
            scale_factors = scale_factors.reshape(img_num)

            # calculate the scale factor from original image
            calc_sums_partial = ptsm.create_partial(_calc_avg,
                                                    fwd_function=ptsm.return_to_second,
                                                    roi_left=roi[0] if roi else 0,
                                                    roi_top=roi[1] if roi else 0,
                                                    roi_right=roi[2] if roi else data[0].shape[1] - 1,
                                                    roi_bottom=roi[3] if roi else data[0].shape[0] - 1)

            data, scale_factors = ptsm.execute(data, scale_factors, calc_sums_partial, cores, chunksize)

        return scale_factors
def find_center(images: Images,
                progress: Progress) -> Tuple[ScalarCoR, Degrees]:
    # assume the ROI is the full image, i.e. the slices are ALL rows of the image
    slices = np.arange(images.height)
    with pu.temp_shared_array((images.height, )) as shift:
        # this is the area that is looked into for the shift after overlapping the images
        search_range = get_search_range(images.width)

        func = shared_mem.create_partial(do_search,
                                         shared_mem.fwd_index_only,
                                         image_width=images.width,
                                         p0=images.projection(0),
                                         p180=np.fliplr(
                                             images.proj180deg.data[0]),
                                         search_range=search_range)
        shared_mem.execute(shift,
                           func,
                           progress=progress,
                           msg="Finding correlation on row")
        par = np.polyfit(slices, shift, deg=1)
        m = par[0]
        q = par[1]
        LOG.debug(f"m={m}, q={q}")
        theta = Degrees(np.rad2deg(np.arctan(0.5 * m)))
        offset = np.round(m * images.height * 0.5 + q) * 0.5
        LOG.info(f"found offset: {-offset} and tilt {theta}")
        return ScalarCoR(images.h_middle + -offset), theta
def generate_images(shape=g_shape, dtype=np.float32, automatic_free=True) -> Images:
    import inspect
    import uuid
    array_name = f"{str(uuid.uuid4())}{inspect.stack()[1].function}"
    if automatic_free:
        with pu.temp_shared_array(shape, dtype, force_name=array_name) as d:
            return _set_random_data(d, shape, array_name)
    else:
        d = pu.create_array(shape, dtype, array_name)
        return _set_random_data(d, shape, array_name)
def _calculate_correlation_error(images, shared_search_range, min_correlation_error, progress):
    # if the projections are passed in the partial they are copied to every process on every iteration
    # this makes the multiprocessing significantly slower
    # so they are copied into a shared array to avoid that copying
    with pu.temp_shared_array((2, images.height, images.width)) as shared_projections:
        shared_projections[0][:] = images.projection(0)
        shared_projections[1][:] = np.fliplr(images.proj180deg.data[0])

        do_search_partial = ps.create_partial(do_calculate_correlation_err, ps.inplace3, image_width=images.width)

        ps.shared_list = [min_correlation_error, shared_search_range, shared_projections]
        ps.execute(do_search_partial,
                   num_operations=min_correlation_error.shape[0],
                   progress=progress,
                   msg="Finding correlation on row")
def find_center(images: Images, progress: Progress) -> Tuple[ScalarCoR, Degrees]:
    # assume the ROI is the full image, i.e. the slices are ALL rows of the image
    slices = np.arange(images.height)
    with pu.temp_shared_array((images.height, )) as shift:
        search_range = get_search_range(images.width)
        with pu.temp_shared_array((len(search_range), images.height)) as min_correlation_error:
            with pu.temp_shared_array((len(search_range), ), dtype=np.int32) as shared_search_range:
                shared_search_range[:] = np.asarray(search_range, dtype=np.int32)
                _calculate_correlation_error(images, shared_search_range, min_correlation_error, progress)

            # Originally the output of do_search is stored in dimensions
            # corresponding to (search_range, square sum). This is awkward to navigate
            # we transpose store to make the array hold (square sum, search range)
            # so that each store[row] accesses the information for the row's square sum across all search ranges
            _find_shift(images, search_range, min_correlation_error, shift)

            par = np.polyfit(slices, shift, deg=1)
            m = par[0]
            q = par[1]
            LOG.debug(f"m={m}, q={q}")
            theta = Degrees(np.rad2deg(np.arctan(0.5 * m)))
            offset = np.round(m * images.height * 0.5 + q) * 0.5
            LOG.info(f"found offset: {-offset} and tilt {theta}")
            return ScalarCoR(images.h_middle + -offset), theta
Esempio n. 7
0
def _execute(data, flat=None, dark=None, cores=None, chunksize=None, progress=None):
    """
    A benchmark justifying the current implementation, performed on
    500x2048x2048 images.

    #1 Separate runs
    Subtract (sequential with np.subtract(data, dark, out=data)) - 13s
    Divide (par) - 1.15s

    #2 Separate parallel runs
    Subtract (par) - 5.5s
    Divide (par) - 1.15s

    #3 Added subtract into _divide so that it is:
                np.true_divide(
                    np.subtract(data, dark, out=data), norm_divide, out=data)
    Subtract then divide (par) - 55s
    """
    with progress:
        progress.update(msg="Applying background correction")

        with pu.temp_shared_array((1, data.shape[1], data.shape[2]), data.dtype) as norm_divide:
            # remove a dimension, I found this to be the easiest way to do it
            norm_divide = norm_divide.reshape(data.shape[1], data.shape[2])

            # subtract dark from flat and copy into shared array with [:]
            norm_divide[:] = np.subtract(flat, dark)

            # prevent divide-by-zero issues, and negative pixels make no sense
            norm_divide[norm_divide == 0] = MINIMUM_PIXEL_VALUE

            # subtract the dark from all images
            f = ptsm.create_partial(_subtract, fwd_function=ptsm.inplace_second_2d)
            data, dark = ptsm.execute(data, dark, f, cores, chunksize, progress=progress)

            # divide the data by (flat - dark)
            f = ptsm.create_partial(_divide, fwd_function=ptsm.inplace_second_2d)
            data, norm_divide = ptsm.execute(data, norm_divide, f, cores, chunksize, progress=progress)

    return data
Esempio n. 8
0
def gen_img_shared_array_with_val(val=1., shape=g_shape):
    with pu.temp_shared_array(shape) as d:
        n = np.full(shape, val)
        # move the data in the shared array
        d[:] = n[:]
        return d
Esempio n. 9
0
def gen_empty_shared_array(shape=g_shape):
    with pu.temp_shared_array(shape) as d:
        return d
Esempio n. 10
0
def generate_shared_array(shape=g_shape, dtype=np.float32) -> np.ndarray:
    with pu.temp_shared_array(shape, dtype) as generated_array:
        np.copyto(generated_array,
                  np.random.rand(shape[0], shape[1], shape[2]).astype(dtype))
        return generated_array
Esempio n. 11
0
def shared_deepcopy(images: Images) -> np.ndarray:
    with pu.temp_shared_array(images.data.shape) as copy:
        np.copyto(copy, images.data)
        return copy