Skip to content

Commit

Permalink
Gaby neubias (#80)
Browse files Browse the repository at this point in the history
* Add ModelZoo comfort export, see Issue #71.

* Add test and sample tzw compressed file, see Issue #44 and specify version of tifffile so it handles LZW compressed files.

* Update requirements. See Issue #82.

* Update README.md

Co-authored-by: tibuch <[email protected]>
  • Loading branch information
turekg and tibuch authored Jun 24, 2020
1 parent 1ba961c commit 5088a94
Show file tree
Hide file tree
Showing 22 changed files with 826 additions and 311 deletions.
16 changes: 12 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
__pycache__/
*.py[cod]
.idea

.DS_Store
.ipynb_checkpoints
.pytest_cache
Expand All @@ -10,18 +9,27 @@ build/
dist/
docs/build/
examples/**/models/

*.png
*.npz
*.npy
*.tif*
!flybrain_lzw.tiff
*.hdf5
*.h5
*.zip
*.tfevents.*
config.json

!tests/test_data/config.json
model.yaml
/.project
history/

.cache/
.cache/
.pydevproject
.settings/org.python.pydev.yaml
.settings/org.eclipse.core.resources.prefs
.pydevproject
/.settings
.pydevproject
test.yml
test*.yml
5 changes: 0 additions & 5 deletions .pydevproject

This file was deleted.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,8 @@ Have a look at our jupyter notebook:

see [here](https://github.com/mpicbg-csbd/structured_N2V) for more info on `StructN2V`.

## Note on functional tests

The functional "tests" are meant to be run as regular programs. They are there to make sure that
examples are still running after changes.

55 changes: 31 additions & 24 deletions examples/2D/denoising2D_BSD68/BSD68_reproducibility.ipynb

Large diffs are not rendered by default.

99 changes: 59 additions & 40 deletions examples/2D/denoising2D_RGB/01_training.ipynb

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion examples/2D/denoising2D_RGB/02_prediction.ipynb

Large diffs are not rendered by default.

67 changes: 43 additions & 24 deletions examples/2D/denoising2D_SEM/01_training.ipynb

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions examples/2D/denoising2D_SEM/02_prediction.ipynb

Large diffs are not rendered by default.

112 changes: 54 additions & 58 deletions examples/2D/structN2V_2D_convallaria/01_training.ipynb

Large diffs are not rendered by default.

32 changes: 15 additions & 17 deletions examples/2D/structN2V_2D_convallaria/02_prediction.ipynb

Large diffs are not rendered by default.

177 changes: 88 additions & 89 deletions examples/2D/structN2V_2D_synth_mem/train_and_predict.ipynb

Large diffs are not rendered by default.

85 changes: 53 additions & 32 deletions examples/3D/01_training.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/3D/02_prediction.ipynb

Large diffs are not rendered by default.

240 changes: 235 additions & 5 deletions n2v/models/n2v_standard.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from csbdeep.models import CARE
from csbdeep.utils import _raise, axes_check_and_normalize, axes_dict, load_json, save_json
from csbdeep.internals import nets
from csbdeep.internals import nets, predict
from csbdeep.models.base_model import suppress_without_basedir
from csbdeep.version import __version__ as package_version

from six import string_types
from csbdeep.utils.six import Path, FileNotFoundError
Expand All @@ -9,17 +11,24 @@
from keras.callbacks import TerminateOnNaN
import tensorflow as tf
from keras import backend as K

from ruamel.yaml import YAML
import json
import os
import datetime
import warnings

from zipfile import ZipFile
from .n2v_config import N2VConfig
from ..utils import n2v_utils
from ..internals.N2V_DataWrapper import N2V_DataWrapper
from ..internals.n2v_losses import loss_mae, loss_mse
from ..internals.n2v_losses import loss_mse, loss_mae
from ..utils import n2v_utils
from ..utils.n2v_utils import pm_identity, pm_normal_additive, pm_normal_fitted, pm_normal_withoutCP, pm_uniform_withCP
from n2v.nets.unet import build_single_unet_per_channel

from tifffile import imsave
import keras
from csbdeep.utils.six import tempfile
import shutil

import numpy as np

class N2V(CARE):
Expand Down Expand Up @@ -420,6 +429,227 @@ def _set_logdir(self):
self.logdir.mkdir(parents=True, exist_ok=True)
save_json(vars(self.config), str(config_file))

@suppress_without_basedir(warn=True)
def export_TF(self, name, description, authors, test_img, axes, patch_shape, fname=None):
"""
name: String
Name of the model.
description: String
A short description of the model e.g. on what data it was trained.
authors: String
Comma seperated list of author names.
patch_shape: The shape of the patches used in model.train().
"""
if fname is None:
fname = self.logdir / 'export.bioimage.io.zip'
else:
fname = Path(fname)

input_n_dims = len(test_img.shape)
if 'C' in axes:
input_n_dims -=1
assert input_n_dims == self.config.n_dim, 'Input and network dimensions do not match.'
assert test_img.shape[axes.index('X')] == test_img.shape[axes.index('Y')], 'X and Y dimensions are not of same length.'
test_output = self.predict(test_img, axes)
# Extract central slice of Z-Stack
if 'Z' in axes:
z_dim = axes.index('Z')
if z_dim != 0:
test_output = np.moveaxis(test_output, z_dim, 0)
test_output = test_output[int(test_output.shape[0]/2)]

# CSBDeep Export
meta = {
'type': self.__class__.__name__,
'version': package_version,
'probabilistic': self.config.probabilistic,
'axes': self.config.axes,
'axes_div_by': self._axes_div_by(self.config.axes),
'tile_overlap': self._axes_tile_overlap(self.config.axes),
}
export_SavedModel(self.keras_model, str(fname), meta=meta)
# CSBDeep Export Done

# Replace : with -
name = name.replace(':', ' -')
yml_dict = self.get_yml_dict(name, description, authors, test_img, axes, patch_shape=patch_shape)
yml_file = self.logdir / 'model.yaml'

'''default_flow_style must be set to TRUE in order for the output to display arrays as [x,y,z]'''
yaml = YAML(typ='rt')
yaml.default_flow_style = False
with open(yml_file, 'w') as outfile:
yaml.dump(yml_dict, outfile)

input_file = self.logdir / 'testinput.tif'
output_file = self.logdir / 'testoutput.tif'
imsave(input_file, test_img)
imsave(output_file, test_output)

with ZipFile(fname, 'a') as myzip:
myzip.write(yml_file, arcname=os.path.basename(yml_file))
myzip.write(input_file, arcname=os.path.basename(input_file))
myzip.write(output_file, arcname=os.path.basename(output_file))

print("\nModel exported in BioImage ModelZoo format:\n%s" % str(fname.resolve()))


def get_yml_dict(self, name, description, authors, test_img, axes, patch_shape=None):
if (patch_shape != None):
self.config.patch_shape = patch_shape

''' Repeated values to avoid reference tags of the form &id002 in yml output when the same variable is used more than
once in the dictionary'''
mean_val = []
mean_val1 = []
for ele in self.config.means:
mean_val.append(float(ele))
mean_val1.append(float(ele))
std_val = []
std_val1 = []
for ele in self.config.stds:
std_val.append(float(ele))
std_val1.append(float(ele))
in_data_range_val = ['-inf', 'inf']
out_data_range_val = ['-inf', 'inf']

axes_val = 'b' + self.config.axes
axes_val = axes_val.lower()
val = 2**self.config.unet_n_depth
val1 = predict.tile_overlap(self.config.unet_n_depth, self.config.unet_kern_size)
min_val = [1, val, val, self.config.n_channel_in ]
step_val = [1, val, val, 0]
halo_val = [0, val1, val1, 0]
scale_val = [1, 1, 1, 1]
offset_val = [0, 0, 0, 0]

yaml = YAML(typ='rt')
with open(self.logdir/'config.json','r') as f:
tr_kwargs_val = yaml.load(f)

if (self.config.n_dim == 3):
min_val = [1, val, val, val, self.config.n_channel_in ]
step_val = [1, val, val, val, 0]
halo_val = [0, val1, val1, val1, 0]
scale_val = [1, 1, 1, 1, 1]
offset_val = [0, 0, 0, 0, 0]

yml_dict = {
'name': name,
'description': description,
'cite': [{
'text': 'Krull, A. and Buchholz, T. and Jug, F. Noise2void - learning denoising from single noisy images.\nProceedings of the IEEE Conference on Computer Vision and Pattern Recognition (2019)',
'doi': '10.1109/CVPR.2019.00223'
}],
'authors': authors,
'language': 'python',
'framework': 'tensorflow',
'format_version': '0.2.0-csbdeep',
'source': 'n2v',
'test_input': 'testinput.tif',
'test_output': 'testoutput.tif',
'inputs': [{
'name': 'input',
'axes': axes_val,
'data_type': 'float32',
'data_range': in_data_range_val,
'halo': halo_val,
'shape': {
'min': min_val,
'step': step_val
}
}],
'outputs': [{
'name': self.keras_model.layers[-1].output.name ,
'axes': axes_val,
'data_type': 'float32',
'data_range': out_data_range_val,
'shape': {
'reference_input': 'input',
'scale': scale_val,
'offset': offset_val
}
}],
'training': {
'source': 'n2v.train()',
'kwargs': tr_kwargs_val
},
'prediction': {
'weights': {'source': './variables/variables'},
'preprocess': [{
'kwargs': {
'mean': mean_val,
'stdDev': std_val
}
}],
'postprocess': [{
'kwargs': {
'mean': mean_val1,
'stdDev': std_val1
}
}]
}
}

return yml_dict

@property
def _config_class(self):
return N2VConfig


def export_SavedModel(model, outpath, meta={}, format='zip'):
"""Export Keras model in TensorFlow's SavedModel_ format.
See `Your Model in Fiji`_ to learn how to use the exported model with our CSBDeep Fiji plugins.
.. _SavedModel: https://www.tensorflow.org/programmers_guide/saved_model#structure_of_a_savedmodel_directory
.. _`Your Model in Fiji`: https://github.com/CSBDeep/CSBDeep_website/wiki/Your-Model-in-Fiji
Parameters
----------
model : :class:`keras.models.Model`
Keras model to be exported.
outpath : str
Path of the file/folder that the model will exported to.
meta : dict, optional
Metadata to be saved in an additional ``meta.json`` file.
format : str, optional
Can be 'dir' to export as a directory or 'zip' (default) to export as a ZIP file.
Raises
------
ValueError
Illegal arguments.
"""

def export_to_dir(dirname):
if len(model.inputs) > 1 or len(model.outputs) > 1:
warnings.warn('Found multiple input or output layers.')
builder = tf.saved_model.builder.SavedModelBuilder(dirname)
# use name 'input'/'output' if there's just a single input/output layer
inputs = dict(zip(model.input_names,model.inputs)) if len(model.inputs) > 1 else dict(input=model.input)
outputs = dict(zip(model.output_names,model.outputs)) if len(model.outputs) > 1 else dict(output=model.output)
signature = tf.saved_model.signature_def_utils.predict_signature_def(inputs=inputs, outputs=outputs)
signature_def_map = { tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature }
builder.add_meta_graph_and_variables(K.get_session(),
[tf.saved_model.tag_constants.SERVING],
signature_def_map=signature_def_map)
builder.save()
if meta is not None and len(meta) > 0:
save_json(meta, os.path.join(dirname,'meta.json'))


## checks
isinstance(model,keras.models.Model) or _raise(ValueError("'model' must be a Keras model."))
# supported_formats = tuple(['dir']+[name for name,description in shutil.get_archive_formats()])
supported_formats = 'dir','zip'
format in supported_formats or _raise(ValueError("Unsupported format '%s', must be one of %s." % (format,str(supported_formats))))

# remove '.zip' file name extension if necessary
if format == 'zip' and outpath.endswith('.zip'):
outpath = os.path.splitext(outpath)[0]

if format == 'dir':
export_to_dir(outpath)
else:
with tempfile.TemporaryDirectory() as tmpdir:
tmpsubdir = os.path.join(tmpdir,'model')
export_to_dir(tmpsubdir)
shutil.make_archive(outpath, format, tmpsubdir, './')
10 changes: 6 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,13 @@
"matplotlib",
"six",
"keras>=2.2.4,<2.3.0",
"tifffile",
"tifffile>=2020.5.11",
"imagecodecs>=2020.2.18",
"tqdm",
"pathlib2;python_version<'3'",
"backports.tempfile;python_version<'3.4'",
"csbdeep>=0.4.0,<0.5.0",
"Pillow"
]
"csbdeep>=0.4.0,<0.6.0",
"Pillow",
"ruamel.yaml>=0.16.10"
]
)
Empty file added tests/__init__.py
Empty file.
Empty file added tests/functional/__init__.py
Empty file.
6 changes: 3 additions & 3 deletions tests/test_Noise2VoidDataGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def test_generate_patches_2D():
if not os.path.isdir('data'):
os.mkdir('data')
zip_path = "data/RGB.zip"
if not os.path.exists(zip_path):
if not os.path.isfile(zip_path):
data = urllib.request.urlretrieve('https://cloud.mpi-cbg.de/index.php/s/Frru2hsjjAljpfW/download', zip_path)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall('data')
Expand All @@ -30,15 +30,15 @@ def test_generate_patches_3D():
if not os.path.isdir('data'):
os.mkdir('data')
zip_path = 'data/flywing-data.zip'
if not os.path.exists(zip_path):
if not os.path.isfile(zip_path):
# download and unzip data
data = urllib.request.urlretrieve('https://cloud.mpi-cbg.de/index.php/s/RKStdwKo4FlFrxE/download', zip_path)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall('data')

datagen = N2V_DataGenerator()

imgs = datagen.load_imgs_from_directory(directory="data", filter='*.tif', dims='ZYX')
imgs = datagen.load_imgs_from_directory(directory="data", filter='flywing.tif', dims='ZYX')
print(imgs[0].shape)
patches = datagen.generate_patches_from_list(imgs[:1], shape=(35, 520, 692))
assert len(patches) == 1
Expand Down
1 change: 1 addition & 0 deletions tests/test_data/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"means": ["0.55066955", "0.5919245", "0.5703182"], "stds": ["0.2943783", "0.30203635", "0.31610382"], "n_dim": 2, "axes": "YXC", "n_channel_in": 3, "n_channel_out": 3, "unet_residual": false, "unet_n_depth": 3, "unet_kern_size": 3, "unet_n_first": 64, "unet_last_activation": "linear", "unet_input_shape": [null, null, 3], "train_loss": "mse", "train_epochs": 2, "train_steps_per_epoch": 2, "train_learning_rate": 0.0004, "train_batch_size": 128, "train_tensorboard": true, "train_checkpoint": "weights_best.h5", "train_reduce_lr": {"factor": 0.5, "patience": 10}, "batch_norm": true, "n2v_perc_pix": 5, "n2v_patch_shape": [64, 64], "n2v_manipulator": "uniform_withCP", "n2v_neighborhood_radius": 5, "probabilistic": false, "patch_shape": [64, 64]}
Binary file added tests/test_data/flybrain_lzw.tiff
Binary file not shown.
Loading

0 comments on commit 5088a94

Please sign in to comment.