ogl_beamforming

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

das.glsl (11193B)


      1 /* See LICENSE for license details. */
      2 #if   DataKind == DataKind_Float32
      3   #define SAMPLE_TYPE           float
      4   #define TEXTURE_KIND          r32f
      5   #define RESULT_TYPE_CAST(a)   (a).x
      6   #define OUTPUT_TYPE_CAST(a)   vec4((a).x, 0, 0, 0)
      7   #if !Fast
      8     #define RESULT_TYPE         vec2
      9     #define RESULT_LAST_INDEX   1
     10   #endif
     11 #elif DataKind == DataKind_Float32Complex
     12   #define SAMPLE_TYPE           vec2
     13   #define TEXTURE_KIND          rg32f
     14   #define RESULT_TYPE_CAST(a)   (a).xy
     15   #define OUTPUT_TYPE_CAST(a)   vec4((a).xy, 0, 0)
     16   #if !Fast
     17     #define RESULT_TYPE         vec3
     18     #define RESULT_LAST_INDEX   2
     19   #endif
     20 #else
     21   #error DataKind unsupported for DAS
     22 #endif
     23 
     24 layout(std430, binding = 1) readonly restrict buffer buffer_1 {
     25 	SAMPLE_TYPE rf_data[];
     26 };
     27 
     28 #ifndef RESULT_TYPE
     29   #define RESULT_TYPE SAMPLE_TYPE
     30 #endif
     31 
     32 #if Fast
     33   #define RESULT_STORE(a, length_a) RESULT_TYPE(a)
     34 	layout(TEXTURE_KIND, binding = 0)           restrict uniform image3D  u_out_data_tex;
     35 #else
     36   #define RESULT_STORE(a, length_a) RESULT_TYPE(a, length_a)
     37 	layout(TEXTURE_KIND, binding = 0) writeonly restrict uniform image3D  u_out_data_tex;
     38 #endif
     39 
     40 layout(r16i,  binding = 1) readonly  restrict uniform iimage1D sparse_elements;
     41 layout(rg32f, binding = 2) readonly  restrict uniform image1D  focal_vectors;
     42 layout(r8i,   binding = 3) readonly  restrict uniform iimage1D transmit_receive_orientations;
     43 
     44 #define RX_ORIENTATION(tx_rx) (((tx_rx) >> 0) & 0x0F)
     45 #define TX_ORIENTATION(tx_rx) (((tx_rx) >> 4) & 0x0F)
     46 
     47 #define C_SPLINE 0.5
     48 
     49 #if DataKind == DataKind_Float32Complex
     50 vec2 rotate_iq(const vec2 iq, const float time)
     51 {
     52 	float arg    = radians(360) * DemodulationFrequency * time;
     53 	mat2  phasor = mat2( cos(arg), sin(arg),
     54 	                    -sin(arg), cos(arg));
     55 	vec2 result = phasor * iq;
     56 	return result;
     57 }
     58 #else
     59   #define rotate_iq(a, b) (a)
     60 #endif
     61 
     62 /* NOTE: See: https://cubic.org/docs/hermite.htm */
     63 SAMPLE_TYPE cubic(const int base_index, const float t)
     64 {
     65 	const mat4 h = mat4(
     66 		 2, -3,  0, 1,
     67 		-2,  3,  0, 0,
     68 		 1, -2,  1, 0,
     69 		 1, -1,  0, 0
     70 	);
     71 
     72 	SAMPLE_TYPE samples[4] = {
     73 		rf_data[base_index + 0],
     74 		rf_data[base_index + 1],
     75 		rf_data[base_index + 2],
     76 		rf_data[base_index + 3],
     77 	};
     78 
     79 	vec4        S  = vec4(t * t * t, t * t, t, 1);
     80 	SAMPLE_TYPE P1 = samples[1];
     81 	SAMPLE_TYPE P2 = samples[2];
     82 	SAMPLE_TYPE T1 = C_SPLINE * (P2 - samples[0]);
     83 	SAMPLE_TYPE T2 = C_SPLINE * (samples[3] - P1);
     84 
     85 #if   DataKind == DataKind_Float32
     86 	vec4 C = vec4(P1.x, P2.x, T1.x, T2.x);
     87 	float result = dot(S, h * C);
     88 #elif DataKind == DataKind_Float32Complex
     89 	mat2x4 C = mat2x4(vec4(P1.x, P2.x, T1.x, T2.x), vec4(P1.y, P2.y, T1.y, T2.y));
     90 	vec2 result = S * h * C;
     91 #endif
     92 	return result;
     93 }
     94 
     95 SAMPLE_TYPE sample_rf(const int channel, const int transmit, const float index)
     96 {
     97 	SAMPLE_TYPE result = SAMPLE_TYPE(0);
     98 	int base_index = int(channel * SampleCount * AcquisitionCount + transmit * SampleCount);
     99 	switch (InterpolationMode) {
    100 	case InterpolationMode_Nearest:{
    101 		if (index >= 0 && int(round(index)) < SampleCount)
    102 			result = rotate_iq(rf_data[base_index + int(round(index))], index / SamplingFrequency);
    103 	}break;
    104 	case InterpolationMode_Linear:{
    105 		if (index >= 0 && round(index) < SampleCount) {
    106 			float tk, t = modf(index, tk);
    107 			int n = base_index + int(tk);
    108 			result = (1 - t) * rf_data[n] + t * rf_data[n + 1];
    109 		}
    110 		result = rotate_iq(result, index / SamplingFrequency);
    111 	}break;
    112 	case InterpolationMode_Cubic:{
    113 		if (index > 0 && int(index) < SampleCount - 2) {
    114 			float tk, t = modf(index, tk);
    115 			result = rotate_iq(cubic(base_index + int(index) - 1, t), index / SamplingFrequency);
    116 		}
    117 	}break;
    118 	}
    119 	return result;
    120 }
    121 
    122 float sample_index(const float distance)
    123 {
    124 	float  time = distance / SpeedOfSound + TimeOffset;
    125 	return time * SamplingFrequency;
    126 }
    127 
    128 float apodize(const float arg)
    129 {
    130 	/* IMPORTANT: do not move calculation of arg into this function. It will generate a
    131 	 * conditional move resulting in cos always being evaluated causing a slowdown */
    132 
    133 	/* NOTE: constant F# dynamic receive apodization. This is implemented as:
    134 	 *
    135 	 *                  /        |x_e - x_i|\
    136 	 *    a(x, z) = cos(F# * π * ----------- ) ^ 2
    137 	 *                  \        |z_e - z_i|/
    138 	 *
    139 	 * where x,z_e are transducer element positions and x,z_i are image positions. */
    140 	float a = cos(radians(180) * arg);
    141 	return a * a;
    142 }
    143 
    144 vec2 rca_plane_projection(const vec3 point, const bool rows)
    145 {
    146 	vec2 result = vec2(point[int(rows)], point[2]);
    147 	return result;
    148 }
    149 
    150 float plane_wave_transmit_distance(const vec3 point, const float transmit_angle, const bool tx_rows)
    151 {
    152 	return dot(rca_plane_projection(point, tx_rows), vec2(sin(transmit_angle), cos(transmit_angle)));
    153 }
    154 
    155 float cylindrical_wave_transmit_distance(const vec3 point, const float focal_depth,
    156                                          const float transmit_angle, const bool tx_rows)
    157 {
    158 	vec2 f = focal_depth * vec2(sin(transmit_angle), cos(transmit_angle));
    159 	return distance(rca_plane_projection(point, tx_rows), f);
    160 }
    161 
    162 int tx_rx_orientation_for_acquisition(const int acquisition)
    163 {
    164 	int result = bool(SingleOrientation) ? TransmitReceiveOrientation : imageLoad(transmit_receive_orientations, acquisition).x;
    165 	return result;
    166 }
    167 
    168 vec2 focal_vector_for_acquisition(const int acquisition)
    169 {
    170 	vec2 result = bool(SingleFocus) ? vec2(TransmitAngle, FocusDepth) : imageLoad(focal_vectors, acquisition).xy;
    171 	return result;
    172 }
    173 
    174 float rca_transmit_distance(const vec3 world_point, const vec2 focal_vector, const int transmit_receive_orientation)
    175 {
    176 	float result = 0;
    177 	if (TX_ORIENTATION(transmit_receive_orientation) != RCAOrientation_None) {
    178 		bool  tx_rows        = TX_ORIENTATION(transmit_receive_orientation) == RCAOrientation_Rows;
    179 		float transmit_angle = radians(focal_vector.x);
    180 		float focal_depth    = focal_vector.y;
    181 
    182 		if (isinf(focal_depth)) {
    183 			result = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    184 		} else {
    185 			result = cylindrical_wave_transmit_distance(world_point, focal_depth, transmit_angle, tx_rows);
    186 		}
    187 	}
    188 	return result;
    189 }
    190 
    191 RESULT_TYPE RCA(const vec3 world_point)
    192 {
    193 	const int acquisition_start = bool(Fast)? u_channel     : 0;
    194 	const int acquisition_end   = bool(Fast)? u_channel + 1 : AcquisitionCount;
    195 	RESULT_TYPE result = RESULT_TYPE(0);
    196 	for (int acquisition = acquisition_start; acquisition < acquisition_end; acquisition++) {
    197 		const int  tx_rx_orientation = tx_rx_orientation_for_acquisition(acquisition);
    198 		const bool rx_rows           = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows;
    199 		const vec2 focal_vector      = focal_vector_for_acquisition(acquisition);
    200 		vec2  xdc_world_point   = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    201 		float transmit_distance = rca_transmit_distance(world_point, focal_vector, tx_rx_orientation);
    202 
    203 		for (int rx_channel = 0; rx_channel < ChannelCount; rx_channel++) {
    204 			vec3  rx_center      = vec3(rx_channel * xdc_element_pitch, 0);
    205 			vec2  receive_vector = xdc_world_point - rca_plane_projection(rx_center, rx_rows);
    206 			float a_arg          = abs(FNumber * receive_vector.x / abs(xdc_world_point.y));
    207 
    208 			if (a_arg < 0.5f) {
    209 				float       sidx  = sample_index(transmit_distance + length(receive_vector));
    210 				SAMPLE_TYPE value = apodize(a_arg) * sample_rf(rx_channel, acquisition, sidx);
    211 				result += RESULT_STORE(value, length(value));
    212 			}
    213 		}
    214 	}
    215 	return result;
    216 }
    217 
    218 RESULT_TYPE HERCULES(const vec3 world_point)
    219 {
    220 	const int   tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
    221 	const bool  rx_cols           = RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Columns;
    222 	const vec2  focal_vector      = focal_vector_for_acquisition(0);
    223 	const float transmit_distance = rca_transmit_distance(world_point, focal_vector, tx_rx_orientation);
    224 	const vec3  xdc_world_point   = (xdc_transform * vec4(world_point, 1)).xyz;
    225 
    226 	RESULT_TYPE result = RESULT_TYPE(0);
    227 	for (int transmit = Sparse; transmit < AcquisitionCount; transmit++) {
    228 		int tx_channel = bool(Sparse) ? imageLoad(sparse_elements, transmit - Sparse).x : transmit;
    229 		#if Fast
    230 		const int rx_channel = u_channel;
    231 		#else
    232 		for (int rx_channel = 0; rx_channel < ChannelCount; rx_channel++)
    233 		#endif
    234 		{
    235 			vec3 element_position;
    236 			if (rx_cols) element_position = vec3(rx_channel, tx_channel, 0) * vec3(xdc_element_pitch, 0);
    237 			else         element_position = vec3(tx_channel, rx_channel, 0) * vec3(xdc_element_pitch, 0);
    238 
    239 			float a_arg = abs(FNumber * distance(xdc_world_point.xy, element_position.xy) /
    240 			                  abs(xdc_world_point.z));
    241 			if (a_arg < 0.5f) {
    242 				float apodization = apodize(a_arg);
    243 				/* NOTE: tribal knowledge */
    244 				if (transmit == 0) apodization *= inversesqrt(AcquisitionCount);
    245 
    246 				float       sidx  = sample_index(transmit_distance + distance(xdc_world_point, element_position));
    247 				SAMPLE_TYPE value = apodization * sample_rf(rx_channel, transmit, sidx);
    248 				result += RESULT_STORE(value, length(value));
    249 			}
    250 		}
    251 	}
    252 	return result;
    253 }
    254 
    255 RESULT_TYPE FORCES(const vec3 xdc_world_point)
    256 {
    257 	const int rx_channel_start = bool(Fast)? u_channel     : 0;
    258 	const int rx_channel_end   = bool(Fast)? u_channel + 1 : ChannelCount;
    259 
    260 	RESULT_TYPE result = RESULT_TYPE(0);
    261 	for (int rx_channel = rx_channel_start; rx_channel < rx_channel_end; rx_channel++) {
    262 		float receive_distance = distance(xdc_world_point.xz, vec2(rx_channel * xdc_element_pitch.x, 0));
    263 		float a_arg = abs(FNumber * (xdc_world_point.x - rx_channel * xdc_element_pitch.x) /
    264 		                  abs(xdc_world_point.z));
    265 		if (a_arg < 0.5f) {
    266 			float apodization = apodize(a_arg);
    267 			for (int transmit = Sparse; transmit < AcquisitionCount; transmit++) {
    268 				int   tx_channel      = bool(Sparse) ? imageLoad(sparse_elements, transmit - Sparse).x : transmit;
    269 				vec3  transmit_center = vec3(xdc_element_pitch * vec2(tx_channel, floor(ChannelCount / 2)), 0);
    270 
    271 				float       sidx  = sample_index(distance(xdc_world_point, transmit_center) + receive_distance);
    272 				SAMPLE_TYPE value = apodization * sample_rf(rx_channel, transmit, sidx);
    273 				result += RESULT_STORE(value, length(value));
    274 			}
    275 		}
    276 	}
    277 	return result;
    278 }
    279 
    280 void main()
    281 {
    282 	ivec3 out_voxel    = ivec3(gl_GlobalInvocationID);
    283 	vec3  image_points = vec3(imageSize(u_out_data_tex)) - 1.0f;
    284 	if (!all(lessThan(out_voxel, imageSize(u_out_data_tex))))
    285 		return;
    286 
    287 	vec3 point       = vec3(out_voxel) / max(vec3(1.0f), image_points);
    288 	vec3 world_point = (voxel_transform * vec4(point, 1)).xyz;
    289 
    290 	RESULT_TYPE sum;
    291 	switch (AcquisitionKind) {
    292 	case AcquisitionKind_FORCES:
    293 	case AcquisitionKind_UFORCES:
    294 	{
    295 		sum = FORCES(world_point);
    296 	}break;
    297 	case AcquisitionKind_HERCULES:
    298 	case AcquisitionKind_UHERCULES:
    299 	case AcquisitionKind_HERO_PA:
    300 	{
    301 		sum = HERCULES(world_point);
    302 	}break;
    303 	case AcquisitionKind_Flash:
    304 	case AcquisitionKind_RCA_TPW:
    305 	case AcquisitionKind_RCA_VLS:
    306 	{
    307 		sum = RCA(world_point);
    308 	}break;
    309 	}
    310 
    311 	#if Fast
    312 		sum += RESULT_TYPE_CAST(imageLoad(u_out_data_tex, out_voxel));
    313 	#endif
    314 
    315 	#if CoherencyWeighting
    316 		/* TODO(rnp): scale such that brightness remains ~constant */
    317 		float denominator = sum[RESULT_LAST_INDEX] + float(sum[RESULT_LAST_INDEX] == 0);
    318 		RESULT_TYPE_CAST(sum) *= RESULT_TYPE_CAST(sum) / denominator;
    319 	#endif
    320 
    321 	imageStore(u_out_data_tex, out_voxel, OUTPUT_TYPE_CAST(sum));
    322 }