Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/ops/signal/shape_ops.py: 16%

85 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-03 07:57 +0000

1# Copyright 2017 The TensorFlow Authors. All Rights Reserved. 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14# ============================================================================== 

15"""General shape ops for frames.""" 

16 

17import numpy as np 

18 

19from tensorflow.python.framework import constant_op 

20from tensorflow.python.framework import ops 

21from tensorflow.python.framework import tensor_util 

22from tensorflow.python.ops import array_ops 

23from tensorflow.python.ops import math_ops 

24from tensorflow.python.ops.signal import util_ops 

25from tensorflow.python.util import dispatch 

26from tensorflow.python.util.tf_export import tf_export 

27 

28 

29def _infer_frame_shape(signal, frame_length, frame_step, pad_end, axis): 

30 """Infers the shape of the return value of `frame`.""" 

31 frame_length = tensor_util.constant_value(frame_length) 

32 frame_step = tensor_util.constant_value(frame_step) 

33 axis = tensor_util.constant_value(axis) 

34 if signal.shape.ndims is None: 

35 return None 

36 if axis is None: 

37 return [None] * (signal.shape.ndims + 1) 

38 

39 signal_shape = signal.shape.as_list() 

40 num_frames = None 

41 frame_axis = signal_shape[axis] 

42 outer_dimensions = signal_shape[:axis] 

43 inner_dimensions = signal_shape[axis:][1:] 

44 if signal_shape and frame_axis is not None: 

45 if frame_step is not None and pad_end: 

46 # Double negative is so that we round up. 

47 num_frames = max(0, -(-frame_axis // frame_step)) 

48 elif frame_step is not None and frame_length is not None: 

49 assert not pad_end 

50 num_frames = max( 

51 0, (frame_axis - frame_length + frame_step) // frame_step) 

52 return outer_dimensions + [num_frames, frame_length] + inner_dimensions 

53 

54 

55@tf_export("signal.frame") 

56@dispatch.add_dispatch_support 

57def frame(signal, frame_length, frame_step, pad_end=False, pad_value=0, axis=-1, 

58 name=None): 

59 """Expands `signal`'s `axis` dimension into frames of `frame_length`. 

60 

61 Slides a window of size `frame_length` over `signal`'s `axis` dimension 

62 with a stride of `frame_step`, replacing the `axis` dimension with 

63 `[frames, frame_length]` frames. 

64 

65 If `pad_end` is True, window positions that are past the end of the `axis` 

66 dimension are padded with `pad_value` until the window moves fully past the 

67 end of the dimension. Otherwise, only window positions that fully overlap the 

68 `axis` dimension are produced. 

69 

70 For example: 

71 

72 >>> # A batch size 3 tensor of 9152 audio samples. 

73 >>> audio = tf.random.normal([3, 9152]) 

74 >>> 

75 >>> # Compute overlapping frames of length 512 with a step of 180 (frames overlap 

76 >>> # by 332 samples). By default, only 49 frames are generated since a frame 

77 >>> # with start position j*180 for j > 48 would overhang the end. 

78 >>> frames = tf.signal.frame(audio, 512, 180) 

79 >>> frames.shape.assert_is_compatible_with([3, 49, 512]) 

80 >>> 

81 >>> # When pad_end is enabled, the final two frames are kept (padded with zeros). 

82 >>> frames = tf.signal.frame(audio, 512, 180, pad_end=True) 

83 >>> frames.shape.assert_is_compatible_with([3, 51, 512]) 

84 

85 If the dimension along `axis` is N, and `pad_end=False`, the number of frames 

86 can be computed by: 

87 ```python 

88 num_frames = 1 + (N - frame_size) // frame_step 

89 ``` 

90 If `pad_end=True`, the number of frames can be computed by: 

91 ```python 

92 num_frames = -(-N // frame_step) # ceiling division 

93 ``` 

94 

95 Args: 

96 signal: A `[..., samples, ...]` `Tensor`. The rank and dimensions 

97 may be unknown. Rank must be at least 1. 

98 frame_length: The frame length in samples. An integer or scalar `Tensor`. 

99 frame_step: The frame hop size in samples. An integer or scalar `Tensor`. 

100 pad_end: Whether to pad the end of `signal` with `pad_value`. 

101 pad_value: An optional scalar `Tensor` to use where the input signal 

102 does not exist when `pad_end` is True. 

103 axis: A scalar integer `Tensor` indicating the axis to frame. Defaults to 

104 the last axis. Supports negative values for indexing from the end. 

105 name: An optional name for the operation. 

106 

107 Returns: 

108 A `Tensor` of frames with shape `[..., num_frames, frame_length, ...]`. 

109 

110 Raises: 

111 ValueError: If `frame_length`, `frame_step`, `pad_value`, or `axis` are not 

112 scalar. 

113 """ 

114 with ops.name_scope(name, "frame", [signal, frame_length, frame_step, 

115 pad_value]): 

116 signal = ops.convert_to_tensor(signal, name="signal") 

117 frame_length = ops.convert_to_tensor(frame_length, name="frame_length") 

118 frame_step = ops.convert_to_tensor(frame_step, name="frame_step") 

119 axis = ops.convert_to_tensor(axis, name="axis") 

120 

121 signal.shape.with_rank_at_least(1) 

122 frame_length.shape.assert_has_rank(0) 

123 frame_step.shape.assert_has_rank(0) 

124 axis.shape.assert_has_rank(0) 

125 

126 result_shape = _infer_frame_shape(signal, frame_length, frame_step, pad_end, 

127 axis) 

128 

129 def maybe_constant(val): 

130 val_static = tensor_util.constant_value(val) 

131 return (val_static, True) if val_static is not None else (val, False) 

132 

133 signal_shape, signal_shape_is_static = maybe_constant( 

134 array_ops.shape(signal)) 

135 axis, axis_is_static = maybe_constant(axis) 

136 

137 if signal_shape_is_static and axis_is_static: 

138 # Axis can be negative. Convert it to positive. 

139 axis = range(len(signal_shape))[axis] 

140 outer_dimensions, length_samples, inner_dimensions = np.split( 

141 signal_shape, indices_or_sections=[axis, axis + 1]) 

142 length_samples = length_samples.item() 

143 else: 

144 signal_rank = array_ops.rank(signal) 

145 # Axis can be negative. Convert it to positive. 

146 axis = math_ops.range(signal_rank)[axis] 

147 outer_dimensions, length_samples, inner_dimensions = array_ops.split( 

148 signal_shape, [axis, 1, signal_rank - 1 - axis]) 

149 length_samples = array_ops.reshape(length_samples, []) 

150 num_outer_dimensions = array_ops.size(outer_dimensions) 

151 num_inner_dimensions = array_ops.size(inner_dimensions) 

152 

153 # If padding is requested, pad the input signal tensor with pad_value. 

154 if pad_end: 

155 pad_value = ops.convert_to_tensor(pad_value, signal.dtype) 

156 pad_value.shape.assert_has_rank(0) 

157 

158 # Calculate number of frames, using double negatives to round up. 

159 num_frames = -(-length_samples // frame_step) 

160 

161 # Pad the signal by up to frame_length samples based on how many samples 

162 # are remaining starting from last_frame_position. 

163 pad_samples = math_ops.maximum( 

164 0, frame_length + frame_step * (num_frames - 1) - length_samples) 

165 

166 # Pad the inner dimension of signal by pad_samples. 

167 paddings = array_ops.concat([ 

168 array_ops.zeros([num_outer_dimensions, 2], dtype=pad_samples.dtype), 

169 ops.convert_to_tensor([[0, pad_samples]]), 

170 array_ops.zeros([num_inner_dimensions, 2], dtype=pad_samples.dtype) 

171 ], 0) 

172 signal = array_ops.pad(signal, paddings, constant_values=pad_value) 

173 

174 signal_shape = array_ops.shape(signal) 

175 length_samples = signal_shape[axis] 

176 else: 

177 num_frames = math_ops.maximum( 

178 constant_op.constant(0, dtype=frame_length.dtype), 

179 1 + (length_samples - frame_length) // frame_step) 

180 

181 subframe_length, _ = maybe_constant(util_ops.gcd(frame_length, frame_step)) 

182 subframes_per_frame = frame_length // subframe_length 

183 subframes_per_hop = frame_step // subframe_length 

184 num_subframes = length_samples // subframe_length 

185 

186 slice_shape = array_ops.concat([outer_dimensions, 

187 [num_subframes * subframe_length], 

188 inner_dimensions], 0) 

189 subframe_shape = array_ops.concat([outer_dimensions, 

190 [num_subframes, subframe_length], 

191 inner_dimensions], 0) 

192 subframes = array_ops.reshape(array_ops.strided_slice( 

193 signal, array_ops.zeros_like(signal_shape), 

194 slice_shape), subframe_shape) 

195 

196 # frame_selector is a [num_frames, subframes_per_frame] tensor 

197 # that indexes into the appropriate frame in subframes. For example: 

198 # [[0, 0, 0, 0], [2, 2, 2, 2], [4, 4, 4, 4]] 

199 frame_selector = array_ops.reshape( 

200 math_ops.range(num_frames, dtype=frame_length.dtype) * 

201 subframes_per_hop, [num_frames, 1]) 

202 

203 # subframe_selector is a [num_frames, subframes_per_frame] tensor 

204 # that indexes into the appropriate subframe within a frame. For example: 

205 # [[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]] 

206 subframe_selector = array_ops.reshape( 

207 math_ops.range(subframes_per_frame, dtype=frame_length.dtype), 

208 [1, subframes_per_frame]) 

209 

210 # Adding the 2 selector tensors together produces a [num_frames, 

211 # subframes_per_frame] tensor of indices to use with tf.gather to select 

212 # subframes from subframes. We then reshape the inner-most 

213 # subframes_per_frame dimension to stitch the subframes together into 

214 # frames. For example: [[0, 1, 2, 3], [2, 3, 4, 5], [4, 5, 6, 7]]. 

215 selector = frame_selector + subframe_selector 

216 

217 # Dtypes have to match. 

218 outer_dimensions = ops.convert_to_tensor(outer_dimensions) 

219 inner_dimensions = ops.convert_to_tensor( 

220 inner_dimensions, dtype=outer_dimensions.dtype) 

221 mid_dimensions = ops.convert_to_tensor([num_frames, frame_length], 

222 dtype=outer_dimensions.dtype) 

223 

224 frames = array_ops.reshape( 

225 array_ops.gather(subframes, selector, axis=axis), 

226 array_ops.concat([outer_dimensions, mid_dimensions, inner_dimensions], 

227 0)) 

228 

229 if result_shape: 

230 frames.set_shape(result_shape) 

231 return frames