├── Data ├── C_Elegans │ ├── Raw │ │ └── README.txt │ ├── Synthetic │ │ └── README.txt │ ├── Original │ │ └── README.txt │ └── README.txt └── Example │ ├── Genuine │ ├── raw_image_0.png │ ├── raw_image_1.png │ ├── raw_image_10.png │ ├── raw_image_11.png │ ├── raw_image_12.png │ ├── raw_image_13.png │ ├── raw_image_14.png │ ├── raw_image_15.png │ ├── raw_image_2.png │ ├── raw_image_3.png │ ├── raw_image_4.png │ ├── raw_image_5.png │ ├── raw_image_6.png │ ├── raw_image_7.png │ ├── raw_image_8.png │ └── raw_image_9.png │ └── Synthetic │ ├── syn_image_0.png │ ├── syn_image_1.png │ ├── syn_image_10.png │ ├── syn_image_11.png │ ├── syn_image_12.png │ ├── syn_image_13.png │ ├── syn_image_14.png │ ├── syn_image_15.png │ ├── syn_image_2.png │ ├── syn_image_3.png │ ├── syn_image_4.png │ ├── syn_image_5.png │ ├── syn_image_6.png │ ├── syn_image_7.png │ ├── syn_image_8.png │ └── syn_image_9.png ├── Utilities ├── Utilities.pyc └── Utilities.py ├── Scripts ├── simple_example.sh ├── README.txt ├── train_UDCT_colored_live_neurons.sh ├── train_UDCT_nanowire.sh ├── train_UDCT_dead_live_neurons.sh └── train_UDCT_c_elegans.sh ├── Discriminator ├── MultiPatch.py ├── HisDis.py ├── PatchGAN34.py ├── PatchGAN70.py └── PatchGAN142.py ├── notebooks ├── make_raw_nanowire.ipynb ├── make_raw_dead_live_neurons.ipynb ├── make_synthetic_wires.ipynb └── VGG_Synthetic_by_localization.ipynb ├── README.md ├── create_h5_dataset.py ├── main.py ├── Generator └── Res_Gen.py └── cycleGAN.py /Data/C_Elegans/Raw/README.txt: -------------------------------------------------------------------------------- 1 | 1) Execute: /notebooks/make_raw_c_elegans.ipynb 2 | -------------------------------------------------------------------------------- /Data/C_Elegans/Synthetic/README.txt: -------------------------------------------------------------------------------- 1 | 1) Execute: /notebooks/make_synthetic_c_elegans.ipynb 2 | -------------------------------------------------------------------------------- /Utilities/Utilities.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Utilities/Utilities.pyc -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_0.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_1.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_10.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_11.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_12.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_13.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_14.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_15.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_2.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_3.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_4.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_5.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_6.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_7.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_8.png -------------------------------------------------------------------------------- /Data/Example/Genuine/raw_image_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Genuine/raw_image_9.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_0.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_1.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_10.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_11.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_12.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_13.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_14.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_15.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_2.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_3.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_4.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_5.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_6.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_7.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_8.png -------------------------------------------------------------------------------- /Data/Example/Synthetic/syn_image_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UDCTGAN/UDCT/HEAD/Data/Example/Synthetic/syn_image_9.png -------------------------------------------------------------------------------- /Scripts/simple_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create the sample dataset 4 | cd .. 5 | python create_h5_dataset.py ./Data/Example/Genuine/ ./Data/Example/Synthetic/ ./Data/Example/example_dataset.h5 6 | 7 | # Create a Directory for model data 8 | mkdir -p Models 9 | 10 | # Train the network 11 | python main.py --dataset=./Data/Example/example_dataset.h5 --name=example_model 12 | -------------------------------------------------------------------------------- /Data/C_Elegans/Original/README.txt: -------------------------------------------------------------------------------- 1 | 1) Download the original images of the c elegans dataset from the Broad Bioimage Bechnmark Collection: 2 | 3 | https://data.broadinstitute.org/bbbc/BBBC010/BBBC010_v1_images.zip 4 | 5 | Accession number: BBBC010, Version 1 6 | 7 | 2) Extract the images into this directory. Afterwards, there should be 195 tif images in total in this directory. 8 | 9 | 3) Execute /notebooks/transform_c_elegans_tif_to_png.ipynb 10 | -------------------------------------------------------------------------------- /Scripts/README.txt: -------------------------------------------------------------------------------- 1 | In this directory you can find scripts that can be exectuted in order to reproduce the results of the publication. 2 | 3 | simple_example.sh 4 | A simple example that tests if the code is executing properly. 5 | 6 | train_UDCT_c_elegans.sh 7 | This script downloads the C. elegans dataset from the Broad Bioimage Bechnmark Collection, creates the Raw/Syn dataset and trains a network. Attention: This script is deleting some data in the Data/C_Elegans/Original directory. 8 | 9 | train_UDCT_dead_live_neurons.sh 10 | This script creates the dead vs alive dataset for the neurons. Afterwards, it trains a UDCT cycleGAN on the dataset. 11 | 12 | 13 | -------------------------------------------------------------------------------- /Utilities/Utilities.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | def produce_tiled_images(im_A,im_B,fake_A,fake_B,cyc_A,cyc_B): 4 | 5 | list_of_images=[im_A,im_B,fake_A,fake_B,cyc_A,cyc_B] 6 | for i in range(6): 7 | if np.shape(list_of_images[i])[-1]==1: 8 | list_of_images[i]=np.tile(list_of_images[i],[1,1,1,3]) 9 | list_of_images[i]=np.pad(list_of_images[i][0,:,:,:], ((20,20),(20,20),(0,0)), mode='constant', constant_values=[0.5]) 10 | im_A,im_B,fake_A,fake_B,cyc_A,cyc_B=list_of_images 11 | a=np.vstack( (im_A,im_B)) 12 | b=np.vstack( (fake_B,fake_A)) 13 | c=np.vstack( (cyc_A,cyc_B)) 14 | return np.hstack((a,b,c)) 15 | -------------------------------------------------------------------------------- /Scripts/train_UDCT_colored_live_neurons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create the raw dataset 4 | echo "Creating the raw dataset" 5 | cd ../notebooks/ 6 | jupyter nbconvert --to notebook --execute make_raw_colored_live_neurons.ipynb --ExecutePreprocessor.timeout=1800 7 | 8 | # Create the hdf5 file 9 | cd ../ 10 | mkdir -p Models 11 | echo "Creating the hdf5 dataset" 12 | python create_h5_dataset.py ./Data/Neuron_Col_Live/Raw/ ./Data/Neuron_Col_Live/Synthetic/ ./Data/Neuron_Col_Live/colored_live_neuron_dataset.h5 13 | 14 | # Train the network 15 | echo "Training the network" 16 | python main.py --dataset=./Data/Neuron_Col_Live/colored_live_neuron_dataset.h5 --name=live_colored_neuron_new 17 | 18 | # Create the generated synthetic images 19 | python main.py --dataset=./Data/Neuron_Col_Live/colored_live_neuron_dataset.h5 --name=live_colored_neuron_new --mode=gen_B 20 | -------------------------------------------------------------------------------- /Data/C_Elegans/README.txt: -------------------------------------------------------------------------------- 1 | Disclaimer: 'We used the C.elegans infection live/dead image set version 1 provided by Fred Ausubel and available from the Broad Bioimage Benchmark Collection [Ljosa et al., Nature Methods, 2012].' 2 | https://data.broadinstitute.org/bbbc/BBBC010/ 3 | 4 | For copyright reasons, the C. elegans dataset needs to be created by downloading the data from the Broad Bioimage Bechnmark Collection. 5 | 6 | To do so, please follow the README.txt instructions in the following order: 7 | 8 | 1) README.txt in Original/ 9 | 10 | 2) README.txt in Raw/ 11 | 12 | 3) README.txt in Synthetic/ 13 | 14 | 4) Execute in root directory: python create_h5_dataset.py ./Data/C_Elegans/Raw/ ./Data/C_Elegans/Synthetic/ ./Data/C_Elegans/c_elegans_dataset.h5 15 | 16 | Instead, you can also execute the script 'train_UDCT_c_elegans.sh' in the directory ../Scripts. It does all these steps automatically. 17 | -------------------------------------------------------------------------------- /Scripts/train_UDCT_nanowire.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create the raw dataset 4 | echo "Creating the raw dataset" 5 | mkdir -p ../Data/Nanowire/Raw 6 | cd ../Data/Nanowire/Raw/ 7 | wget https://downloads.lbb.ethz.ch/Data/lbb_raw_nanowire_images.h5 8 | cd ../../../notebooks/ 9 | jupyter nbconvert --to notebook --execute make_raw_nanowire.ipynb --ExecutePreprocessor.timeout=1800 10 | 11 | # Create the synthetic images 12 | mkdir -p ../Data/Nanowire/Synthetic 13 | echo "Creating the synthetic dataset" 14 | jupyter nbconvert --to notebook --execute make_synthetic_wires.ipynb --ExecutePreprocessor.timeout=1800 15 | 16 | # Create the hdf5 file 17 | cd ../ 18 | mkdir -p Models 19 | echo "Creating the hdf5 dataset" 20 | python create_h5_dataset.py ./Data/Nanowire/Raw/ ./Data/Nanowire/Synthetic/ ./Data/Nanowire/nanowire_dataset.h5 21 | 22 | # Train the network 23 | echo "Training the network" 24 | python main.py --dataset=./Data/Nanowire/nanowire_dataset.h5 --name=nanowire_new 25 | 26 | # Create the generated synthetic images 27 | python main.py --dataset=./Data/Nanowire/nanowire_dataset.h5 --name=nanowire_new --mode=gen_B 28 | -------------------------------------------------------------------------------- /Scripts/train_UDCT_dead_live_neurons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Create the synthetic images for both live_vs_dead and colored_live neuron datasets 4 | cd ../notebooks/ 5 | echo "Creating the synthetic dataset" 6 | jupyter nbconvert --to notebook --execute make_synthetic_livedead_neurons_and_colored.ipynb --ExecutePreprocessor.timeout=1800 7 | 8 | # Create the raw dataset 9 | echo "Creating the raw dataset" 10 | mkdir -p ../Data/Neuron_Dead_Live/Raw 11 | cd ../Data/Neuron_Dead_Live/Raw/ 12 | wget https://downloads.lbb.ethz.ch/Data/lbb_raw_neuron_images.h5 13 | cd ../../../notebooks/ 14 | jupyter nbconvert --to notebook --execute make_raw_dead_live_neurons.ipynb --ExecutePreprocessor.timeout=1800 15 | 16 | # Create the hdf5 file 17 | cd ../ 18 | mkdir -p Models 19 | echo "Creating the hdf5 dataset" 20 | python create_h5_dataset.py ./Data/Neuron_Dead_Live/Raw/ ./Data/Neuron_Dead_Live/Synthetic/ ./Data/Neuron_Dead_Live/live_dead_neuron_dataset.h5 21 | 22 | # Train the network 23 | echo "Training the network" 24 | python main.py --dataset=./Data/Neuron_Dead_Live/live_dead_neuron_dataset.h5 --name=live_dead_neuron_new 25 | 26 | # Create the generated synthetic images 27 | python main.py --dataset=./Data/Neuron_Dead_Live/live_dead_neuron_dataset.h5 --name=live_dead_neuron_new --mode=gen_B 28 | -------------------------------------------------------------------------------- /Scripts/train_UDCT_c_elegans.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Download the original BBBC dataset and extract it 4 | cd ../Data/C_Elegans/Original 5 | rm ./00*.png 6 | rm ./1649_1109_0003*.tif 7 | wget https://data.broadinstitute.org/bbbc/BBBC010/BBBC010_v1_images.zip 8 | unzip BBBC010_v1_images.zip -d ./ 9 | rm BBBC010_v1_images.zip 10 | mv BBBC010_v1_images/* ./ 11 | rm -R BBBC010_v1_images 12 | 13 | # Transform the images to pngs 14 | cd ../../../notebooks/ 15 | jupyter nbconvert --to notebook --execute transform_c_elegans_tif_to_png.ipynb --ExecutePreprocessor.timeout=1800 16 | 17 | # Create the raw dataset 18 | echo "Creating the raw dataset" 19 | jupyter nbconvert --to notebook --execute make_raw_c_elegans.ipynb --ExecutePreprocessor.timeout=1800 20 | 21 | # Create the synthetic dataset 22 | echo "Creating the synthetic dataset" 23 | jupyter nbconvert --to notebook --execute make_synthetic_c_elegans.ipynb --ExecutePreprocessor.timeout=1800 24 | 25 | # Create the hdf5 file 26 | cd ../ 27 | mkdir -p Models 28 | echo "Creating the hdf5 dataset" 29 | python create_h5_dataset.py ./Data/C_Elegans/Raw/ ./Data/C_Elegans/Synthetic/ ./Data/C_Elegans/c_elegans_dataset.h5 30 | 31 | # Train the network 32 | echo "Training the network" 33 | python main.py --dataset=./Data/C_Elegans/c_elegans_dataset.h5 --name=c_elegans_new 34 | 35 | # Create the generated synthetic images 36 | python main.py --dataset=./Data/C_Elegans/c_elegans_dataset.h5 --name=c_elegans_new --mode=gen_B 37 | -------------------------------------------------------------------------------- /Discriminator/MultiPatch.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | import PatchGAN34 5 | import PatchGAN70 6 | import PatchGAN142 7 | 8 | class MultiPatch: 9 | """ 10 | This class is creating a PatchGAN discriminator as described by Zhu et al. 2018. 11 | -) save() - Save the current model parameter 12 | -) create() - Create the model layers (graph construction) 13 | -) init() - Initialize the model (load model if exists) 14 | -) load() - Load the parameters from the file 15 | -) run() - ToDo write this 16 | 17 | Only the following functions should be called from outside: 18 | -) create() 19 | -) constructor 20 | """ 21 | 22 | def __init__(self,dis_name,noise=0.25): 23 | """ 24 | Create a PatchGAN model (init). It will check, if a model with such a name has already been saved. If so, the model 25 | is being loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function 26 | is being called. The describtion of every parameter is given in the code below. 27 | 28 | INPUT: dis_name - This is the name of the discriminator. It is mainly used to establish the place, where the model 29 | is being saved. 30 | 31 | OUTPUT: - The model 32 | """ 33 | self.dis_name = dis_name 34 | self.noise = noise 35 | 36 | self.Patch34 = PatchGAN34.PatchGAN34(self.dis_name,noise=self.noise) 37 | self.Patch70 = PatchGAN70.PatchGAN70(self.dis_name,noise=self.noise) 38 | self.Patch142 = PatchGAN142.PatchGAN142(self.dis_name,noise=self.noise) 39 | 40 | def create(self,X,reuse=True): 41 | 42 | self.out34 = self.Patch34.create(X,reuse) 43 | self.out70 = self.Patch70.create(X,reuse) 44 | self.out142 = self.Patch142.create(X,reuse) 45 | 46 | reshaped34 = tf.reshape(self.out34,[-1,tf.shape(self.out34)[1]*tf.shape(self.out34)[2],1]) 47 | reshaped70 = tf.reshape(self.out70,[-1,tf.shape(self.out70)[1]*tf.shape(self.out70)[2],1]) 48 | reshaped142 = tf.reshape(self.out142,[-1,tf.shape(self.out142)[1]*tf.shape(self.out142)[2],1]) 49 | 50 | self.prediction = tf.concat([reshaped34,reshaped70,reshaped142],axis=1) 51 | return self.prediction -------------------------------------------------------------------------------- /Discriminator/HisDis.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | 5 | class HisDis: 6 | """ 7 | This class is creating a PatchGAN discriminator as described by Zhu et al. 2018. 8 | -) save() - Save the current model parameter 9 | -) create() - Create the model layers (graph construction) 10 | -) init() - Initialize the model (load model if exists) 11 | -) load() - Load the parameters from the file 12 | -) run() - ToDo write this 13 | 14 | Only the following functions should be called from outside: 15 | -) create() 16 | -) constructor 17 | """ 18 | 19 | def __init__(self,dis_name,noise=0.1,keep_prob=0.5): 20 | """ 21 | Create a histogramm discriminator 22 | 23 | INPUT: dis_name - This is the name of the discriminator. It is mainly used to establish the place, where the model 24 | is being saved. 25 | 26 | OUTPUT: - The model 27 | """ 28 | self.dis_name = dis_name 29 | self.noise = noise 30 | self.keep_prob = keep_prob 31 | 32 | 33 | def create(self,X,reuse=True): 34 | """ 35 | Create a histogramm discriminator 36 | 37 | INPUT: X - [None,256*n_chan] 38 | 39 | OUTPUT: - The HisDis prediction 40 | """ 41 | self.hidden_1 = tf.layers.dense(tf.nn.dropout(X-0.5+tf.random_normal(tf.shape(X),0.,self.noise),keep_prob=self.keep_prob), 42 | 64, 43 | reuse=reuse, 44 | name='dis_'+self.dis_name+'_hidden_1', 45 | activation=tf.nn.tanh) 46 | 47 | self.hidden_2 = tf.layers.dense(tf.nn.dropout(self.hidden_1,keep_prob=self.keep_prob), 48 | 64, 49 | reuse=reuse, 50 | name='dis_'+self.dis_name+'_hidden_2', 51 | activation=tf.nn.tanh) 52 | 53 | self.out = tf.layers.dense(tf.nn.dropout(self.hidden_2,keep_prob=self.keep_prob), 54 | 1, 55 | reuse=reuse, 56 | name='dis_'+self.dis_name+'_h_out', 57 | activation=None) + 0.5 58 | 59 | return self.out 60 | 61 | -------------------------------------------------------------------------------- /notebooks/make_raw_nanowire.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Creates the raw images for the nanowire dataset. The raw images can be downloaded from:\n", 8 | "\n", 9 | "https://downloads.lbb.ethz.ch/Data/lbb_raw_nanowire_images.h5" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "path_raw_images = '../Data/Nanowire/Raw/lbb_raw_nanowire_images.h5'" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import numpy as np\n", 28 | "import matplotlib\n", 29 | "import matplotlib.pyplot as plt\n", 30 | "import h5py as h5\n", 31 | "\n", 32 | "import random\n", 33 | "\n", 34 | "import imageio as io\n", 35 | "\n", 36 | "from skimage.filters import gaussian" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "f = h5.File(path_raw_images,\"r\")" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "data = f['Raw/data'][...]/(2**8-1.)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "np.max(data)" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "plt.figure(figsize=(10,10))\n", 73 | "plt.imshow(data[0,:,:,0],cmap='gray')\n", 74 | "plt.show()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "for i in range(data.shape[0]):\n", 84 | " io.imsave('../Data/Nanowire/Raw/'+str(i).zfill(5)+'.png',data[i,:,:,0])" 85 | ] 86 | } 87 | ], 88 | "metadata": { 89 | "kernelspec": { 90 | "display_name": "Python 2", 91 | "language": "python", 92 | "name": "python2" 93 | }, 94 | "language_info": { 95 | "codemirror_mode": { 96 | "name": "ipython", 97 | "version": 2 98 | }, 99 | "file_extension": ".py", 100 | "mimetype": "text/x-python", 101 | "name": "python", 102 | "nbconvert_exporter": "python", 103 | "pygments_lexer": "ipython2", 104 | "version": "2.7.15" 105 | } 106 | }, 107 | "nbformat": 4, 108 | "nbformat_minor": 2 109 | } 110 | -------------------------------------------------------------------------------- /notebooks/make_raw_dead_live_neurons.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Creates the raw images for the dead vs live neuron dataset. The raw brightfield images can be downloaded from:\n", 8 | "\n", 9 | "https://downloads.lbb.ethz.ch/Data/lbb_raw_neuron_images.h5" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": null, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "path_raw_images = '../Data/Neuron_Dead_Live/Raw/lbb_raw_neuron_images.h5'" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "import numpy as np\n", 28 | "import matplotlib\n", 29 | "import matplotlib.pyplot as plt\n", 30 | "import h5py as h5\n", 31 | "\n", 32 | "import random\n", 33 | "\n", 34 | "import imageio as io\n", 35 | "\n", 36 | "from skimage.filters import gaussian" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "f = h5.File(path_raw_images,\"r\")" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "data = f['Raw/data'][...]/(2**16-1.)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "np.max(data)" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "plt.figure(figsize=(10,10))\n", 73 | "plt.imshow(data[1,:,:,0],cmap='gray')\n", 74 | "plt.show()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "for i in range(data.shape[0]):\n", 84 | " io.imsave('../Data/Neuron_Dead_Live/Raw/'+str(i).zfill(4)+'.png',data[i,:,:,0])" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [] 93 | } 94 | ], 95 | "metadata": { 96 | "kernelspec": { 97 | "display_name": "Python 2", 98 | "language": "python", 99 | "name": "python2" 100 | }, 101 | "language_info": { 102 | "codemirror_mode": { 103 | "name": "ipython", 104 | "version": 2 105 | }, 106 | "file_extension": ".py", 107 | "mimetype": "text/x-python", 108 | "name": "python", 109 | "nbconvert_exporter": "python", 110 | "pygments_lexer": "ipython2", 111 | "version": "2.7.15" 112 | } 113 | }, 114 | "nbformat": 4, 115 | "nbformat_minor": 2 116 | } 117 | -------------------------------------------------------------------------------- /Discriminator/PatchGAN34.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | 5 | class PatchGAN34: 6 | """ 7 | This class is creating a PatchGAN discriminator as described by Zhu et al. 2018. 8 | -) save() - Save the current model parameter 9 | -) create() - Create the model layers (graph construction) 10 | -) init() - Initialize the model (load model if exists) 11 | -) load() - Load the parameters from the file 12 | -) run() - ToDo write this 13 | 14 | Only the following functions should be called from outside: 15 | -) create() 16 | -) constructor 17 | """ 18 | 19 | def __init__(self,dis_name,noise=0.25): 20 | """ 21 | Create a PatchGAN model (init). It will check, if a model with such a name has already been saved. If so, the model 22 | is being loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function 23 | is being called. The describtion of every parameter is given in the code below. 24 | 25 | INPUT: dis_name - This is the name of the discriminator. It is mainly used to establish the place, where the model 26 | is being saved. 27 | 28 | OUTPUT: - The model 29 | """ 30 | self.dis_name = dis_name 31 | self.noise = noise 32 | 33 | 34 | def create(self,X,reuse=True): 35 | 36 | # C128 37 | # To add noise: 38 | self.C128_c = tf.layers.conv2d(tf.pad(X+tf.random_normal(tf.shape(X),0.,self.noise),[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 39 | filters=128, 40 | kernel_size=4, 41 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 42 | strides=(2,2), 43 | padding='valid', 44 | reuse=reuse, 45 | name='dis_'+self.dis_name+'_34_conv_1') 46 | self.C128_n = tf.contrib.layers.instance_norm(self.C128_c,reuse=reuse,scope='dis_'+self.dis_name+'_34_bnorm_1',trainable=False) 47 | self.C128 = tf.nn.leaky_relu(self.C128_n) 48 | 49 | # C256 50 | self.C256_c = tf.layers.conv2d(tf.pad(self.C128,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 51 | filters=256, 52 | kernel_size=4, 53 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 54 | strides=(2,2), 55 | padding='valid', 56 | reuse=reuse, 57 | name='dis_'+self.dis_name+'_34_conv_2') 58 | self.C256_n = tf.contrib.layers.instance_norm(self.C256_c,reuse=reuse,scope='dis_'+self.dis_name+'_34_bnorm_2',trainable=False) 59 | self.C256 = tf.nn.leaky_relu(self.C256_n) 60 | 61 | # C512 62 | self.C512_c = tf.layers.conv2d(tf.pad(self.C256,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 63 | filters=512, 64 | kernel_size=4, 65 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 66 | strides=(1,1), 67 | padding='valid', 68 | reuse=reuse, 69 | name='dis_'+self.dis_name+'_34_conv_3') 70 | self.C512_n = tf.contrib.layers.instance_norm(self.C512_c,reuse=reuse,scope='dis_'+self.dis_name+'_34_bnorm_3',trainable=False) 71 | self.C512 = tf.nn.leaky_relu(self.C512_n) 72 | 73 | # c1 74 | self.c1_c = tf.layers.conv2d(tf.pad(self.C512,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 75 | filters=1, 76 | kernel_size=4, 77 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 78 | strides=(1,1), 79 | padding='valid', 80 | reuse=reuse, 81 | name='dis_'+self.dis_name+'_34_conv_4') 82 | return self.c1_c -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UDCT 2 | You can find here the Cycle-GAN network (Tensorflow, Python 2.7+) with our histogram loss. Additionally, we provide the scripts we used to generate the synthetic datasets. 3 | 4 | Our results can be found at https://www.biorxiv.org/content/biorxiv/early/2019/03/01/563734.full.pdf 5 | 6 | ## How to use 7 | 1. Clone or download the repository 8 | 2. Create a synthetic dataset similar to your real dataset or use example dataset in ./Data/Example 9 | 3. Execute:
python create_h5_dataset.py <directory_of_raw_images> <directory_of_syn_images> <filename_of_hdf5_file>  
10 | Example:
python create_h5_dataset.py ./Data/Example/Genuine/ \\
./Data/Example/Synthetic/ ./Data/Example/example_dataset.h5
11 | 4. Create the directory 'Models' in the root directory 12 | 5. Execute:
 python main.py --dataset=./Data/..../dataset.h5 --name=name_of_model 
13 | Example:
 python main.py --dataset=./Data/Example/example_dataset.h5 --name=example_model 
14 | 6. This will create a network that is saved in ./Models/ along with a parameter textfile. Furthermore, the average loss terms for each epoch are saved in this directory. 15 | 7. To generate the results after training, use: 16 |
 python main.py --dataset=./Data/Example/example_dataset.h5 --name=example_model --mode=gen_B 
17 | The generated synthetic images can be found in ./Models/<name_of_model>_gen_B.h5 18 | 19 | ### Parameters 20 | 21 | All parameters are of the shape: --<parameter_name>=<value>
22 | Below is the list of all possible parameters that can be set. The standard value used if the parameter is not defined is given in brackets 23 | 24 | name ('unnamed')
25 | Name of the model. This value should be unique to not load/overwrite old models. Its value must be changed to ensure functionality! 26 |
27 | 28 | dataset ('pathtodata.h5')
29 | Describes which h5 files is used. Its value must be changed to ensure functionality! 30 |
31 | 32 | architecture ('Res6')
33 | The network architecture for the generators. Currently, you can choose between 'Res6' and 'Res9', which corresponds to 6 and 9 residual layers, respectively. 34 |
35 | 36 | deconv ('transpose')
37 | Upsampling method used in the generators. You can either choose transpose CNNs ('transpose') or image resizing ('resize'). 38 |
39 | 40 | PatchGAN ('Patch70')
41 | Different PatchGAN Architectures: 'Patch34', 'Patch70', or 'Patch142'. A mixture of these is possible: 'MultiPatch' (experimental). 42 |
43 | 44 | mode ('training')
45 | Decides what should be done with the network. You can either train it ('training'), or create generated images: 'gen_A' for raw images from synthetic images and 'gen_B' for synthetic images from raw images. 46 |
47 | 48 | 49 | 50 | dataset ('pathtodata.h5')
51 | Describes which h5 files is used. Its value must be changed to ensure functionality! 52 |
53 | 54 | lambda_c (10.)
55 | The loss multiplier of the cycle consistency term used while training the generators. 56 |
57 | 58 | lambda_h (1.)
59 | The loss multiplier of the histogram discriminators. If the histogram should not be used, set this term to 0. 60 |
61 | 62 | dis_noise (0.1)
63 | To make the network more stable, we added noise to the input of the discriminators, which slowly decays over time. This value describes how high the std of the gaussian noise is, which is added to the inputs. 64 |
65 | 66 | syn_noise (0.)
67 | It is possible to add gaussian noise to the synthetic dataset. Default: not used. 68 |
69 | 70 | real_noise (0.)
71 | It is possible to add gaussian noise to the real dataset. Default: not used. 72 |
73 | 74 | epoch (200)
75 | Number of training epochs. 76 |
77 | 78 | batch_size (4)
79 | Batch size during training. 80 |
81 | 82 | buffer_size (50)
83 | Size of the buffer (history) saved to train the discriminators. This makes the network more stable. 84 |
85 | 86 | save (1)
87 | If value is not 0, the network progress is saved at the end of each epoch. 88 |
89 | 90 | gpu (0)
91 | If multiple GPUs exist, this parameter choses which GPU should be used. Only one GPU can currently be used. 92 |
93 | 94 | verbose (0)
95 | If value is not 0, the network is more verbose. 96 |
97 | -------------------------------------------------------------------------------- /Discriminator/PatchGAN70.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | 5 | class PatchGAN70: 6 | """ 7 | This class is creating a PatchGAN discriminator as described by Zhu et al. 2018. 8 | -) save() - Save the current model parameter 9 | -) create() - Create the model layers (graph construction) 10 | -) init() - Initialize the model (load model if exists) 11 | -) load() - Load the parameters from the file 12 | -) run() - ToDo write this 13 | 14 | Only the following functions should be called from outside: 15 | -) create() 16 | -) constructor 17 | """ 18 | 19 | def __init__(self,dis_name,noise=0.25): 20 | """ 21 | Create a PatchGAN model (init). It will check, if a model with such a name has already been saved. If so, the model 22 | is being loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function 23 | is being called. The describtion of every parameter is given in the code below. 24 | 25 | INPUT: dis_name - This is the name of the discriminator. It is mainly used to establish the place, where the model 26 | is being saved. 27 | 28 | OUTPUT: - The model 29 | """ 30 | self.dis_name = dis_name 31 | self.noise = noise 32 | 33 | 34 | def create(self,X,reuse=True): 35 | 36 | # C64 37 | # To add noise: 38 | self.C64_c = tf.layers.conv2d(tf.pad(X+tf.random_normal(tf.shape(X),0.,self.noise),[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 39 | filters=64, 40 | kernel_size=4, 41 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 42 | strides=(2,2), 43 | padding='valid', 44 | reuse=reuse, 45 | name='dis_'+self.dis_name+'_70_conv_0') 46 | self.C64 = tf.nn.leaky_relu(self.C64_c) 47 | 48 | # C128 49 | self.C128_c = tf.layers.conv2d(tf.pad(self.C64,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 50 | filters=128, 51 | kernel_size=4, 52 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 53 | strides=(2,2), 54 | padding='valid', 55 | reuse=reuse, 56 | name='dis_'+self.dis_name+'_70_conv_1') 57 | self.C128_n = tf.contrib.layers.instance_norm(self.C128_c,reuse=reuse,scope='dis_'+self.dis_name+'_70_bnorm_1',trainable=False) 58 | self.C128 = tf.nn.leaky_relu(self.C128_n) 59 | 60 | # C256 61 | self.C256_c = tf.layers.conv2d(tf.pad(self.C128,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 62 | filters=256, 63 | kernel_size=4, 64 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 65 | strides=(2,2), 66 | padding='valid', 67 | reuse=reuse, 68 | name='dis_'+self.dis_name+'_70_conv_2') 69 | self.C256_n = tf.contrib.layers.instance_norm(self.C256_c,reuse=reuse,scope='dis_'+self.dis_name+'_70_bnorm_2',trainable=False) 70 | self.C256 = tf.nn.leaky_relu(self.C256_n) 71 | 72 | # C512 73 | self.C512_c = tf.layers.conv2d(tf.pad(self.C256,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 74 | filters=512, 75 | kernel_size=4, 76 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 77 | strides=(1,1), 78 | padding='valid', 79 | reuse=reuse, 80 | name='dis_'+self.dis_name+'_70_conv_3') 81 | self.C512_n = tf.contrib.layers.instance_norm(self.C512_c,reuse=reuse,scope='dis_'+self.dis_name+'_70_bnorm_3',trainable=False) 82 | self.C512 = tf.nn.leaky_relu(self.C512_n) 83 | 84 | # c1 85 | self.c1_c = tf.layers.conv2d(tf.pad(self.C512,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 86 | filters=1, 87 | kernel_size=4, 88 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 89 | strides=(1,1), 90 | padding='valid', 91 | reuse=reuse, 92 | name='dis_'+self.dis_name+'_70_conv_4') 93 | return self.c1_c -------------------------------------------------------------------------------- /create_h5_dataset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import numpy as np 4 | import h5py 5 | import cv2 6 | import os 7 | import sys 8 | 9 | def get_file_list(data_path): 10 | """ 11 | This function returns the list of all png images in a given directory, such as their dimensions. It returns an error, if the image dimensions are not consistent. 12 | 13 | Arguments: 14 | data_path (string) Path to directory of the set of images to be extracted 15 | 16 | Returns: 17 | file_list (List of strings) Filenames of all png images in dataset 18 | dimensions (3 x integer) Dimensions of images: height, width, channels 19 | flag (boolean) Is true, iff the images are grayscale 20 | """ 21 | 22 | # Create list of all png files 23 | file_list = [] 24 | for element in os.listdir(data_path): 25 | if element[-4:] == ".png": 26 | file_list.append(data_path + element) 27 | 28 | 29 | # Compare sizes 30 | dimensions = cv2.imread(file_list[0],cv2.IMREAD_UNCHANGED).shape 31 | for i in range(1,len(file_list)): 32 | if not np.array_equal(dimensions,cv2.imread(file_list[i],cv2.IMREAD_UNCHANGED).shape): 33 | raise Exception('The following two images have different dimensions. Please make sure all images in this directory have the same size \n\r ' +\ 34 | file_list[0] + '\n\r' +\ 35 | file_list[i]) 36 | 37 | # Add a 3rd value two dimensions, if it does not exist (this means it has 1 data channel) 38 | flag = False 39 | if len(dimensions) == 2: 40 | dimensions = np.array([dimensions[0],dimensions[1],1]) 41 | flag = True 42 | 43 | return file_list,dimensions,flag 44 | 45 | def main(): 46 | # Check if the right amount of arguments has been given to the program 47 | if len(sys.argv[1:]) != 3: 48 | print('This script recuires three arguments in order to work:') 49 | print('1: Path to directory containing the genuine/raw images (only png images in directory are used!)') 50 | print('2: Path to directory containing the synthetic images (only png images in directory are used!)') 51 | print('3: Output hdf5 filename') 52 | print(' ') 53 | print('Example: python create_h5_dataset.py ./Data/Example/Genuine/ ./Data/Example/Synthetic/ ./Data/Example/example_dataset.h5') 54 | print(' ') 55 | print('Script aborted') 56 | return -1 57 | 58 | # get the addresses 59 | raw_path = sys.argv[1] 60 | syn_path = sys.argv[2] 61 | filename = sys.argv[3] 62 | 63 | # Create the output hdf5 file 64 | f = h5py.File(filename,"w") 65 | 66 | # Save the raw dataset into the file 67 | raw_files, raw_dimensions, raw_flag = get_file_list(raw_path) 68 | 69 | num_samples = len(raw_files) 70 | num_channel = raw_dimensions[2] 71 | 72 | group = f.create_group('A') 73 | group.create_dataset(name='num_samples', data=num_samples) 74 | group.create_dataset(name='num_channel', data=num_channel) 75 | dtype = np.uint8 76 | 77 | data_A = np.zeros([num_samples,\ 78 | raw_dimensions[0],\ 79 | raw_dimensions[1],\ 80 | num_channel], dtype=dtype) 81 | 82 | for idx,fname in enumerate(raw_files): 83 | if raw_flag: # This means, the images are gray scale 84 | data_A[idx,:,:,0] = np.array(cv2.imread(fname,cv2.IMREAD_GRAYSCALE)) 85 | else: 86 | data_A[idx,:,:,:] = np.flip(np.array(cv2.imread(fname,cv2.IMREAD_COLOR)),2) 87 | 88 | print('Genuine dataset: ', group.create_dataset(name='data', data=(data_A),dtype=dtype)) 89 | 90 | 91 | 92 | 93 | 94 | # Save the syn dataset into the file 95 | syn_files, syn_dimensions, syn_flag = get_file_list(syn_path) 96 | 97 | num_samples = len(syn_files) 98 | num_channel = syn_dimensions[2] 99 | 100 | group = f.create_group('B') 101 | group.create_dataset(name='num_samples', data=num_samples) 102 | group.create_dataset(name='num_channel', data=num_channel) 103 | dtype = np.uint8 104 | 105 | data_B = np.zeros([num_samples,\ 106 | syn_dimensions[0],\ 107 | syn_dimensions[1],\ 108 | num_channel], dtype=dtype) 109 | 110 | for idx,fname in enumerate(syn_files): 111 | if syn_flag: # This means, the images are gray scale 112 | data_B[idx,:,:,0] = np.array(cv2.imread(fname,cv2.IMREAD_GRAYSCALE)) 113 | else: 114 | data_B[idx,:,:,:] = np.flip(np.array(cv2.imread(fname,cv2.IMREAD_COLOR)),2) 115 | 116 | print('Synthetic dataset: ', group.create_dataset(name='data', data=(data_B),dtype=dtype)) 117 | 118 | 119 | # Close the file 120 | f.close() 121 | 122 | if __name__ == "__main__": 123 | main() 124 | -------------------------------------------------------------------------------- /notebooks/make_synthetic_wires.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import numpy as np\n", 10 | "import matplotlib\n", 11 | "import matplotlib.pyplot as plt\n", 12 | "%matplotlib inline\n", 13 | "import os\n", 14 | "import colorsys\n", 15 | "import imageio as io\n", 16 | "from scipy.ndimage import binary_dilation\n", 17 | "from skimage.filters import gaussian\n", 18 | "from numpy import random\n", 19 | "from scipy.stats import skewnorm" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "def gen_fake_img(imsize,length_var,width_var,rng_wire_bounds,rng_length_loc_scale,fix_length,fix_width):\n", 29 | " #image = np.zeros(( imsize,imsize, 3)) #replace with noise for stability\n", 30 | " image = np.random.normal(loc=0.1,scale=0.05,size=(imsize,imsize,3))\n", 31 | " saturation_map=np.random.normal(loc=0.8,scale=0.2,size=(imsize,imsize))\n", 32 | " saturation_map=gaussian(saturation_map,sigma=3) #introduce long range noise correlations\n", 33 | " hue_map=np.random.normal(loc=0.8,scale=0.2,size=(imsize,imsize))\n", 34 | " hue_map=gaussian(hue_map,sigma=2)\n", 35 | " n_wires=np.random.randint(rng_wire_bounds[0],rng_wire_bounds[1])\n", 36 | " for n in range(n_wires):\n", 37 | " if length_var:\n", 38 | " length=skewnorm.rvs(a=3,loc=rng_length_loc_scale[0],scale=rng_length_loc_scale[1],size=1)\n", 39 | " if not length_var:\n", 40 | " length=np.array([fix_length])\n", 41 | "\n", 42 | " x_loc = np.array([np.random.rand() * imsize]) \n", 43 | " y_loc = np.array(np.random.rand() * imsize) \n", 44 | " angle = np.random.random() * np.pi\n", 45 | " vec_x=np.cos(angle)\n", 46 | " vec_y=np.sin(angle)\n", 47 | " x_wires = (vec_x.reshape(1, 1) * length / 2. * np.linspace(-1, 1, 200) + x_loc.reshape(1,1)).astype(int)\n", 48 | " y_wires = (vec_y.reshape(1, 1) * length / 2. * np.linspace(-1, 1, 200) + y_loc.reshape(1,1)).astype(int)\n", 49 | " indices = (x_wires * imsize + y_wires).flatten() \n", 50 | " to_rem=np.where(indices>=imsize*imsize)[0] #make sure wire doesnt go out of image\n", 51 | " indices=[indices[i] for i in range(len(indices)) if i not in to_rem]\n", 52 | "\n", 53 | " wire = np.zeros([imsize*imsize], dtype=np.double)\n", 54 | " wire[indices] = 1\n", 55 | " wire = wire.reshape( imsize, imsize)\n", 56 | "\n", 57 | " if width_var:\n", 58 | " it=np.random.choice(np.arange(1,6),p=[0.7,0.2,0.07,0.02,0.01])\n", 59 | " if not width_var:\n", 60 | " it=fix_width\n", 61 | " wire = binary_dilation(wire,iterations=it)\n", 62 | " wire=wire.astype(bool)\n", 63 | " image[:,:,0][wire]=angle/np.pi #hue\n", 64 | " image[:,:,1][wire]=hue_map[wire] #saturation\n", 65 | " image[:,:,2][wire]=saturation_map[wire] #brightness\n", 66 | " image=np.clip(image,0,1)\n", 67 | " image=matplotlib.colors.hsv_to_rgb(image) #Better visualization, also probably better use of all 3 channels (hsv varies only little in channel 2 and 3)\n", 68 | " return image" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": {}, 75 | "outputs": [], 76 | "source": [ 77 | "params={'imsize': 256,\n", 78 | " 'length_var': True,\n", 79 | " 'width_var': True,\n", 80 | " 'rng_wire_bounds': [10,40],\n", 81 | " 'rng_length_loc_scale': [20,60],\n", 82 | " 'fix_length': 50,\n", 83 | " 'fix_width': 1}\n", 84 | "N_images=500" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "example=gen_fake_img(**params)\n", 94 | "plt.imshow(example)" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "directory='../Data/Nanowire/Synthetic/'" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "for i in range(N_images):\n", 113 | " name=directory+str(i).zfill(5)+'.png'\n", 114 | " image=(gen_fake_img(**params)*255).astype(np.uint8) #map to 0-255,8 bit image\n", 115 | " io.imsave(name,image)\n", 116 | " if i%50==0:\n", 117 | " print(i)" 118 | ] 119 | } 120 | ], 121 | "metadata": { 122 | "kernelspec": { 123 | "display_name": "Python 2", 124 | "language": "python", 125 | "name": "python2" 126 | }, 127 | "language_info": { 128 | "codemirror_mode": { 129 | "name": "ipython", 130 | "version": 2 131 | }, 132 | "file_extension": ".py", 133 | "mimetype": "text/x-python", 134 | "name": "python", 135 | "nbconvert_exporter": "python", 136 | "pygments_lexer": "ipython2", 137 | "version": "2.7.15" 138 | } 139 | }, 140 | "nbformat": 4, 141 | "nbformat_minor": 2 142 | } 143 | -------------------------------------------------------------------------------- /Discriminator/PatchGAN142.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | 5 | class PatchGAN142: 6 | """ 7 | This class is creating a PatchGAN discriminator as described by Zhu et al. 2018. 8 | -) save() - Save the current model parameter 9 | -) create() - Create the model layers (graph construction) 10 | -) init() - Initialize the model (load model if exists) 11 | -) load() - Load the parameters from the file 12 | -) run() - ToDo write this 13 | 14 | Only the following functions should be called from outside: 15 | -) create() 16 | -) constructor 17 | """ 18 | 19 | def __init__(self,dis_name,noise=0.25): 20 | """ 21 | Create a PatchGAN model (init). It will check, if a model with such a name has already been saved. If so, the model 22 | is being loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function 23 | is being called. The describtion of every parameter is given in the code below. 24 | 25 | INPUT: dis_name - This is the name of the discriminator. It is mainly used to establish the place, where the model 26 | is being saved. 27 | 28 | OUTPUT: - The model 29 | """ 30 | self.dis_name = dis_name 31 | self.noise = noise 32 | 33 | 34 | def create(self,X,reuse=True): 35 | 36 | # C32 37 | # To add noise: 38 | self.C32_c = tf.layers.conv2d(tf.pad(X+tf.random_normal(tf.shape(X),0.,self.noise),[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 39 | filters=32, 40 | kernel_size=4, 41 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 42 | strides=(2,2), 43 | padding='valid', 44 | reuse=reuse, 45 | name='dis_'+self.dis_name+'_142_conv_0') 46 | self.C32 = tf.nn.leaky_relu(self.C32_c) 47 | 48 | # C128 49 | self.C64_c = tf.layers.conv2d(tf.pad(self.C32,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 50 | filters=64, 51 | kernel_size=4, 52 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 53 | strides=(2,2), 54 | padding='valid', 55 | reuse=reuse, 56 | name='dis_'+self.dis_name+'_142_conv_1') 57 | self.C64_n = tf.contrib.layers.instance_norm(self.C64_c,reuse=reuse,scope='dis_'+self.dis_name+'_142_bnorm_1',trainable=False) 58 | self.C64 = tf.nn.leaky_relu(self.C64_n) 59 | 60 | # C128 61 | self.C128_c = tf.layers.conv2d(tf.pad(self.C64,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 62 | filters=128, 63 | kernel_size=4, 64 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 65 | strides=(2,2), 66 | padding='valid', 67 | reuse=reuse, 68 | name='dis_'+self.dis_name+'_142_conv_2') 69 | self.C128_n = tf.contrib.layers.instance_norm(self.C128_c,reuse=reuse,scope='dis_'+self.dis_name+'_142_bnorm_2',trainable=False) 70 | self.C128 = tf.nn.leaky_relu(self.C128_n) 71 | 72 | # C256 73 | self.C256_c = tf.layers.conv2d(tf.pad(self.C128,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 74 | filters=256, 75 | kernel_size=4, 76 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 77 | strides=(2,2), 78 | padding='valid', 79 | reuse=reuse, 80 | name='dis_'+self.dis_name+'_142_conv_3') 81 | self.C256_n = tf.contrib.layers.instance_norm(self.C256_c,reuse=reuse,scope='dis_'+self.dis_name+'_142_bnorm_3',trainable=False) 82 | self.C256 = tf.nn.leaky_relu(self.C256_n) 83 | 84 | # C512 85 | self.C512_c = tf.layers.conv2d(tf.pad(self.C256,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 86 | filters=512, 87 | kernel_size=4, 88 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 89 | strides=(1,1), 90 | padding='valid', 91 | reuse=reuse, 92 | name='dis_'+self.dis_name+'_142_conv_4') 93 | self.C512_n = tf.contrib.layers.instance_norm(self.C512_c,reuse=reuse,scope='dis_'+self.dis_name+'_142_bnorm_4',trainable=False) 94 | self.C512 = tf.nn.leaky_relu(self.C512_n) 95 | 96 | # c1 97 | self.c1_c = tf.layers.conv2d(tf.pad(self.C512,[[0,0],[1,1],[1,1],[0,0]],"Reflect"), 98 | filters=1, 99 | kernel_size=4, 100 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 101 | strides=(1,1), 102 | padding='valid', 103 | reuse=reuse, 104 | name='dis_'+self.dis_name+'_142_conv_5') 105 | return self.c1_c -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cycleGAN 2 | import re 3 | import sys 4 | from os import environ as cuda_environment 5 | import os 6 | import numpy as np 7 | 8 | if __name__ == "__main__": 9 | # List of floats 10 | sub_value_f = {} 11 | sub_value_f['lambda_c'] = 10. # Loss multiplier for cycle 12 | sub_value_f['lambda_h'] = 1. # Loss multiplier for histogram 13 | sub_value_f['dis_noise'] = 0.1 # Std of gauss noise added to Dis 14 | sub_value_f['syn_noise'] = 0. # Add gaussian noise to syn images to make non-flat backgrounds 15 | sub_value_f['real_noise'] = 0. # Add gaussian noise to real images to make non-flat backgrounds 16 | 17 | # List of ints 18 | sub_value_i = {} 19 | sub_value_i['epoch'] = 200 # Number of epochs to be trained 20 | sub_value_i['batch_size'] = 4 # Batch size for training 21 | sub_value_i['buffer_size'] = 50 # Number of history elements used for Dis 22 | sub_value_i['save'] = 1 # If not 0, model is saved 23 | sub_value_i['gpu'] = 0 # Choose the GPU ID (if only CPU training, choose nonexistent number) 24 | sub_value_i['verbose'] = 0 # If not 0, some network information is being plotted 25 | 26 | # List of strings 27 | sub_string = {} 28 | sub_string['name'] = 'unnamed' # Name of model (should be unique). Is used to save/load models 29 | sub_string['dataset'] = 'pathtodata.h5' # Describes which h5 file is used 30 | sub_string['architecture'] = 'Res6' # Network architecture: 'Res6' or 'Res9' 31 | sub_string['deconv'] = 'transpose' # Upsampling method: 'transpose' or 'resize' 32 | sub_string['PatchGAN'] = 'Patch70' # Choose the Gan type: 'Patch34', 'Patch70', 'Patch142', 'MultiPatch' 33 | sub_string['mode'] = 'training' # 'train', 'gen_A', 'gen_B' 34 | 35 | # Create complete dictonary 36 | var_dict = sub_string.copy() 37 | var_dict.update(sub_value_i) 38 | var_dict.update(sub_value_f) 39 | 40 | # Update all defined parameters in dictionary 41 | for arg_i in sys.argv[1:]: 42 | var = re.search('(.*)\=', arg_i) # everything before the '=' 43 | g_var = var.group(1)[2:] 44 | if g_var in sub_value_i: 45 | dtype = 'int' 46 | elif g_var in sub_value_f: 47 | dtype = 'float' 48 | elif g_var in sub_string: 49 | dtype = 'string' 50 | else: 51 | print("Unknown key word: " + g_var) 52 | print("Write parameters as: =") 53 | print("Example: 'python main.py buffer_size=32'") 54 | print("Possible key words: " + str(var_dict.keys())) 55 | continue 56 | 57 | content = re.search('\=(.*)',arg_i) # everything after the '=' 58 | g_content = content.group(1) 59 | if dtype == 'int': 60 | var_dict[g_var] = int(g_content) 61 | elif dtype == 'float': 62 | var_dict[g_var] = float(g_content) 63 | else: 64 | var_dict[g_var] = g_content 65 | if not os.path.isfile(var_dict['dataset']): 66 | raise ValueError('Dataset does not exist. Specify loation of an existing h5 file.') 67 | # Get the dataset filename 68 | 69 | 70 | # Restrict usage of GPUs 71 | cuda_environment["CUDA_VISIBLE_DEVICES"]=str(var_dict['gpu']) 72 | with open('Models/'+var_dict['name']+"_params.txt", "w") as myfile: 73 | for key in sorted(var_dict): 74 | myfile.write(key + "," + str(var_dict[key]) + "\n") 75 | 76 | # Find out, if whole network is needed or only the generators 77 | gen_only = False 78 | if 'gen' in var_dict['mode']: 79 | gen_only = True 80 | 81 | # Define the model 82 | model = cycleGAN.Model(\ 83 | mod_name=var_dict['name'],\ 84 | data_file=var_dict['dataset'],\ 85 | buffer_size=var_dict['buffer_size'],\ 86 | dis_noise=var_dict['dis_noise'],\ 87 | architecture=var_dict['architecture'],\ 88 | lambda_c=var_dict['lambda_c'],\ 89 | lambda_h=var_dict['lambda_h'],\ 90 | deconv=var_dict['deconv'],\ 91 | patchgan=var_dict['PatchGAN'],\ 92 | verbose=(var_dict['verbose']!=0),\ 93 | gen_only=gen_only) 94 | 95 | # Plot parameter properties, if applicable 96 | if var_dict['verbose']: 97 | # Print the number of parameters 98 | model.print_count_variables() 99 | model.print_train_and_not_train_variables() 100 | 101 | # Create a graph file 102 | model.save_graph() 103 | 104 | elif var_dict['mode'] == 'training': 105 | # Train the model 106 | loss_gen_A = [] 107 | loss_gen_B = [] 108 | loss_dis_A = [] 109 | loss_dis_B = [] 110 | 111 | for i in range(var_dict['epoch']): 112 | print('') 113 | print('Epoch: ' + str(i+1)) 114 | print('') 115 | lgA,lgB,ldA,ldB = \ 116 | model.train(batch_size=var_dict['batch_size'],\ 117 | lambda_c=var_dict['lambda_c'],\ 118 | lambda_h=var_dict['lambda_h'],\ 119 | save=bool(var_dict['save']),\ 120 | epoch=i,\ 121 | syn_noise=var_dict['syn_noise'],\ 122 | real_noise=var_dict['real_noise']) 123 | loss_gen_A.append(lgA) 124 | loss_gen_B.append(lgB) 125 | loss_dis_A.append(ldA) 126 | loss_dis_B.append(ldB) 127 | np.save("./Models/" + var_dict['name'] + '_loss_gen_A.npy',np.array(loss_gen_A).T) 128 | np.save("./Models/" + var_dict['name'] + '_loss_gen_B.npy',np.array(loss_gen_B).T) 129 | np.save("./Models/" + var_dict['name'] + '_loss_dis_A.npy',np.array(loss_dis_A).T) 130 | np.save("./Models/" + var_dict['name'] + '_loss_dis_B.npy',np.array(loss_dis_B).T) 131 | 132 | elif var_dict['mode'] == 'gen_A': 133 | model.generator_A(batch_size=var_dict['batch_size'],\ 134 | lambda_c=var_dict['lambda_c'],\ 135 | lambda_h=var_dict['lambda_h']) 136 | 137 | elif var_dict['mode'] == 'gen_B': 138 | model.generator_B(batch_size=var_dict['batch_size'],\ 139 | lambda_c=var_dict['lambda_c'],\ 140 | lambda_h=var_dict['lambda_h']) 141 | -------------------------------------------------------------------------------- /Generator/Res_Gen.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | import tensorflow as tf 4 | 5 | class ResGen: 6 | """ 7 | This class is creating a ResNet generator as described by Zhu et al. 2018. 8 | -) save() - Save the current model parameter 9 | -) create() - Create the model layers (graph construction) 10 | -) init() - Initialize the model (load model if exists) 11 | -) load() - Load the parameters from the file 12 | -) run() - ToDo write this 13 | 14 | Only the following functions should be called from outside: 15 | -) create() 16 | -) constructor 17 | """ 18 | 19 | def __init__(self, 20 | gen_name, 21 | out_dim, 22 | gen_dim= [32, 64,128, 128,128,128,128,128,128, 64,32 ], 23 | kernel_size=[7, 3,3, 3,3,3,3,3,3, 3,3, 7], 24 | deconv='transpose', 25 | verbose=False): 26 | 27 | # gen_dim= [64, 128,256, 256,256,256,256,256,256,256,256,256, 128,64 ], 28 | # kernel_size=[7, 3,3, 3,3,3,3,3,3,3,3,3, 3,3, 7]): 29 | """ 30 | Create a generator model (init). It will check, if a model with such a name has already been saved. If so, the model 31 | is being loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function 32 | is being called. The describtion of every parameter is given in the code below. 33 | 34 | INPUT: gen_name - This is the name of the generator. It is mainly used to establish the place, where the model 35 | is being saved. 36 | gen_dim - The number of channels in every layer. The first two elements are stride-2 convolutional layers. 37 | The last two layers (1 value) are fractional stride 1/2 convolutional layers. Everything in between 38 | is a ResNet layer. 39 | 40 | kernel_size - The kernel sizes of all layers. The dimension of this list must be the same as of gen_dim + 1. 41 | 42 | OUTPUT: - The model 43 | """ 44 | self.gen_name = gen_name 45 | self.out_dim = out_dim 46 | self.gen_dim = gen_dim 47 | self.kernel_size = kernel_size 48 | self.deconv = deconv 49 | self.verbose = verbose 50 | 51 | if len(gen_dim) + 1 != len(kernel_size): 52 | raise NameError('The dimensions of the ResGenerator are wrong') 53 | 54 | 55 | 56 | def create(self,X,reuse=True): 57 | num_layers = len(self.kernel_size) 58 | 59 | layer_list = [] 60 | layer_list.append(X) 61 | 62 | if self.verbose: 63 | print('-------------------------------') 64 | print(' ') 65 | print('Create generator ' + self.gen_name) 66 | print(' ') 67 | print('Number of layers: ' + str(num_layers)) 68 | print('Input diminesion: ' + str(tf.shape(X))) 69 | print(' ') 70 | 71 | for i in range(num_layers): 72 | if (i < (num_layers-3)) or (i==(num_layers - 1)): 73 | ps = int((self.kernel_size[i]-1)/2) # pad size 74 | new_pad = tf.pad(layer_list[-1],[[0,0],[ps,ps],[ps,ps],[0,0]],"Reflect") 75 | if self.verbose: 76 | print('- - - - - - - - - - - - - - - -') 77 | print('Load last layer: Do padding with size ' + str(ps)) 78 | else: 79 | new_pad = layer_list[-1] 80 | if self.verbose: 81 | print('- - - - - - - - - - - - - - - -') 82 | print('Load last layer: No padding') 83 | if i==0 or i==(num_layers - 1): 84 | if i==0: 85 | filters = self.gen_dim[i] 86 | else: 87 | filters = self.out_dim 88 | new_conv = tf.layers.conv2d(new_pad, 89 | filters=filters, 90 | kernel_size=self.kernel_size[i], 91 | strides=(1,1), 92 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 93 | padding='valid', 94 | reuse=reuse, 95 | name='gen_'+self.gen_name+'_conv_'+str(i)) 96 | if self.verbose: 97 | print('Conv layer stride 1 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(filters)) 98 | elif i < 3: 99 | # Conv layers 100 | new_conv = tf.layers.conv2d(new_pad, 101 | filters=self.gen_dim[i], 102 | kernel_size=self.kernel_size[i], 103 | strides=(2,2), 104 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 105 | padding='valid', 106 | reuse=reuse, 107 | name='gen_'+self.gen_name+'_conv_'+str(i)) 108 | if self.verbose: 109 | print('Conv layer stride 2 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(self.gen_dim[i])) 110 | elif i < num_layers-3: 111 | # Res layers 112 | new_conv_0 = tf.layers.conv2d(new_pad, 113 | filters=self.gen_dim[i], 114 | kernel_size=self.kernel_size[i], 115 | strides=(1,1), 116 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 117 | padding='valid', 118 | reuse=reuse, 119 | name='gen_'+self.gen_name+'_conv0_'+str(i)) 120 | # new_norm_0 = tf.contrib.layers.instance_norm(new_conv_0,reuse=reuse,scale=False,center=False,scope='gen_'+self.gen_name+'_bnorm0_'+str(i),trainable=False) 121 | new_mean_0, new_var_0 = tf.nn.moments(new_conv_0,axes=(1,2),keep_dims=True) 122 | new_norm_0 = (new_conv_0 - new_mean_0) / tf.sqrt(new_var_0 + 1e-5) 123 | new_layer_0= tf.nn.relu(new_norm_0) 124 | new_pad_0 = tf.pad(new_layer_0,[[0,0],[ps,ps],[ps,ps],[0,0]],"Reflect") 125 | 126 | new_conv = tf.layers.conv2d(new_pad_0, 127 | filters=self.gen_dim[i], 128 | kernel_size=self.kernel_size[i], 129 | strides=(1,1), 130 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 131 | padding='valid', 132 | reuse=reuse, 133 | name='gen_'+self.gen_name+'_conv_'+str(i)) 134 | 135 | if self.verbose: 136 | print('Conv layer stride 1 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(self.gen_dim[i])) 137 | print('Instance Normalization') 138 | print('Relu') 139 | print('Padding with pad size of ' + str(ps)) 140 | print('Conv layer stride 1 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(self.gen_dim[i])) 141 | else: 142 | # Deconv layers 143 | if self.deconv == 'transpose': 144 | new_conv = tf.layers.conv2d_transpose(new_pad, 145 | filters=self.gen_dim[i], 146 | kernel_size=self.kernel_size[i], 147 | strides=(2,2), 148 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 149 | padding='same', 150 | reuse=reuse, 151 | name='gen_'+self.gen_name+'_conv_'+str(i)) 152 | 153 | if self.verbose: 154 | print('Conv transpose layer stride 2 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(self.gen_dim[i])) 155 | elif self.deconv == 'resize': 156 | if i == num_layers-3: 157 | new_resize = tf.image.resize_images(new_pad,[tf.cast(tf.shape(X)[1]/2,dtype=tf.int32),\ 158 | tf.cast(tf.shape(X)[2]/2,dtype=tf.int32)]) 159 | 160 | else: 161 | new_resize = tf.image.resize_images(new_pad,[tf.shape(X)[1],tf.shape(X)[2]]) 162 | ps = int((self.kernel_size[i]-1)/2) # pad size 163 | new_pad_0 = tf.pad(new_resize,[[0,0],[ps,ps],[ps,ps],[0,0]],"Reflect") 164 | new_conv = tf.layers.conv2d(new_pad_0, 165 | filters=self.gen_dim[i], 166 | kernel_size=self.kernel_size[i], 167 | strides=(1,1), 168 | kernel_initializer=tf.initializers.random_normal(stddev=0.02), 169 | padding='valid', 170 | reuse=reuse, 171 | name='gen_'+self.gen_name+'_conv_'+str(i)) 172 | if self.verbose: 173 | print('Resize image') 174 | print('Padding with pad size of ' + str(ps)) 175 | print('Conv layer stride 1 with kernel size ' + str(self.kernel_size[i]) + ' and number of filters ' + str(self.gen_dim[i])) 176 | else: 177 | print('Unknown deconvolution method') 178 | if i < num_layers - 1: 179 | # new_norm = tf.contrib.layers.instance_norm(new_conv,reuse=reuse,scale=False,center=False,\ 180 | # scope='gen_'+self.gen_name+'_bnorm_'+str(i),trainable=False) 181 | new_mean, new_var = tf.nn.moments(new_conv,axes=(1,2),keep_dims=True) 182 | new_norm = (new_conv - new_mean) / tf.sqrt(new_var + 1e-5) 183 | 184 | if self.verbose: 185 | print('Instance normalization') 186 | else: 187 | new_norm = new_conv 188 | 189 | if i>=3 and i < num_layers-3: 190 | new_layer = new_norm + layer_list[-1] 191 | if self.verbose: 192 | print('Make residual layer (linear activation function)') 193 | elif i < num_layers-1: 194 | new_layer = tf.nn.relu(new_norm) 195 | if self.verbose: 196 | print('ReLu') 197 | else: 198 | self.bef_layer = new_norm 199 | new_layer = (tf.nn.tanh(new_norm)+1)/2. 200 | if self.verbose: 201 | print('[tanh(x)+1]/2') 202 | 203 | layer_list.append(new_layer) 204 | if self.verbose: 205 | print(' ') 206 | print('Final layer: ',new_layer) 207 | 208 | self.layer_list = layer_list 209 | return new_layer 210 | -------------------------------------------------------------------------------- /cycleGAN.py: -------------------------------------------------------------------------------- 1 | from __future__ import division, print_function, unicode_literals 2 | 3 | 4 | import tensorflow as tf 5 | 6 | import h5py 7 | import numpy as np 8 | 9 | import os 10 | 11 | import sys 12 | sys.path.append('./Discriminator') 13 | sys.path.append('./Generator') 14 | sys.path.append('./Utilities/') 15 | import Res_Gen 16 | import PatchGAN34 17 | import PatchGAN70 18 | import PatchGAN142 19 | import MultiPatch 20 | import HisDis 21 | import Utilities 22 | import cv2 23 | class Model: 24 | """ 25 | ToDo 26 | -) save() - Save the current model parameter 27 | -) create() - Create the model layers 28 | -) init() - Initialize the model (load model if exists) 29 | -) load() - Load the parameters from the file 30 | -) ToDo 31 | 32 | Only the following functions should be called from outside: 33 | -) ToDo 34 | -) constructor 35 | """ 36 | 37 | def __init__(self, 38 | mod_name, 39 | data_file, 40 | buffer_size=32, 41 | architecture='Res6', 42 | lambda_h=10.,\ 43 | lambda_c=10.,\ 44 | dis_noise=0.25,\ 45 | deconv='transpose',\ 46 | patchgan='Patch70',\ 47 | verbose=False,\ 48 | gen_only=False): 49 | """ 50 | Create a Model (init). It will check, if a model with such a name has already been saved. If so, the model is being 51 | loaded. Otherwise, a new model with this name will be created. It will only be saved, if the save function is being 52 | called. The describtion of every parameter is given in the code below. 53 | 54 | INPUT: mod_name - This is the name of the model. It is mainly used to establish the place, where the model is being 55 | saved. 56 | data_file - hdf5 file that contains the dataset 57 | imsize - The dimension of the input images 58 | 59 | OUTPUT: - The model 60 | """ 61 | 62 | self.mod_name = mod_name # Model name (see above) 63 | 64 | self.data_file = data_file # hdf5 data file 65 | 66 | f = h5py.File(self.data_file,"r") 67 | self.a_chan = int(np.array(f['A/num_channel'])) # Number channels in A 68 | self.b_chan = int(np.array(f['B/num_channel'])) # Number channels in B 69 | self.imsize = int(np.shape(f['A/data'][0,:,0,0])[0]) # Image size (squared) 70 | self.a_size = int(np.array(f['A/num_samples'])) # Number of samples in A 71 | self.b_size = int(np.array(f['B/num_samples'])) # Number of samples in B 72 | f.close() 73 | 74 | # Reset all current saved tf stuff 75 | tf.reset_default_graph() 76 | 77 | self.architecture = architecture 78 | self.lambda_h = lambda_h 79 | self.lambda_c = lambda_c 80 | self.dis_noise_0 = dis_noise # ATTENTION: Name change from dis_noise to dis_noise_0 81 | self.deconv = deconv 82 | self.patchgan = patchgan 83 | self.verbose = verbose 84 | self.gen_only = gen_only # If true, only the generator are used (and loaded) 85 | 86 | # Create the model that is built out of two discriminators and a generator 87 | self.create() 88 | 89 | # Image buffer 90 | self.buffer_size = buffer_size 91 | self.temp_b_s = 0. 92 | self.buffer_real_a = np.zeros([self.buffer_size,self.imsize,self.imsize,self.a_chan]) 93 | self.buffer_real_b = np.zeros([self.buffer_size,self.imsize,self.imsize,self.b_chan]) 94 | self.buffer_fake_a = np.zeros([self.buffer_size,self.imsize,self.imsize,self.a_chan]) 95 | self.buffer_fake_b = np.zeros([self.buffer_size,self.imsize,self.imsize,self.b_chan]) 96 | 97 | # Create the model saver 98 | with self.graph.as_default(): 99 | if not self.gen_only: 100 | self.saver = tf.train.Saver() 101 | else: 102 | self.saver = tf.train.Saver(var_list=self.list_gen) 103 | 104 | def create(self): 105 | """ 106 | Create the model. ToDo 107 | """ 108 | # Create a graph and add all layers 109 | self.graph = tf.Graph() 110 | with self.graph.as_default(): 111 | # Define variable learning rate and dis_noise 112 | self.relative_lr = tf.placeholder_with_default([1.],[1],name="relative_lr") 113 | self.relative_lr = self.relative_lr[0] 114 | 115 | self.rel_dis_noise = tf.placeholder_with_default([1.],[1],name="rel_dis_noise") 116 | self.rel_dis_noise = self.rel_dis_noise[0] 117 | self.dis_noise = self.rel_dis_noise * self.dis_noise_0 118 | 119 | 120 | # Create the generator and discriminator 121 | if self.architecture == 'Res6': 122 | gen_dim = [64, 128,256, 256,256,256,256,256,256, 128,64 ] 123 | kernel_size =[7, 3,3, 3,3,3,3,3,3, 3,3, 7] 124 | elif self.architecture == 'Res9': 125 | gen_dim= [64, 128,256, 256,256,256,256,256,256,256,256,256, 128,64 ] 126 | kernel_size=[7, 3,3, 3,3,3,3,3,3,3,3,3, 3,3, 7] 127 | else: 128 | print('Unknown generator architecture') 129 | return None 130 | 131 | self.genA = Res_Gen.ResGen('BtoA',self.a_chan,gen_dim=gen_dim,kernel_size=kernel_size,deconv=self.deconv,verbose=self.verbose) 132 | self.genB = Res_Gen.ResGen('AtoB',self.b_chan,gen_dim=gen_dim,kernel_size=kernel_size,deconv=self.deconv,verbose=self.verbose) 133 | 134 | if self.patchgan == 'Patch34': 135 | self.disA = PatchGAN34.PatchGAN34('A',noise=self.dis_noise) 136 | self.disB = PatchGAN34.PatchGAN34('B',noise=self.dis_noise) 137 | elif self.patchgan == 'Patch70': 138 | self.disA = PatchGAN70.PatchGAN70('A',noise=self.dis_noise) 139 | self.disB = PatchGAN70.PatchGAN70('B',noise=self.dis_noise) 140 | elif self.patchgan == 'Patch142': 141 | self.disA = PatchGAN142.PatchGAN142('A',noise=self.dis_noise) 142 | self.disB = PatchGAN142.PatchGAN142('B',noise=self.dis_noise) 143 | elif self.patchgan == 'MultiPatch': 144 | self.disA = MultiPatch.MultiPatch('A',noise=self.dis_noise) 145 | self.disB = MultiPatch.MultiPatch('B',noise=self.dis_noise) 146 | else: 147 | print('Unknown Patch discriminator type') 148 | return None 149 | 150 | self.disA_His = HisDis.HisDis('A',noise=self.dis_noise,keep_prob=1.) 151 | self.disB_His = HisDis.HisDis('B',noise=self.dis_noise,keep_prob=1.) 152 | 153 | # Create a placeholder for the input data 154 | self.A = tf.placeholder(tf.float32,[None, None, None, self.a_chan],name="a") 155 | self.B = tf.placeholder(tf.float32,[None, None, None, self.b_chan],name="b") 156 | 157 | if self.verbose: 158 | print('Size A: ' +str(self.a_chan)) # Often 1 --> Real 159 | print('Size B: ' +str(self.b_chan)) # Often 3 --> Syn 160 | 161 | # Create cycleGAN 162 | 163 | self.fake_A = self.genA.create(self.B,False) 164 | self.fake_B = self.genB.create(self.A,False) 165 | 166 | 167 | 168 | # Define the histogram loss 169 | t_A = tf.transpose(tf.reshape(self.A,[-1, self.a_chan]),[1,0]) 170 | t_B = tf.transpose(tf.reshape(self.B,[-1, self.b_chan]),[1,0]) 171 | t_fake_A = tf.transpose(tf.reshape(self.fake_A,[-1, self.a_chan]),[1,0]) 172 | t_fake_B = tf.transpose(tf.reshape(self.fake_B,[-1, self.b_chan]),[1,0]) 173 | 174 | self.s_A,_ = tf.nn.top_k(t_A,tf.shape(t_A)[1]) 175 | self.s_B,_ = tf.nn.top_k(t_B,tf.shape(t_B)[1]) 176 | self.s_fake_A,_ = tf.nn.top_k(t_fake_A,tf.shape(t_fake_A)[1]) 177 | self.s_fake_B,_ = tf.nn.top_k(t_fake_B,tf.shape(t_fake_B)[1]) 178 | 179 | self.m_A = tf.reshape(tf.reduce_mean(tf.reshape(self.s_A,[self.a_chan, self.imsize, -1]),axis=2),[1, -1]) 180 | self.m_B = tf.reshape(tf.reduce_mean(tf.reshape(self.s_B,[self.b_chan, self.imsize, -1]),axis=2),[1, -1]) 181 | self.m_fake_A = tf.reshape(tf.reduce_mean(tf.reshape(self.s_fake_A,[self.a_chan, self.imsize, -1]),axis=2),[1, -1]) 182 | self.m_fake_B = tf.reshape(tf.reduce_mean(tf.reshape(self.s_fake_B,[self.b_chan, self.imsize, -1]),axis=2),[1, -1]) 183 | 184 | # Define generator loss functions 185 | self.lambda_c = tf.placeholder_with_default([self.lambda_c],[1],name="lambda_c") 186 | self.lambda_c = self.lambda_c[0] 187 | self.lambda_h = tf.placeholder_with_default([self.lambda_h],[1],name="lambda_h") 188 | self.lambda_h = self.lambda_h[0] 189 | 190 | self.dis_real_A = self.disA.create(self.A,False) 191 | self.dis_real_Ah = self.disA_His.create(self.m_A,False) 192 | self.dis_real_B = self.disB.create(self.B,False) 193 | self.dis_real_Bh = self.disB_His.create(self.m_B,False) 194 | self.dis_fake_A = self.disA.create(self.fake_A,True) 195 | self.dis_fake_Ah = self.disA_His.create(self.m_fake_A,True) 196 | self.dis_fake_B = self.disB.create(self.fake_B,True) 197 | self.dis_fake_Bh = self.disB_His.create(self.m_fake_B,True) 198 | 199 | self.cyc_A = self.genA.create(self.fake_B,True) 200 | self.cyc_B = self.genB.create(self.fake_A,True) 201 | 202 | 203 | # Define cycle loss (eq. 2) 204 | self.loss_cyc_A = tf.reduce_mean(tf.abs(self.cyc_A-self.A)) 205 | self.loss_cyc_B = tf.reduce_mean(tf.abs(self.cyc_B-self.B)) 206 | 207 | self.loss_cyc = self.loss_cyc_A + self.loss_cyc_B 208 | 209 | # Define discriminator losses (eq. 1) 210 | self.loss_dis_A = (tf.reduce_mean(tf.square(self.dis_real_A)) +\ 211 | tf.reduce_mean(tf.square(1-self.dis_fake_A)))*0.5 +\ 212 | (tf.reduce_mean(tf.square(self.dis_real_Ah)) +\ 213 | tf.reduce_mean(tf.square(1-self.dis_fake_Ah)))*0.5*self.lambda_h 214 | 215 | 216 | self.loss_dis_B = (tf.reduce_mean(tf.square(self.dis_real_B)) +\ 217 | tf.reduce_mean(tf.square(1-self.dis_fake_B)))*0.5 +\ 218 | (tf.reduce_mean(tf.square(self.dis_real_Bh)) +\ 219 | tf.reduce_mean(tf.square(1-self.dis_fake_Bh)))*0.5*self.lambda_h 220 | 221 | self.loss_gen_A = tf.reduce_mean(tf.square(self.dis_fake_A)) +\ 222 | self.lambda_h * tf.reduce_mean(tf.square(self.dis_fake_Ah)) +\ 223 | self.lambda_c * self.loss_cyc/2. 224 | self.loss_gen_B = tf.reduce_mean(tf.square(self.dis_fake_B)) +\ 225 | self.lambda_h * tf.reduce_mean(tf.square(self.dis_fake_Bh)) +\ 226 | self.lambda_c * self.loss_cyc/2. 227 | 228 | # Create the different optimizer 229 | with self.graph.as_default(): 230 | # Optimizer for Gen 231 | self.list_gen = [] 232 | for var in tf.trainable_variables(): 233 | if 'gen' in str(var): 234 | self.list_gen.append(var) 235 | optimizer_gen = tf.train.AdamOptimizer(learning_rate=self.relative_lr*0.0002,beta1=0.5) 236 | self.opt_gen = optimizer_gen.minimize(self.loss_gen_A+self.loss_gen_B,var_list=self.list_gen) 237 | 238 | # Optimizer for Dis 239 | self.list_dis = [] 240 | for var in tf.trainable_variables(): 241 | if 'dis' in str(var): 242 | self.list_dis.append(var) 243 | optimizer_dis = tf.train.AdamOptimizer(learning_rate=self.relative_lr*0.0002,beta1=0.5) 244 | self.opt_dis = optimizer_dis.minimize(self.loss_dis_A + self.loss_dis_B,var_list=self.list_dis) 245 | 246 | def save(self,sess): 247 | """ 248 | Save the model parameter in a ckpt file. The filename is as 249 | follows: 250 | ./Models/.ckpt 251 | 252 | INPUT: sess - The current running session 253 | """ 254 | self.saver.save(sess,"./Models/" + self.mod_name + ".ckpt") 255 | 256 | def init(self,sess): 257 | """ 258 | Init the model. If the model exists in a file, load the model. Otherwise, initalize the variables 259 | 260 | INPUT: sess - The current running session 261 | """ 262 | if not os.path.isfile(\ 263 | "./Models/" + self.mod_name + ".ckpt.meta"): 264 | sess.run(tf.global_variables_initializer()) 265 | return 0 266 | else: 267 | if self.gen_only: 268 | sess.run(tf.global_variables_initializer()) 269 | self.load(sess) 270 | return 1 271 | 272 | def load(self,sess): 273 | """ 274 | Load the model from the parameter file: 275 | ./Models/.ckpt 276 | 277 | INPUT: sess - The current running session 278 | """ 279 | self.saver.restore(sess, "./Models/" + self.mod_name + ".ckpt") 280 | 281 | def train(self,batch_size=32,lambda_c=0.,lambda_h=0.,epoch=0,save=True,syn_noise=0.,real_noise=0.): 282 | f = h5py.File(self.data_file,"r") 283 | 284 | num_samples = min(self.a_size,self.b_size) 285 | num_iterations = num_samples // batch_size 286 | 287 | a_order = np.random.permutation(self.a_size) 288 | b_order = np.random.permutation(self.b_size) 289 | 290 | if self.verbose: 291 | print('lambda_c: ' + str(lambda_c)) 292 | print('lambda_h: ' + str(lambda_h)) 293 | 294 | with tf.Session(graph=self.graph) as sess: 295 | # initialize variables 296 | self.init(sess) 297 | 298 | vec_lcA = [] 299 | vec_lcB = [] 300 | 301 | vec_ldrA = [] 302 | vec_ldrAh = [] 303 | vec_ldrB = [] 304 | vec_ldrBh = [] 305 | vec_ldfA = [] 306 | vec_ldfAh = [] 307 | vec_ldfB = [] 308 | vec_ldfBh = [] 309 | 310 | vec_l_dis_A = [] 311 | vec_l_dis_B = [] 312 | vec_l_gen_A = [] 313 | vec_l_gen_B = [] 314 | 315 | rel_lr = 1. 316 | if epoch > 100: 317 | rel_lr = 2. - epoch/100. 318 | 319 | if epoch < 100: 320 | rel_noise = 0.9**epoch 321 | else: 322 | rel_noise = 0. 323 | 324 | for iteration in range(num_iterations): 325 | images_a = f['A/data'][np.sort(a_order[(iteration*batch_size):((iteration+1)*batch_size)]),:,:,:] 326 | images_b = f['B/data'][np.sort(b_order[(iteration*batch_size):((iteration+1)*batch_size)]),:,:,:] 327 | if images_a.dtype=='uint8': 328 | images_a=images_a/float(2**8-1) 329 | elif images_a.dtype=='uint16': 330 | images_a=images_a/float(2**16-1) 331 | else: 332 | raise ValueError('Dataset A is not int8 or int16') 333 | if images_b.dtype=='uint8': 334 | images_b=images_b/float(2**8-1) 335 | elif images_b.dtype=='uint16': 336 | images_b=images_b/float(2**16-1) 337 | else: 338 | raise ValueError('Dataset B is not int8 or int16') 339 | 340 | images_a += np.random.randn(*images_a.shape)*real_noise 341 | images_b += np.random.randn(*images_b.shape)*syn_noise 342 | 343 | _, l_gen_A, im_fake_A, l_gen_B, im_fake_B, cyc_A, cyc_B, sA, sB, sfA, sfB, lcA, lcB = sess.run([self.opt_gen,\ 344 | self.loss_gen_A,\ 345 | self.fake_A,\ 346 | self.loss_gen_B,\ 347 | self.fake_B,\ 348 | self.cyc_A,\ 349 | self.cyc_B,\ 350 | self.s_A,self.s_B,self.s_fake_A,self.s_fake_B,\ 351 | self.loss_cyc_A,\ 352 | self.loss_cyc_B],\ 353 | feed_dict={self.A: images_a,\ 354 | self.B: images_b,\ 355 | self.lambda_c: lambda_c,\ 356 | self.lambda_h: lambda_h,\ 357 | self.relative_lr: rel_lr,\ 358 | self.rel_dis_noise: rel_noise}) 359 | 360 | if self.temp_b_s >= self.buffer_size: 361 | rand_vec_a = np.random.permutation(self.buffer_size)[:batch_size] 362 | rand_vec_b = np.random.permutation(self.buffer_size)[:batch_size] 363 | 364 | self.buffer_real_a[rand_vec_a,...] = images_a 365 | self.buffer_real_b[rand_vec_b,...] = images_b 366 | self.buffer_fake_a[rand_vec_a,...] = im_fake_A 367 | self.buffer_fake_b[rand_vec_b,...] = im_fake_B 368 | else: 369 | low = int(self.temp_b_s) 370 | high = int(min(self.temp_b_s + batch_size,self.buffer_size)) 371 | self.temp_b_s = high 372 | 373 | self.buffer_real_a[low:high,...] = images_a[:(high-low),...] 374 | self.buffer_real_b[low:high,...] = images_b[:(high-low),...] 375 | self.buffer_fake_a[low:high,...] = im_fake_A[:(high-low),...] 376 | self.buffer_fake_b[low:high,...] = im_fake_B[:(high-low),...] 377 | 378 | # Create dataset out of buffer and gen images to train dis 379 | dis_real_a = np.copy(images_a) 380 | dis_real_b = np.copy(images_b) 381 | dis_fake_a = np.copy(im_fake_A) 382 | dis_fake_b = np.copy(im_fake_B) 383 | 384 | half_b_s = int(batch_size/2) 385 | rand_vec_a = np.random.permutation(self.temp_b_s)[:half_b_s] 386 | rand_vec_b = np.random.permutation(self.temp_b_s)[:half_b_s] 387 | dis_real_a[:half_b_s,...] = self.buffer_real_a[rand_vec_a,...] 388 | dis_fake_a[:half_b_s,...] = self.buffer_fake_a[rand_vec_a,...] 389 | dis_real_b[:half_b_s,...] = self.buffer_real_b[rand_vec_b,...] 390 | dis_fake_b[:half_b_s,...] = self.buffer_fake_b[rand_vec_b,...] 391 | 392 | _, l_dis_A, l_dis_B, \ 393 | ldrA,ldrAh,ldfA,ldfAh,\ 394 | ldrB,ldrBh,ldfB,ldfBh = sess.run([\ 395 | self.opt_dis, 396 | self.loss_dis_A, 397 | self.loss_dis_B, 398 | self.dis_real_A, 399 | self.dis_real_Ah, 400 | self.dis_fake_A, 401 | self.dis_fake_Ah, 402 | self.dis_real_B, 403 | self.dis_real_Bh, 404 | self.dis_fake_B, 405 | self.dis_fake_Bh],feed_dict={self.A: dis_real_a,\ 406 | self.B: dis_real_b,\ 407 | self.fake_A: dis_fake_a,\ 408 | self.fake_B: dis_fake_b,\ 409 | self.lambda_c: lambda_c,\ 410 | self.lambda_h: lambda_h,\ 411 | self.relative_lr: rel_lr,\ 412 | self.rel_dis_noise: rel_noise}) 413 | 414 | vec_l_dis_A.append(l_dis_A) 415 | vec_l_dis_B.append(l_dis_B) 416 | vec_l_gen_A.append(l_gen_A) 417 | vec_l_gen_B.append(l_gen_B) 418 | 419 | vec_lcA.append(lcA) 420 | vec_lcB.append(lcB) 421 | 422 | vec_ldrA.append(ldrA) 423 | vec_ldrAh.append(ldrAh) 424 | vec_ldrB.append(ldrB) 425 | vec_ldrBh.append(ldrBh) 426 | vec_ldfA.append(ldfA) 427 | vec_ldfAh.append(ldfAh) 428 | vec_ldfB.append(ldfB) 429 | vec_ldfBh.append(ldfBh) 430 | 431 | if np.shape(images_b)[-1]==4: 432 | 433 | images_b=np.vstack((images_b[0,:,:,0:3],np.tile(images_b[0,:,:,3].reshape(320,320,1),[1,1,3]))) 434 | im_fake_B=np.vstack((im_fake_B[0,:,:,0:3],np.tile(im_fake_B[0,:,:,3].reshape(320,320,1),[1,1,3]))) 435 | cyc_B=np.vstack((cyc_B[0,:,:,0:3],np.tile(cyc_B[0,:,:,3].reshape(320,320,1),[1,1,3]))) 436 | images_b=images_b[np.newaxis,:,:,:] 437 | im_fake_B=im_fake_B[np.newaxis,:,:,:] 438 | cyc_B=cyc_B[np.newaxis,:,:,:] 439 | 440 | if iteration%5==0: 441 | sneak_peak=Utilities.produce_tiled_images(images_a,images_b,im_fake_A, im_fake_B,cyc_A,cyc_B) 442 | 443 | cv2.imshow("",sneak_peak[:,:,[2,1,0]]) 444 | cv2.waitKey(1) 445 | 446 | print("\rTrain: {}/{} ({:.1f}%)".format(iteration+1, num_iterations,(iteration) * 100 / (num_iterations-1)) + \ 447 | " Loss_dis_A={:.4f}, Loss_dis_B={:.4f}".format(np.mean(vec_l_dis_A),np.mean(vec_l_dis_B)) + \ 448 | ", Loss_gen_A={:.4f}, Loss_gen_B={:.4f}".format(np.mean(vec_l_gen_A),np.mean(vec_l_gen_B))\ 449 | ,end=" ") 450 | 451 | # Save model 452 | if save: 453 | self.save(sess) 454 | cv2.imwrite("./Models/Images/" + self.mod_name + "_Epoch_" + str(epoch) + ".png",sneak_peak[:,:,[2,1,0]]*255) 455 | print("") 456 | 457 | f.close() 458 | 459 | loss_gen_A = [np.mean(np.square(np.array(vec_ldfA))),np.mean(np.square(np.array(vec_ldfAh))),np.mean(np.array(lcA))] 460 | loss_gen_B = [np.mean(np.square(np.array(vec_ldfB))),np.mean(np.square(np.array(vec_ldfBh))),np.mean(np.array(lcB))] 461 | loss_dis_A = [np.mean(np.square(np.array(vec_ldrA))),np.mean(np.square(1.-np.array(vec_ldfA))),\ 462 | np.mean(np.square(np.array(vec_ldrAh))),np.mean(np.square(1.-np.array(vec_ldfAh)))] 463 | loss_dis_B = [np.mean(np.square(np.array(vec_ldrB))),np.mean(np.square(1.-np.array(vec_ldfB))),\ 464 | np.mean(np.square(np.array(vec_ldrBh))),np.mean(np.square(1.-np.array(vec_ldfBh)))] 465 | 466 | return [loss_gen_A,loss_gen_B,loss_dis_A,loss_dis_B] 467 | 468 | def predict(self,lambda_c=0.,lambda_h=0.): 469 | f = h5py.File(self.data_file,"r") 470 | 471 | rand_a = np.random.randint(self.a_size-32) 472 | rand_b = np.random.randint(self.b_size-32) 473 | 474 | images_a = f['A/data'][rand_a:(rand_a+32),:,:,:]/255. 475 | images_b = f['B/data'][rand_b:(rand_b+32),:,:,:]/255. 476 | with tf.Session(graph=self.graph) as sess: 477 | # initialize variables 478 | self.init(sess) 479 | 480 | fake_A, fake_B, cyc_A, cyc_B = \ 481 | sess.run([self.fake_A,self.fake_B,self.cyc_A,self.cyc_B],\ 482 | feed_dict={self.A: images_a,\ 483 | self.B: images_b,\ 484 | self.lambda_c: lambda_c,\ 485 | self.lambda_h: lambda_h}) 486 | 487 | f.close() 488 | return images_a, images_b, fake_A, fake_B, cyc_A, cyc_B 489 | 490 | def generator_A(self,batch_size=32,lambda_c=0.,lambda_h=0.): 491 | f = h5py.File(self.data_file,"r") 492 | f_save = h5py.File("./Models/" + self.mod_name + '_gen_A.h5',"w") 493 | 494 | # Find number of samples 495 | num_samples = self.b_size 496 | num_iterations = num_samples // batch_size 497 | 498 | gen_data = np.zeros((f['B/data'].shape[0],f['B/data'].shape[1],f['B/data'].shape[2],f['A/data'].shape[3]),dtype=np.uint16) 499 | 500 | with tf.Session(graph=self.graph) as sess: 501 | # initialize variables 502 | self.init(sess) 503 | 504 | for iteration in range(num_iterations): 505 | images_b = f['B/data'][(iteration*batch_size):((iteration+1)*batch_size),:,:,:] 506 | if images_b.dtype=='uint8': 507 | images_b=images_b/float(2**8-1) 508 | elif images_b.dtype=='uint16': 509 | images_b=images_b/float(2**16-1) 510 | else: 511 | raise ValueError('Dataset B is not int8 or int16') 512 | 513 | gen_A = sess.run(self.fake_A,feed_dict={self.B: images_b,\ 514 | self.lambda_c: lambda_c,\ 515 | self.lambda_h: lambda_h}) 516 | gen_data[(iteration*batch_size):((iteration+1)*batch_size),:,:,:] = (np.minimum(np.maximum(gen_A,0),1)*(2**16-1)).astype(np.uint16) 517 | 518 | print("\rGenerator A: {}/{} ({:.1f}%)".format(iteration+1, num_iterations, iteration*100/(num_iterations-1)),end=" ") 519 | 520 | group = f_save.create_group('A') 521 | group.create_dataset(name='data', data=gen_data,dtype=np.uint16) 522 | 523 | f_save.close() 524 | f.close() 525 | 526 | return None 527 | 528 | def generator_B(self,batch_size=32,lambda_c=0.,lambda_h=0.): 529 | f = h5py.File(self.data_file,"r") 530 | f_save = h5py.File("./Models/" + self.mod_name + '_gen_B.h5',"w") 531 | 532 | # Find number of samples 533 | num_samples = self.a_size 534 | num_iterations = num_samples // batch_size 535 | 536 | gen_data = np.zeros((f['A/data'].shape[0],f['A/data'].shape[1],f['A/data'].shape[2],f['B/data'].shape[3]),dtype=np.uint16) 537 | 538 | with tf.Session(graph=self.graph) as sess: 539 | # initialize variables 540 | self.init(sess) 541 | 542 | for iteration in range(num_iterations): 543 | images_a = f['A/data'][(iteration*batch_size):((iteration+1)*batch_size),:,:,:] 544 | if images_a.dtype=='uint8': 545 | images_a=images_a/float(2**8-1) 546 | elif images_a.dtype=='uint16': 547 | images_a=images_a/float(2**16-1) 548 | else: 549 | raise ValueError('Dataset A is not int8 or int16') 550 | 551 | gen_B = sess.run(self.fake_B,feed_dict={self.A: images_a,\ 552 | self.lambda_c: lambda_c,\ 553 | self.lambda_h: lambda_h}) 554 | gen_data[(iteration*batch_size):((iteration+1)*batch_size),:,:,:] = (np.minimum(np.maximum(gen_B,0),1)*(2**16-1)).astype(np.uint16) 555 | 556 | print("\rGenerator B: {}/{} ({:.1f}%)".format(iteration+1, num_iterations, iteration*100/(num_iterations-1)),end=" ") 557 | 558 | group = f_save.create_group('B') 559 | group.create_dataset(name='data', data=gen_data,dtype=np.uint16) 560 | 561 | f_save.close() 562 | f.close() 563 | 564 | return None 565 | 566 | 567 | def get_loss(self,lambda_c=0.,lambda_h=0.): 568 | f = h5py.File(self.data_file,"r") 569 | 570 | rand_a = np.random.randint(self.a_size-32) 571 | rand_b = np.random.randint(self.b_size-32) 572 | 573 | images_a = f['A/data'][rand_a:(rand_a+32),:,:,:]/255. 574 | images_b = f['B/data'][rand_b:(rand_b+32),:,:,:]/255. 575 | with tf.Session(graph=self.graph) as sess: 576 | # initialize variables 577 | self.init(sess) 578 | 579 | l_rA,l_rB,l_fA,l_fB = \ 580 | sess.run([self.dis_real_A,self.dis_real_B,self.dis_fake_A,self.dis_fake_B,],\ 581 | feed_dict={self.A: images_a,\ 582 | self.B: images_b,\ 583 | self.lambda_c: lambda_c,\ 584 | self.lambda_h: lambda_h}) 585 | 586 | f.close() 587 | return l_rA,l_rB,l_fA,l_fB 588 | -------------------------------------------------------------------------------- /notebooks/VGG_Synthetic_by_localization.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import imageio as io\n", 10 | "import matplotlib.pyplot as plt\n", 11 | "%matplotlib inline\n", 12 | "import numpy as np\n", 13 | "import os\n", 14 | "from skimage.transform import rotate" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "min_cells,max_cells=70,310" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 13, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "pad=10\n", 33 | "xx,yy=np.meshgrid(np.arange(256+2*pad),np.arange(256+2*pad))\n", 34 | "#border pad\n", 35 | "fringe=np.pad(np.zeros((256,256)),((pad,pad),(pad,pad)),mode='constant',constant_values=1)\n", 36 | "fringe=fringe.astype(bool)\n", 37 | "\n", 38 | "\n", 39 | "def make_localization_synthetic_vgg():\n", 40 | " n_cell=np.random.randint(min_cells,max_cells)\n", 41 | " x,y=np.random.randint(0,255,size=(2,n_cell))+pad\n", 42 | " image=np.zeros((256+2*pad,256+2*pad,3))\n", 43 | " #homing_gaussian=np.zeros((256+2*pad,256+2*pad))\n", 44 | " #square_for_area=np.zeros((256+2*pad,256+2*pad))\n", 45 | " noise=np.clip(np.random.normal(loc=0.02,scale=0.03,size=(256+2*pad,256+2*pad)),0,1)\n", 46 | " image[:,:,0]=np.clip(np.random.normal(loc=0.02,scale=0.03,size=(256+2*pad,256+2*pad)),0,1)\n", 47 | " \n", 48 | " for i in range(n_cell):\n", 49 | " mask=(xx-x[i])**2+(yy-y[i])**2<(6)**2\n", 50 | " image[mask,0]=0.3*np.random.rand()+0.6+noise[mask]\n", 51 | " image[:,:,1]+=0.4*np.exp(-((yy-y[i])/3.)**2-((xx-x[i])/3.)**2)\n", 52 | " image[:,:,2]+=0.5*np.exp(-((yy-y[i])/1.)**2-((xx-x[i])/1.)**2)\n", 53 | " return image" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 14, 59 | "metadata": {}, 60 | "outputs": [ 61 | { 62 | "data": { 63 | "text/plain": [ 64 | "" 65 | ] 66 | }, 67 | "execution_count": 14, 68 | "metadata": {}, 69 | "output_type": "execute_result" 70 | }, 71 | { 72 | "data": { 73 | "image/png": "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\n", 74 | "text/plain": [ 75 | "
" 76 | ] 77 | }, 78 | "metadata": { 79 | "needs_background": "light" 80 | }, 81 | "output_type": "display_data" 82 | } 83 | ], 84 | "source": [ 85 | "plt.imshow(make_localization_synthetic_vgg())" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": 17, 91 | "metadata": {}, 92 | "outputs": [ 93 | { 94 | "name": "stdout", 95 | "output_type": "stream", 96 | "text": [ 97 | "\n" 98 | ] 99 | } 100 | ], 101 | "source": [ 102 | "N_images=50\n", 103 | "dataname='VGG_localization'\n", 104 | "train_name='trainA'\n", 105 | "directory='../Data/'+dataname+'/'+train_name+'/'\n", 106 | "if not os.path.exists(directory):\n", 107 | " os.makedirs(directory)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [ 115 | { 116 | "name": "stderr", 117 | "output_type": "stream", 118 | "text": [ 119 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.02268088151]\n", 120 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n" 121 | ] 122 | }, 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "0" 128 | ] 129 | }, 130 | { 131 | "name": "stderr", 132 | "output_type": "stream", 133 | "text": [ 134 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01464227872]\n", 135 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 136 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.02984934249]\n", 137 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 138 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01064395335]\n", 139 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 140 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01649309037]\n", 141 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 142 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.02021895102]\n", 143 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 144 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00492180949]\n", 145 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 146 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01165869974]\n", 147 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 148 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01293333199]\n", 149 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 150 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00414250075]\n", 151 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 152 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00521218855]\n", 153 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 154 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.27627977337]\n", 155 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 156 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00136268245]\n", 157 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 158 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.03352604424]\n", 159 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 160 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01857872308]\n", 161 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 162 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00677343811]\n", 163 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 164 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.13678852504]\n", 165 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 166 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.02354139996]\n", 167 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 168 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00641660986]\n", 169 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 170 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00318504915]\n", 171 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 172 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.01248495563]\n", 173 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 174 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.03589042459]\n", 175 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 176 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00244328906]\n", 177 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n", 178 | "/usr/local/lib/python2.7/dist-packages/imageio/core/util.py:104: UserWarning: Conversion from float64 to uint8, range [0.0, 1.00883233411]\n", 179 | " 'range [{2}, {3}]'.format(dtype_str, out_type.__name__, mi, ma))\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "for i in range(N_images):\n", 185 | " name=directory+str(i).zfill(5)+'.png'\n", 186 | " image=make_localization_synthetic_vgg()\n", 187 | " io.imsave(name,image)\n", 188 | " if i%50==0:\n", 189 | " print i," 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [] 198 | } 199 | ], 200 | "metadata": { 201 | "kernelspec": { 202 | "display_name": "Python 2", 203 | "language": "python", 204 | "name": "python2" 205 | }, 206 | "language_info": { 207 | "codemirror_mode": { 208 | "name": "ipython", 209 | "version": 2 210 | }, 211 | "file_extension": ".py", 212 | "mimetype": "text/x-python", 213 | "name": "python", 214 | "nbconvert_exporter": "python", 215 | "pygments_lexer": "ipython2", 216 | "version": "2.7.12" 217 | } 218 | }, 219 | "nbformat": 4, 220 | "nbformat_minor": 2 221 | } 222 | --------------------------------------------------------------------------------