ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 9959704e88e5abbfa34476e41a1304808bcff4af
Parent: d7e50c8308ff59a5870b78b0c2c6d2dca7f3e5f6
Author: Randy Palamar
Date:   Thu, 15 May 2025 19:33:27 -0600

das: add optional coherency weighting to output data

Diffstat:
Mbeamformer_parameters.h | 9+++++----
Mshaders/das.glsl | 31+++++++++++++++++++------------
Mui.c | 7+++++--
3 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/beamformer_parameters.h b/beamformer_parameters.h @@ -7,7 +7,7 @@ * programatically would be nice. */ -#define BEAMFORMER_PARAMETERS_VERSION (1UL) +#define BEAMFORMER_PARAMETERS_VERSION (2UL) /* X(enumarant, number, shader file name, needs header, pretty name) */ #define COMPUTE_SHADERS \ @@ -83,7 +83,8 @@ typedef enum { X(off_axis_pos, float, , float, , "/* [m] Position on screen normal to beamform in TPW/VLSHERCULES */") \ X(beamform_plane, int32_t, , int, , "/* Plane to Beamform in TPW/VLS/HERCULES */") \ X(f_number, float, , float, , "/* F# (set to 0 to disable) */") \ - X(interpolate, uint32_t, , bool, , "/* Perform Cubic Interpolation of RF Samples */") + X(interpolate, uint32_t, , bool, , "/* Perform Cubic Interpolation of RF Samples */") \ + X(coherency_weighting, uint32_t, , bool, , "/* Apply coherency weighting to output data */") #define BEAMFORMER_PARAMS_HEAD_V0 \ X(channel_mapping, uint16_t, [256], uvec4, [32], "/* Transducer Channel to Verasonics Channel */") \ @@ -122,7 +123,7 @@ typedef struct { BEAMFORMER_PARAMS_HEAD_V0 BEAMFORMER_UI_PARAMS BEAMFORMER_PARAMS_TAIL - float _pad[3]; + float _pad[2]; } BeamformerParametersV0; /* NOTE: This struct follows the OpenGL std140 layout. DO NOT modify unless you have @@ -131,7 +132,7 @@ typedef struct { BEAMFORMER_PARAMS_HEAD BEAMFORMER_UI_PARAMS BEAMFORMER_PARAMS_TAIL - float _pad[3]; + float _pad[2]; } BeamformerParameters; #undef X diff --git a/shaders/das.glsl b/shaders/das.glsl @@ -107,7 +107,7 @@ float cylindricalwave_transmit_distance(vec3 point, float focal_depth, float tra return length(orientation_projection(point - f, tx_rows)); } -vec2 RCA(vec3 image_point, vec3 delta, float apodization_arg) +vec3 RCA(vec3 image_point, vec3 delta, float apodization_arg) { int ridx = 0; int direction = beamform_plane; @@ -119,7 +119,7 @@ vec2 RCA(vec3 image_point, vec3 delta, float apodization_arg) vec3 receive_point = world_space_to_rca_space(image_point, !rx_col); delta = orientation_projection(delta, !rx_col); - vec2 sum = vec2(0); + vec3 sum = vec3(0); /* NOTE: For Each Acquistion in Raw Data */ // uint i = (dec_data_dim.z - 1) * uint(clamp(u_cycle_t, 0, 1)); { for (int i = 0; i < dec_data_dim.z; i++) { @@ -142,7 +142,9 @@ vec2 RCA(vec3 image_point, vec3 delta, float apodization_arg) for (uint j = 0; j < dec_data_dim.y; j++) { float sidx = sample_index(transmit_distance + length(receive_distance)); vec2 valid = vec2(sidx >= 0) * vec2(sidx < dec_data_dim.x); - sum += apodize(sample_rf(ridx, sidx), apodization_arg, length(receive_distance.xy)) * valid; + vec2 samp = valid * apodize(sample_rf(ridx, sidx), apodization_arg, + length(receive_distance.xy)); + sum += vec3(samp, length(samp)); receive_distance -= delta; ridx += int(dec_data_dim.x); } @@ -150,7 +152,7 @@ vec2 RCA(vec3 image_point, vec3 delta, float apodization_arg) return sum; } -vec2 HERCULES(vec3 image_point, vec3 delta, float apodization_arg) +vec3 HERCULES(vec3 image_point, vec3 delta, float apodization_arg) { int uhercules = int(das_shader_id == DAS_ID_UHERCULES); int ridx = int(dec_data_dim.y * dec_data_dim.x * uhercules); @@ -174,7 +176,7 @@ vec2 HERCULES(vec3 image_point, vec3 delta, float apodization_arg) !tx_col); } - vec2 sum = vec2(0); + vec3 sum = vec3(0); /* NOTE: For Each Acquistion in Raw Data */ for (int i = uhercules; i < dec_data_dim.z; i++) { int channel = imageLoad(sparse_elements, i - uhercules).x; @@ -190,15 +192,16 @@ vec2 HERCULES(vec3 image_point, vec3 delta, float apodization_arg) /* NOTE: tribal knowledge */ if (i == 0) valid *= inversesqrt(dec_data_dim.z); - sum += apodize(sample_rf(ridx, sidx), apodization_arg, - length(receive_distance.xy)) * valid; + vec2 samp = valid * apodize(sample_rf(ridx, sidx), apodization_arg, + length(receive_distance.xy)); + sum += vec3(samp, length(samp)); ridx += int(dec_data_dim.x); } } return sum; } -vec2 uFORCES(vec3 image_point, vec3 delta, float apodization_arg) +vec3 uFORCES(vec3 image_point, vec3 delta, float apodization_arg) { /* NOTE: skip first acquisition in uforces since its garbage */ int uforces = int(das_shader_id == DAS_ID_UFORCES); @@ -206,7 +209,7 @@ vec2 uFORCES(vec3 image_point, vec3 delta, float apodization_arg) image_point = (xdc_transform * vec4(image_point, 1)).xyz; - vec2 sum = vec2(0); + vec3 sum = vec3(0); for (int i = uforces; i < dec_data_dim.z; i++) { int channel = imageLoad(sparse_elements, i - uforces).x; vec2 recieve_point = image_point.xz; @@ -216,8 +219,9 @@ vec2 uFORCES(vec3 image_point, vec3 delta, float apodization_arg) for (uint j = 0; j < dec_data_dim.y; j++) { float sidx = sample_index(transmit_dist + length(recieve_point)); vec2 valid = vec2(sidx >= 0) * vec2(sidx < dec_data_dim.x); - sum += valid * apodize(sample_rf(ridx, sidx), apodization_arg, + vec2 samp = valid * apodize(sample_rf(ridx, sidx), apodization_arg, recieve_point.x); + sum += vec3(samp, length(samp)); ridx += int(dec_data_dim.x); recieve_point.x -= delta.x; } @@ -241,7 +245,7 @@ void main() float apod_arg = f_number * radians(180) / abs(image_point.z); /* NOTE: skip over channels corresponding to other arrays */ - vec2 sum; + vec3 sum; switch (das_shader_id) { case DAS_ID_FORCES: case DAS_ID_UFORCES: @@ -258,5 +262,8 @@ void main() break; } - imageStore(u_out_data_tex, out_coord, vec4(sum, 0, 0)); + /* TODO(rnp): scale such that brightness remains ~constant */ + if (coherency_weighting) sum.xy *= sum.xy / (sum.z + float(sum.z == 0)); + + imageStore(u_out_data_tex, out_coord, vec4(sum.xy, 0, 0)); } diff --git a/ui.c b/ui.c @@ -955,9 +955,12 @@ add_beamformer_parameters_view(Variable *parent, BeamformerCtx *ctx) add_beamformer_variable_f32(ui, group, &ui->arena, s8("F#:"), s8(""), &bp->f_number, (v2){.y = 1e3}, 1, 0.1, V_INPUT|V_TEXT|V_CAUSES_COMPUTE, ui->font); - local_persist s8 interpolate_labels[] = {s8("False"), s8("True")}; + local_persist s8 true_false_labels[] = {s8("False"), s8("True")}; add_variable_cycler(ui, group, &ui->arena, V_CAUSES_COMPUTE, ui->font, s8("Interpolate:"), - &bp->interpolate, interpolate_labels, countof(interpolate_labels)); + &bp->interpolate, true_false_labels, countof(true_false_labels)); + + add_variable_cycler(ui, group, &ui->arena, V_CAUSES_COMPUTE, ui->font, s8("Coherency Weighting:"), + &bp->coherency_weighting, true_false_labels, countof(true_false_labels)); return result; }