ogl_beamforming

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

das.glsl (9159B)


      1 /* See LICENSE for license details. */
      2 layout(local_size_x = 32, local_size_y = 1, local_size_z = 32) in;
      3 
      4 layout(std430, binding = 1) readonly restrict buffer buffer_1 {
      5 	vec2 rf_data[];
      6 };
      7 
      8 layout(rg32f, binding = 0) writeonly uniform image3D u_out_data_tex;
      9 
     10 layout(location = 2) uniform int   u_volume_export_pass;
     11 layout(location = 3) uniform ivec3 u_volume_export_dim_offset;
     12 layout(location = 4) uniform mat4  u_xdc_transform;
     13 layout(location = 5) uniform int   u_xdc_index;
     14 layout(location = 6) uniform float u_cycle_t;
     15 
     16 #define C_SPLINE 0.5
     17 
     18 #define TX_ROWS 0
     19 #define TX_COLS 1
     20 
     21 #if 1
     22 /* NOTE: interpolation is unnecessary if the data has been demodulated and not decimated */
     23 vec2 cubic(uint ridx, float t)
     24 {
     25 	return rf_data[ridx + uint(floor(t))];
     26 }
     27 #else
     28 /* NOTE: See: https://cubic.org/docs/hermite.htm */
     29 vec2 cubic(uint ridx, float x)
     30 {
     31 	mat4 h = mat4(
     32 		 2, -3,  0, 1,
     33 		-2,  3,  0, 0,
     34 		 1, -2,  1, 0,
     35 		 1, -1,  0, 0
     36 	);
     37 
     38 	uint  xk = uint(floor(x));
     39 	float t  = (x  - float(xk));
     40 	vec4  S  = vec4(t * t * t, t * t, t, 1);
     41 
     42 	vec2 P1 = rf_data[ridx + xk];
     43 	vec2 P2 = rf_data[ridx + xk + 1];
     44 	vec2 T1 = C_SPLINE * (P2 - rf_data[ridx + xk - 1]);
     45 	vec2 T2 = C_SPLINE * (rf_data[ridx + xk + 2] - P1);
     46 
     47 	vec4 C1 = vec4(P1.x, P2.x, T1.x, T2.x);
     48 	vec4 C2 = vec4(P1.y, P2.y, T1.y, T2.y);
     49 	return vec2(dot(S, h * C1), dot(S, h * C2));
     50 }
     51 #endif
     52 
     53 vec3 calc_image_point(vec3 voxel)
     54 {
     55 	ivec3 out_data_dim = imageSize(u_out_data_tex);
     56 	vec4 output_size   = abs(output_max_coord - output_min_coord);
     57 	vec3 image_point   = output_min_coord.xyz + voxel * output_size.xyz / out_data_dim;
     58 
     59 	switch (das_shader_id) {
     60 	case DAS_ID_UFORCES:
     61 		/* TODO: fix the math so that the image plane can be aritrary */
     62 		image_point.y = 0;
     63 		break;
     64 	case DAS_ID_HERCULES:
     65 	case DAS_ID_RCA:
     66 		if (u_volume_export_pass == 0)
     67 			image_point.y = off_axis_pos;
     68 		break;
     69 	}
     70 
     71 	return image_point;
     72 }
     73 
     74 vec2 apodize(vec2 value, float apodization_arg, float distance)
     75 {
     76 	/* NOTE: apodization value for this transducer element */
     77 	float a  = cos(clamp(abs(apodization_arg * distance), 0, 0.25 * radians(360)));
     78 	return value * a * a;
     79 }
     80 
     81 vec3 row_column_point_scale(bool tx_rows)
     82 {
     83 	return vec3(float(!tx_rows), float(tx_rows), 1);
     84 }
     85 
     86 vec3 world_space_to_rca_space(vec3 image_point, int transmit_orientation)
     87 {
     88 	return (u_xdc_transform * vec4(image_point, 1)).xyz * row_column_point_scale(transmit_orientation != TX_ROWS);
     89 }
     90 
     91 float sample_index(float distance)
     92 {
     93 	float  time = distance / speed_of_sound + time_offset;
     94 	return time * sampling_frequency;
     95 }
     96 
     97 float planewave_transmit_distance(vec3 point, float transmit_angle)
     98 {
     99 	return dot(point, vec3(sin(transmit_angle), sin(transmit_angle), cos(transmit_angle)));
    100 }
    101 
    102 vec2 RCA(vec3 image_point, vec3 delta, uint starting_offset, float apodization_arg)
    103 {
    104 	/* TODO: pass this in (there is a problem in that it depends on the orientation
    105 	 * of the array relative to the target/subject). */
    106 	int  transmit_orientation = TX_ROWS;
    107 	uint ridx      = starting_offset;
    108 	int  direction = beamform_plane * (u_volume_export_pass ^ 1);
    109 	if (direction == TX_COLS) image_point = image_point.yxz;
    110 
    111 	vec3 transmit_point = image_point * row_column_point_scale(transmit_orientation == TX_ROWS);
    112 	vec3 recieve_point  = world_space_to_rca_space(image_point, transmit_orientation);
    113 	// vec3  recieve_point  = (u_xdc_transform * vec4(image_point, 1)).xyz;
    114 
    115 	vec2 sum = vec2(0);
    116 	/* NOTE: For Each Acquistion in Raw Data */
    117 	// uint i = (dec_data_dim.z - 1) * uint(clamp(u_cycle_t, 0, 1)); {
    118 	for (uint i = 0; i < dec_data_dim.z; i++) {
    119 		uint base_idx = i / 4;
    120 		uint sub_idx  = i % 4;
    121 
    122 		float focal_depth    = focal_depths[base_idx][sub_idx];
    123 		float transmit_angle = transmit_angles[base_idx][sub_idx];
    124 		float tdist;
    125 		if (isinf(focal_depth)) {
    126 			tdist = planewave_transmit_distance(transmit_point, transmit_angle);
    127 		} else {
    128 			vec3 f  = vec3(sin(transmit_angle), sin(transmit_angle), cos(transmit_angle));
    129 			f      *= focal_depth * row_column_point_scale(transmit_orientation == TX_ROWS);
    130 			tdist   = distance(transmit_point, f);
    131 		}
    132 
    133 		vec3 rdist = recieve_point;
    134 		/* NOTE: For Each Receiver */
    135 		// uint j = (dec_data_dim.z - 1) * uint(clamp(u_cycle_t, 0, 1)); {
    136 		for (uint j = 0; j < dec_data_dim.y; j++) {
    137 			float sidx  = sample_index(tdist + length(rdist));
    138 			vec2 valid  = vec2(sidx < dec_data_dim.x);
    139 			sum        += apodize(cubic(ridx, sidx), apodization_arg, rdist[0]) * valid;
    140 			rdist[0]   -= delta[0];
    141 			ridx       += dec_data_dim.x;
    142 		}
    143 	}
    144 	return sum;
    145 }
    146 
    147 vec2 HERCULES(vec3 image_point, vec3 delta, uint starting_offset, float apodization_arg)
    148 {
    149 	/* TODO: pass this in (there is a problem in that it depends on the orientation
    150 	 * of the array relative to the target/subject). */
    151 	int   transmit_orientation = TX_ROWS;
    152 	float focal_depth    = focal_depths[0][0];
    153 	float transmit_angle = transmit_angles[0][0];
    154 	vec3  transmit_point = image_point * row_column_point_scale(transmit_orientation == TX_ROWS);
    155 	//vec3  recieve_point  = world_space_to_rca_space(image_point, transmit_orientation);
    156 	vec3  recieve_point  = (u_xdc_transform * vec4(image_point, 1)).xyz;
    157 
    158 	float tdist;
    159 	if (isinf(focal_depth)) {
    160 		tdist = planewave_transmit_distance(transmit_point, transmit_angle);
    161 	} else {
    162 		vec3 f  = vec3(sin(transmit_angle), sin(transmit_angle), cos(transmit_angle));
    163 		f      *= focal_depth * row_column_point_scale(transmit_orientation == TX_ROWS);
    164 		tdist   = distance(transmit_point, f);
    165 	}
    166 
    167 	uint ridx      = starting_offset;
    168 	vec3 rdist     = recieve_point;
    169 	int  direction = beamform_plane * (u_volume_export_pass ^ 1);
    170 
    171 	vec2 sum = vec2(0);
    172 	/* NOTE: For Each Acquistion in Raw Data */
    173 	for (uint i = 0; i < dec_data_dim.z; i++) {
    174 		/* NOTE: For Each Virtual Source */
    175 		for (uint j = 0; j < dec_data_dim.y; j++) {
    176 			float sidx = sample_index(tdist + length(rdist));
    177 			vec2 valid = vec2(sidx < dec_data_dim.x);
    178 
    179 			sum += apodize(cubic(ridx, sidx), apodization_arg, rdist.x) * valid;
    180 
    181 			rdist[direction] -= delta[direction];
    182 			ridx             += dec_data_dim.x;
    183 		}
    184 
    185 		rdist[direction]      = recieve_point[direction];
    186 		rdist[direction ^ 1] -= delta[direction ^ 1];
    187 	}
    188 	return sum;
    189 }
    190 
    191 vec2 uFORCES(vec3 image_point, vec3 delta, uint starting_offset, float apodization_arg)
    192 {
    193 	/* NOTE: skip first acquisition in uforces since its garbage */
    194 	uint uforces = uint(dec_data_dim.y != dec_data_dim.z);
    195 	uint ridx    = starting_offset + dec_data_dim.y * dec_data_dim.x * uforces;
    196 
    197 	image_point  = (u_xdc_transform * vec4(image_point, 1)).xyz;
    198 
    199 	vec2 sum = vec2(0);
    200 	for (uint i = uforces; i < dec_data_dim.z; i++) {
    201 		uint base_idx = (i - uforces) / 4;
    202 		uint sub_idx  = (i - uforces) % 4;
    203 
    204 		vec3  rdist         = image_point;
    205 		vec3  focal_point   = uforces_channels[base_idx][sub_idx] * delta;
    206 		float transmit_dist = distance(image_point, focal_point);
    207 
    208 		for (uint j = 0; j < dec_data_dim.y; j++) {
    209 			float sidx  = sample_index(transmit_dist + length(rdist));
    210 			vec2 valid  = vec2(sidx < dec_data_dim.x);
    211 			sum        += apodize(cubic(ridx, sidx), apodization_arg, rdist.x) * valid;
    212 			rdist      -= delta;
    213 			ridx       += dec_data_dim.x;
    214 		}
    215 	}
    216 	return sum;
    217 }
    218 
    219 void main()
    220 {
    221 
    222 	/* NOTE: Convert voxel to physical coordinates */
    223 	ivec3 out_coord    = ivec3(gl_GlobalInvocationID);
    224 	vec3  image_point  = calc_image_point(vec3(gl_GlobalInvocationID));
    225 
    226 	/* NOTE: array edge vectors for calculating element step delta */
    227 	vec3 edge1 = xdc_corner1[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz;
    228 	vec3 edge2 = xdc_corner2[u_xdc_index].xyz - xdc_origin[u_xdc_index].xyz;
    229 
    230 	/* NOTE: used for constant F# dynamic receive apodization. This is implemented as:
    231 	 *
    232 	 *                  /        |x_e - x_i|\
    233 	 *    a(x, z) = cos(F# * π * ----------- ) ^ 2
    234 	 *                  \        |z_e - z_i|/
    235 	 *
    236 	 * where x,z_e are transducer element positions and x,z_i are image positions. */
    237 	float apod_arg = f_number * 0.5 * radians(360) / abs(image_point.z);
    238 
    239 	/* NOTE: skip over channels corresponding to other arrays */
    240 	uint starting_offset = u_xdc_index * (dec_data_dim.y / xdc_count) * dec_data_dim.x * dec_data_dim.z;
    241 
    242 	/* NOTE: in (u)FORCES we step along line elements */
    243 	vec3 delta;
    244 
    245 	vec2 sum;
    246 	switch (das_shader_id) {
    247 	case DAS_ID_UFORCES:
    248 		/* TODO: there should be a smarter way of detecting this */
    249 		if (edge2.x != 0) delta = vec3(edge2.x, 0, 0) / float(dec_data_dim.y);
    250 		else              delta = vec3(edge1.x, 0, 0) / float(dec_data_dim.y);
    251 		sum = uFORCES(image_point, delta, starting_offset, apod_arg);
    252 		break;
    253 	case DAS_ID_HERCULES:
    254 		/* TODO: there should be a smarter way of detecting this */
    255 		if (edge2.x != 0) delta = vec3(edge2.x, edge1.y, 0) / float(dec_data_dim.y);
    256 		else              delta = vec3(edge1.x, edge2.y, 0) / float(dec_data_dim.y);
    257 		sum = HERCULES(image_point, delta, starting_offset, apod_arg);
    258 		break;
    259 	case DAS_ID_RCA:
    260 		/* TODO: there should be a smarter way of detecting this */
    261 		if (edge2.x != 0) delta = vec3(edge2.x, edge1.y, 0) / float(dec_data_dim.y);
    262 		else              delta = vec3(edge1.x, edge2.y, 0) / float(dec_data_dim.y);
    263 		sum = RCA(image_point, delta, starting_offset, apod_arg);
    264 		break;
    265 	}
    266 
    267 	imageStore(u_out_data_tex, out_coord, vec4(sum.x, sum.y, 0, 0));
    268 }