Coverage for kwave/reconstruction/converter.py: 0%

1 import scipy

2 import uff

3 import numpy as np

4 from kwave import NotATransducer

5 from uff.linear_array import LinearArray

6 import time

7

8

9 def build_channel_data(sensor_data: np.ndarray,

10 kgrid,

11 not_transducer: NotATransducer,

12 sampling_frequency,

13 prf,

14 focal_depth):

15 number_scan_lines = sensor_data.shape[1]

16 input_signal = not_transducer.input_signal

17 transducer = not_transducer.transducer

18

19 x0 = np.arange(0, 96) * transducer.element_width * kgrid.dy

20 x0 = x0 - (x0.max() / 2)

21

22 unique_excitations = uff.excitation.Excitation(

23 waveform=input_signal,

24 sampling_frequency=sampling_frequency,

25 pulse_shape='Gaussian'

26 )

27

28 probe = LinearArray(

29 number_elements=transducer.number_elements * 4,

30 pitch=transducer.element_width * kgrid.dy + transducer.element_spacing * kgrid.dx,

31 element_width=transducer.element_width * kgrid.dy,

32 element_height=transducer.element_length * kgrid.dx,

33 transform=uff.transform.Transform(uff.transform.Translation(0, 0, 0), uff.transform.Rotation(0, 0, 0))

34 )

35

36 ####### Move to LinearArray.update

37

38 if probe.pitch and probe.number_elements:

39

40 # for all lines in this block, probe.element_geometry[0] is hard coded as 0. Should be dynamic!

41

42 # update perimeter

43 if probe.element_width and probe.element_height:

44

45 if not probe.element_geometry:

46 probe.element_geometry = [uff.element_geometry.ElementGeometry(

47 uff.perimeter.Perimeter([

48 uff.Position(0, 0, 0),

49 uff.Position(0, 0, 0),

50 uff.Position(0, 0, 0),

51 uff.Position(0, 0, 0),

52 ])

53 )]

54

55 if not probe.element_geometry[0].perimeter:

56 probe.element_geometry.perimeter = uff.perimeter.Perimeter([

57 uff.Position(0, 0, 0),

58 uff.Position(0, 0, 0),

59 uff.Position(0, 0, 0),

60 uff.Position(0, 0, 0),

61 ])

62

63 probe.element_geometry[0].perimeter.position[0] = uff.Position(x=-probe.element_width / 2,

64 y=-probe.element_height / 2, z=0)

65 probe.element_geometry[0].perimeter.position[1] = uff.Position(x=probe.element_width / 2,

66 y=-probe.element_height / 2, z=0)

67 probe.element_geometry[0].perimeter.position[2] = uff.Position(x=probe.element_width / 2,

68 y=probe.element_height / 2, z=0)

69 probe.element_geometry[0].perimeter.position[3] = uff.Position(x=-probe.element_width / 2,

70 y=probe.element_height / 2, z=0)

71

72 # compute element position in the x-axis

73 x0_local = np.arange(1, probe.number_elements + 1) * probe.pitch

74 x0_local = x0_local - x0_local.mean()

75 x0_local = x0_local.astype(np.float32)

76

77 # create array of elements

78 probe.element = []

79 for n in range(probe.number_elements):

80 element = uff.Element(

81 transform=uff.Transform(

82 translation=uff.Translation(x=x0_local[n], y=0, z=0),

83 rotation=uff.Rotation(x=0, y=0, z=0)

84 ),

85 # linear array can only have a single perimenter impulse_response

86 # impulse_response=[], # probe.element_impulse_response,

87 element_geometry=probe.element_geometry[0]

88 )

89

90 probe.element.append(element)

91

92 ####### Move end

93

94 unique_waves = []

95 for n in range(number_scan_lines):

96 wave = uff.Wave(

97 wave_type=uff.WaveType.CONVERGING,

98 origin=uff.SphericalWaveOrigin(

99 position=uff.Position(x=x0[n], z=focal_depth)

100 ),

101 aperture=uff.Aperture(

102 origin=uff.Position(x=x0[n]),

103 fixed_size=transducer.element_length*kgrid.dx, # wrong, correct one is in below

104 # fixed_size=[active_aperture*el_pitch, el_height],

105 window='rectangular'

106 )

107 )

108 unique_waves.append(wave)

109

110 unique_events = []

111 lag = 1.370851370851371e-06 # (length(input_signal) / 2 + 1) * dt

112 downsample_factor = 1

113 first_sample_time = 650e-9 * np.ones((number_scan_lines, 1)) # CHANGE

114

115 for n in range(number_scan_lines):

116 # we select a time zero reference point: in this case the location of the first element fired

117 arg_min = np.argmax(np.sqrt((x0[n] - x0) ** 2 + focal_depth ** 2))

118 time_zero_reference_point = x0[arg_min]

119

120 transmit_wave = uff.TransmitWave(

121 wave=n,

122 time_zero_reference_point=uff.TimeZeroReferencePoint(x=time_zero_reference_point, y=0, z=0),

123 time_offset=lag,

124 weight=1

125 )

126

127 # transmit and receive channel

128 # channel_mapping = np.arange(n, number_elements + n).tolist()

129 channel_mapping = np.arange(n, transducer.number_elements + n)[None, :].T # shape: [number_elements x 1]

130 transmit_setup = uff.TransmitSetup(

131 probe=1, # different from Matlab

132 channel_mapping=channel_mapping,

133 transmit_waves=[transmit_wave] # should be list!

134 )

135

136 # active_elment_positions = channel_data.probes{1}.get_elements_centre();

137

138 receive_setup = uff.ReceiveSetup(

139 probe=1, # different from Matlab

140 time_offset=first_sample_time[n],

141 channel_mapping=channel_mapping,

142 sampling_frequency=sampling_frequency / downsample_factor

143 )

144

145 unique_event = uff.Event(

146 transmit_setup=transmit_setup,

147 receive_setup=receive_setup

148 )

149

150 unique_events.append(unique_event)

151

152 sequence = []

153 for n in range(len(unique_events)):

154 sequence.append(uff.TimedEvent(

155 event=n,

156 time_offset=(n - 1) / prf

157 ))

158

159 channel_data = uff.channel_data.ChannelData(

160 data=sensor_data,

161 probes=[probe],

162 unique_excitations=[unique_excitations],

163 unique_waves=unique_waves,

164 unique_events=unique_events,

165 description='US B-Mode Linear Transducer Scan, kWave.',

166 authors='kWave.py',

167 sound_speed=not_transducer.sound_speed,

168 system='kWave.py',

169 country_code='DE',

170 local_time=time.strftime('%Y-%m-%dTHH:MM:SS+00'), # fix

171 repetition_rate=prf / len(unique_events),

172 sequence=sequence

173 )

174 return channel_data