├── 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 |
--------------------------------------------------------------------------------