ogl_beamforming

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

das.glsl (12312B)


      1 /* See LICENSE for license details. */
      2 layout(std430, binding = 1) readonly restrict buffer buffer_1 {
      3 	vec2 rf_data[];
      4 };
      5 
      6 #if DAS_FAST
      7 layout(rg32f, binding = 0)           restrict uniform image3D  u_out_data_tex;
      8 #else
      9 layout(rg32f, binding = 0) writeonly restrict uniform image3D  u_out_data_tex;
     10 #endif
     11 
     12 layout(r16i,  binding = 1) readonly  restrict uniform iimage1D sparse_elements;
     13 layout(rg32f, binding = 2) readonly  restrict uniform image1D  focal_vectors;
     14 
     15 #define C_SPLINE 0.5
     16 
     17 vec2 rotate_iq(vec2 iq, float time)
     18 {
     19 	vec2 result = iq;
     20 	if (demodulation_frequency > 0) {
     21 		float arg    = radians(360) * demodulation_frequency * time;
     22 		mat2  phasor = mat2( cos(arg), sin(arg),
     23 		                    -sin(arg), cos(arg));
     24 		result = phasor * iq;
     25 	}
     26 	return result;
     27 }
     28 
     29 /* NOTE: See: https://cubic.org/docs/hermite.htm */
     30 vec2 cubic(int base_index, float index)
     31 {
     32 	const mat4 h = mat4(
     33 		 2, -3,  0, 1,
     34 		-2,  3,  0, 0,
     35 		 1, -2,  1, 0,
     36 		 1, -1,  0, 0
     37 	);
     38 
     39 	float tk, t = modf(index, tk);
     40 	vec2 samples[4] = {
     41 		rf_data[base_index + int(tk) - 1],
     42 		rf_data[base_index + int(tk) + 0],
     43 		rf_data[base_index + int(tk) + 1],
     44 		rf_data[base_index + int(tk) + 2],
     45 	};
     46 
     47 	vec4 S  = vec4(t * t * t, t * t, t, 1);
     48 	vec2 P1 = samples[1];
     49 	vec2 P2 = samples[2];
     50 	vec2 T1 = C_SPLINE * (P2 - samples[0]);
     51 	vec2 T2 = C_SPLINE * (samples[3] - P1);
     52 
     53 	mat2x4 C = mat2x4(vec4(P1.x, P2.x, T1.x, T2.x), vec4(P1.y, P2.y, T1.y, T2.y));
     54 	vec2 result = S * h * C;
     55 	return result;
     56 }
     57 
     58 vec2 sample_rf(int channel, int transmit, float index)
     59 {
     60 	bool interpolate = bool(shader_flags & ShaderFlags_Interpolate);
     61 	vec2 result     = vec2(index >= 0.0f) * vec2((int(index) + 1 + int(interpolate)) < sample_count);
     62 	int  base_index = int(channel * sample_count * acquisition_count + transmit * sample_count);
     63 	if (interpolate) result *= cubic(base_index, index);
     64 	else             result *= rf_data[base_index + int(round(index))];
     65 	result = rotate_iq(result, index / sampling_frequency);
     66 	return result;
     67 }
     68 
     69 float sample_index(float distance)
     70 {
     71 	float  time = distance / speed_of_sound + time_offset;
     72 	return time * sampling_frequency;
     73 }
     74 
     75 float apodize(float arg)
     76 {
     77 	/* NOTE: used for constant F# dynamic receive apodization. This is implemented as:
     78 	 *
     79 	 *                  /        |x_e - x_i|\
     80 	 *    a(x, z) = cos(F# * π * ----------- ) ^ 2
     81 	 *                  \        |z_e - z_i|/
     82 	 *
     83 	 * where x,z_e are transducer element positions and x,z_i are image positions. */
     84 	float a = cos(clamp(abs(arg), 0, 0.25 * radians(360)));
     85 	return a * a;
     86 }
     87 
     88 vec2 rca_plane_projection(vec3 point, bool rows)
     89 {
     90 	vec2 result = vec2(point[int(rows)], point[2]);
     91 	return result;
     92 }
     93 
     94 float plane_wave_transmit_distance(vec3 point, float transmit_angle, bool tx_rows)
     95 {
     96 	return dot(rca_plane_projection(point, tx_rows), vec2(sin(transmit_angle), cos(transmit_angle)));
     97 }
     98 
     99 float cylindrical_wave_transmit_distance(vec3 point, float focal_depth, float transmit_angle, bool tx_rows)
    100 {
    101 	vec2 f = focal_depth * vec2(sin(transmit_angle), cos(transmit_angle));
    102 	return distance(rca_plane_projection(point, tx_rows), f);
    103 }
    104 
    105 #if DAS_FAST
    106 vec3 RCA(vec3 world_point)
    107 {
    108 	bool  tx_rows         = bool((shader_flags & ShaderFlags_TxColumns) == 0);
    109 	bool  rx_rows         = bool((shader_flags & ShaderFlags_RxColumns) == 0);
    110 	vec2  xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    111 	vec2  focal_vector    = imageLoad(focal_vectors, u_channel).xy;
    112 	float transmit_angle  = radians(focal_vector.x);
    113 	float focal_depth     = focal_vector.y;
    114 
    115 	float transmit_distance;
    116 	if (isinf(focal_depth)) {
    117 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    118 	} else {
    119 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    120 		                                                       transmit_angle, tx_rows);
    121 	}
    122 
    123 	vec2 result = vec2(0);
    124 	for (int channel = 0; channel < channel_count; channel++) {
    125 		vec2  receive_vector   = xdc_world_point - rca_plane_projection(vec3(channel * xdc_element_pitch, 0), rx_rows);
    126 		float receive_distance = length(receive_vector);
    127 		float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.y) * receive_vector.x);
    128 
    129 		if (apodization > 0) {
    130 			float sidx  = sample_index(transmit_distance + receive_distance);
    131 			result     += apodization * sample_rf(channel, u_channel, sidx);
    132 		}
    133 	}
    134 	return vec3(result, 0);
    135 }
    136 #else
    137 vec3 RCA(vec3 world_point)
    138 {
    139 	bool tx_rows         = bool((shader_flags & ShaderFlags_TxColumns) == 0);
    140 	bool rx_rows         = bool((shader_flags & ShaderFlags_RxColumns) == 0);
    141 	vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    142 
    143 	vec3 sum = vec3(0);
    144 	for (int transmit = 0; transmit < acquisition_count; transmit++) {
    145 		vec2  focal_vector   = imageLoad(focal_vectors, transmit).xy;
    146 		float transmit_angle = radians(focal_vector.x);
    147 		float focal_depth    = focal_vector.y;
    148 
    149 		float transmit_distance;
    150 		if (isinf(focal_depth)) {
    151 			transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    152 		} else {
    153 			transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    154 			                                                       transmit_angle, tx_rows);
    155 		}
    156 
    157 		for (int rx_channel = 0; rx_channel < channel_count; rx_channel++) {
    158 			vec3  rx_center      = vec3(rx_channel * xdc_element_pitch, 0);
    159 			vec2  receive_vector = xdc_world_point - rca_plane_projection(rx_center, rx_rows);
    160 			float apodization    = apodize(f_number * radians(180) / abs(xdc_world_point.y) * receive_vector.x);
    161 
    162 			if (apodization > 0) {
    163 				float sidx   = sample_index(transmit_distance + length(receive_vector));
    164 				vec2  value  = apodization * sample_rf(rx_channel, transmit, sidx);
    165 				sum         += vec3(value, length(value));
    166 			}
    167 		}
    168 	}
    169 	return sum;
    170 }
    171 #endif
    172 
    173 #if DAS_FAST
    174 vec3 HERCULES(vec3 world_point)
    175 {
    176 	bool  uhercules       = shader_kind == ShaderKind_UHERCULES;
    177 	vec3  xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    178 	bool  tx_rows         = bool((shader_flags & ShaderFlags_TxColumns) == 0);
    179 	bool  rx_cols         = bool((shader_flags & ShaderFlags_RxColumns));
    180 	vec2  focal_vector    = imageLoad(focal_vectors, 0).xy;
    181 	float transmit_angle  = radians(focal_vector.x);
    182 	float focal_depth     = focal_vector.y;
    183 
    184 	float transmit_distance;
    185 	if (isinf(focal_depth)) {
    186 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    187 	} else {
    188 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    189 		                                                       transmit_angle, tx_rows);
    190 	}
    191 
    192 	vec2 result = vec2(0);
    193 	for (int transmit = int(uhercules); transmit < acquisition_count; transmit++) {
    194 		int tx_channel = uhercules ? imageLoad(sparse_elements, transmit - int(uhercules)).x : transmit;
    195 		vec3 element_position;
    196 		if (rx_cols) element_position = vec3(u_channel,  tx_channel, 0) * vec3(xdc_element_pitch, 0);
    197 		else         element_position = vec3(tx_channel, u_channel,  0) * vec3(xdc_element_pitch, 0);
    198 
    199 		float apodization = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    200 		                            distance(xdc_world_point.xy, element_position.xy));
    201 		if (apodization > 0) {
    202 			/* NOTE: tribal knowledge */
    203 			if (transmit == 0) apodization *= inversesqrt(acquisition_count);
    204 
    205 			float sidx  = sample_index(transmit_distance + distance(xdc_world_point, element_position));
    206 			result     += apodization * sample_rf(u_channel, transmit, sidx);
    207 		}
    208 	}
    209 	return vec3(result, 0);
    210 }
    211 #else
    212 vec3 HERCULES(vec3 world_point)
    213 {
    214 	bool  uhercules       = shader_kind == ShaderKind_UHERCULES;
    215 	vec3  xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    216 	bool  tx_rows         = bool((shader_flags & ShaderFlags_TxColumns) == 0);
    217 	bool  rx_cols         = bool((shader_flags & ShaderFlags_RxColumns));
    218 	vec2  focal_vector    = imageLoad(focal_vectors, 0).xy;
    219 	float transmit_angle  = radians(focal_vector.x);
    220 	float focal_depth     = focal_vector.y;
    221 
    222 	float transmit_distance;
    223 	if (isinf(focal_depth)) {
    224 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    225 	} else {
    226 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    227 		                                                       transmit_angle, tx_rows);
    228 	}
    229 
    230 	vec3 result = vec3(0);
    231 	for (int transmit = int(uhercules); transmit < acquisition_count; transmit++) {
    232 		int tx_channel = uhercules ? imageLoad(sparse_elements, transmit - int(uhercules)).x : transmit;
    233 		for (int rx_channel = 0; rx_channel < channel_count; rx_channel++) {
    234 			vec3 element_position;
    235 			if (rx_cols) element_position = vec3(rx_channel, tx_channel, 0) * vec3(xdc_element_pitch, 0);
    236 			else         element_position = vec3(tx_channel, rx_channel, 0) * vec3(xdc_element_pitch, 0);
    237 
    238 			float apodization = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    239 			                            distance(xdc_world_point.xy, element_position.xy));
    240 			if (apodization > 0) {
    241 				/* NOTE: tribal knowledge */
    242 				if (transmit == 0) apodization *= inversesqrt(acquisition_count);
    243 
    244 				float sidx   = sample_index(transmit_distance + distance(xdc_world_point, element_position));
    245 				vec2  value  = apodization * sample_rf(rx_channel, transmit, sidx);
    246 				result      += vec3(value, length(value));
    247 			}
    248 		}
    249 	}
    250 	return result;
    251 }
    252 #endif
    253 
    254 #if DAS_FAST
    255 vec3 FORCES(vec3 world_point)
    256 {
    257 	bool  uforces          = shader_kind == ShaderKind_UFORCES;
    258 	vec3  xdc_world_point  = (xdc_transform * vec4(world_point, 1)).xyz;
    259 	float receive_distance = distance(xdc_world_point.xz, vec2(u_channel * xdc_element_pitch.x, 0));
    260 	float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    261 	                                 (xdc_world_point.x - u_channel * xdc_element_pitch.x));
    262 
    263 	vec2 result = vec2(0);
    264 	if (apodization > 0) {
    265 		for (int transmit = int(uforces); transmit < acquisition_count; transmit++) {
    266 			int   tx_channel      = uforces ? imageLoad(sparse_elements, transmit - int(uforces)).x : transmit;
    267 			vec3  transmit_center = vec3(xdc_element_pitch * vec2(tx_channel, floor(channel_count / 2)), 0);
    268 
    269 			float sidx  = sample_index(distance(xdc_world_point, transmit_center) + receive_distance);
    270 			result     += apodization * sample_rf(u_channel, transmit, sidx);
    271 		}
    272 	}
    273 	return vec3(result, 0);
    274 }
    275 #else
    276 vec3 FORCES(vec3 world_point)
    277 {
    278 	bool uforces         = shader_kind == ShaderKind_UFORCES;
    279 	vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    280 
    281 	vec3 result = vec3(0);
    282 	for (int rx_channel = 0; rx_channel < channel_count; rx_channel++) {
    283 		float receive_distance = distance(xdc_world_point.xz, vec2(rx_channel * xdc_element_pitch.x, 0));
    284 		float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    285 		                                 (xdc_world_point.x - rx_channel * xdc_element_pitch.x));
    286 		if (apodization > 0) {
    287 			for (int transmit = int(uforces); transmit < acquisition_count; transmit++) {
    288 				int   tx_channel      = uforces ? imageLoad(sparse_elements, transmit - int(uforces)).x : transmit;
    289 				vec3  transmit_center = vec3(xdc_element_pitch * vec2(tx_channel, floor(channel_count / 2)), 0);
    290 
    291 				float sidx   = sample_index(distance(xdc_world_point, transmit_center) + receive_distance);
    292 				vec2  value  = apodization * sample_rf(rx_channel, tx_channel, sidx);
    293 				result      += vec3(value, length(value));
    294 			}
    295 		}
    296 	}
    297 	return result;
    298 }
    299 #endif
    300 
    301 void main()
    302 {
    303 	ivec3 out_voxel = ivec3(gl_GlobalInvocationID);
    304 #if DAS_FAST
    305 	vec3 sum = vec3(imageLoad(u_out_data_tex, out_voxel).xy, 0);
    306 #else
    307 	vec3 sum = vec3(0);
    308 	out_voxel += u_voxel_offset;
    309 #endif
    310 
    311 	vec3 world_point = (voxel_transform * vec4(out_voxel, 1)).xyz;
    312 
    313 	switch (shader_kind) {
    314 	case ShaderKind_FORCES:
    315 	case ShaderKind_UFORCES:
    316 	{
    317 		sum += FORCES(world_point);
    318 	}break;
    319 	case ShaderKind_HERCULES:
    320 	case ShaderKind_UHERCULES:
    321 	{
    322 		sum += HERCULES(world_point);
    323 	}break;
    324 	case ShaderKind_Flash:
    325 	case ShaderKind_RCA_TPW:
    326 	case ShaderKind_RCA_VLS:
    327 	{
    328 		sum += RCA(world_point);
    329 	}break;
    330 	}
    331 
    332 	/* TODO(rnp): scale such that brightness remains ~constant */
    333 	if (bool(shader_flags & ShaderFlags_CoherencyWeighting))
    334 		sum.xy *= sum.xy / (sum.z + float(sum.z == 0));
    335 
    336 	imageStore(u_out_data_tex, out_voxel, vec4(sum.xy, 0, 0));
    337 }