├── .gitignore ├── Notebooks ├── Preprocessor.ipynb ├── SuperPixel_Decoder.ipynb ├── create_json.ipynb ├── train_ensemble.ipynb └── v2_graph.ipynb ├── README.md ├── Scripts ├── Preprocessor.py ├── SuperPixel_Decoder.py ├── Test.py ├── arg_parse_test.py ├── create_json.py ├── ensemble_decoder.py ├── train_ensemble.py ├── v2_graph.py └── v3_graph_batch_norm.py ├── Visualizations ├── Pictures │ ├── GUI.png │ ├── h_conv1_random_patch.png │ ├── h_conv2_random_patch.png │ └── h_conv3_random_patch.png ├── feature_viz.ipynb ├── feature_viz_gui.ipynb └── weight_visualization.ipynb ├── arglists ├── cl_args.json ├── heavy.json ├── model_paths.json └── model_paths_heavy.json └── images ├── 01_manual1.jpg ├── 01_test.jpg ├── 01_test_src.jpg ├── Architecture.jpg ├── Magnified1.jpg ├── Magnified1_1.jpg ├── Magnified1_2.jpg ├── Magnified1_3.jpg ├── Magnified1_4.jpg ├── Magnified2_1.jpg ├── Magnified2_2.jpg ├── Magnified2_3.jpg ├── Magnified2_4.jpg ├── Magnified3_1.jpg ├── Magnified3_2.jpg ├── Magnified3_3.jpg ├── Magnified3_4.jpg ├── Magnified4_1.jpg ├── Magnified4_2.jpg ├── Magnified4_3.jpg ├── Magnified4_4.jpg ├── Performance_Paper.jpg ├── Proposed-Method.jpg └── ROC_Paper.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | *~ 64 | *.csv 65 | Notebooks/Untitled.ipynb 66 | images_high_res/ 67 | Experiments/ 68 | Visualizations/images 69 | -------------------------------------------------------------------------------- /Notebooks/Preprocessor.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import numpy as np\n", 12 | "import pandas as pd\n", 13 | "from skimage import io\n", 14 | "from skimage.util import img_as_float, img_as_ubyte\n", 15 | "import os\n", 16 | "import glob\n", 17 | "import time\n", 18 | "import sys\n", 19 | "import argparse" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 2, 25 | "metadata": { 26 | "collapsed": true 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "def get_path(directory):\n", 31 | " \"\"\" Gets the filenames of all training, mask and ground truth images in the given \n", 32 | " directory \n", 33 | " Args:\n", 34 | " directory: The path to the root folder\n", 35 | " Output:\n", 36 | " imgs: List of paths to files containing images\n", 37 | " mask: List of paths to files containing masks of the images\n", 38 | " gt: List of paths to files containing corresponding ground truth images\n", 39 | " \"\"\"\n", 40 | " imgs = glob.glob(directory + '/images/*.tif')\n", 41 | " imgs.sort()\n", 42 | " #a = [x.split('/')[-1].split('.')[0] for x in train]\n", 43 | " \n", 44 | " mask = glob.glob(directory + '/mask/*.gif')\n", 45 | " mask.sort()\n", 46 | " #b = [x.split('/')[-1].split('.')[0] for x in mask]\n", 47 | " \n", 48 | " gt = glob.glob(directory + '/1st_manual/*.gif')\n", 49 | " gt.sort()\n", 50 | " #c = [x.split('/')[-1].split('.')[0] for x in gt]\n", 51 | " \n", 52 | " return map(os.path.abspath, imgs), map(os.path.abspath, mask), map(os.path.abspath, gt)" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": 3, 58 | "metadata": { 59 | "collapsed": false 60 | }, 61 | "outputs": [], 62 | "source": [ 63 | "# Hyper Params\n", 64 | "total_patches = 4800\n", 65 | "num_training_images = None\n", 66 | "patches_per_image = None\n", 67 | "patch_dim = 31 # Dimension of window used for training\n", 68 | "current_img_index = -1 # Index of the current image in 'train'\n", 69 | "current_img = None \n", 70 | "current_mask = None\n", 71 | "current_gt = None\n", 72 | "positive_proprtion = 0.5\n", 73 | "\n", 74 | "df = None" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 4, 80 | "metadata": { 81 | "collapsed": true 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "def load_next_img(data,mask_data,gt_data):\n", 86 | " \"\"\"When we have extracted 'PATCHES_PER_IMAGE' number of patches from our \n", 87 | " current image we call this function to change the current image\n", 88 | " Args:\n", 89 | " data: The list of paths to the images\n", 90 | " mask_data: List of paths to the corresponding masks of images\n", 91 | " gt_data: List of paths to the corresponding ground truth images\n", 92 | " \n", 93 | " \"\"\"\n", 94 | " global current_img_index, current_img, current_mask, current_gt\n", 95 | " \n", 96 | " if current_img_index < len(data)-1:\n", 97 | " current_img_index +=1\n", 98 | " print \"Working on image %d\"%(current_img_index + 1)\n", 99 | " current_img = io.imread(data[current_img_index]) \n", 100 | " current_mask = img_as_float(io.imread(mask_data[current_img_index])) \n", 101 | " current_gt = img_as_float(io.imread(gt_data[current_img_index])) \n", 102 | " return True\n", 103 | " else:\n", 104 | " print 'No more images left in set'\n", 105 | " return False" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 5, 111 | "metadata": { 112 | "collapsed": false 113 | }, 114 | "outputs": [], 115 | "source": [ 116 | "def save_img_data(data, mask_data, gt_data):\n", 117 | " \"\"\"Extracts PATCHES_PER_IMAGE number of patches from each image\n", 118 | " \n", 119 | " It maintains a count of positive and negative patches and maintains\n", 120 | " the ratio POSITIVE_PROPORTION = pos/(pos+neg)\n", 121 | " Args:\n", 122 | " data: The list of paths to the images\n", 123 | " mask_data: List of paths to the corresponding masks of images\n", 124 | " gt_data: List of paths to the corresponding ground truth images\n", 125 | " \n", 126 | " \"\"\"\n", 127 | " pos_count = 0\n", 128 | " neg_count = 0\n", 129 | " global df\n", 130 | " while pos_count +neg_count < patches_per_image: \n", 131 | " # Choose a random point\n", 132 | " i = np.random.randint(patch_dim/2,current_img.shape[0]-patch_dim/2)\n", 133 | " j = np.random.randint(patch_dim/2,current_img.shape[1]-patch_dim/2)\n", 134 | " h = (patch_dim - 1)/2\n", 135 | " if int(np.sum(current_mask[i-h:i+h+1,j-h:j+h+1])/patch_dim**2) == 1:\n", 136 | " ind = current_img_index*patches_per_image+pos_count+neg_count\n", 137 | " \n", 138 | " # If a positive sample is found and positive count hasn't reached its limit\n", 139 | " if int(current_gt[i,j])==1 and pos_count < positive_proprtion*patches_per_image:\n", 140 | " df.loc[ind][0:-1] = np.reshape(current_img[i-h:i+h+1,j-h:j+h+1], -1)\n", 141 | " df.loc[ind][patch_dim**2*3] = int(current_gt[i,j])\n", 142 | " pos_count += 1\n", 143 | " # If a negative sample is found and negative count hasn't reached its limit\n", 144 | " elif int(current_gt[i,j])==0 and neg_count < (1-positive_proprtion)*patches_per_image:\n", 145 | " df.loc[ind][0:-1] = np.reshape(current_img[i-h:i+h+1,j-h:j+h+1], -1)\n", 146 | " df.loc[ind][patch_dim**2*3] = int(current_gt[i,j])\n", 147 | " neg_count += 1" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 6, 153 | "metadata": { 154 | "collapsed": true 155 | }, 156 | "outputs": [], 157 | "source": [ 158 | "def finish_parsing():\n", 159 | " parser = argparse.ArgumentParser(description=\n", 160 | " 'Python script to save window patches for training')\n", 161 | " parser.add_argument(\"--total_patches\", type=int,\n", 162 | " help=\"Total number of training images/patches to be used [Default - 4800]\")\n", 163 | " parser.add_argument(\"--patch_dim\", type=int,\n", 164 | " help=\"Dimension of window to be used as a training patch [Default - 31]\")\n", 165 | " parser.add_argument(\"--positive\", type=float,\n", 166 | " help=\"Proportion of positive classes to be kept in training data [Default - 0.5]\")\n", 167 | " \n", 168 | " args = parser.parse_args()\n", 169 | " \n", 170 | " global total_patches, patch_dim, positive_proprtion\n", 171 | " if args.total_patches is not None:\n", 172 | " total_patches = args.total_patches\n", 173 | " print \"New total patches = %d\" % total_patches\n", 174 | " if args.patch_dim is not None:\n", 175 | " patch_dim = args.patch_dim\n", 176 | " print \"New patch_dim = %d\" % patch_dim\n", 177 | " if args.positive is not None:\n", 178 | " positive_proprtion = args.positive\n", 179 | " print \"New positive_proprtion = %.2f\" % positive_proprtion\n", 180 | " " 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 7, 186 | "metadata": { 187 | "collapsed": true 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "def main():\n", 192 | "\n", 193 | " finish_parsing()\n", 194 | " \n", 195 | " train, mask_train, gt_train = get_path('../../Data/DRIVE/training')\n", 196 | " test, mask_test, mask_gt = get_path('../../Data/DRIVE/test')\n", 197 | " \n", 198 | " # Redefining some hyperparams and global variables\n", 199 | " global num_training_images, patches_per_image, current_img, current_mask, current_gt\n", 200 | " num_training_images = len(train)\n", 201 | " patches_per_image = total_patches/num_training_images\n", 202 | " current_img = io.imread(train[0]) \n", 203 | " current_mask = img_as_float(io.imread(mask_train[0]))\n", 204 | " current_gt = img_as_float(io.imread(gt_train[0]))\n", 205 | "\n", 206 | " begin = time.time()\n", 207 | " print \"Creating DataFrame\"\n", 208 | " global df\n", 209 | " df = pd.DataFrame(index=np.arange(total_patches), columns = np.arange(patch_dim**2*3+1))\n", 210 | " print \"Dataframe ready\"\n", 211 | "\n", 212 | " while load_next_img(train, mask_train, gt_train):\n", 213 | " start = time.time()\n", 214 | " save_img_data(train,mask_train, gt_train)\n", 215 | " print \"Time taken for this image = %f secs\" %( (time.time()-start))\n", 216 | "\n", 217 | " print \"\\nMean Normalising\\n\"\n", 218 | " last = len(df.columns) -1\n", 219 | " mean_img = np.mean(df)[:-1]\n", 220 | " labels = df[last]\n", 221 | " mean_normalised_df = df - np.mean(df)\n", 222 | " mean_normalised_df[last] = labels\n", 223 | "\n", 224 | " print \"Randomly shuffling the datasets\\n\"\n", 225 | " mean_normalised_df = mean_normalised_df.iloc[np.random.permutation(len(df))]\n", 226 | " mean_normalised_df = mean_normalised_df.reset_index(drop=True)\n", 227 | "\n", 228 | " print \"Writing to pickle\\n\"\n", 229 | " mean_normalised_df.to_pickle('../../Data/mean_normalised_df_no_class_bias.pkl')\n", 230 | " mean_img.to_pickle('../../Data/mean_img_no_class_bias.pkl')\n", 231 | "\n", 232 | " print \"Total time taken = %f mins\\n\" %( (time.time()-begin)/60.0)" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": 8, 238 | "metadata": { 239 | "collapsed": false 240 | }, 241 | "outputs": [ 242 | { 243 | "name": "stdout", 244 | "output_type": "stream", 245 | "text": [ 246 | "New total patches = 3000\n", 247 | "Creating DataFrame\n", 248 | "Dataframe ready\n", 249 | "Working on image 1\n", 250 | "Time taken for this image = 0.095596 secs\n", 251 | "Working on image 2\n", 252 | "Time taken for this image = 0.087083 secs\n", 253 | "Working on image 3\n", 254 | "Time taken for this image = 0.096429 secs\n", 255 | "Working on image 4\n", 256 | "Time taken for this image = 0.085965 secs\n", 257 | "Working on image 5\n", 258 | "Time taken for this image = 0.091146 secs\n", 259 | "Working on image 6\n", 260 | "Time taken for this image = 0.089588 secs\n", 261 | "Working on image 7\n", 262 | "Time taken for this image = 0.091107 secs\n", 263 | "Working on image 8\n", 264 | "Time taken for this image = 0.091986 secs\n", 265 | "Working on image 9\n", 266 | "Time taken for this image = 0.179963 secs\n", 267 | "Working on image 10\n", 268 | "Time taken for this image = 0.196373 secs\n", 269 | "Working on image 11\n", 270 | "Time taken for this image = 0.202149 secs\n", 271 | "Working on image 12\n", 272 | "Time taken for this image = 0.129593 secs\n", 273 | "Working on image 13\n", 274 | "Time taken for this image = 0.092147 secs\n", 275 | "Working on image 14\n", 276 | "Time taken for this image = 0.087060 secs\n", 277 | "Working on image 15\n", 278 | "Time taken for this image = 0.087276 secs\n", 279 | "Working on image 16\n", 280 | "Time taken for this image = 0.085490 secs\n", 281 | "Working on image 17\n", 282 | "Time taken for this image = 0.176709 secs\n", 283 | "Working on image 18\n", 284 | "Time taken for this image = 0.136276 secs\n", 285 | "Working on image 19\n", 286 | "Time taken for this image = 0.090537 secs\n", 287 | "Working on image 20\n", 288 | "Time taken for this image = 0.091870 secs\n", 289 | "No more images left in set\n", 290 | "\n", 291 | "Mean Normalising\n", 292 | "\n", 293 | "Randomly shuffling the datasets\n", 294 | "\n", 295 | "Writing to pickle\n", 296 | "\n", 297 | "Total time taken = 0.124066 mins\n", 298 | "\n" 299 | ] 300 | }, 301 | { 302 | "name": "stderr", 303 | "output_type": "stream", 304 | "text": [ 305 | "/home/ankush/anaconda2/lib/python2.7/site-packages/skimage/external/tifffile/tifffile.py:1794: RuntimeWarning: py_decodelzw encountered unexpected end of stream\n", 306 | " strip = decompress(strip)\n" 307 | ] 308 | } 309 | ], 310 | "source": [ 311 | "if __name__ == \"__main__\":\n", 312 | " sys.argv = ['Preprocessor.py','--total_patches','3000']\n", 313 | " main()" 314 | ] 315 | } 316 | ], 317 | "metadata": { 318 | "kernelspec": { 319 | "display_name": "Python 2", 320 | "language": "python", 321 | "name": "python2" 322 | }, 323 | "language_info": { 324 | "codemirror_mode": { 325 | "name": "ipython", 326 | "version": 2 327 | }, 328 | "file_extension": ".py", 329 | "mimetype": "text/x-python", 330 | "name": "python", 331 | "nbconvert_exporter": "python", 332 | "pygments_lexer": "ipython2", 333 | "version": "2.7.11" 334 | } 335 | }, 336 | "nbformat": 4, 337 | "nbformat_minor": 0 338 | } 339 | -------------------------------------------------------------------------------- /Notebooks/SuperPixel_Decoder.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from __future__ import division\n", 12 | "import numpy as np\n", 13 | "import pandas as pd\n", 14 | "import os\n", 15 | "import glob\n", 16 | "from skimage import io, color, measure\n", 17 | "from skimage.util import img_as_float, img_as_ubyte\n", 18 | "from skimage.segmentation import slic, mark_boundaries\n", 19 | "import tensorflow as tf\n", 20 | "%matplotlib inline\n", 21 | "import time\n", 22 | "from six.moves import xrange " 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 2, 28 | "metadata": { 29 | "collapsed": true 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "mean_img = pd.read_pickle('../Data/mean_img_no_class_bias.pkl')" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 12, 39 | "metadata": { 40 | "collapsed": true 41 | }, 42 | "outputs": [], 43 | "source": [ 44 | "PATCH_DIM = 31\n", 45 | "BATCH_SIZE = 100 # Must be a perfect square\n", 46 | "NUM_CLASSES = 2\n", 47 | "SP_COMPACTNESS = 1\n", 48 | "SP_SIGMA = 1\n", 49 | "NUM_SP=10000" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 4, 55 | "metadata": { 56 | "collapsed": true 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "def get_path(directory):\n", 61 | " imgs = glob.glob(directory + '/images/*.tif')\n", 62 | " imgs.sort()\n", 63 | " #a = [x.split('/')[-1].split('.')[0] for x in train]\n", 64 | " \n", 65 | " mask = glob.glob(directory + '/mask/*.gif')\n", 66 | " mask.sort()\n", 67 | " #b = [x.split('/')[-1].split('.')[0] for x in mask]\n", 68 | " \n", 69 | " gt = glob.glob(directory + '/1st_manual/*.gif')\n", 70 | " gt.sort()\n", 71 | " #c = [x.split('/')[-1].split('.')[0] for x in gt]\n", 72 | " \n", 73 | " return map(os.path.abspath, imgs), map(os.path.abspath, mask), map(os.path.abspath, gt)\n", 74 | "\n", 75 | "train, mask_train, gt_train = get_path('../Data/DRIVE/training')\n", 76 | "test, mask_test, mask_gt = get_path('../Data/DRIVE/test')" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 5, 82 | "metadata": { 83 | "collapsed": true 84 | }, 85 | "outputs": [], 86 | "source": [ 87 | "def inference(images, keep_prob, fc_hidden_units1=512):\n", 88 | " \"\"\" Builds the model as far as is required for running the network\n", 89 | " forward to make predictions.\n", 90 | "\n", 91 | " Args:\n", 92 | " images: Images placeholder, from inputs().\n", 93 | " keep_prob: Probability used for Droupout in the final Affine Layer\n", 94 | " fc_hidden_units1: Number of hidden neurons in final Affine layer\n", 95 | " Returns:\n", 96 | " softmax_linear: Output tensor with the computed logits.\n", 97 | " \"\"\"\n", 98 | " with tf.variable_scope('h_conv1') as scope:\n", 99 | " weights = tf.get_variable('weights', shape=[4, 4, 3, 64], \n", 100 | " initializer=tf.contrib.layers.xavier_initializer_conv2d())\n", 101 | " biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05))\n", 102 | " \n", 103 | " # Flattening the 3D image into a 1D array\n", 104 | " x_image = tf.reshape(images, [-1,PATCH_DIM,PATCH_DIM,3])\n", 105 | " z = tf.nn.conv2d(x_image, weights, strides=[1, 1, 1, 1], padding='VALID')\n", 106 | " h_conv1 = tf.nn.relu(z+biases, name=scope.name)\n", 107 | " with tf.variable_scope('h_conv2') as scope:\n", 108 | " weights = tf.get_variable('weights', shape=[4, 4, 64, 64], \n", 109 | " initializer=tf.contrib.layers.xavier_initializer_conv2d())\n", 110 | " biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05))\n", 111 | " z = tf.nn.conv2d(h_conv1, weights, strides=[1, 1, 1, 1], padding='SAME')\n", 112 | " h_conv2 = tf.nn.relu(z+biases, name=scope.name)\n", 113 | " \n", 114 | " h_pool1 = tf.nn.max_pool(h_conv2, ksize=[1, 2, 2, 1],\n", 115 | " strides=[1, 2, 2, 1], padding='SAME', name='h_pool1')\n", 116 | " \n", 117 | " with tf.variable_scope('h_conv3') as scope:\n", 118 | " weights = tf.get_variable('weights', shape=[4, 4, 64, 64], \n", 119 | " initializer=tf.contrib.layers.xavier_initializer_conv2d())\n", 120 | " biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05))\n", 121 | " z = tf.nn.conv2d(h_pool1, weights, strides=[1, 1, 1, 1], padding='SAME')\n", 122 | " h_conv3 = tf.nn.relu(z+biases, name=scope.name)\n", 123 | " \n", 124 | " h_pool2 = tf.nn.max_pool(h_conv3, ksize=[1, 2, 2, 1],\n", 125 | " strides=[1, 2, 2, 1], padding='SAME', name='h_pool2')\n", 126 | " \n", 127 | " with tf.variable_scope('h_fc1') as scope:\n", 128 | " weights = tf.get_variable('weights', shape=[7**2*64, fc_hidden_units1], \n", 129 | " initializer=tf.contrib.layers.xavier_initializer())\n", 130 | " biases = tf.get_variable('biases', shape=[fc_hidden_units1], initializer=tf.constant_initializer(0.05))\n", 131 | " h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])\n", 132 | " \n", 133 | " h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, weights) + biases, name = 'h_fc1')\n", 134 | " h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)\n", 135 | " \n", 136 | " \n", 137 | " with tf.variable_scope('h_fc2') as scope:\n", 138 | " weights = tf.get_variable('weights', shape=[fc_hidden_units1, NUM_CLASSES], \n", 139 | " initializer=tf.contrib.layers.xavier_initializer())\n", 140 | " biases = tf.get_variable('biases', shape=[NUM_CLASSES])\n", 141 | " \n", 142 | " logits = (tf.matmul(h_fc1_drop, weights) + biases)\n", 143 | " return logits" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 45, 149 | "metadata": { 150 | "collapsed": true 151 | }, 152 | "outputs": [], 153 | "source": [ 154 | "def softmax(logits):\n", 155 | " return tf.nn.softmax(logits)" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": 46, 161 | "metadata": { 162 | "collapsed": true 163 | }, 164 | "outputs": [], 165 | "source": [ 166 | "def placeholder_inputs(batch_size):\n", 167 | " \"\"\"Generate placeholder variables to represent the input tensors.\n", 168 | " Args:\n", 169 | " batch_size: The batch size will be baked into both placeholders.\n", 170 | " Returns:\n", 171 | " images_placeholder: Images placeholder.\n", 172 | " \"\"\"\n", 173 | " images_placeholder = tf.placeholder(tf.float32, shape=(batch_size, PATCH_DIM**2*3))\n", 174 | " return images_placeholder" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 88, 180 | "metadata": { 181 | "collapsed": false 182 | }, 183 | "outputs": [], 184 | "source": [ 185 | "def nbd(image, point):\n", 186 | " i = point[0]\n", 187 | " j = point[1]\n", 188 | " h = int(PATCH_DIM/2)\n", 189 | " return image[i-h:i+h+1,j-h:j+h+1].reshape(-1)\n", 190 | "def segment_region(segmented, row_col, segments_slic, region_id, prediction):\n", 191 | " a = row_col[segments_slic==region_id]\n", 192 | " segmented[a[:,0],a[:,1]] = prediction" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 7, 198 | "metadata": { 199 | "collapsed": true 200 | }, 201 | "outputs": [], 202 | "source": [ 203 | "image = io.imread(train[0])\n", 204 | "mask = img_as_float(io.imread(mask_train[0]))\n", 205 | "gt = img_as_float(io.imread(gt_train[0]))\n", 206 | "mean_np_img = np.asarray(mean_img)" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 25, 212 | "metadata": { 213 | "collapsed": true 214 | }, 215 | "outputs": [], 216 | "source": [ 217 | "segments_slic = slic(image, n_segments=NUM_SP, compactness=SP_COMPACTNESS, \n", 218 | " sigma= SP_SIGMA, convert2lab = True)\n", 219 | "segments_slic = segments_slic + 1 # So that no labelled region is 0 and ignored by regionprops\n", 220 | "regions = measure.regionprops(segments_slic)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 26, 226 | "metadata": { 227 | "collapsed": true 228 | }, 229 | "outputs": [], 230 | "source": [ 231 | "#sp_image = mark_boundaries(image, segments_slic,[0,0,0])" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 93, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [ 241 | { 242 | "name": "stdout", 243 | "output_type": "stream", 244 | "text": [ 245 | "48 / 329960\n", 246 | "Time taken - > 1.850338\n", 247 | "96 / 329960\n", 248 | "Time taken - > 0.000120\n", 249 | "100030 / 329960\n", 250 | "Time taken - > 21.055947\n", 251 | "100062 / 329960\n", 252 | "Time taken - > 0.000255\n", 253 | "100095 / 329960\n", 254 | "Time taken - > 0.000124\n", 255 | "200049 / 329960\n", 256 | "Time taken - > 30.053467\n", 257 | "200065 / 329960\n", 258 | "Time taken - > 0.011845\n", 259 | "200083 / 329960\n", 260 | "Time taken - > 0.000156\n", 261 | "300011 / 329960\n", 262 | "Time taken - > 23.617220\n", 263 | "300041 / 329960\n", 264 | "Time taken - > 0.000225\n", 265 | "300058 / 329960\n", 266 | "Time taken - > 0.000198\n", 267 | "300098 / 329960\n", 268 | "Time taken - > 0.000205\n" 269 | ] 270 | } 271 | ], 272 | "source": [ 273 | "segmented = np.zeros(image.shape[:2])\n", 274 | "# We will use arrays to index the image and mask later\n", 275 | "cols, rows = np.meshgrid(np.arange(image.shape[1]), np.arange(image.shape[0]))\n", 276 | "row_col = np.stack([rows,cols], axis = 2)\n", 277 | "region_no = 1\n", 278 | "feed = np.zeros((BATCH_SIZE, PATCH_DIM**2*3))\n", 279 | "feed_count = 0\n", 280 | "pixel_count = 0\n", 281 | "predictions = np.zeros(BATCH_SIZE)\n", 282 | "region_count = np.zeros(BATCH_SIZE)\n", 283 | "h = PATCH_DIM/2\n", 284 | "\n", 285 | "begin = time.time()\n", 286 | "start_time = time.time()\n", 287 | "\n", 288 | "with tf.Graph().as_default():\n", 289 | " # Generate placeholders for the images and labels.\n", 290 | " images_placeholder = placeholder_inputs(BATCH_SIZE)\n", 291 | "\n", 292 | " # Build a Graph that computes predictions from the inference model.\n", 293 | " logits = inference(images_placeholder, 1.0, 512)\n", 294 | " sm = softmax(logits)\n", 295 | "\n", 296 | " # Create a saver for writing training checkpoints.\n", 297 | " saver = tf.train.Saver()\n", 298 | "\n", 299 | " # Create a session for running Ops on the Graph.\n", 300 | " with tf.Session() as sess:\n", 301 | " saver.restore(sess, '../Data/model.ckpt')\n", 302 | " for r in regions:\n", 303 | " c = r.coords[0]\n", 304 | " \n", 305 | " pixel_count += len(r.coords)\n", 306 | " if np.mod(pixel_count, 100000) < BATCH_SIZE:\n", 307 | " print \"%d / %d\"%(pixel_count, image.shape[0]*image.shape[1])\n", 308 | " current_time = time.time()\n", 309 | " print \"Time taken - > %f\" % (current_time - start_time)\n", 310 | " start_time = current_time\n", 311 | " \n", 312 | " skip = (c[0]>h+1)&(c[0]h+1)&(c[1] 5 | 6 | FUNDUS Image | Manual Segmentation | Predicted Segmentation 7 | :-------------------------:|:-------------------------:|:-------------------------: 8 | | | 9 | 10 |
11 | ## Proposed Method 12 | 13 | **Ensemble learning** is a technique of using multiple models or experts for solving a particular artificial intelligence problem. Ensemble methods seek to promote diversity among the models they combine and reduce the problem related to overfitting of the training data. The outputs of the individual models of the ensemble are combined (e.g. by averaging) to form the final prediction. 14 | 15 | **Convolutional neural networks** (CNN or ConvNet) are a special category of artificial neural networks designed for processing data with a gridlike structure. The ConvNet architecture is based on sparse interactions and parameter sharing and is highly effective for efficient learning of spatial invariances in images. There are four kinds of layers in a typical ConvNet architecture: convolutional (conv), pooling (pool), fullyconnected (affine) and rectifying linear unit (ReLU). Each convolutional layer transforms one set of feature maps into another set of feature maps by convolution with a set of filters. 16 | 17 | This paper makes an attempt to ameliorate the issue of subjectivity induced bias in feature representation by training an ensemble of 12 Convolutional Neural Networks (ConvNets) on raw color fundus images to discriminate vessel pixels from non-vessel ones. 18 | 19 | **Dataset**: The ensemble of ConvNets is evaluated by learning with the DRIVE training set (image id. 21-40) and 20 | testing over the DRIVE test set (image id. 1-20). 21 | 22 | **Learning mechanism**: Each ConvNet is trained independently on a set of 120000 randomly chosen 3×31×31 patches. 23 | Learning rate was kept constant across models at 5e − 4. Dropout probability and number of hidden units in 24 | the penultimate affine layer of the different models were sampled respectively from U ([0.5, 0.9]) and U ({128, 256, 512}) where U(.) denotes uniform probability distribution over a given range. The models were trained using Adam algorithm with minibatch size 256. Some of these parameters are different from the paper. The user can set some of these parameters using command line arguments which is explained in later sections. 25 | 26 | 27 | 28 |
29 | 30 | ## Architecture 31 | 32 | The ConvNets have the same organization of layers which can be described as: 33 | 34 | **input- [conv - relu]-[conv - relu - pool] x 2 - affine - relu - [affine with dropout] - softmax** 35 | 36 | (Schematic representation below) 37 | 38 | 39 | 40 | The system was trained on a machine with dual Intel Xeon E5-2630 v2 CPUs, 32 GB RAM and NVIDIA Tesla K-20C GPU. Average training time for each model was 3.5 hours (for 10000 epochs). Average inference time for each image was 55 secs on the said machine. 41 | 42 |
43 | ## Some Results 44 | 45 | FUNDUS Image | Magnified Section | Ground Truth | Prediction 46 | :-------------------------:|:-------------------------:|:-------------------------:|:-------------------------: 47 | | | | 48 | | | | 49 | | | | 50 | | | | 51 | 52 | **Note** that in the 3rd image the blood vessels are not easily visible to the human eye but our network does a good job at discerning the fine structure of the vessel. 53 | 54 | The Ensemble of ConvNets efficiently captures the underlying statistics that govern the degree of vesselness of a point in a color fundus image. This is particularly demonstrated in the 4th row, where the Ensemble detects a clinically important condition called [Neovascularization](https://en.wikipedia.org/wiki/Neovascularization) (which we got verified by multiple ophthalmologists) not marked in the ground truth. 55 | 56 | 57 | 58 |
59 |
60 | 61 | ## Setup 62 | 63 | Download the DRIVE dataset from [this link](http://www.isi.uu.nl/Research/Databases/DRIVE/). In order to run this code smoothly without having to change the code, please set up the directory tree in a way similar to the tree structure presented below. 64 | ``` 65 | Project 66 | |-- Data 67 | | |-- DRIVE 68 | | | |-- test 69 | | | | |-- Contains 4 folders 70 | | | |-- training 71 | | | | |-- Contains 4 folders 72 | | |-- models 73 | | | |-- This folder is auto-generated by the code 74 | | | |-- It contains the saved models 75 | | |-- logs 76 | | | |-- Event files necessary for Tensorboard (Auto-generated folder) 77 | |-- Deep-Vessel 78 | | |-- Notebooks 79 | | | |-- Contains necessary notebooks for development 80 | | |-- Scripts 81 | | | |-- Contains scripts needed to preprocess, train and deploy the models 82 | | |-- arglists 83 | | | |-- JSON files with required parameters for each model 84 | | |-- images 85 | | | |-- Contains images for website. You may delete this folder 86 | | |-- README.md 87 | ``` 88 | 89 |
90 | 91 | ## Usage 92 | 93 | ### Scripts 94 | 95 | All these python scripts can be invoked with `--help` to display a brief help message. 96 | 97 | - `Preprocessor.py` crops random pathces from all training images and saves them in a PANDAS DataFrame 98 | - `v2_graph.py` trains a single convolutional network 99 | - `train_ensemble.py` trains an esemble of convolutional networks with different parameters 100 | - `Test.py` decodes the test images 101 | - `ensemble_decoder.py` decodes the test images using an ensemble of different saved models 102 | - `create_json.py` small utility script to create a json file with model args which can be edited later in a text file 103 | - `SuperPixel_Decoder.py` is an experimental fast SuperPixel based decoder, but isn't very accurate and hence is not updated 104 | 105 | Make sure to run these scripts from within the ```Scripts``` folder, otherwise it may throw an ```IOError``` as the paths used are relative 106 | 107 | 108 | ##### Preprocessor.py 109 | 110 | ``` 111 | Usage: Preprocessor.py [OPTIONS] 112 | 113 | Options: 114 | --total_patches TOTAL_PATCHES 115 | Total number of training images/patches to be used [Default - 4800] 116 | --patch_dim PATCH_DIM 117 | Dimension of window to be used as a training patch [Default - 31] 118 | --positive POSITIVE Proportion of positive classes to be kept in training data [Default - 0.5] 119 | ``` 120 | 121 | Example usage: 122 | 123 | We used 124 | ``` 125 | python Preprocessor.py --total_patches 120000 126 | ``` 127 | To save ```30000``` patches with dimension of ```3*31*31``` and a ```4:1``` proportion of positive classes, use 128 | ``` 129 | python Preprocessor.py --total_patches 30000 --patch_dim 15 --positive 0.8 130 | ``` 131 | 132 | ##### v2_graph.py 133 | 134 | ``` 135 | Usage: v2_graph.py [OPTIONS] 136 | 137 | Options: 138 | --batch BATCH Batch Size [Default - 64] 139 | --fchu1 FCHU1 Number of hidden units in FC1 layer [Default - 512] 140 | --learning_rate LEARNING_RATE Learning rate for optimiser [Default - 5e-4] 141 | --training_prop TRAINING_PROP Proportion of data to be used for training data [Default - 0.8] 142 | --max_steps MAX_STEPS Maximum number of iteration till which the program must run [Default - 100] 143 | --checkpoint_step CHECKPOINT_STEP Step after which an evaluation is carried out on validation set and model is saved [Default - 50] 144 | --loss_step LOSS_STEP Step after which loss is printed [Default - 5] 145 | --keep_prob KEEP_PROB Keep Probability for dropout layer [Default - 0.5] 146 | --model_name MODEL_NAME Index of the model [Default - '1'] 147 | ``` 148 | 149 | Example usage: 150 | 151 | We used 152 | ``` 153 | python v2_graph.py --batch 256 --learning_rate 5e-4 --training_prop 0.9 --max_steps 8000 --checkpoint_step 400 --loss_step 25 154 | ``` 155 | 156 | 157 | ##### train_ensemble.py 158 | 159 | ``` 160 | Usage: train_ensemble.py [OPTIONS] 161 | 162 | Options: 163 | --a A Path to JSON file containing model arguments 164 | ``` 165 | 166 | Example usage: 167 | 168 | We used 169 | ``` 170 | python train_ensemble.py --a ../arglists/heavy.json 171 | ``` 172 | 173 | 174 | ##### Test.py 175 | 176 | ``` 177 | Usage: Test.py [OPTIONS] 178 | 179 | Options: 180 | --fchu1 FCHU1 Number of hidden units in FC1 layer. This should be identical to the one used in the model 181 | [Default - 256] 182 | --out OUT Directory to put rendered images to 183 | --inp INP Directory containing images for testing 184 | --model MODEL Path to the saved tensorflow model checkpoint 185 | --format FORMAT Format to save the images in. [Available formats: npz, jpg and png] 186 | 187 | ``` 188 | Example usage: 189 | 190 | We used 191 | ``` 192 | python Test.py --fchu1 512 --format png --out ../../Data/DRIVE/tmp/ --inp ../../Data/DRIVE/test/ --model ../../Data/models/model1/model.ckpt-7999 193 | 194 | ``` 195 | 196 | ##### ensemble_decoder.py 197 | 198 | ``` 199 | Usage: ensemble_decoder.py [OPTIONS] 200 | 201 | Options: 202 | --a A Path to JSON file containing model arguments 203 | --m M Path to JSON file containing saved model paths 204 | --out OUT Directory to put rendered images to 205 | --inp INP Directory containing images for testing 206 | 207 | ``` 208 | 209 | Example usage: 210 | 211 | We used 212 | ``` 213 | python ensemble_decoder.py --m ../arglists/model_paths_heavy.json --a ../arglists/heavy.json --out ../../Data/DRIVE/ensemble_test_results --inp ../../Data/DRIVE/test/ 214 | 215 | ``` 216 | 217 |
218 | 219 | ### Configuration 220 | 221 | The ```arglists/``` folder contains JSON files that store necessary command line arguments. It is human readable and hence can be edited easily by a user. 222 | 223 | ##### heavy.json 224 | 225 | This is necessary for training the ensemble and needs to be passed to ```train_ensemble.py```. It contains the list of arguments that needs to be passed to ```v2_graph.py``` for every model. As the name suggests, this configuration took us 2 days to run on a server, and unless you have a powerful GPU, I would suggest editing this file before running it. 226 | 227 | ##### model\_paths_heavy.json 228 | 229 | The models saved by training the ensemble are saved as checkpoints. This is a simple file that stores the paths to the best checkpoints for each model. 230 | 231 |
232 | ### Acknowledgement 233 | 234 | This repository is a TensorFlow re-implementation by [Ankush Chatterjee](https://in.linkedin.com/in/ankushchatterjee) [during his internship with [Anirban Santara](http://santara.github.io/) and [Pabitra Mitra](http://cse.iitkgp.ac.in/~pabitra/) at the [Department of Computer Science and Engineering, IIT Kharagpur](http://cse.iitkgp.ac.in/) during the summer of 2016] of the work done by [Debapriya Maji](https://www.linkedin.com/in/debapriya-maji-a66594102), [Anirban Santara](http://santara.github.io/), [Pabitra Mitra](http://cse.iitkgp.ac.in/~pabitra/) and [Debdoot Sheet](http://www.facweb.iitkgp.ernet.in/~debdoot/). Check out the original paper (http://arxiv.org/abs/1603.04833) for more details. The authors would like to thank [Dr. Sujoy Kar](https://www.linkedin.com/in/drsujoykar), Dr. Tapas Paul and Dr. Asim Kumar Kandar from [Apollo Gleneagles Hospitals Kolkata](http://kolkata.apollohospitals.com/) for valuable discussions in course of development of this system. 235 | -------------------------------------------------------------------------------- /Scripts/Preprocessor.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | import pandas as pd 4 | from skimage import io 5 | from skimage.util import img_as_float, img_as_ubyte 6 | import os 7 | import glob 8 | import time 9 | import sys 10 | import argparse 11 | 12 | 13 | # In[2]: 14 | 15 | def get_path(directory): 16 | """ Gets the filenames of all training, mask and ground truth images in the given 17 | directory 18 | Args: 19 | directory: The path to the root folder 20 | Output: 21 | imgs: List of paths to files containing images 22 | mask: List of paths to files containing masks of the images 23 | gt: List of paths to files containing corresponding ground truth images 24 | """ 25 | imgs = glob.glob(directory + '/images/*.tif') 26 | imgs.sort() 27 | #a = [x.split('/')[-1].split('.')[0] for x in train] 28 | 29 | mask = glob.glob(directory + '/mask/*.gif') 30 | mask.sort() 31 | #b = [x.split('/')[-1].split('.')[0] for x in mask] 32 | 33 | gt = glob.glob(directory + '/1st_manual/*.gif') 34 | gt.sort() 35 | #c = [x.split('/')[-1].split('.')[0] for x in gt] 36 | 37 | return map(os.path.abspath, imgs), map(os.path.abspath, mask), map(os.path.abspath, gt) 38 | 39 | 40 | # In[3]: 41 | 42 | # Hyper Params 43 | total_patches = 4800 44 | num_training_images = None 45 | patches_per_image = None 46 | patch_dim = 31 # Dimension of window used for training 47 | current_img_index = -1 # Index of the current image in 'train' 48 | current_img = None 49 | current_mask = None 50 | current_gt = None 51 | positive_proprtion = 0.5 52 | 53 | df = None 54 | 55 | 56 | # In[4]: 57 | 58 | def load_next_img(data,mask_data,gt_data): 59 | """When we have extracted 'PATCHES_PER_IMAGE' number of patches from our 60 | current image we call this function to change the current image 61 | Args: 62 | data: The list of paths to the images 63 | mask_data: List of paths to the corresponding masks of images 64 | gt_data: List of paths to the corresponding ground truth images 65 | 66 | """ 67 | global current_img_index, current_img, current_mask, current_gt 68 | 69 | if current_img_index < len(data)-1: 70 | current_img_index +=1 71 | print "Working on image %d"%(current_img_index + 1) 72 | current_img = io.imread(data[current_img_index]) 73 | current_mask = img_as_float(io.imread(mask_data[current_img_index])) 74 | current_gt = img_as_float(io.imread(gt_data[current_img_index])) 75 | return True 76 | else: 77 | print 'No more images left in set' 78 | return False 79 | 80 | 81 | # In[5]: 82 | 83 | def save_img_data(data, mask_data, gt_data): 84 | """Extracts PATCHES_PER_IMAGE number of patches from each image 85 | 86 | It maintains a count of positive and negative patches and maintains 87 | the ratio POSITIVE_PROPORTION = pos/(pos+neg) 88 | Args: 89 | data: The list of paths to the images 90 | mask_data: List of paths to the corresponding masks of images 91 | gt_data: List of paths to the corresponding ground truth images 92 | 93 | """ 94 | pos_count = 0 95 | neg_count = 0 96 | global df 97 | while pos_count +neg_count < patches_per_image: 98 | # Choose a random point 99 | i = np.random.randint(patch_dim/2,current_img.shape[0]-patch_dim/2) 100 | j = np.random.randint(patch_dim/2,current_img.shape[1]-patch_dim/2) 101 | h = (patch_dim - 1)/2 102 | if int(np.sum(current_mask[i-h:i+h+1,j-h:j+h+1])/patch_dim**2) == 1: 103 | ind = current_img_index*patches_per_image+pos_count+neg_count 104 | 105 | # If a positive sample is found and positive count hasn't reached its limit 106 | if int(current_gt[i,j])==1 and pos_count < positive_proprtion*patches_per_image: 107 | df.loc[ind][0:-1] = np.reshape(current_img[i-h:i+h+1,j-h:j+h+1], -1) 108 | df.loc[ind][patch_dim**2*3] = int(current_gt[i,j]) 109 | pos_count += 1 110 | # If a negative sample is found and negative count hasn't reached its limit 111 | elif int(current_gt[i,j])==0 and neg_count < (1-positive_proprtion)*patches_per_image: 112 | df.loc[ind][0:-1] = np.reshape(current_img[i-h:i+h+1,j-h:j+h+1], -1) 113 | df.loc[ind][patch_dim**2*3] = int(current_gt[i,j]) 114 | neg_count += 1 115 | 116 | 117 | # In[6]: 118 | 119 | def finish_parsing(): 120 | parser = argparse.ArgumentParser(description= 121 | 'Python script to save window patches for training') 122 | parser.add_argument("--total_patches", type=int, 123 | help="Total number of training images/patches to be used [Default - 4800]") 124 | parser.add_argument("--patch_dim", type=int, 125 | help="Dimension of window to be used as a training patch [Default - 31]") 126 | parser.add_argument("--positive", type=float, 127 | help="Proportion of positive classes to be kept in training data [Default - 0.5]") 128 | 129 | args = parser.parse_args() 130 | 131 | global total_patches, patch_dim, positive_proprtion 132 | if args.total_patches is not None: 133 | total_patches = args.total_patches 134 | print "New total patches = %d" % total_patches 135 | if args.patch_dim is not None: 136 | patch_dim = args.patch_dim 137 | print "New patch_dim = %d" % patch_dim 138 | if args.positive is not None: 139 | positive_proprtion = args.positive 140 | print "New positive_proprtion = %.2f" % positive_proprtion 141 | 142 | 143 | 144 | # In[7]: 145 | 146 | def main(): 147 | 148 | finish_parsing() 149 | 150 | train, mask_train, gt_train = get_path('../../Data/DRIVE/training') 151 | test, mask_test, mask_gt = get_path('../../Data/DRIVE/test') 152 | 153 | # Redefining some hyperparams and global variables 154 | global num_training_images, patches_per_image, current_img, current_mask, current_gt 155 | num_training_images = len(train) 156 | patches_per_image = total_patches/num_training_images 157 | current_img = io.imread(train[0]) 158 | current_mask = img_as_float(io.imread(mask_train[0])) 159 | current_gt = img_as_float(io.imread(gt_train[0])) 160 | 161 | begin = time.time() 162 | print "Creating DataFrame" 163 | global df 164 | df = pd.DataFrame(index=np.arange(total_patches), columns = np.arange(patch_dim**2*3+1)) 165 | print "Dataframe ready" 166 | 167 | while load_next_img(train, mask_train, gt_train): 168 | start = time.time() 169 | save_img_data(train,mask_train, gt_train) 170 | print "Time taken for this image = %f secs" %( (time.time()-start)) 171 | 172 | print "\nMean Normalising\n" 173 | last = len(df.columns) -1 174 | mean_img = np.mean(df)[:-1] 175 | labels = df[last] 176 | mean_normalised_df = df - np.mean(df) 177 | mean_normalised_df[last] = labels 178 | 179 | print "Randomly shuffling the datasets\n" 180 | mean_normalised_df = mean_normalised_df.iloc[np.random.permutation(len(df))] 181 | mean_normalised_df = mean_normalised_df.reset_index(drop=True) 182 | 183 | print "Writing to pickle\n" 184 | mean_normalised_df.to_pickle('../../Data/mean_normalised_df_no_class_bias.pkl') 185 | mean_img.to_pickle('../../Data/mean_img_no_class_bias.pkl') 186 | 187 | print "Total time taken = %f mins\n" %( (time.time()-begin)/60.0) 188 | 189 | 190 | # In[8]: 191 | 192 | if __name__ == "__main__": 193 | main() 194 | 195 | -------------------------------------------------------------------------------- /Scripts/SuperPixel_Decoder.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[1]: 5 | 6 | from __future__ import division 7 | import numpy as np 8 | import pandas as pd 9 | import os 10 | import glob 11 | from skimage import io, color, measure 12 | from skimage.util import img_as_float, img_as_ubyte 13 | from skimage.segmentation import slic, mark_boundaries 14 | import tensorflow as tf 15 | get_ipython().magic(u'matplotlib inline') 16 | import time 17 | from six.moves import xrange 18 | 19 | 20 | # In[2]: 21 | 22 | mean_img = pd.read_pickle('../Data/mean_img_no_class_bias.pkl') 23 | 24 | 25 | # In[12]: 26 | 27 | PATCH_DIM = 31 28 | BATCH_SIZE = 100 # Must be a perfect square 29 | NUM_CLASSES = 2 30 | SP_COMPACTNESS = 1 31 | SP_SIGMA = 1 32 | NUM_SP=10000 33 | 34 | 35 | # In[4]: 36 | 37 | def get_path(directory): 38 | imgs = glob.glob(directory + '/images/*.tif') 39 | imgs.sort() 40 | #a = [x.split('/')[-1].split('.')[0] for x in train] 41 | 42 | mask = glob.glob(directory + '/mask/*.gif') 43 | mask.sort() 44 | #b = [x.split('/')[-1].split('.')[0] for x in mask] 45 | 46 | gt = glob.glob(directory + '/1st_manual/*.gif') 47 | gt.sort() 48 | #c = [x.split('/')[-1].split('.')[0] for x in gt] 49 | 50 | return map(os.path.abspath, imgs), map(os.path.abspath, mask), map(os.path.abspath, gt) 51 | 52 | train, mask_train, gt_train = get_path('../Data/DRIVE/training') 53 | test, mask_test, mask_gt = get_path('../Data/DRIVE/test') 54 | 55 | 56 | # In[5]: 57 | 58 | def inference(images, keep_prob, fc_hidden_units1=512): 59 | """ Builds the model as far as is required for running the network 60 | forward to make predictions. 61 | 62 | Args: 63 | images: Images placeholder, from inputs(). 64 | keep_prob: Probability used for Droupout in the final Affine Layer 65 | fc_hidden_units1: Number of hidden neurons in final Affine layer 66 | Returns: 67 | softmax_linear: Output tensor with the computed logits. 68 | """ 69 | with tf.variable_scope('h_conv1') as scope: 70 | weights = tf.get_variable('weights', shape=[4, 4, 3, 64], 71 | initializer=tf.contrib.layers.xavier_initializer_conv2d()) 72 | biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05)) 73 | 74 | # Flattening the 3D image into a 1D array 75 | x_image = tf.reshape(images, [-1,PATCH_DIM,PATCH_DIM,3]) 76 | z = tf.nn.conv2d(x_image, weights, strides=[1, 1, 1, 1], padding='VALID') 77 | h_conv1 = tf.nn.relu(z+biases, name=scope.name) 78 | with tf.variable_scope('h_conv2') as scope: 79 | weights = tf.get_variable('weights', shape=[4, 4, 64, 64], 80 | initializer=tf.contrib.layers.xavier_initializer_conv2d()) 81 | biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05)) 82 | z = tf.nn.conv2d(h_conv1, weights, strides=[1, 1, 1, 1], padding='SAME') 83 | h_conv2 = tf.nn.relu(z+biases, name=scope.name) 84 | 85 | h_pool1 = tf.nn.max_pool(h_conv2, ksize=[1, 2, 2, 1], 86 | strides=[1, 2, 2, 1], padding='SAME', name='h_pool1') 87 | 88 | with tf.variable_scope('h_conv3') as scope: 89 | weights = tf.get_variable('weights', shape=[4, 4, 64, 64], 90 | initializer=tf.contrib.layers.xavier_initializer_conv2d()) 91 | biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.05)) 92 | z = tf.nn.conv2d(h_pool1, weights, strides=[1, 1, 1, 1], padding='SAME') 93 | h_conv3 = tf.nn.relu(z+biases, name=scope.name) 94 | 95 | h_pool2 = tf.nn.max_pool(h_conv3, ksize=[1, 2, 2, 1], 96 | strides=[1, 2, 2, 1], padding='SAME', name='h_pool2') 97 | 98 | with tf.variable_scope('h_fc1') as scope: 99 | weights = tf.get_variable('weights', shape=[7**2*64, fc_hidden_units1], 100 | initializer=tf.contrib.layers.xavier_initializer()) 101 | biases = tf.get_variable('biases', shape=[fc_hidden_units1], initializer=tf.constant_initializer(0.05)) 102 | h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) 103 | 104 | h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, weights) + biases, name = 'h_fc1') 105 | h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) 106 | 107 | 108 | with tf.variable_scope('h_fc2') as scope: 109 | weights = tf.get_variable('weights', shape=[fc_hidden_units1, NUM_CLASSES], 110 | initializer=tf.contrib.layers.xavier_initializer()) 111 | biases = tf.get_variable('biases', shape=[NUM_CLASSES]) 112 | 113 | logits = (tf.matmul(h_fc1_drop, weights) + biases) 114 | return logits 115 | 116 | 117 | # In[45]: 118 | 119 | def softmax(logits): 120 | return tf.nn.softmax(logits) 121 | 122 | 123 | # In[46]: 124 | 125 | def placeholder_inputs(batch_size): 126 | """Generate placeholder variables to represent the input tensors. 127 | Args: 128 | batch_size: The batch size will be baked into both placeholders. 129 | Returns: 130 | images_placeholder: Images placeholder. 131 | """ 132 | images_placeholder = tf.placeholder(tf.float32, shape=(batch_size, PATCH_DIM**2*3)) 133 | return images_placeholder 134 | 135 | 136 | # In[88]: 137 | 138 | def nbd(image, point): 139 | i = point[0] 140 | j = point[1] 141 | h = int(PATCH_DIM/2) 142 | return image[i-h:i+h+1,j-h:j+h+1].reshape(-1) 143 | def segment_region(segmented, row_col, segments_slic, region_id, prediction): 144 | a = row_col[segments_slic==region_id] 145 | segmented[a[:,0],a[:,1]] = prediction 146 | 147 | 148 | # In[7]: 149 | 150 | image = io.imread(train[0]) 151 | mask = img_as_float(io.imread(mask_train[0])) 152 | gt = img_as_float(io.imread(gt_train[0])) 153 | mean_np_img = np.asarray(mean_img) 154 | 155 | 156 | # In[25]: 157 | 158 | segments_slic = slic(image, n_segments=NUM_SP, compactness=SP_COMPACTNESS, 159 | sigma= SP_SIGMA, convert2lab = True) 160 | segments_slic = segments_slic + 1 # So that no labelled region is 0 and ignored by regionprops 161 | regions = measure.regionprops(segments_slic) 162 | 163 | 164 | # In[26]: 165 | 166 | #sp_image = mark_boundaries(image, segments_slic,[0,0,0]) 167 | 168 | 169 | # In[93]: 170 | 171 | segmented = np.zeros(image.shape[:2]) 172 | # We will use arrays to index the image and mask later 173 | cols, rows = np.meshgrid(np.arange(image.shape[1]), np.arange(image.shape[0])) 174 | row_col = np.stack([rows,cols], axis = 2) 175 | region_no = 1 176 | feed = np.zeros((BATCH_SIZE, PATCH_DIM**2*3)) 177 | feed_count = 0 178 | pixel_count = 0 179 | predictions = np.zeros(BATCH_SIZE) 180 | region_count = np.zeros(BATCH_SIZE) 181 | h = PATCH_DIM/2 182 | 183 | begin = time.time() 184 | start_time = time.time() 185 | 186 | with tf.Graph().as_default(): 187 | # Generate placeholders for the images and labels. 188 | images_placeholder = placeholder_inputs(BATCH_SIZE) 189 | 190 | # Build a Graph that computes predictions from the inference model. 191 | logits = inference(images_placeholder, 1.0, 512) 192 | sm = softmax(logits) 193 | 194 | # Create a saver for writing training checkpoints. 195 | saver = tf.train.Saver() 196 | 197 | # Create a session for running Ops on the Graph. 198 | with tf.Session() as sess: 199 | saver.restore(sess, '../Data/model.ckpt') 200 | for r in regions: 201 | c = r.coords[0] 202 | 203 | pixel_count += len(r.coords) 204 | if np.mod(pixel_count, 100000) < BATCH_SIZE: 205 | print "%d / %d"%(pixel_count, image.shape[0]*image.shape[1]) 206 | current_time = time.time() 207 | print "Time taken - > %f" % (current_time - start_time) 208 | start_time = current_time 209 | 210 | skip = (c[0]>h+1)&(c[0]h+1)&(c[1]