Ejemplo n.º 1
0
def test_multiview_tfm(use_real_grid):
    # make probe
    probe = arim.Probe.make_matrix_probe(5, 0.5e-3, 1, np.nan, 1e6)
    probe.set_reference_element("first")
    probe.reset_position()
    probe.translate([0.0, 0.0, -1e-3])

    # make frame
    tx_arr, rx_arr = arim.ut.fmc(probe.numelements)
    time = arim.Time(0.5e-6, 1 / 20e6, 100)
    # use random data but ensure reciprocity
    scanlines = np.zeros((len(tx_arr), len(time)))
    for i, (tx, rx) in enumerate(zip(tx_arr, rx_arr)):
        np.random.seed((tx * rx)**2)  # symmetric in tx and rx
        scanlines[i] = np.random.rand(len(time))
    block = arim.Material(6300, 3100)
    frame = arim.Frame(scanlines, time, tx_arr, rx_arr, probe,
                       arim.ExaminationObject(block))

    # prepare view LL-T in contact
    if use_real_grid:
        grid = arim.Grid(0.0, 0.0, 0.0, 0.0, 5e-3, 5e-3, np.nan)
        grid_interface = arim.Interface(*grid.to_oriented_points())
    else:
        grid = arim.Points(np.array([0.0, 0.0, 5e-3]), name="Grid")
        grid_interface = arim.Interface(
            *arim.geometry.default_oriented_points(grid.to_1d_points()))

    backwall = arim.geometry.points_1d_wall_z(-1e-3, 1e-3, 10e-3, 200)
    backwall_interface = arim.Interface(*backwall)
    probe_interface = arim.Interface(*probe.to_oriented_points())

    path_LL = arim.Path(
        [probe_interface, backwall_interface, grid_interface],
        [block, block],
        ["L", "L"],
    )
    path_T = arim.Path([probe_interface, grid_interface], [block], ["T"])
    view = arim.View(path_LL, path_T, "LL-T")
    arim.ray.ray_tracing([view], convert_to_fortran_order=True)

    # make TFM
    tfm = im.tfm.tfm_for_view(frame, grid, view, fillvalue=np.nan)

    # Check this value is unchanged over time!
    expected_val = 12.745499105785953
    assert tfm.res.shape == grid.shape
    if use_real_grid:
        np.testing.assert_array_almost_equal(tfm.res, [[[expected_val]]])
    else:
        np.testing.assert_allclose(tfm.res, expected_val)

    # Reverse view
    view_rev = arim.View(path_LL, path_T, "T-LL")
    tfm_rev = im.tfm.tfm_for_view(frame, grid, view_rev, fillvalue=np.nan)
    assert tfm.res.shape == grid.shape
    if use_real_grid:
        np.testing.assert_array_almost_equal(tfm_rev.res, [[[expected_val]]])
    else:
        np.testing.assert_allclose(tfm_rev.res, expected_val)
Ejemplo n.º 2
0
def downsample_frame(frame, k):
    """
    Return a Frame obtained by grouping the probe elements by 'k'
    
    New scanlines are obtained by averaging original scanlines.
    
    Emulates a Frame with a larger pitch. 1D array only.
    
    If 'k' is not a divisor of the number of elements, do not use
    remaining elements.
    
    """
    probe = frame.probe
    new_numelements = probe.numelements // k
    numelements_to_use = k * new_numelements

    new_locations = np.zeros((new_numelements, 3))

    for i in range(new_numelements):
        new_locations[i] = probe.locations[i * k : (i + 1) * k].mean(axis=0)

    new_probe = arim.Probe(
        new_locations, probe.frequency, bandwidth=probe.bandwidth, pcs=probe.pcs.copy()
    )

    # Prepare downsampled FMC:
    new_scanlines = []
    new_tx = []
    new_rx = []
    elements_idx = np.arange(numelements_to_use)
    for i in range(new_numelements):
        for j in range(new_numelements):
            retained_scanlines_idx = np.logical_and(
                np.isin(frame.tx, elements_idx[i * k : (i + 1) * k]),
                np.isin(frame.rx, elements_idx[j * k : (j + 1) * k]),
            )
            if np.sum(retained_scanlines_idx) != k * k:
                # Missing signals, ignore
                pass
            else:
                new_tx.append(i)
                new_rx.append(j)
                new_scanlines.append(
                    frame.scanlines[retained_scanlines_idx].mean(axis=0)
                )
    new_tx = np.array(new_tx)
    new_rx = np.array(new_rx)
    new_scanlines = np.array(new_scanlines)

    new_frame = arim.Frame(
        new_scanlines, frame.time, new_tx, new_rx, new_probe, frame.examination_object
    )
    return new_frame
Ejemplo n.º 3
0
def test_contact_tfm(use_hmc):
    # make probe
    probe = arim.Probe.make_matrix_probe(5, 0.5e-3, 1, np.nan, 1e6)
    probe.set_reference_element("first")
    probe.reset_position()
    probe.translate([0.0, 0.0, -1e-3])

    # make frame
    if use_hmc:
        tx_arr, rx_arr = arim.ut.hmc(probe.numelements)
    else:
        tx_arr, rx_arr = arim.ut.fmc(probe.numelements)

    time = arim.Time(0.5e-6, 1 / 20e6, 100)

    # use random data but ensure reciprocity
    scanlines = np.zeros((len(tx_arr), len(time)))
    for i, (tx, rx) in enumerate(zip(tx_arr, rx_arr)):
        np.random.seed((tx * rx)**2)  # symmetric in tx and rx
        scanlines[i] = np.random.rand(len(time))

    # check reciprocity
    if not use_hmc:
        for i, (tx, rx) in enumerate(zip(tx_arr, rx_arr)):
            scanline_1 = scanlines[i]
            scanline_2 = scanlines[np.logical_and(tx_arr == rx,
                                                  rx_arr == tx)][0]
            np.testing.assert_allclose(scanline_1,
                                       scanline_2,
                                       err_msg="fmc data not symmetric")

    block = arim.Material(6300, 3100)
    frame = arim.Frame(scanlines, time, tx_arr, rx_arr, probe,
                       arim.ExaminationObject(block))

    # prepare view LL-T in contact
    grid = arim.Points(np.array([0.0, 0.0, 5e-3]), name="Grid")

    tfm = im.tfm.contact_tfm(frame,
                             grid,
                             block.longitudinal_vel,
                             fillvalue=np.nan)

    # Check this value is unchanged over time!
    expected_val = 12.49925772283528
    assert tfm.res.shape == grid.shape
    np.testing.assert_allclose(tfm.res, expected_val)
Ejemplo n.º 4
0
def model_full(dataset_name, use_multifreq, full_tfm=True):
    # %%
    conf = arim.io.load_conf(dataset_name)
    result_dir = conf["result_dir"]

    logger.info(f"dataset_name: {dataset_name}")

    probe = common.load_probe(conf)
    examination_object = arim.io.block_in_immersion_from_conf(conf)
    tx, rx = arim.ut.fmc(probe.numelements)
    numscanlines = len(tx)

    scatterers = common.defect_oriented_point(conf)
    grid = common.grid_near_defect(conf)
    grid_p = grid.to_oriented_points()

    probe_p = probe.to_oriented_points()
    views = bim.make_views(
        examination_object,
        probe_p,
        scatterers,
        max_number_of_reflection=1,
        tfm_unique_only=True,
    )
    views_imaging = bim.make_views(
        examination_object,
        probe_p,
        grid_p,
        max_number_of_reflection=1,
        tfm_unique_only=True,
    )

    arim.ray.ray_tracing(views.values(), convert_to_fortran_order=True)
    arim.ray.ray_tracing(views_imaging.values(), convert_to_fortran_order=True)

    if full_tfm:
        grid_large = common.make_grid_tfm(conf)
        grid_large_p = grid_large.to_oriented_points()
        views_imaging_large = bim.make_views(
            examination_object,
            probe_p,
            grid_large_p,
            max_number_of_reflection=1,
            tfm_unique_only=True,
        )
        arim.ray.ray_tracing(views_imaging_large.values(),
                             convert_to_fortran_order=True)

    if use_multifreq:
        multifreq_key = "multif"
        multifreq_key_title = "MultiFreq"
    else:
        multifreq_key = "singlef"
        multifreq_key_title = "SingleFreq"

    # %% Toneburst and time vector

    max_delay = max(
        (view.tx_path.rays.times.max() + view.rx_path.rays.times.max()
         for view in views.values()))

    numcycles = conf["model"]["toneburst"]["numcycles"]
    centre_freq = common.get_centre_freq(conf, probe)
    dt = 0.25 / centre_freq  # to adjust so that the whole toneburst is sampled
    _tmax = max_delay + 4 * numcycles / centre_freq

    numsamples = scipy.fftpack.next_fast_len(math.ceil(_tmax / dt))
    time = arim.Time(0.0, dt, numsamples)
    freq_array = np.fft.rfftfreq(len(time), dt)
    numfreq = len(freq_array)

    toneburst = arim.model.make_toneburst(numcycles,
                                          centre_freq,
                                          dt,
                                          numsamples,
                                          wrap=True)
    toneburst *= 1.0 / np.abs(hilbert(toneburst)[0])
    toneburst_f = np.fft.rfft(toneburst)

    # plot toneburst
    plt.figure(figsize=(10, 5))
    plt.subplot(1, 2, 1)
    plt.plot(1e6 * time.samples, toneburst)
    plt.title("toneburst (time domain)")
    plt.xlabel("time (µs)")

    plt.subplot(1, 2, 2)
    plt.plot(1e-6 * np.fft.rfftfreq(len(toneburst), dt), abs(toneburst_f))
    plt.title("toneburst (frequency domain)")
    plt.xlabel("frequency (MHz)")

    # %% Compute transfer function
    numangles_for_scat_precomp = 180  # 0 to disable

    model_options = dict(
        probe_element_width=probe.dimensions.x[0],
        numangles_for_scat_precomp=numangles_for_scat_precomp,
    )

    scat_obj = arim.scat.scat_factory(
        material=examination_object.block_material,
        **conf["scatterer"]["specs"])

    scat_angle = np.deg2rad(conf["scatterer"]["angle_deg"])

    transfer_function_f = np.zeros((numscanlines, numfreq), np.complex_)
    tfms = OrderedDict()
    if full_tfm:
        tfms_large = OrderedDict()
    else:
        tfms_large = None

    if use_multifreq:
        # Multi frequency model
        transfer_function_iterator = bim.multifreq_scat_transfer_functions(
            views,
            tx,
            rx,
            freq_array=freq_array,
            scat_obj=scat_obj,
            scat_angle=scat_angle,
            **model_options,
        )
    else:
        # Single frequency model
        transfer_function_iterator = bim.singlefreq_scat_transfer_functions(
            views,
            tx,
            rx,
            freq_array=freq_array,
            scat_obj=scat_obj,
            scat_angle=scat_angle,
            **model_options,
            frequency=common.get_centre_freq(conf, probe),
        )

    with arim.helpers.timeit("Main loop"):
        for viewname, partial_transfer_func in transfer_function_iterator:
            transfer_function_f += partial_transfer_func

            # imaging:
            partial_response = arim.signal.rfft_to_hilbert(
                partial_transfer_func * toneburst_f, numsamples)
            partial_frame = arim.Frame(partial_response, time, tx, rx, probe,
                                       examination_object)

            tfms[viewname] = arim.im.tfm.tfm_for_view(
                partial_frame,
                grid,
                views_imaging[viewname],
                interpolation=common.TFM_FINE_INTERP,
                fillvalue=0.0,
            )
            if full_tfm:
                tfms_large[viewname] = arim.im.tfm.tfm_for_view(
                    partial_frame,
                    grid_large,
                    views_imaging_large[viewname],
                    fillvalue=0.0,
                )

    # %% Save raw TFM results
    if save:
        with open(result_dir / f"tfm_{multifreq_key}.pickle", "wb") as f:
            pickle.dump(tfms, f, pickle.HIGHEST_PROTOCOL)
        if full_tfm:
            with open(result_dir / f"tfm_{multifreq_key}_large.pickle",
                      "wb") as f:
                pickle.dump(tfms_large, f, pickle.HIGHEST_PROTOCOL)

    # %% Measure TFM intensities

    tmp = []
    scatterer_idx = grid.closest_point(*scatterers.points[0])

    for viewname, tfm in tfms.items():
        max_tfm_idx = np.argmax(np.abs(tfm.res))
        tmp.append((
            viewname,
            np.abs(tfm.res.flat[scatterer_idx]),
            np.abs(tfm.res.flat[max_tfm_idx]),
            grid.x.flat[max_tfm_idx],
            grid.y.flat[max_tfm_idx],
            grid.z.flat[max_tfm_idx],
        ))
    intensities_df = pd.DataFrame(
        tmp,
        columns=[
            "view",
            f"Model_{multifreq_key_title}_Centre",
            f"Model_{multifreq_key_title}_Max",
            "x_max_intensity",
            "y_max_intensity",
            "z_max_intensity",
        ],
    ).set_index("view")

    if save:
        intensities_df.to_csv(
            str(result_dir / f"intensities_{multifreq_key}_unscaled.csv"))

    # %% Plot TFM (defect only)
    scale_tfm = aplt.common_dynamic_db_scale(
        [tfm.res for tfm in tfms.values()])
    # scale_tfm = itertools.repeat((None, None))

    ncols = 6
    fig, axes = plt.subplots(
        ncols=ncols,
        nrows=math.ceil(len(tfms) / ncols),
        figsize=(16, 9),
        sharex=True,
        sharey=True,
    )

    for (viewname, tfm), ax in zip(tfms.items(), axes.ravel()):
        ref_db, clim = next(scale_tfm)
        aplt.plot_tfm(
            tfm,
            ax=ax,
            scale="db",
            ref_db=ref_db,
            clim=clim,
            interpolation="none",
            savefig=False,
        )
        ax.set_title(viewname)

        if ax in axes[-1, :]:
            ax.set_xlabel("x (mm)")
        else:
            ax.set_xlabel("")
        if ax in axes[:, 0]:
            ax.set_ylabel("z (mm)")
        else:
            ax.set_ylabel("")
        amp = intensities_df.loc[viewname]
        ax.plot(amp["x_max_intensity"], amp["z_max_intensity"], "1m")
        ax.plot(scatterers.points.x, scatterers.points.z, "dm")

    fig.savefig(str(result_dir / f"tfm_model_{multifreq_key}"))

    # %%

    return tfms, tfms_large, intensities_df
Ejemplo n.º 5
0
with arim.helpers.timeit("Main loop for walls:"):
    for pathname, partial_transfer_func in transfer_function_iterator:
        transfer_function_wall_f += partial_transfer_func

#%% Compute the response in frequency then time domain
response_timetraces_f = (transfer_function_f + transfer_function_wall_f) * toneburst_f
# response_timetraces_f = transfer_function_f  * toneburst_f
# response_timetraces_f = transfer_function_wall_f  * toneburst_f
response_timetraces = arim.signal.rfft_to_hilbert(
    response_timetraces_f, numsamples, axis=-1
)
real_response_timetraces = np.real(response_timetraces)

frame = arim.Frame(
    response_timetraces, time, tx_list, rx_list, probe, examination_object
)

plt.figure()
idx = 31
plt.plot(
    frame.time.samples * 1e6,
    np.real(frame.timetraces[idx]),
    label=f"tx={frame.tx[idx]}, rx={frame.rx[idx]}",
)
plt.xlabel("time (µs)")
plt.title("time-domain response")
plt.legend()
if aplt.conf["savefig"]:
    plt.savefig("time_domain_response")
Ejemplo n.º 6
0
def make_model_walls(conf, use_multifreq=False, wall_keys=WALL_KEYS):
    """
    Generate a FMC containing the walls

    Parameters
    ----------
    conf : dict
    wall_keys : set
    use_multifreq : bool

    Returns
    -------
    Frame
    """
    probe = common.load_probe(conf)
    examination_object = arim.io.examination_object_from_conf(conf)
    tx_list, rx_list = arim.ut.fmc(probe.numelements)
    wall_keys = set(wall_keys)

    model_options = dict(
        probe_element_width=probe.dimensions.x[0],
        use_directivity=True,
        use_beamspread=True,
        use_transrefl=True,
        use_attenuation=True,
    )

    unknown_wall_keys = wall_keys - WALL_KEYS
    if unknown_wall_keys:
        raise ValueError(f"Unknown wall keys: {unknown_wall_keys}")

    # used paths:
    wall_paths = []

    if "frontwall" in wall_keys:
        # Frontwall path
        frontwall_path = bim.frontwall_path(
            examination_object.couplant_material,
            examination_object.block_material,
            *probe.to_oriented_points(),
            *examination_object.frontwall,
        )
        wall_paths.append(frontwall_path)

    # Backwall paths
    backwall_paths = bim.backwall_paths(
        examination_object.couplant_material,
        examination_object.block_material,
        probe.to_oriented_points(),
        examination_object.frontwall,
        examination_object.backwall,
        max_number_of_reflection=2,
    )
    if "backwall_LL" in wall_keys:
        wall_paths.append(backwall_paths["LL"])
    if "backwall_LT" in wall_keys:
        wall_paths.append(backwall_paths["LT"])
        wall_paths.append(backwall_paths["TL"])
    if "backwall_LLLL" in wall_keys:
        wall_paths.append(backwall_paths["LLLL"])

    arim.ray.ray_tracing_for_paths(wall_paths)

    # Toneburst
    numcycles = conf["model"]["toneburst"]["numcycles"]
    centre_freq = common.get_centre_freq(conf, probe)
    max_delay = max(path.rays.times.max() for path in wall_paths)
    dt = 0.25 / centre_freq  # to adjust so that the whole toneburst is sampled
    _tmax = max_delay + 4 * numcycles / centre_freq
    numsamples = scipy.fftpack.next_fast_len(math.ceil(_tmax / dt))
    time = arim.Time(0.0, dt, numsamples)
    freq_array = np.fft.rfftfreq(len(time), dt)
    toneburst = arim.model.make_toneburst(numcycles,
                                          centre_freq,
                                          dt,
                                          numsamples,
                                          wrap=True)
    toneburst_f = np.fft.rfft(toneburst)

    # convert to dict due to bim API
    wall_paths_dict = dict(zip(range(len(wall_paths)), wall_paths))
    if use_multifreq:
        transfer_function_iterator = bim.multifreq_wall_transfer_functions(
            wall_paths_dict, tx_list, rx_list, freq_array, **model_options)
    else:
        transfer_function_iterator = bim.singlefreq_wall_transfer_functions(
            wall_paths_dict, tx_list, rx_list, centre_freq, freq_array,
            **model_options)

    scanlines = None
    for _, transfer_function_wall_f in transfer_function_iterator:
        tmp_scanlines = arim.signal.rfft_to_hilbert(transfer_function_wall_f *
                                                    toneburst_f,
                                                    numsamples,
                                                    axis=-1)
        if scanlines is None:
            scanlines = tmp_scanlines
        else:
            scanlines += tmp_scanlines

    return arim.Frame(scanlines, time, tx_list, rx_list, probe,
                      examination_object)
Ejemplo n.º 7
0
def test_fulltime_model(use_multifreq, show_plots):
    # Setup
    couplant = arim.Material(longitudinal_vel=1480.0,
                             density=1000.0,
                             state_of_matter="liquid")
    block = arim.Material(
        longitudinal_vel=6320.0,
        transverse_vel=3130.0,
        density=2700.0,
        state_of_matter="solid",
        longitudinal_att=arim.material_attenuation_factory("constant", 2.0),
        transverse_att=arim.material_attenuation_factory("constant", 20.0),
    )

    probe = arim.Probe.make_matrix_probe(20, 1e-3, 1, np.nan, 5e6)
    probe_element_width = 0.8e-3
    probe.set_reference_element("first")
    probe.reset_position()
    probe.translate([0.0, 0.0, -5e-3])
    probe.rotate(arim.geometry.rotation_matrix_y(np.deg2rad(10)))

    probe_p = probe.to_oriented_points()
    frontwall = arim.geometry.points_1d_wall_z(numpoints=1000,
                                               xmin=0.0e-3,
                                               xmax=40.0e-3,
                                               z=0.0,
                                               name="Frontwall")
    backwall = arim.geometry.points_1d_wall_z(numpoints=1000,
                                              xmin=0.0e-3,
                                              xmax=40.0e-3,
                                              z=30.0e-3,
                                              name="Backwall")
    scatterer_p = arim.geometry.default_oriented_points(
        arim.Points([[35e-3, 0.0, 20e-3]]))
    all_points = [probe_p, frontwall, backwall, scatterer_p]

    # if show_plots:
    #     import arim.plot as aplt
    #     aplt.plot_interfaces(
    #         all_points, markers=["o", "o", "o", "d"], show_orientations=True
    #     )
    #     aplt.plt.show()

    exam_obj = arim.BlockInImmersion(block, couplant, frontwall, backwall,
                                     scatterer_p)
    scat_obj = arim.scat.scat_factory(material=block,
                                      kind="sdh",
                                      radius=0.5e-3)
    scat_funcs = scat_obj.as_angles_funcs(probe.frequency)
    scat_angle = 0.0

    tx_list, rx_list = arim.ut.fmc(probe.numelements)

    # Toneburst
    dt = 0.25 / probe.frequency  # to adjust so that the whole toneburst is sampled
    toneburst_time, toneburst, toneburst_t0_idx = arim.model.make_toneburst2(
        5, probe.frequency, dt, num_before=1)
    toneburst_f = np.fft.rfft(toneburst)
    toneburst_freq = np.fft.rfftfreq(len(toneburst_time), dt)

    # Allocate a long enough time vector for the timetraces
    views = bim.make_views(
        exam_obj,
        probe_p,
        scatterer_p,
        max_number_of_reflection=0,
        tfm_unique_only=False,
    )
    arim.ray.ray_tracing(views.values())
    max_delay = max(
        (view.tx_path.rays.times.max() + view.rx_path.rays.times.max()
         for view in views.values()))
    timetraces_time = arim.Time(
        0.0, dt,
        math.ceil(max_delay / dt) + len(toneburst_time))
    timetraces = None

    # Run model
    if use_multifreq:
        model_freq_array = toneburst_freq
    else:
        model_freq_array = probe.frequency

    transfer_function_iterator = bim.scat_unshifted_transfer_functions(
        views,
        tx_list,
        rx_list,
        model_freq_array,
        scat_obj,
        probe_element_width=probe_element_width,
        use_directivity=True,
        use_beamspread=True,
        use_transrefl=True,
        use_attenuation=True,
        scat_angle=scat_angle,
        numangles_for_scat_precomp=120,
    )

    for unshifted_transfer_func, delays in transfer_function_iterator:
        timetraces = arim.model.transfer_func_to_timetraces(
            unshifted_transfer_func,
            delays,
            timetraces_time,
            toneburst_time,
            toneburst_freq,
            toneburst_f,
            toneburst_t0_idx,
            timetraces=timetraces,
        )
    frame = arim.Frame(timetraces, timetraces_time, tx_list, rx_list, probe,
                       exam_obj)
    if show_plots:
        import matplotlib.pyplot as plt
        import arim.plot as aplt

        aplt.plot_bscan_pulse_echo(frame)
        plt.title(
            f"test_fulltime_model - Bscan - use_multifreq={use_multifreq}")

        tx = 0
        rx = probe.numelements - 1
        plt.figure()
        plt.plot(np.real(frame.get_timetrace(tx, rx)),
                 label=f"tx={tx}, rx={rx}")
        plt.plot(np.real(frame.get_timetrace(rx, tx)),
                 label=f"tx={rx}, rx={tx}")
        plt.title(f"test_fulltime_model - use_multifreq={use_multifreq}")
        plt.legend()
        plt.show()