Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for tensorflow together with ODL #972

Merged
merged 70 commits into from
Aug 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
3387656
ENH: Add (initial) support for tensorflow interconnectivity
adler-j Feb 27, 2017
3ab822b
ENH: Add more tensorflow examples
adler-j Feb 27, 2017
83c27e4
ENH: Improve handling of shapes of tensorflow layers
adler-j Feb 27, 2017
b4b4a20
ENH: Add more tensorflow examples
adler-j Feb 27, 2017
8d500aa
ENH: Optimizations to the tensorflow interface
adler-j Feb 27, 2017
faef60f
ENH: Improved CNN gradient tomography
adler-j Feb 27, 2017
c28586f
ENH: Support variable size input for tensorflow
adler-j Feb 28, 2017
8b0efdd
ENH: Improved cnn gradient example
adler-j Feb 28, 2017
428087c
ENH: Improvements to the CNN tensorflow example
adler-j Feb 28, 2017
787eb99
BUG: Fix bug with derivative of tensorflow operators
adler-j Mar 3, 2017
4ee1f9b
ENH: Allow non-differentialbe operators in tensorflow
adler-j Mar 13, 2017
7a3a86b
ENH: Add odl.contrib for less-than-optimal code
adler-j Mar 16, 2017
8817c05
ENH: Add TensorflowSpace
adler-j Mar 16, 2017
96ed70c
BUG: Fix bug with default LinearSpace.zeros
adler-j Mar 17, 2017
8a61714
ENH: Improvements to tensorflow support
adler-j Mar 17, 2017
7977ff0
ENH: Minor performance optimization to douglas rachford solver
adler-j Mar 17, 2017
2e7cc2a
ENH: Add name and name scopes to tf space
adler-j Mar 17, 2017
e332854
ENH: Add (initial) support for tensorflow interconnectivity
adler-j Feb 27, 2017
8dba85c
ENH: Add more tensorflow examples
adler-j Feb 27, 2017
2586f50
ENH: Improve handling of shapes of tensorflow layers
adler-j Feb 27, 2017
70fe54f
ENH: Add more tensorflow examples
adler-j Feb 27, 2017
7c7943b
ENH: Optimizations to the tensorflow interface
adler-j Feb 27, 2017
ccc8220
ENH: Improved CNN gradient tomography
adler-j Feb 27, 2017
921fe33
ENH: Support variable size input for tensorflow
adler-j Feb 28, 2017
ce186b5
ENH: Improved cnn gradient example
adler-j Feb 28, 2017
bf896c2
ENH: Improvements to the CNN tensorflow example
adler-j Feb 28, 2017
4a34da3
BUG: Fix bug with derivative of tensorflow operators
adler-j Mar 3, 2017
b241453
ENH: Allow non-differentialbe operators in tensorflow
adler-j Mar 13, 2017
21fb1e1
ENH: Add odl.contrib for less-than-optimal code
adler-j Mar 16, 2017
4241f0b
ENH: Add TensorflowSpace
adler-j Mar 16, 2017
148a8ec
ENH: Add name and name scopes to tf space
adler-j Mar 17, 2017
0c3f2d3
tmp
adler-j Mar 20, 2017
cb67901
ENH: Add derivative of gradient of l1 norm
adler-j Mar 20, 2017
5a78b97
ENH: Enable functionals with tensorflow
adler-j Mar 20, 2017
03c3f34
BUG: Fix bug with tensorflow layer gradient with fixed sizes
adler-j Mar 21, 2017
ac977a3
API: Change shape of tensors returned from functionals with tensorflow
adler-j Mar 21, 2017
762aa11
ENH: Improve doc of tensorflow layer
adler-j Mar 23, 2017
6809e11
ENH: Fix bug with astra cuda in case of several callers
adler-j Mar 24, 2017
fdd41dd
ENH: Improve doc and add name scopes to tensorflow
adler-j Mar 24, 2017
daf7d83
MAINT: Bug fixes and test improvements
adler-j Mar 24, 2017
889430d
ENH: Change ellipse_phantom to use radians
adler-j Mar 24, 2017
0a09fb8
ENH: Fix return dtypes in tensorflow space
adler-j Mar 27, 2017
616d914
ENH: Remove unncessary warning when calling matplotlib.pause
adler-j Mar 29, 2017
a619f4c
MAINT: Move tensorflow examples to contrib folder
adler-j Apr 2, 2017
5baaee6
ENH: Improve doc of tensorflow support
adler-j Apr 2, 2017
b2ab020
ENH: Improve doc of tensorflow
adler-j Apr 2, 2017
283031a
ENH: Further enhancements to tensorflow doc
adler-j Apr 2, 2017
ef163ca
ENH: Add util/statistics with MSE and PSNR calculators
adler-j Apr 3, 2017
fb336c7
MAINT: Doc fixes to MSE
adler-j Apr 13, 2017
c9ef813
MAINT: Cleanup tensorflow examples
adler-j Apr 13, 2017
1fe5ab2
API: Change shape of productspace and add asarray
adler-j Apr 19, 2017
ba647fd
ENH: Add estimate_noise
adler-j Apr 20, 2017
188d9dd
ENH: Add option to normalize PSNR
adler-j May 12, 2017
34f11fc
ENH: Add ADAM solver, see #984
adler-j May 22, 2017
7722357
MAINT: Clean up tensorflow package structure
adler-j Aug 1, 2017
f4028b8
MAINT: fix bugs with tensorflow
adler-j Aug 1, 2017
b563410
MAINT: Bug fixes for product space
adler-j Aug 1, 2017
a25a575
ENH: Add TensorflowOperator
adler-j Aug 1, 2017
8809225
MAINT: Improve tensorflow examples
adler-j Aug 3, 2017
e6db7e0
DOC: Remove overview_functionality stub
adler-j Aug 18, 2017
c397833
MAINT: Doc improvements to adam solver
adler-j Aug 18, 2017
6d06488
MAINT: Improve doc of ProductSpace.element.__array__
adler-j Aug 18, 2017
c7e0ca5
ENH: Add zscore
adler-j Aug 22, 2017
38e3ab6
ENH: Add util/statistics to contrib/fom
adler-j Aug 22, 2017
0acdc3c
MAINT: Update licence of contrib/tensorflow
adler-j Aug 22, 2017
1737e0e
DOC: Add readme to contrib/tensorflow
adler-j Aug 22, 2017
127814e
MAINT: imporovements to contrib/tensorflow
adler-j Aug 22, 2017
a97fcd5
TST: Add rudimentary test for tensorflow
adler-j Aug 22, 2017
e870da8
MAINT: Minor improvements to tensorflow
adler-j Aug 22, 2017
9923fda
MAINT: Minor style fixes
adler-j Aug 29, 2017
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
1 change: 0 additions & 1 deletion odl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,5 @@
from . import datasets
from . import contrib


from .util import test
__all__ += ('test',)
3 changes: 3 additions & 0 deletions odl/contrib/fom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@

from .supervised import *
__all__ += supervised.__all__

from .unsupervised import *
__all__ += unsupervised.__all__
61 changes: 60 additions & 1 deletion odl/contrib/fom/supervised.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

__all__ = ('mean_squared_error', 'mean_absolute_error',
'mean_value_difference', 'standard_deviation_difference',
'range_difference', 'blurring', 'false_structures', 'ssim')
'range_difference', 'blurring', 'false_structures', 'ssim', 'psnr')


def mean_squared_error(data, ground_truth, mask=None, normalized=False):
Expand Down Expand Up @@ -573,3 +573,62 @@ def smoothen(img):
return 0.5 - ssim / 2
else:
return ssim


def psnr(data, ground_truth, normalize=False):
"""Return the Peak Signal-to-Noise Ratio.

Parameters
----------
data : `FnBaseVector`
Input data or reconstruction.
ground_truth : `FnBaseVector`
Reference to compare ``data`` to.
normalize : bool
If true, normalizes ``data`` and ``ground_truth`` to have the same mean
and variance before comparison.

Returns
-------
psnr : float

Examples
--------
Compute the PSNR for two vectors:

>>> spc = odl.rn(5)
>>> data = spc.element([1, 1, 1, 1, 1])
>>> ground_truth = spc.element([1, 1, 1, 1, 2])
>>> result = psnr(data, ground_truth)
>>> print('{:.3f}'.format(result))
6.021

If data == ground_truth, the result is positive infinity:

>>> psnr(ground_truth, ground_truth)
inf

With ``normalize=True``, internal scaling and constant offsets are ignored:

>>> (psnr(data, ground_truth, normalize=True) ==
... psnr(data, 3 + 4 * ground_truth, normalize=True))
True
"""
if normalize:
data = odl.util.zscore(data)
ground_truth = odl.util.zscore(ground_truth)

mse_result = mean_squared_error(data, ground_truth)
max_true = np.max(np.abs(ground_truth))

if mse_result == 0:
return np.inf
elif max_true == 0:
return -np.inf
else:
return 20 * np.log10(max_true) - 10 * np.log10(mse_result)

if __name__ == '__main__':
# pylint: disable=wrong-import-position
from odl.util.testutils import run_doctests
run_doctests()
57 changes: 57 additions & 0 deletions odl/contrib/fom/test/test_unsupervised.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright 2014-2017 The ODL contributors
#
# This file is part of ODL.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.

"""Tests for unsupervised FoMs."""

import numpy as np
import pytest
import odl
import odl.contrib.fom


def test_estimate_noise_std_constant_1d():
"""Verify ``estimate_noise_std(0) == 0``"""
img = np.zeros(10)
result = odl.contrib.fom.estimate_noise_std(img)
assert pytest.approx(result) == 0.0


def test_estimate_noise_std_normal_1d():
"""Verify ``estimate_noise_std(N(0, 1)) == 1`` in 1d."""
img = np.random.randn(10)
result = odl.contrib.fom.estimate_noise_std(img)
expected = np.std(img)
assert pytest.approx(result, abs=0.2) == expected


def test_estimate_noise_std_normal_2d():
"""Verify ``estimate_noise_std(N(0, 1)) == 1`` in 2d."""
img = np.random.randn(10, 10)
result = odl.contrib.fom.estimate_noise_std(img)
expected = np.std(img)
assert pytest.approx(result, abs=0.2) == expected


def test_estimate_noise_std_normal_4d():
"""Verify ``estimate_noise_std(N(0, 1)) == 1`` in 4d."""
img = np.random.randn(5, 5, 5, 5)
result = odl.contrib.fom.estimate_noise_std(img)
expected = np.std(img)
assert pytest.approx(result, abs=0.2) == expected


def test_estimate_noise_std_normal_large_1d():
"""Verify ``estimate_noise_std(N(0, 1)) == 1`` with low error."""
img = np.random.randn(100000)
result = odl.contrib.fom.estimate_noise_std(img)
expected = np.std(img)
assert pytest.approx(result, abs=0.01) == expected


if __name__ == '__main__':
pytest.main([str(__file__.replace('\\', '/')), '-v'])
65 changes: 65 additions & 0 deletions odl/contrib/fom/unsupervised.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2014-2017 The ODL contributors
#
# This file is part of ODL.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.

"""Figures of Merit (FOMs) for measuring image quality without a reference."""

import numpy as np

__all__ = ('estimate_noise_std',)


def estimate_noise_std(img):
"""Estimate standard deviation of noise in ``img``.

The algorithm, given in [Immerkaer1996], estimates the noise in an image
by

Parameters
----------
img : array-like

Returns
-------
noise : float

Examples
--------
Create image with noise 1.0, verify result

>>> img = np.random.randn(10, 10)
>>> result = estimate_noise_std(img) # should be about 1

Also works with higher dimensional arrays

>>> img = np.random.randn(3, 3, 3)
>>> result = estimate_noise_std(img)

References
----------
[Immerkaer1996] Immerkaer, J. *Fast Noise Variance Estimation*.
Computer Vision and Image Understanding, 1996.
"""
import scipy.signal
import functools
img = np.asarray(img, dtype='float')

M = functools.reduce(np.add.outer, [[-1, 2, -1]] * img.ndim)

convolved = scipy.signal.fftconvolve(img, M, mode='valid')
conv_var = np.sum(convolved ** 2)

scale = np.sum(np.square(M)) * convolved.size
sigma = np.sqrt(conv_var / scale)

return sigma


if __name__ == '__main__':
# pylint: disable=wrong-import-position
from odl.util.testutils import run_doctests
run_doctests()
24 changes: 24 additions & 0 deletions odl/contrib/tensorflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Tensorflow

This package contains ODL functionality related to [TensorFlow](https://www.tensorflow.org/).

## Content

* `TensorflowOperator` in [operator.py](operator.py) wraps a tensorflow expression into an ODL operator.
This allows using tensorflow neural networks as operators in ODL.
* `as_tensorflow_layer` in [layer.py](layer.py) wraps an ODL operator into a tensorflow layer.
This allows using arbitrary ODL operators inside tensorflow neural networks.
* `TensorflowSpace` in [space.py](space.py) is a `FnBase`-type space which uses tensorflow as a backend.

## Example usage

The [examples](examples) folder contains examples on how to use the above functionality.
Specifically:

* [tensorflow_layer_matrix.py](examples/tensorflow_layer_matrix.py) shows how an ODL `MatrixOperator` can be converted to a tensorflow layer.
* [tensorflow_layer_productspace.py](examples/tensorflow_layer_productspace.py) shows how an ODL operator acting on `ProductSpace`s can be converted to a tensorflow layer.
* [tensorflow_layer_ray_transform.py](examples/tensorflow_layer_ray_transform.py) shows how a `RayTransform` can be converted to a tensorflow layer.
* [tensorflow_operator_matrix.py](examples/tensorflow_operator_matrix.py) shows how `tf.matmul` can be used as an ODL operator.
* [tensorflow_tomography.py](examples/tensorflow_tomography.py) shows how tensorflow optimizers can be used with ODL operators to solve inverse problems.

There are also some rudimentary tests in the [test](test) folder.
23 changes: 23 additions & 0 deletions odl/contrib/tensorflow/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2014-2017 The ODL contributors
#
# This file is part of ODL.
#
# This Source Code Form is subject to the terms of the Mozilla Public License,
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
# obtain one at https://mozilla.org/MPL/2.0/.

"""ODL interactions with tensorflow."""


from __future__ import absolute_import

__all__ = ()

from .layer import *
__all__ += layer.__all__

from .space import *
__all__ += space.__all__

from .operator import *
__all__ += operator.__all__
51 changes: 51 additions & 0 deletions odl/contrib/tensorflow/examples/tensorflow_layer_matrix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Example of how to convert an ODL operator to a tensorflow layer.

In this example we take an ODL operator given by a `MatrixOperator` and
convert it into a tensorflow layer that can be used inside any tensorflow
computational graph.

We also demonstrate that we can compute the "gradients" properly using the
adjoint of the derivative.
"""

import tensorflow as tf
import numpy as np
import odl
import odl.contrib.tensorflow

sess = tf.InteractiveSession()

# Define ODL operator
matrix = np.array([[1, 2],
[0, 0],
[0, 1]], dtype='float32')
odl_op = odl.MatrixOperator(matrix)

# Define evaluation points
x = [1., 2.]
z = [1., 2., 3.]

# Add empty axes for batch and channel
x_tf = tf.constant(x)[None, ..., None]
z_tf = tf.constant(z)[None, ..., None]

# Create tensorflow layer from odl operator
odl_op_layer = odl.contrib.tensorflow.as_tensorflow_layer(
odl_op, 'MatrixOperator')
y_tf = odl_op_layer(x_tf)

# Evaluate using tensorflow
print('Tensorflow eval: ',
y_tf.eval().ravel())

# Compare result with pure ODL
print('ODL eval: ',
odl_op(x))

# Evaluate the adjoint of the derivative, called gradient in tensorflow
print('Tensorflow adjoint of derivative: ',
tf.gradients(y_tf, [x_tf], z_tf)[0].eval().ravel())

# Compare result with pure ODL
print('ODL adjoint of derivative: ',
odl_op.derivative(x).adjoint(z))
46 changes: 46 additions & 0 deletions odl/contrib/tensorflow/examples/tensorflow_layer_productspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Example of how to convert an ODL operator to a tensorflow layer.

This example is similar to ``tensorflow_layer_matrix``, but demonstrates how
to handle product-spaces.
"""

import tensorflow as tf
import odl
import odl.contrib.tensorflow

sess = tf.InteractiveSession()

# Define ODL operator
space = odl.uniform_discr([0, 0], [1, 1], [10, 10], dtype='float32')

odl_op = odl.Gradient(space)

# Define evaluation points
x = odl_op.domain.one()
z = odl_op.range.one()

# Add empty axes for batch and channel
x_tf = tf.ones([1, 10, 10, 1])
z_tf = tf.ones([1, 2, 10, 10, 1])

# Create tensorflow layer from odl operator
odl_op_layer = odl.contrib.tensorflow.as_tensorflow_layer(
odl_op, 'Gradient')
y_tf = odl_op_layer(x_tf)

# Evaluate using tensorflow
print('Tensorflow eval:')
print(y_tf.eval().squeeze())

# Compare result with pure ODL
print('ODL eval')
print(odl_op(x))

# Evaluate the adjoint of the derivative, called gradient in tensorflow
scale = 1 / space.cell_volume
print('Tensorflow adjoint of derivative (gradients):')
print(tf.gradients(y_tf, [x_tf], z_tf)[0].eval().squeeze() * scale)

# Compare result with pure ODL
print('ODL adjoint of derivative:')
print(odl_op.derivative(x).adjoint(z))
Loading