Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions tfjs-core/src/ops/frame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {op} from '../ops/operation';
import {Tensor, Tensor1D} from '../tensor';

import {concat} from './concat';
import {fill} from './fill';
import {reshape} from './reshape';
import {slice} from './slice';
import {tensor2d} from './tensor_ops';

/**
* Expands input into frames of frameLength.
* Slides a window size with frameStep.
*
* ```js
* tf.signal.frame([1, 2, 3], 2, 1).print();
* ```
* @param signal The input tensor to be expanded
* @param frameLength Length of each frame
* @param frameStep The frame hop size in samples.
* @param padEnd Whether to pad the end of signal with padValue.
* @param padValue An number to use where the input signal does
* not exist when padEnd is True.
*/
/**
* @doc {heading: 'Operations', subheading: 'Signal', namespace: 'signal'}
*/
function frame_(
signal: Tensor1D, frameLength: number, frameStep: number, padEnd = false,
padValue = 0): Tensor {
let start = 0;
const output: Tensor[] = [];
while (start + frameLength <= signal.size) {
output.push(slice(signal, start, frameLength));
start += frameStep;
}

if (padEnd) {
while (start < signal.size) {
const padLen = (start + frameLength) - signal.size;
const pad = concat([
slice(signal, start, frameLength - padLen), fill([padLen], padValue)
]);
output.push(pad);
start += frameStep;
}
}

if (output.length === 0) {
return tensor2d([], [0, frameLength]);
}

return reshape(concat(output), [output.length, frameLength]);
}
export const frame = op({frame_});
124 changes: 124 additions & 0 deletions tfjs-core/src/ops/frame_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import * as tf from '../index';
import {ALL_ENVS, describeWithFlags} from '../jasmine_util';
import {expectArraysClose} from '../test_util';

describeWithFlags('frame', ALL_ENVS, () => {
it('3 length frames', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 3;
const frameStep = 1;
const output = tf.signal.frame(input, frameLength, frameStep);
expect(output.shape).toEqual([3, 3]);
expectArraysClose(await output.data(), [1, 2, 3, 2, 3, 4, 3, 4, 5]);
});

it('3 length frames with step 2', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 3;
const frameStep = 2;
const output = tf.signal.frame(input, frameLength, frameStep);
expect(output.shape).toEqual([2, 3]);
expectArraysClose(await output.data(), [1, 2, 3, 3, 4, 5]);
});

it('3 length frames with step 5', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 3;
const frameStep = 5;
const output = tf.signal.frame(input, frameLength, frameStep);
expect(output.shape).toEqual([1, 3]);
expectArraysClose(await output.data(), [1, 2, 3]);
});

it('Exceeding frame length', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 6;
const frameStep = 1;
const output = tf.signal.frame(input, frameLength, frameStep);
expect(output.shape).toEqual([0, 6]);
expectArraysClose(await output.data(), []);
});

it('Zero frame step', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 6;
const frameStep = 0;
const output = tf.signal.frame(input, frameLength, frameStep);
expect(output.shape).toEqual([0, 6]);
expectArraysClose(await output.data(), []);
});

it('Padding with default value', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 3;
const frameStep = 3;
const padEnd = true;
const output = tf.signal.frame(input, frameLength, frameStep, padEnd);
expect(output.shape).toEqual([2, 3]);
expectArraysClose(await output.data(), [1, 2, 3, 4, 5, 0]);
});

it('Padding with the given value', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 3;
const frameStep = 3;
const padEnd = true;
const padValue = 100;
const output =
tf.signal.frame(input, frameLength, frameStep, padEnd, padValue);
expect(output.shape).toEqual([2, 3]);
expectArraysClose(await output.data(), [1, 2, 3, 4, 5, 100]);
});

it('Padding all remaining frames with step=1', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 4;
const frameStep = 1;
const padEnd = true;
const output = tf.signal.frame(input, frameLength, frameStep, padEnd);
expect(output.shape).toEqual([5, 4]);
expectArraysClose(
await output.data(),
[1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 0, 4, 5, 0, 0, 5, 0, 0, 0]);
});

it('Padding all remaining frames with step=1 and given pad-value',
async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const frameLength = 4;
const frameStep = 1;
const padEnd = true;
const padValue = 42;
const output =
tf.signal.frame(input, frameLength, frameStep, padEnd, padValue);
expect(output.shape).toEqual([5, 4]);
expectArraysClose(
await output.data(),
[1, 2, 3, 4, 2, 3, 4, 5, 3, 4, 5, 42, 4, 5, 42, 42, 5, 42, 42, 42]);
});

it('Padding all remaining frames with step=2', async () => {
const input = tf.tensor1d([1, 2, 3, 4, 5]);
const output = tf.signal.frame(input, 4, 2, true);
expect(output.shape).toEqual([3, 4]);
expectArraysClose(
await output.data(), [1, 2, 3, 4, 3, 4, 5, 0, 5, 0, 0, 0]);
});
});
38 changes: 38 additions & 0 deletions tfjs-core/src/ops/hamming_window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {op} from '../ops/operation';
import {Tensor1D} from '../tensor';
import {cosineWindow} from './signal_ops_util';

/**
* Generate a hamming window.
*
* See: https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows
*
* ```js
* tf.signal.hammingWindow(10).print();
* ```
* @param The length of window
*/
/**
* @doc {heading: 'Operations', subheading: 'Signal', namespace: 'signal'}
*/
function hammingWindow_(windowLength: number): Tensor1D {
return cosineWindow(windowLength, 0.54, 0.46);
}
export const hammingWindow = op({hammingWindow_});
48 changes: 48 additions & 0 deletions tfjs-core/src/ops/hamming_window_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import * as tf from '../index';
import {ALL_ENVS, describeWithFlags} from '../jasmine_util';
import {expectArraysClose} from '../test_util';

describeWithFlags('hammingWindow', ALL_ENVS, () => {
it('length=3', async () => {
const ret = tf.signal.hammingWindow(3);
expectArraysClose(await ret.data(), [0.08, 1, 0.08]);
});

it('length=6', async () => {
const ret = tf.signal.hammingWindow(6);
expectArraysClose(await ret.data(), [0.08, 0.31, 0.77, 1., 0.77, 0.31]);
});

it('length=7', async () => {
const ret = tf.signal.hammingWindow(7);
expectArraysClose(
await ret.data(), [0.08, 0.31, 0.77, 1, 0.77, 0.31, 0.08]);
});

it('length=20', async () => {
const ret = tf.signal.hammingWindow(20);
expectArraysClose(await ret.data(), [
0.08000001, 0.10251403, 0.16785222, 0.2696188, 0.3978522,
0.54, 0.68214786, 0.8103813, 0.9121479, 0.977486,
1., 0.977486, 0.9121478, 0.8103812, 0.6821477,
0.54, 0.39785212, 0.2696187, 0.16785222, 0.102514
]);
});
});
39 changes: 39 additions & 0 deletions tfjs-core/src/ops/hann_window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @license
* Copyright 2019 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import {op} from '../ops/operation';
import {Tensor1D} from '../tensor';
import {cosineWindow} from './signal_ops_util';

/**
* Generate a Hann window.
*
* See: https://en.wikipedia.org/wiki/Window_function#Hann_and_Hamming_windows
*
* ```js
* tf.signal.hannWindow(10).print();
* ```
* @param The length of window
*/
/**
* @doc {heading: 'Operations', subheading: 'Signal', namespace: 'signal'}
*/
function hannWindow_(windowLength: number): Tensor1D {
return cosineWindow(windowLength, 0.5, 0.5);
}

export const hannWindow = op({hannWindow_});
47 changes: 47 additions & 0 deletions tfjs-core/src/ops/hann_window_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license
* Copyright 2020 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/

import * as tf from '../index';
import {ALL_ENVS, describeWithFlags} from '../jasmine_util';
import {expectArraysClose} from '../test_util';

describeWithFlags('hannWindow', ALL_ENVS, () => {
it('length=3', async () => {
const ret = tf.signal.hannWindow(3);
expectArraysClose(await ret.data(), [0, 1, 0]);
});

it('length=7', async () => {
const ret = tf.signal.hannWindow(7);
expectArraysClose(await ret.data(), [0, 0.25, 0.75, 1, 0.75, 0.25, 0]);
});

it('length=6', async () => {
const ret = tf.signal.hannWindow(6);
expectArraysClose(await ret.data(), [0., 0.25, 0.75, 1., 0.75, 0.25]);
});

it('length=20', async () => {
const ret = tf.signal.hannWindow(20);
expectArraysClose(await ret.data(), [
0., 0.02447176, 0.09549153, 0.20610738, 0.34549153,
0.5, 0.65450853, 0.79389274, 0.9045085, 0.97552824,
1., 0.97552824, 0.9045085, 0.7938925, 0.65450835,
0.5, 0.34549144, 0.20610726, 0.09549153, 0.02447173
]);
});
});
14 changes: 12 additions & 2 deletions tfjs-core/src/ops/ops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,24 @@ export * from './spectral_ops';
export * from './sparse_to_dense';
export * from './gather_nd';
export * from './dropout';
export * from './signal_ops';
export * from './signal_ops_util';
export * from './in_top_k';

export {op} from './operation';

import * as spectral from './spectral_ops';
import * as fused from './fused_ops';
import * as signal from './signal_ops';

import {hammingWindow} from './hamming_window';
import {hannWindow} from './hann_window';
import {frame} from './frame';
import {stft} from './stft';
const signal = {
hammingWindow,
hannWindow,
frame,
stft,
};

// Image Ops namespace
import {cropAndResize} from './crop_and_resize';
Expand Down
Loading