From 2da3b8292b4a2fb73b8aadb15baf0c093a5fddd6 Mon Sep 17 00:00:00 2001 From: Juan C Date: Mon, 4 Feb 2019 20:25:37 +0000 Subject: [PATCH] Initial commit --- .../sort_images-checkpoint.ipynb | 191 ++ CellProfiler/README.md | 14 + .../bbbc039_set/segmentation_cp3.cppipe | 217 ++ .../van_valen_set/pipelines/van_valen.cppipe | 152 ++ .../van_valen_set/pipelines/van_valen.cpproj | Bin 0 -> 410320 bytes .../pipelines/van_valen_bright_and_dim.cppipe | 349 +++ .../pipelines/van_valen_bright_and_dim.cpproj | Bin 0 -> 441344 bytes .../van_valen_with_micronuclei.cppipe | 183 ++ .../van_valen_with_micronuclei.cpproj | Bin 0 -> 411584 bytes .../van_valen_set/software/sort_images.ipynb | 191 ++ README.md | 52 + cell-cycle/Compound_Images_List.ipynb | 1919 +++++++++++++++++ cell-cycle/README.md | 8 + cell-cycle/Results.ipynb | 858 ++++++++ classic-ml/README.md | 48 + .../empty_project_3_labels.ilp | Bin 0 -> 649460 bytes ...abels_0.10_0.10_0.50_sampling_imported.ilp | Bin 0 -> 6176 bytes classic-ml/src/ilastik/__init__.py | 0 .../src/ilastik/absolutize_input_path.py | 29 + .../ilastik/import_training_from_external.py | 36 + classic-ml/src/ilastik_import_script.py | 47 + examples/bbbc039_seg_config.py | 108 + examples/combined_set_config.py | 108 + examples/vanvalen_data_config.py | 108 + unet4nuclei/00-download-dataset.ipynb | 180 ++ unet4nuclei/01-preprocessing.ipynb | 342 +++ unet4nuclei/02-training.ipynb | 227 ++ unet4nuclei/03-prediction.ipynb | 204 ++ unet4nuclei/04-evaluation.ipynb | 335 +++ unet4nuclei/batch_prediction.py | 176 ++ unet4nuclei/config.py | 108 + unet4nuclei/elastic-deformation.ipynb | 186 ++ unet4nuclei/full-experiment.ipynb | 166 ++ unet4nuclei/utils/__init__.py | 0 unet4nuclei/utils/augmentation.py | 46 + unet4nuclei/utils/contours_to_labels.ipynb | 316 +++ unet4nuclei/utils/data_provider.py | 157 ++ unet4nuclei/utils/dirtools.py | 103 + unet4nuclei/utils/evaluation.py | 114 + unet4nuclei/utils/experiment.py | 220 ++ unet4nuclei/utils/metrics.py | 180 ++ unet4nuclei/utils/model_builder.py | 98 + unet4nuclei/utils/objectives.py | 18 + unet4nuclei/utils/preprocessing.py | 20 + unet4nuclei/utils/visualize.py | 274 +++ 45 files changed, 8088 insertions(+) create mode 100644 CellProfiler/.ipynb_checkpoints/sort_images-checkpoint.ipynb create mode 100644 CellProfiler/README.md create mode 100644 CellProfiler/bbbc039_set/segmentation_cp3.cppipe create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen.cppipe create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen.cpproj create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cppipe create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cpproj create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen_with_micronuclei.cppipe create mode 100644 CellProfiler/van_valen_set/pipelines/van_valen_with_micronuclei.cpproj create mode 100644 CellProfiler/van_valen_set/software/sort_images.ipynb create mode 100644 README.md create mode 100644 cell-cycle/Compound_Images_List.ipynb create mode 100644 cell-cycle/README.md create mode 100644 cell-cycle/Results.ipynb create mode 100644 classic-ml/README.md create mode 100644 classic-ml/ilastik-projects/empty_project_3_labels.ilp create mode 100644 classic-ml/ilastik-projects/test_3_labels_0_3_labels_0.10_0.10_0.50_sampling_imported.ilp create mode 100644 classic-ml/src/ilastik/__init__.py create mode 100644 classic-ml/src/ilastik/absolutize_input_path.py create mode 100644 classic-ml/src/ilastik/import_training_from_external.py create mode 100644 classic-ml/src/ilastik_import_script.py create mode 100644 examples/bbbc039_seg_config.py create mode 100644 examples/combined_set_config.py create mode 100644 examples/vanvalen_data_config.py create mode 100644 unet4nuclei/00-download-dataset.ipynb create mode 100644 unet4nuclei/01-preprocessing.ipynb create mode 100644 unet4nuclei/02-training.ipynb create mode 100644 unet4nuclei/03-prediction.ipynb create mode 100644 unet4nuclei/04-evaluation.ipynb create mode 100644 unet4nuclei/batch_prediction.py create mode 100644 unet4nuclei/config.py create mode 100644 unet4nuclei/elastic-deformation.ipynb create mode 100644 unet4nuclei/full-experiment.ipynb create mode 100644 unet4nuclei/utils/__init__.py create mode 100644 unet4nuclei/utils/augmentation.py create mode 100644 unet4nuclei/utils/contours_to_labels.ipynb create mode 100644 unet4nuclei/utils/data_provider.py create mode 100644 unet4nuclei/utils/dirtools.py create mode 100644 unet4nuclei/utils/evaluation.py create mode 100644 unet4nuclei/utils/experiment.py create mode 100644 unet4nuclei/utils/metrics.py create mode 100644 unet4nuclei/utils/model_builder.py create mode 100644 unet4nuclei/utils/objectives.py create mode 100644 unet4nuclei/utils/preprocessing.py create mode 100644 unet4nuclei/utils/visualize.py diff --git a/CellProfiler/.ipynb_checkpoints/sort_images-checkpoint.ipynb b/CellProfiler/.ipynb_checkpoints/sort_images-checkpoint.ipynb new file mode 100644 index 0000000..05044f8 --- /dev/null +++ b/CellProfiler/.ipynb_checkpoints/sort_images-checkpoint.ipynb @@ -0,0 +1,191 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import os\n", + "\n", + "from pprint import pprint\n", + "\n", + "import skimage.io\n", + "import sklearn.cluster\n", + "import sklearn.preprocessing" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "image_list = glob.glob(\"../images/*.png\")" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[array([1382400, 0, 0, 0, 0, 0, 0,\n", + " 0, 0, 0], dtype=int64),\n", + " array([1382400, 0, 0, 0, 0, 0, 0,\n", + " 0, 0, 0], dtype=int64),\n", + " array([1376628, 4927, 697, 110, 23, 15, 0,\n", + " 0, 0, 0], dtype=int64)]\n", + "'break'\n", + "array([[1382400, 0, 0, 0, 0, 0, 0,\n", + " 0, 0, 0],\n", + " [1382400, 0, 0, 0, 0, 0, 0,\n", + " 0, 0, 0],\n", + " [1376628, 4927, 697, 110, 23, 15, 0,\n", + " 0, 0, 0]], dtype=int64)\n" + ] + } + ], + "source": [ + "category_list = []\n", + "hist_list = []\n", + "\n", + "for im_name in image_list:\n", + " im = skimage.io.imread(im_name)\n", + " data, _ = np.histogram(im, range=(0,32767))\n", + " hist_list.append(data)\n", + "\n", + "hist_list = np.asarray(hist_list)\n", + "pprint(hist_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "kmeans = sklearn.cluster.KMeans(n_clusters=2, random_state=0).fit(hist_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 0, 1])" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "kmeans.labels_" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\karhohs\\AppData\\Local\\Continuum\\Miniconda3\\envs\\bioimg\\lib\\site-packages\\skimage\\io\\_io.py:132: UserWarning: ../images/class_0\\testing_0.png is a low contrast image\n", + " warn('%s is a low contrast image' % fname)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\karhohs\\AppData\\Local\\Continuum\\Miniconda3\\envs\\bioimg\\lib\\site-packages\\skimage\\io\\_io.py:132: UserWarning: ../images/class_0\\testing_1.png is a low contrast image\n", + " warn('%s is a low contrast image' % fname)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\karhohs\\AppData\\Local\\Continuum\\Miniconda3\\envs\\bioimg\\lib\\site-packages\\skimage\\io\\_io.py:132: UserWarning: ../images/class_1\\testing_2.png is a low contrast image\n", + " warn('%s is a low contrast image' % fname)\n" + ] + } + ], + "source": [ + "os.makedirs(\"../images/class_0\", exist_ok = True)\n", + "os.makedirs(\"../images/class_1\", exist_ok = True)\n", + "\n", + "for ind, label in enumerate(kmeans.labels_):\n", + " im = skimage.io.imread(image_list[ind])\n", + " _, filename = os.path.split(image_list[ind])\n", + " if label==0:\n", + " skimage.io.imsave(os.path.join(\"../images/class_0\", filename), im)\n", + " if label==1:\n", + " skimage.io.imsave(os.path.join(\"../images/class_1\", filename), im)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/CellProfiler/README.md b/CellProfiler/README.md new file mode 100644 index 0000000..2b8d593 --- /dev/null +++ b/CellProfiler/README.md @@ -0,0 +1,14 @@ +# A note about the software and hardware used to run the pipeline + +By Kyle W. Karhohs @karhohs + +## software +* CellProfiler, v3.0.0 +* OS: Microsoft Windows 10 Pro x64 + +## hardware +* Dell XPS 15 9550 +* Intel Core i7-6700HQ CPU @ 2.60GHz 4 Cores 8 Logical Processors +* 16 GB Physical Memory/RAM + + diff --git a/CellProfiler/bbbc039_set/segmentation_cp3.cppipe b/CellProfiler/bbbc039_set/segmentation_cp3.cppipe new file mode 100644 index 0000000..bb2c7f7 --- /dev/null +++ b/CellProfiler/bbbc039_set/segmentation_cp3.cppipe @@ -0,0 +1,217 @@ +CellProfiler Pipeline: http://www.cellprofiler.org +Version:3 +DateRevision:300 +GitHash: +ModuleCount:15 +HasImagePlaneDetails:False + +Images:[module_num:1|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'To begin creating your project, use the Images module to compile a list of files and/or folders that you want to analyze. You can also specify a set of rules to include only the desired files in your selected folders.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + : + Filter images?:Images only + Select the rule criteria:and (extension does isimage) (directory doesnot containregexp "\x5B\\\\\\\\\\\\\\\\/\x5D\\\\\\\\.") + +Metadata:[module_num:2|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\'The Metadata module optionally allows you to extract information describing your images (i.e, metadata) which will be stored along with your measurements. This information can be contained in the file name and/or location, or in an external file.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Extract metadata?:No + Metadata data type:Text + Metadata types:{} + Extraction method count:1 + Metadata extraction method:Extract from file/folder names + Metadata source:File name + Regular expression to extract from file name:^(?P.*)_(?P\x5BA-P\x5D\x5B0-9\x5D{2})_s(?P\x5B0-9\x5D)_w(?P\x5B0-9\x5D) + Regular expression to extract from folder name:(?P\x5B0-9\x5D{4}_\x5B0-9\x5D{2}_\x5B0-9\x5D{2})$ + Extract metadata from:All images + Select the filtering criteria:and (file does contain "") + Metadata file location: + Match file and image metadata:\x5B\x5D + Use case insensitive matching?:No + +NamesAndTypes:[module_num:3|svn_version:\'Unknown\'|variable_revision_number:8|show_window:False|notes:\x5B\'The NamesAndTypes module allows you to assign a meaningful name to each image by which other modules will refer to it.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Assign a name to:All images + Select the image type:Grayscale image + Name to assign these images:DNA + Match metadata:\x5B\x5D + Image set matching method:Order + Set intensity range from:Image metadata + Assignments count:1 + Single images count:0 + Maximum intensity:255.0 + Process as 3D?:No + Relative pixel spacing in X:1.0 + Relative pixel spacing in Y:1.0 + Relative pixel spacing in Z:1.0 + Select the rule criteria:and (file does contain "") + Name to assign these images:DNA + Name to assign these objects:Cell + Select the image type:Grayscale image + Set intensity range from:Image metadata + Maximum intensity:255.0 + +Groups:[module_num:4|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'The Groups module optionally allows you to split your list of images into image subsets (groups) which will be processed independently of each other. Examples of groupings include screening batches, microtiter plates, time-lapse movies, etc.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Do you want to group your images?:No + grouping metadata count:1 + Metadata category:None + +Resize:[module_num:5|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:DNA_downsample + Resizing method:Resize by a fraction or multiple of the original size + Resizing factor:0.5 + Width of the final image:100 + Height of the final image:100 + Interpolation method:Nearest Neighbor + Method to specify the dimensions:Manual + Select the image with the desired dimensions:None + Additional image count:0 + +Opening:[module_num:6|svn_version:\'Unknown\'|variable_revision_number:1|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA_downsample + Name the output image:DNA_opening_downsample + Structuring element:disk,35 + +Resize:[module_num:7|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA_opening_downsample + Name the output image:DNA_opening + Resizing method:Resize by a fraction or multiple of the original size + Resizing factor:2.0 + Width of the final image:100 + Height of the final image:100 + Interpolation method:Nearest Neighbor + Method to specify the dimensions:Manual + Select the image with the desired dimensions:None + Additional image count:0 + +ImageMath:[module_num:8|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Operation:Subtract + Raise the power of the result by:1.0 + Multiply the result by:1.0 + Add to result:0.0 + Set values less than 0 equal to 0?:Yes + Set values greater than 1 equal to 1?:Yes + Ignore the image masks?:No + Name the output image:DNA_subtract_background + Image or measurement?:Image + Select the first image:DNA + Multiply the first image by:1.0 + Measurement: + Image or measurement?:Image + Select the second image:DNA_opening + Multiply the second image by:1.0 + Measurement: + +Closing:[module_num:9|svn_version:\'Unknown\'|variable_revision_number:1|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA_subtract_background + Name the output image:DNA_Closing + Structuring element:disk,9 + +EnhanceEdges:[module_num:10|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA_Closing + Name the output image:DNA_Edged + Automatically calculate the threshold?:Yes + Absolute threshold:0.2 + Threshold adjustment factor:1.0 + Select an edge-finding method:Sobel + Select edge direction to enhance:All + Calculate Gaussian\'s sigma automatically?:Yes + Gaussian\'s sigma value:10.0 + Calculate value for low threshold automatically?:Yes + Low threshold value:0.1 + +ImageMath:[module_num:11|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Operation:Subtract + Raise the power of the result by:1.0 + Multiply the result by:1.0 + Add to result:0.0 + Set values less than 0 equal to 0?:Yes + Set values greater than 1 equal to 1?:Yes + Ignore the image masks?:No + Name the output image:DNA_subtract_edges_background + Image or measurement?:Image + Select the first image:DNA_subtract_background + Multiply the first image by:1.0 + Measurement: + Image or measurement?:Image + Select the second image:DNA_Edged + Multiply the second image by:1.0 + Measurement: + +IdentifyPrimaryObjects:[module_num:12|svn_version:\'Unknown\'|variable_revision_number:13|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA_subtract_edges_background + Name the primary objects to be identified:Nuclei + Typical diameter of objects, in pixel units (Min,Max):3,60 + Discard objects outside the diameter range?:Yes + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:Shape + Method to draw dividing lines between clumped objects:Intensity + Size of smoothing filter:15 + Suppress local maxima that are closer than this minimum allowed distance:10 + Speed up by using lower-resolution image to find local maxima?:No + Fill holes in identified objects?:After both thresholding and declumping + Automatically calculate size of smoothing filter for declumping?:No + Automatically calculate minimum allowed distance between local maxima?:No + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Minimum cross entropy + Threshold smoothing scale:1.0 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0005,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +ConvertObjectsToImage:[module_num:13|svn_version:\'Unknown\'|variable_revision_number:1|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input objects:Nuclei + Name the output image:NucleiMask + Select the color format:Grayscale + Select the colormap:Default + +SaveImages:[module_num:14|svn_version:\'Unknown\'|variable_revision_number:13|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the type of image to save:Image + Select the image to save:NucleiMask + Select method for constructing file names:From image filename + Select image name for file prefix:DNA + Enter single file name:OrigBlue + Number of digits:4 + Append a suffix to the image file name?:No + Text to append to the image name:_mask + Saved file format:tiff + Output file location:Default Output Folder sub-folder\x7Cmask + Image bit depth:16-bit integer + Overwrite existing files without warning?:Yes + When to save:Every cycle + Record the file and path information to the saved image?:No + Create subfolders in the output folder?:No + Base image folder:Elsewhere...\x7C + +ExportToSpreadsheet:[module_num:15|svn_version:\'Unknown\'|variable_revision_number:12|show_window:False|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the column delimiter:Comma (",") + Add image metadata columns to your object data file?:No + Select the measurements to export:No + Calculate the per-image mean values for object measurements?:No + Calculate the per-image median values for object measurements?:No + Calculate the per-image standard deviation values for object measurements?:No + Output file location:Default Output Folder\x7C + Create a GenePattern GCT file?:No + Select source of sample row name:Metadata + Select the image to use as the identifier:None + Select the metadata to use as the identifier:None + Export all measurement types?:Yes + Press button to select measurements: + Representation of Nan/Inf:NaN + Add a prefix to file names?:Yes + Filename prefix:bbbc022_ + Overwrite existing files without warning?:No + Data to export:Do not use + Combine these object measurements with those of the previous object?:No + File name:DATA.csv + Use the object name for the file name?:Yes diff --git a/CellProfiler/van_valen_set/pipelines/van_valen.cppipe b/CellProfiler/van_valen_set/pipelines/van_valen.cppipe new file mode 100644 index 0000000..a30395e --- /dev/null +++ b/CellProfiler/van_valen_set/pipelines/van_valen.cppipe @@ -0,0 +1,152 @@ +CellProfiler Pipeline: http://www.cellprofiler.org +Version:3 +DateRevision:300 +GitHash: +ModuleCount:11 +HasImagePlaneDetails:False + +Images:[module_num:1|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'To begin creating your project, use the Images module to compile a list of files and/or folders that you want to analyze. You can also specify a set of rules to include only the desired files in your selected folders.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + : + Filter images?:Images only + Select the rule criteria:and (extension does isimage) (directory doesnot containregexp "\x5B\\\\\\\\\\\\\\\\/\x5D\\\\\\\\.") + +Metadata:[module_num:2|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\'The Metadata module optionally allows you to extract information describing your images (i.e, metadata) which will be stored along with your measurements. This information can be contained in the file name and/or location, or in an external file.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Extract metadata?:No + Metadata data type:Text + Metadata types:{} + Extraction method count:1 + Metadata extraction method:Extract from file/folder names + Metadata source:File name + Regular expression to extract from file name:^(?P.*)_(?P\x5BA-P\x5D\x5B0-9\x5D{2})_s(?P\x5B0-9\x5D)_w(?P\x5B0-9\x5D) + Regular expression to extract from folder name:(?P\x5B0-9\x5D{4}_\x5B0-9\x5D{2}_\x5B0-9\x5D{2})$ + Extract metadata from:All images + Select the filtering criteria:and (file does contain "") + Metadata file location: + Match file and image metadata:\x5B\x5D + Use case insensitive matching?:No + +NamesAndTypes:[module_num:3|svn_version:\'Unknown\'|variable_revision_number:8|show_window:False|notes:\x5B\'The NamesAndTypes module allows you to assign a meaningful name to each image by which other modules will refer to it.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Assign a name to:All images + Select the image type:Grayscale image + Name to assign these images:DNA + Match metadata:\x5B\x5D + Image set matching method:Order + Set intensity range from:Image metadata + Assignments count:1 + Single images count:0 + Maximum intensity:255.0 + Process as 3D?:No + Relative pixel spacing in X:1.0 + Relative pixel spacing in Y:1.0 + Relative pixel spacing in Z:1.0 + Select the rule criteria:and (file does contain "") + Name to assign these images:DNA + Name to assign these objects:Cell + Select the image type:Grayscale image + Set intensity range from:Image metadata + Maximum intensity:255.0 + +Groups:[module_num:4|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'The Groups module optionally allows you to split your list of images into image subsets (groups) which will be processed independently of each other. Examples of groupings include screening batches, microtiter plates, time-lapse movies, etc.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Do you want to group your images?:No + grouping metadata count:1 + Metadata category:None + +CorrectIlluminationCalculate:[module_num:5|svn_version:\'Unknown\'|variable_revision_number:2|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:IllumDNA + Select how the illumination function is calculated:Background + Dilate objects in the final averaged image?:No + Dilation radius:1 + Block size:60 + Rescale the illumination function?:No + Calculate function for each image individually, or based on all images?:Each + Smoothing method:Gaussian Filter + Method to calculate smoothing filter size:Automatic + Approximate object diameter:10 + Smoothing filter size:10 + Retain the averaged image?:No + Name the averaged image:IllumBlueAvg + Retain the dilated image?:No + Name the dilated image:IllumBlueDilated + Automatically calculate spline parameters?:Yes + Background mode:auto + Number of spline points:5 + Background threshold:2.0 + Image resampling factor:2.0 + Maximum number of iterations:40 + Residual value for convergence:0.001 + +CorrectIlluminationApply:[module_num:6|svn_version:\'Unknown\'|variable_revision_number:3|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:CorrDNA + Select the illumination function:IllumDNA + Select how the illumination function is applied:Subtract + +MedianFilter:[module_num:7|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:CorrDNA + Name the output image:FilteredDNA + Window:3 + +Opening:[module_num:8|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:FilteredDNA + Name the output image:OpeningDNA + Structuring element:disk,5 + +IdentifyPrimaryObjects:[module_num:9|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:OpeningDNA + Name the primary objects to be identified:Nuclei + Typical diameter of objects, in pixel units (Min,Max):20,60 + Discard objects outside the diameter range?:No + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:Shape + Method to draw dividing lines between clumped objects:Shape + Size of smoothing filter:10 + Suppress local maxima that are closer than this minimum allowed distance:15 + Speed up by using lower-resolution image to find local maxima?:No + Fill holes in identified objects?:After both thresholding and declumping + Automatically calculate size of smoothing filter for declumping?:Yes + Automatically calculate minimum allowed distance between local maxima?:No + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Minimum cross entropy + Threshold smoothing scale:1.3488 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +ConvertObjectsToImage:[module_num:10|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:True] + Select the input objects:Nuclei + Name the output image:NucleiImage + Select the color format:uint16 + Select the colormap:Default + +SaveImages:[module_num:11|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the type of image to save:Image + Select the image to save:NucleiImage + Select method for constructing file names:From image filename + Select image name for file prefix:DNA + Enter single file name:OrigBlue + Number of digits:4 + Append a suffix to the image file name?:No + Text to append to the image name: + Saved file format:tiff + Output file location:Default Output Folder sub-folder\x7Clabels + Image bit depth:16-bit integer + Overwrite existing files without warning?:Yes + When to save:Every cycle + Record the file and path information to the saved image?:Yes + Create subfolders in the output folder?:No + Base image folder:Elsewhere...\x7C diff --git a/CellProfiler/van_valen_set/pipelines/van_valen.cpproj b/CellProfiler/van_valen_set/pipelines/van_valen.cpproj new file mode 100644 index 0000000000000000000000000000000000000000..2062b907ab1058db82657cb90ccd3d8342ac610c GIT binary patch literal 410320 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 G?*9js^SZ47 literal 0 HcmV?d00001 diff --git a/CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cppipe b/CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cppipe new file mode 100644 index 0000000..c29b3dd --- /dev/null +++ b/CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cppipe @@ -0,0 +1,349 @@ +CellProfiler Pipeline: http://www.cellprofiler.org +Version:3 +DateRevision:300 +GitHash: +ModuleCount:23 +HasImagePlaneDetails:False + +Images:[module_num:1|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'To begin creating your project, use the Images module to compile a list of files and/or folders that you want to analyze. You can also specify a set of rules to include only the desired files in your selected folders.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + : + Filter images?:Images only + Select the rule criteria:and (extension does isimage) (directory doesnot containregexp "\x5B\\\\\\\\\\\\\\\\/\x5D\\\\\\\\.") + +Metadata:[module_num:2|svn_version:\'Unknown\'|variable_revision_number:4|show_window:False|notes:\x5B\'The Metadata module optionally allows you to extract information describing your images (i.e, metadata) which will be stored along with your measurements. This information can be contained in the file name and/or location, or in an external file.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Extract metadata?:No + Metadata data type:Text + Metadata types:{} + Extraction method count:1 + Metadata extraction method:Extract from file/folder names + Metadata source:File name + Regular expression to extract from file name:^(?P.*)_(?P\x5BA-P\x5D\x5B0-9\x5D{2})_s(?P\x5B0-9\x5D)_w(?P\x5B0-9\x5D) + Regular expression to extract from folder name:(?P\x5B0-9\x5D{4}_\x5B0-9\x5D{2}_\x5B0-9\x5D{2})$ + Extract metadata from:All images + Select the filtering criteria:and (file does contain "") + Metadata file location: + Match file and image metadata:\x5B\x5D + Use case insensitive matching?:No + +NamesAndTypes:[module_num:3|svn_version:\'Unknown\'|variable_revision_number:8|show_window:False|notes:\x5B\'The NamesAndTypes module allows you to assign a meaningful name to each image by which other modules will refer to it.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Assign a name to:All images + Select the image type:Grayscale image + Name to assign these images:DNA + Match metadata:\x5B\x5D + Image set matching method:Order + Set intensity range from:Image metadata + Assignments count:1 + Single images count:0 + Maximum intensity:255.0 + Process as 3D?:No + Relative pixel spacing in X:1.0 + Relative pixel spacing in Y:1.0 + Relative pixel spacing in Z:1.0 + Select the rule criteria:and (file does contain "") + Name to assign these images:DNA + Name to assign these objects:Cell + Select the image type:Grayscale image + Set intensity range from:Image metadata + Maximum intensity:255.0 + +Groups:[module_num:4|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'The Groups module optionally allows you to split your list of images into image subsets (groups) which will be processed independently of each other. Examples of groupings include screening batches, microtiter plates, time-lapse movies, etc.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Do you want to group your images?:No + grouping metadata count:1 + Metadata category:None + +CorrectIlluminationCalculate:[module_num:5|svn_version:\'Unknown\'|variable_revision_number:2|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:IllumDNA + Select how the illumination function is calculated:Background + Dilate objects in the final averaged image?:No + Dilation radius:1 + Block size:60 + Rescale the illumination function?:No + Calculate function for each image individually, or based on all images?:Each + Smoothing method:Gaussian Filter + Method to calculate smoothing filter size:Automatic + Approximate object diameter:10 + Smoothing filter size:10 + Retain the averaged image?:No + Name the averaged image:IllumBlueAvg + Retain the dilated image?:No + Name the dilated image:IllumBlueDilated + Automatically calculate spline parameters?:Yes + Background mode:auto + Number of spline points:5 + Background threshold:2.0 + Image resampling factor:2.0 + Maximum number of iterations:40 + Residual value for convergence:0.001 + +CorrectIlluminationApply:[module_num:6|svn_version:\'Unknown\'|variable_revision_number:3|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:CorrDNA + Select the illumination function:IllumDNA + Select how the illumination function is applied:Subtract + +MedianFilter:[module_num:7|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:CorrDNA + Name the output image:FilteredDNA + Window:3 + +Opening:[module_num:8|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:FilteredDNA + Name the output image:OpeningDNA + Structuring element:disk,5 + +IdentifyPrimaryObjects:[module_num:9|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:OpeningDNA + Name the primary objects to be identified:Nuclei + Typical diameter of objects, in pixel units (Min,Max):20,60 + Discard objects outside the diameter range?:No + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:Shape + Method to draw dividing lines between clumped objects:Shape + Size of smoothing filter:10 + Suppress local maxima that are closer than this minimum allowed distance:15 + Speed up by using lower-resolution image to find local maxima?:No + Fill holes in identified objects?:After both thresholding and declumping + Automatically calculate size of smoothing filter for declumping?:Yes + Automatically calculate minimum allowed distance between local maxima?:No + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Minimum cross entropy + Threshold smoothing scale:1.3488 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +MaskImage:[module_num:10|svn_version:\'Unknown\'|variable_revision_number:3|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:OpeningDNA + Name the output image:MaskDNA + Use objects or an image as a mask?:Objects + Select object for mask:Nuclei + Select image for mask:None + Invert the mask?:Yes + +Threshold:[module_num:11|svn_version:\'Unknown\'|variable_revision_number:10|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:MaskDNA + Name the output image:ThresholdSecondStage + Threshold strategy:Global + Thresholding method:Minimum cross entropy + Threshold smoothing scale:0.0 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.001,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +IdentifyPrimaryObjects:[module_num:12|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:MaskDNA + Name the primary objects to be identified:Nuclei_dim + Typical diameter of objects, in pixel units (Min,Max):20,60 + Discard objects outside the diameter range?:No + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:Shape + Method to draw dividing lines between clumped objects:Shape + Size of smoothing filter:10 + Suppress local maxima that are closer than this minimum allowed distance:15 + Speed up by using lower-resolution image to find local maxima?:No + Fill holes in identified objects?:Never + Automatically calculate size of smoothing filter for declumping?:Yes + Automatically calculate minimum allowed distance between local maxima?:No + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Measurement + Threshold smoothing scale:1.3488 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:Threshold_FinalThreshold_ThresholdSecondStage + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +MeasureObjectSizeShape:[module_num:13|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select objects to measure:Nuclei_dim + Calculate the Zernike features?:No + +FilterObjects:[module_num:14|svn_version:\'Unknown\'|variable_revision_number:8|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the objects to filter:Nuclei_dim + Name the output objects:FilterObjects_Nuclei_dim + Select the filtering mode:Measurements + Select the filtering method:Limits + Select the objects that contain the filtered objects:None + Select the location of the rules or classifier file:Elsewhere...\x7C + Rules or classifier file name:rules.txt + Class number:1 + Measurement count:3 + Additional object count:0 + Assign overlapping child to:Both parents + Select the measurement to filter by:AreaShape_Extent + Filter using a minimum measurement value?:Yes + Minimum value:0.5 + Filter using a maximum measurement value?:No + Maximum value:1.0 + Select the measurement to filter by:AreaShape_Area + Filter using a minimum measurement value?:Yes + Minimum value:50 + Filter using a maximum measurement value?:No + Maximum value:1.0 + Select the measurement to filter by:AreaShape_EulerNumber + Filter using a minimum measurement value?:Yes + Minimum value:1 + Filter using a maximum measurement value?:No + Maximum value:1.0 + +ShrinkToObjectCenters:[module_num:15|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input object:Nuclei + Name the output object:ShrinkToObjectCentersNuclei + +ConvertObjectsToImage:[module_num:16|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input objects:ShrinkToObjectCentersNuclei + Name the output image:ImageShrinkNuclei + Select the color format:Binary (black & white) + Select the colormap:Default + +ShrinkToObjectCenters:[module_num:17|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input object:FilterObjects_Nuclei_dim + Name the output object:ShrinkToObjectCentersNucleiDim + +ConvertObjectsToImage:[module_num:18|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input objects:ShrinkToObjectCentersNucleiDim + Name the output image:ImageShrinkNucleiDim + Select the color format:Binary (black & white) + Select the colormap:Default + +ImageMath:[module_num:19|svn_version:\'Unknown\'|variable_revision_number:4|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Operation:Add + Raise the power of the result by:1.0 + Multiply the result by:1.0 + Add to result:0.0 + Set values less than 0 equal to 0?:Yes + Set values greater than 1 equal to 1?:Yes + Ignore the image masks?:Yes + Name the output image:ImageAfterMath + Image or measurement?:Image + Select the first image:ImageShrinkNuclei + Multiply the first image by:1.0 + Measurement: + Image or measurement?:Image + Select the second image:ImageShrinkNucleiDim + Multiply the second image by:1.0 + Measurement: + +IdentifyPrimaryObjects:[module_num:20|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:ImageAfterMath + Name the primary objects to be identified:NucleiSeeds + Typical diameter of objects, in pixel units (Min,Max):1,1 + Discard objects outside the diameter range?:No + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:None + Method to draw dividing lines between clumped objects:Shape + Size of smoothing filter:10 + Suppress local maxima that are closer than this minimum allowed distance:7.0 + Speed up by using lower-resolution image to find local maxima?:Yes + Fill holes in identified objects?:Never + Automatically calculate size of smoothing filter for declumping?:No + Automatically calculate minimum allowed distance between local maxima?:Yes + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Manual + Threshold smoothing scale:1.3488 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.5 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +IdentifySecondaryObjects:[module_num:21|svn_version:\'Unknown\'|variable_revision_number:10|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input objects:NucleiSeeds + Name the objects to be identified:Nuclei2 + Select the method to identify the secondary objects:Propagation + Select the input image:OpeningDNA + Number of pixels by which to expand the primary objects:10 + Regularization factor:0.05 + Discard secondary objects touching the border of the image?:No + Discard the associated primary objects?:No + Name the new primary objects:FilteredNuclei + Fill holes in identified objects?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Measurement + Threshold smoothing scale:0.0 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:Threshold_FinalThreshold_ThresholdSecondStage + Two-class or three-class thresholding?:Two classes + Assign pixels in the middle intensity class to the foreground or the background?:Foreground + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +ConvertObjectsToImage:[module_num:22|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:True] + Select the input objects:Nuclei2 + Name the output image:NucleiImage + Select the color format:uint16 + Select the colormap:Default + +SaveImages:[module_num:23|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the type of image to save:Image + Select the image to save:NucleiImage + Select method for constructing file names:From image filename + Select image name for file prefix:DNA + Enter single file name:OrigBlue + Number of digits:4 + Append a suffix to the image file name?:No + Text to append to the image name: + Saved file format:tiff + Output file location:Default Output Folder sub-folder\x7Clabels + Image bit depth:16-bit integer + Overwrite existing files without warning?:Yes + When to save:Every cycle + Record the file and path information to the saved image?:Yes + Create subfolders in the output folder?:No + Base image folder:Elsewhere...\x7C diff --git a/CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cpproj b/CellProfiler/van_valen_set/pipelines/van_valen_bright_and_dim.cpproj new file mode 100644 index 0000000000000000000000000000000000000000..4a516c3ae354f5922d522e915d38e9180964c167 GIT binary patch literal 441344 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.*)_(?P\x5BA-P\x5D\x5B0-9\x5D{2})_s(?P\x5B0-9\x5D)_w(?P\x5B0-9\x5D) + Regular expression to extract from folder name:(?P\x5B0-9\x5D{4}_\x5B0-9\x5D{2}_\x5B0-9\x5D{2})$ + Extract metadata from:All images + Select the filtering criteria:and (file does contain "") + Metadata file location: + Match file and image metadata:\x5B\x5D + Use case insensitive matching?:No + +NamesAndTypes:[module_num:3|svn_version:\'Unknown\'|variable_revision_number:8|show_window:False|notes:\x5B\'The NamesAndTypes module allows you to assign a meaningful name to each image by which other modules will refer to it.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Assign a name to:All images + Select the image type:Grayscale image + Name to assign these images:DNA + Match metadata:\x5B\x5D + Image set matching method:Order + Set intensity range from:Image metadata + Assignments count:1 + Single images count:0 + Maximum intensity:255.0 + Process as 3D?:No + Relative pixel spacing in X:1.0 + Relative pixel spacing in Y:1.0 + Relative pixel spacing in Z:1.0 + Select the rule criteria:and (file does contain "") + Name to assign these images:DNA + Name to assign these objects:Cell + Select the image type:Grayscale image + Set intensity range from:Image metadata + Maximum intensity:255.0 + +Groups:[module_num:4|svn_version:\'Unknown\'|variable_revision_number:2|show_window:False|notes:\x5B\'The Groups module optionally allows you to split your list of images into image subsets (groups) which will be processed independently of each other. Examples of groupings include screening batches, microtiter plates, time-lapse movies, etc.\'\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Do you want to group your images?:No + grouping metadata count:1 + Metadata category:None + +CorrectIlluminationCalculate:[module_num:5|svn_version:\'Unknown\'|variable_revision_number:2|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:IllumDNA + Select how the illumination function is calculated:Background + Dilate objects in the final averaged image?:No + Dilation radius:1 + Block size:60 + Rescale the illumination function?:No + Calculate function for each image individually, or based on all images?:Each + Smoothing method:Gaussian Filter + Method to calculate smoothing filter size:Automatic + Approximate object diameter:10 + Smoothing filter size:10 + Retain the averaged image?:No + Name the averaged image:IllumBlueAvg + Retain the dilated image?:No + Name the dilated image:IllumBlueDilated + Automatically calculate spline parameters?:Yes + Background mode:auto + Number of spline points:5 + Background threshold:2.0 + Image resampling factor:2.0 + Maximum number of iterations:40 + Residual value for convergence:0.001 + +CorrectIlluminationApply:[module_num:6|svn_version:\'Unknown\'|variable_revision_number:3|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:DNA + Name the output image:CorrDNA + Select the illumination function:IllumDNA + Select how the illumination function is applied:Subtract + +MedianFilter:[module_num:7|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:CorrDNA + Name the output image:FilteredDNA + Window:3 + +Opening:[module_num:8|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:FilteredDNA + Name the output image:OpeningDNA + Structuring element:disk,5 + +EnhanceOrSuppressFeatures:[module_num:9|svn_version:\'Unknown\'|variable_revision_number:6|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:CorrDNA + Name the output image:EnhanceOrSuppressFeatures + Select the operation:Enhance + Feature size:3 + Feature type:Speckles + Range of hole sizes:1,10 + Smoothing scale:2.0 + Shear angle:0.0 + Decay:0.95 + Enhancement method:Tubeness + Speed and accuracy:Fast + +ImageMath:[module_num:10|svn_version:\'Unknown\'|variable_revision_number:4|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Operation:Add + Raise the power of the result by:1.0 + Multiply the result by:1.0 + Add to result:0.0 + Set values less than 0 equal to 0?:Yes + Set values greater than 1 equal to 1?:Yes + Ignore the image masks?:No + Name the output image:ImageAfterMath + Image or measurement?:Image + Select the first image:EnhanceOrSuppressFeatures + Multiply the first image by:1.0 + Measurement: + Image or measurement?:Image + Select the second image:OpeningDNA + Multiply the second image by:1.0 + Measurement: + +IdentifyPrimaryObjects:[module_num:11|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the input image:ImageAfterMath + Name the primary objects to be identified:Nuclei + Typical diameter of objects, in pixel units (Min,Max):20,60 + Discard objects outside the diameter range?:No + Discard objects touching the border of the image?:No + Method to distinguish clumped objects:Shape + Method to draw dividing lines between clumped objects:Shape + Size of smoothing filter:10 + Suppress local maxima that are closer than this minimum allowed distance:15 + Speed up by using lower-resolution image to find local maxima?:No + Fill holes in identified objects?:After both thresholding and declumping + Automatically calculate size of smoothing filter for declumping?:Yes + Automatically calculate minimum allowed distance between local maxima?:No + Handling of objects if excessive number of objects identified:Continue + Maximum number of objects:500 + Use advanced settings?:Yes + Threshold setting version:10 + Threshold strategy:Global + Thresholding method:Otsu + Threshold smoothing scale:1.3488 + Threshold correction factor:1.0 + Lower and upper bounds on threshold:0.0,1.0 + Manual threshold:0.0 + Select the measurement to threshold with:None + Two-class or three-class thresholding?:Three classes + Assign pixels in the middle intensity class to the foreground or the background?:Background + Size of adaptive window:50 + Lower outlier fraction:0.05 + Upper outlier fraction:0.05 + Averaging method:Mean + Variance method:Standard deviation + # of deviations:2.0 + Thresholding method:Otsu + +ConvertObjectsToImage:[module_num:12|svn_version:\'Unknown\'|variable_revision_number:1|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:True] + Select the input objects:Nuclei + Name the output image:NucleiImage + Select the color format:uint16 + Select the colormap:Default + +SaveImages:[module_num:13|svn_version:\'Unknown\'|variable_revision_number:13|show_window:True|notes:\x5B\x5D|batch_state:array(\x5B\x5D, dtype=uint8)|enabled:True|wants_pause:False] + Select the type of image to save:Image + Select the image to save:NucleiImage + Select method for constructing file names:From image filename + Select image name for file prefix:DNA + Enter single file name:OrigBlue + Number of digits:4 + Append a suffix to the image file name?:No + Text to append to the image name: + Saved file format:tiff + Output file location:Default Output Folder sub-folder\x7Clabels + Image bit depth:16-bit integer + Overwrite existing files without warning?:Yes + When to save:Every cycle + Record the file and path information to the saved image?:Yes + Create subfolders in the output folder?:No + Base image folder:Elsewhere...\x7C diff --git a/CellProfiler/van_valen_set/pipelines/van_valen_with_micronuclei.cpproj b/CellProfiler/van_valen_set/pipelines/van_valen_with_micronuclei.cpproj new file mode 100644 index 0000000000000000000000000000000000000000..17384f853beb4d98c8d63b3e3521cbe3d84937aa GIT binary patch literal 411584 zcmeI53w&Kwo$vQaC=C`QJk)}M8)PJP`Z(!}aOBdIrZ1&QXo{2)On1)N$vO69pTl`H z37}>`C4QVM^`cNku_`lmoR4EiW$gICFpAbILhohfT4a=7Uv#)Ay*gqYhui!Auk~N& zvD=-KMw&F|{0=nhthLwkyB_<$*53dBdH=e$HD|rz!dF=QX=t!cvl`V`^XI61#@3#$ zf{fo>-zr~1;*N^DyH0-zS^S@6&6V(TB!7RrO&eFQwkW2_-?);@n(OIrO(pSzalYm9(@T$#TARZEl~FPv#2aZe9816ZQqIt9HH4s#VIbG=4|7>G&AG zC)@>9ksxUN+P(qNN9!s#*xcv;)bYD^RmZg|FPShV-?N0OS;yCAQu#E6nPvVAx!#t@ zxY1lHGc0}?_EymK(?OL)hyf92*XZp%nOZCe}9o@Nf&G1i1ylAAmJD;VS zW_R~CClgP_=}RmX&8r(~cXxjx7ti+=uJxodsdrGnP^87-=H)%%mY#5PPsWKQoa8_v zmx{(yUMxeN7~P^XiQasU?!`T^l;=58-YdmjChjUK|9W5fZK**onQ~%O$~*Fo*Rx&y zOS)cncPgJt=lSM;wxqvMT&hmXg7D%6&C546FOMu;90|89T-qF_kB(q?oA&42Y>xV` zXYs*vXJDbC{g6j`)r(W=NI)x1SQcTu5ni^{5S{^ZlE zD!yv+=~WY6E$PiXHSKs!K)8|j{qZjdh;RDIAOE!h@n0w57l}LWIH5Quo^dP1zxV|6 zf4#&vTu!uq?a8NC{d>ve)0-ixa!8v6M)HFL0w4eaAOHd&00JNY0xt)F5bfw$*54|b zndbrr&4yJr%x7Ay^h|=!9Zt8-r&HEdNhiw(xff7k-m~#Py=Tw~^mo0F9g;jatkOAH z>pLhCeat>x`|!6`2>8#z%>K2>Y(ND>_u@YN|ElgK4uzTV);dX0LG$2{RG>l1pX@x~ z^DHwD_zbPmJU9~94Z%E^WQWf1Wd3=;>nnf4=0HSyEp=!`-_7`6C*hNIg66?t8UKw^ z{>w2B_^i0nJlMmBe)PdS&>U**Z_qqwxul$g9NV7sd2mF=|6D2m<(LO7S5=w^@jl&N z%!BfNuN9wv9vHc(^|EpTg~t{WZ0-xK<c(f1AfL`%;yzqzr86jKu#ULi73tF?5Fwe+YwcsK2OF#YHop8VAR zTFLW?-%&B|@fr6z$;a=E`hkq-7$#qTeE2~C1V8`;KmY`0K7paP|LVJcf5w>IXtiGc zitoI4@(}b_r7J_RsZ_ir+yoH^x~WOy||r!w`3&syu1l_@W#togqizn-8t{GK2(zG zJD1*)exm=0+2`N(?{B9FW4TX+ZvV_;UHC}o<=) z{@vf#uemhKuS|t+?zf)VT`20!Qq(rP|TF?OWl-_x`c# zJukMk-_`k-zvz7a!mE~^@$L6r_SSPAf9ll_J-GEB^FQi&=ALW2zL-xfJM_T8AAjN} zulfAK&%NP={Wm3+e)a>yt!Eti?;n5AYgzDtC)=X;;rKKJoUqCabS z{0A3&`M+JCO1h7~KKtYq$r~Qp^&a=Y*st$=H1q2DiP!z&ElXQ}^Wi6RFO9zN`LDP3 zuYc34;$Inh@{_xs&Aofw2WS26;qRRjdGk}>>u5Thy5~nd|MJM+w7e;H^JjY-FT3X( z7oFSQe8+u{-hI`rAMO8}FFf5IU-#Jk7ytDc?ngg+=>Ke8aDIBrXa4Cu^Ut_3@u4$s zd(}I)ZTbGjCC@!RdXHOwAauqzzJ1+U55^z4`_boa_z!pf2M@e>$9?C%_OfR;M)rPi z!ScR8KkdNuay5-~XM*bf4e;#L?N7HS!-9zyIOT3#Fa+x(D7mc=f}t51;$dFHy{qe~Z)~S>x(7 z))PPC7v9ENUh|>1x&N<)ES&zK+SIcf=>G7q6getzVg)I%3T>7tj3G zt{;B$-~PCH$a!5F8ugYSM5CelfR4(UH$p5e!A|)`<7hy)r)Vq z;nRmUzH!YD=iK?+o8EU__!VnH|JAbQd~DScDpyUX1}prAn_s%~w=K^+KKr>>-&X;` zgICmT;FR2_l=2VjcCGodhgY2S&%f$!@)4Z zRx3&PHRpY*ZV@F6?lIGhjxD>-8eMz)yD!}R^glkv?>vq@{khM*=J5MR4{_z`)BTPw zJjG=VJr??TbKNc2E-5PbGt&r!tl8?EA@RjrE54*F( zU#E{ttT|ST3J=MAuT$4URyLmR>!Y{bb*`M2^9flow=e1BT=lL2m)9WW*Vol;HfKE} z#p4_6T#CUT&gbrH@7hv4H#xEQ6ljGmJfEf8i`CWM(Z**!I(N$&ZK1$%KAfIEjT^KZ z^w*qQ?&;JqO#A1yYk!M`2c@5B;X&t51+W1PYFzERAfRLz6~>1z7Q7$;0w4eaAOHd& z00J|Vz=fBebzwmXc80bA)dm3&009sH0T4Jb0>_?1>eIhaojwSX*PROXNX&VpIUlMg zjUhH1VleUT{$D5R&K8G~tlR9nbIb{8Th{{QwIdX9-%FA^Q$@VRb zo(P{kR(c=upuEUs&L5>}aB75r|9wbvzP#CYj%?n2H19*oajMliNlrm_ul(oO$IfG2 zL-Rbr6mRD56h2QdMOq^#`FR5Ce(C24yt?Sm0bl<}aKSP2d8>JXUAR))UncuJVe8F4 zZZZMP{lcvCzF6>r00@8p2!H?xfB*>0Py$CS{tus8C4l~~)t=YwM*r7#|G(Sf|M00* z0>_?1o-)r9n!2P@=t%0MK2Kni!ulMN-?rADUGhS=IS=;hvr9BMJ_3G@J1^4_3`f6x z3}141g@B*qj!Vip`t9ui-0=zcIgW~*A~PQS_PdUs3qt)IN5t-#;pn#q2x7-4;O96b z4<-#qzkSi;=Ymkpv8ac%SM&`HM_XQmFuyZop8p=XMYA*Uwx+ayM8dW7V_AFFm|s+> zZq}9FqlmB89E@C4-89fTKLP)H6t+azCG*9^FaCZF{e%twkj=6s>XMmfEhgdw-Jtg< z{PVTa_#L}fKgl-pe}cs$T9pt88oyf$zrd&)x_T(>X2_P9C*kV{oPK`AMrKh$ux_6?Z((O>P=a zyFG2~D=qn*$DZ{b{SMihR5Ir30cpZJ)DM&uFQ|FY<>q>}B+@-?i75XdnnS;8*O^GW z$%Niy0mGH&Ne`8_2f>(CkJ*_#S?-nB)a z&ldGM4&5OQ3vf(EEwbMYh*opKL2{%O9v?-^-M;H0Pa6F|@%kdc3j!bj0w4eaAOHd& zFw+T`eZx#|2kH+3AOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd& z00JNY0w4eaAOHd&00JNY0w4eaAOHd&00PxWAY{!_`oW{pxR9kS8=BvVGAGD}f ztP@Q1cOn}|51Bvpb@kSJbyEJ}N6uEMn3n$jV^J@7cEQf_4pAmH_3_(Lu_ezv-_l=9 zyt$8Pe~W}0b=c84I);gNxIuqwl<;+u-`1}3c>6lF|IkM5Zk2c~_m{`pwNv}YKBnCv ziP!M6@_6Zk+P~wc;!3=c>$E*O9co#J+O@wy!X;Wor;>f71CQw}j9zuc3=yHK2n5+j z+AQS@ttQ@oFtRNRF&zA8GReA1RS1>m^vczDV$b00@8p2!H?x zfB*>0bOPoaex|nr^#=hE009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sHfodc$4fc`j#|0$mma>|uQZe=G1y{1` zv*&!Rv0(IptFdp;reFd=c74KP-^!$%D!V>@`&vQz*vn1Dd~O@;`uK>z3j!bj0w4ea z)lXms+4b3DL@W~A3*vXF{QjPYRr=%|N|D00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1VErF z2~2}spChjlWrHBQKCBm9$*#|6x6Z`q16O6|pizMYg6#Sl4`5xBvnm00JNY0@X@j2HEu)H6qrD*!9_aQ@LE`??)rYH|!igor?GR1?>X?5C8!X009sH0Y$(k1%e6)fB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@A< zDMVlz?E37#S(FX_6uUl~AJmx`ec)3FDXKCB1cL1P9Q;_hCd*XW_1Pu*=wniTkUsYM z^6C{x?Fq2!6A-K_9Gs;l zM$blfdqVre;cjnjBDc=T#v|R{hEyz{bXTSFUM|v1aVdEHfYa}GCLPahb8}82nT@P* zl3BOg>-N;8Y~-c^m8!?f4@8=8&u;g6wo7T<-IrhQZSzut9(}ugyOT*cy-BwxBc*eS zUN;k2e0w&Y8tfTNc(K%=&i-~Um2oNk156?n=*O#cCK)?C+!dcP44Dy&!!J`COc~q$sCQ61b1R~MMU^; zE%o&-RUuVlZVT~EQ2c}wAwYJMJCt)h9w&AzMeU>^!}-j!n_|?qXfBl*Ru`zR2((9| z!^^n+?oiskR4U&+?q`u|xbKV zd>1)98@cVBI#_aJ%#>j~6{EJR^~slt^5K3d#*3JG^kq^5s>zG=v{RU}zT{b2P@*)4 z3`TuI|5zgOisP8t3H;j2g6RZISV8 zU&^zeSFXZPQE|**mSt@Nua*PxYCXLUW@9ik5XNqAXa&vJ?Fu1zyH z8mAiR!P)J#)6~hX^kSRT;48hSEt$-s(=wq&r>s!3@{}((4vo5gn!VIK$D{i8<&%1v z^Eh;ff^K~8u&gdAS`0FhRaUPx8Mlvu_%4+@d6Rynsi#_la;fo#gRZig{%h%0myJ5K zcBw$!VT?uHg>|0PBJMnO4~w+5uap6z#()`u$8`F7j;`+0rl)23h;(G=p0D%hqT4v# zRMnsuwlj`LNz_=@sZHiOvTmVTm&=!+F6tK3fUI01u}Y0%XDBg{A1D?QS-fo7Lb*sn zT$C0S+sWEX+KP+hMwgZl9-`^Qkeei-!-?|Dp(T8Cq}ikmj&$QhB5f%~Dw(Fo5dp@T zp%dz*<3>vLve=f5uq10bf(0XWBUAmX?+wQt94FLg8-nTtx=B2lVWqR` zEx`zFnftt)X|a{%QHOSnR42KU7kg2qVTmr|#1i>zq*-Tm4T%HW>}=vzH?myT(v2j6 z^Ie4VB>B3~cZJ#&Gc9yy;TmnnNW?4NOlc#U*q(^xS^8Jvb1z8`G1}bmRzPkI5^7{E?Z>hSlDO9>!AHlUF~`y?P2&P3J6lLe3oXwq)u@&ET#9!V zQJrvQnx-_}M~e9DSb}bm6hG1|?e98nM&{~9-nwhxL8uF(pgclEje~2Fd3WV@I!EAp zT1jRx)u+dFTOqHq>&2|}P>^v^Xe`q%MMF!WM>jzyqnk?G*Bf1t+KXe0S5(?D6EIyl zEhqDu#!D%uPtnSmjV#lbOA_YdB+{+JBfQ`BXonGA7!K1o?)Hwe(ojz)hs#!(<&#-u zNMbEHP&t)X8D`*LWCEwK9zg!+71tz(peE?Nq$}U6c8L0Pfo71WXJ;AjD<;F6PQ6YQ z-eo`w^RR4X*R6JA1&}xBgEu-IWGUZ9a*#)S4VCoUG9LQIZn@bS4w(+gO-a+!QI zmsi_XIwoVmKN3r1x6P-uq}yBV(Z)RLc4WHpv^D;DVqP%i4=xEN*py3n< zLa(?b(nl&SYh^^z=okV{-}-0}@;%0csXbyB9lLHLNmXCksCH?z^wQc*8$1#MNG_Of z?sUcbppplQOLte?rE?rgn^a4%&a2IhI>WRgUn#c-sTX&(xs`9q_qrZ+uTDT!Awhu8 z9Gqx0Pv=d;bn2R=11vcW-{9opWpk%FJed(teq=c{3pfpbeJ6{ojLEHeqmv-Xp4a1a zYLJ8zg3cZq^G*C|UDd~`~_1qx2uf2+G>)~5bd=kT5#w%uDu z^y2v8l6yYy`y_5ZKLy|;4Vr>>b1`~zN%Zyo9v!c$`Y3sq4t%oPNQN*+k;h*HlmxJ{ zk22?;)Rojy0ZE7@C$n;tZdy^;y!ItB^q4^vXa@PQD}+u}hO8uAIoIN0upE7zNNut% z2_7PyMf}TKS^1?g$Eg5~a^6hRGmy>>CAz@sidp zFKlFJn9_NCLUUm`qdgx@x(VIMbefnTX}=(^^AwUD(0twk=_8)Jm*DOEhJ-htB+Pk{ z#o_s8U(=SLbu2@B4-K04Xjv*tjccUm@X>JJLs3Xm? z#_J_KpQd3>&ka>&d2w4nYf~zjSI-X=6<&Y(NHQubxQL2`iln*)@tH|U_ZvzpB5DBH zy|go=>Qe5!*Wlv#?h$hpRPt($ft#QJJZe`_%%o)93(6^oOQDlW`4*eGj%us6LdAh= zC+Hw)h>w`~kg~WHGDEavDy*WXf(b8g28$fV9g`%oj2}buc;cgaC$^nCEXIeoJO)cQ zewz$PbI~@>nvDQZ5`(0DI@}qKtWBnR9XV($T;`sjEi~`eJ962)OxI%eh5fXaBbpa3 zS=!R#j}_I2MSMabTgAcw?X~KT$$d=&m_}$XpM=n18}GtpFz|hFej&jI$5VH`(yO|> z5)rE87R2MTkd=CVC}&vAanoRGK{QFv4*208$8yD6NI~si5!u97Xbw?CBVNflzFL^n z(B?b-Kq4076Yj!Mxz1bFK<#i{ebS>#bLq^`oc*jg<{Ze(czU`^k6390$#ef@G6Xa@ zTIXofMo;MFnI5x}34FccIZlL?>NKgOgE!El6OHn1{0;&QF4Og0v^KC`K}|!b~n3KfIymU4E`ZFO!>6>NZj?4NmF-_Ec@9Sk9E!2TF#VJ~K3H zf9cJwa7!%PUg{XC?Z*t?XevqLQ9n12@aqiC%a0#tz)45i+&+iIN|vp==uk@KSiZYr znPCz;(`lA8cu&K_t8kaeQtR}q8OOjVj}wGek3)0PlvVpy9{zpiVK%+A!_UBVZXD{- znOa$4`Vt4LY*b3x*7hZaiU-B3Y5MaCrFz;`$eW(0CHnb*Q0G@XfQu#iNvw%1)!}rs zMNcC5)fxKru*8sFz2vE0v7*Hr55E_{k?8b>)5%g(tfq*=JTmlKHfC6nblRtj?9k`g zs-}ep$TF&Ixwu9@8PDc>7wDG*=ppzOtCCKyo0KizdUYt+OOIvfVG8Z;nwKwN53hy& z<~d>q&HF)qb%t(?T5wWL;^(h)hoK|)K_|nqTMU+8nKwd`N?F}p+(-;s0ZY# zt@=`AHNECFNDq_Ug$wD44pHg$)C+0m_vXg*>uIE!QT}^#=6yAzr%_MWIqU-Z!|$;3 zKOJXszdL8%gP*|f&V_#_m7!PYO|r7zor`Zg_IKwj(I=7S9pjjcI@{%k@s5d6n*p0h zPlta8(Db`;EB)?V=fysb_}#hM96)F_2!H?xfB*=9K+O>lQN8BcjRu1N2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfWS;3Fb%&uH}`x|ISu;VIo1nieb(`QcW(C<{O;UL7+)u=jzPaW*K}REJ~RK; z0oQ?S3Asb$8a1v-q9Aa>MU(#$g&RHST2|Od(hbRX}Wj(c>Z+N z?hle9E7|pF>hp1gU7uudWY_0-dcpe#H9MoHa4;tA5++_pJ^^F5-{?HS?b$}`0cjS)wcuUhf`eyjD4i7GGPu%^Va$1%c{8J zd2X_&t$n46u_lpp+noW|1gy!Fcqqo&Oe&x5SufUhzQ)TCDzubop ze?pfZWS`>F0Bp$sJ7;2^A|MrlK>!3m00ck)1ZFY;W9MwIG$JIHR6`*xvDZtu+1A#R zeO12FNQT6;j9{x6v&ga-hu76B$C_ics4pQAi`eoQ%i!5~zOOIoN?Nx6s>5S$U((4H z!yBX?^>uYSjA%vW6~vNr#gZA9dAxIsvA-^t$5bUt{uueaLH6Y(mumWJko;K5zFEVU ze7vQn%=jsETuswO<3RufKmY_l00cmwdI;#dZ~NLqFI41_^Hx&YKtM7MXZs^R@rrLhZIlyr#kOcsp|1 zzh}F4CE6+LRI-b-`>^I?^r}zUhM;06g+P#9q$Zg!rqHRfi{ziLL3-^GQ!$_W!2r8R z7~ein@PYscfB*=9Kot=%c9F)UO_l8;%~d+j7tlt4Wd#1G_XK1pi9RQ4AIZoW6R?l8 z+lW}S0W!&~0w4eaAOHd&P;~^%^8t2Q5;QK$f9yAMAbrIL1V8`;KmY_l00ck)1V8`;KmY_l z00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_lV44U_ zgMFmK|5sE_Nw<{M3zUkfUoW_lU7w*At(jo-fu{){ik&6`L3VvCv3q5zHC1+f{Pwki z^sz5A74y0K0_^%g|Jnx%UJw8Q5C8!Xs3HQ!uFqa+Q)Rn8jY?OykqqZ(8NpU5Pt=}| zkuN4-&u7GlR+Ppht7y+>&lTfq5F7dZNOt^qRn*lJYD$p&SjnExkySp9(o-h1&~X#0 z1tNg}2!H?xfB*=900^Aw1YpnSRPQ0w4+KB}1V8`;KmY_l00ck)1V8`;KmY_l00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;`1gBfDZSuPff2Hr z)VENb)(kLupvFBaALFmLCZ<>1V9ize*4NdKT`7r4x0Jsr71I;7=d*4^XJYh$rTTDc zgg}rzpZ(j-$kZUF%AU`ToGy2dlpmyzeNjOF{1o~g31V8`;KmY`)m%t3L=hHbT zq7@131+y@IxxK5sqb+3BS!8BNeMa70E|2+p(8%xmzBYb5f4XY-2g#3>?D_2bfRCf} zlnE_#T(tv4`#=B$KmY_l00cmwMhL*3PmOdmnh63R00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JN|qX|rdJ)a$a zAFN7OW-|lxuQ0q0t}!0w4eaAOHeYOaS(LsE#;BQ)SO5e3~w|Q_2t0$G#|_b+29OyCBUdZ;dYwydVGqAOHd&00J|bzzneG zbJ&PhB$E`x62IK;x94MvoF3FUMt+aKwUA{{)S3zpk{>JC^NGLK$5Fhn=TlR|2aN>* z5C8!X009uFAp)@HQ$wANrh)(nfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p z2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*>0SOU{v&nJA2=$Mi-X?s3FImCAm(OJs;~@U9K(V2kB$q6wun10DC@0KPMm=fc)&{MUqZIEU9GA=kVLg!3m00ck)1Zt0f5zLoU(^=$aH%b3k{$tnd zBj_tWAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4eaAOHd&00JNY0w4ea zAOHd&00JNY0w4eaAOHd&00JNY0@Flb8tnO4H;RraIg_^Mv*VLG6Qd72P54mkG!Y20 z=hJwv3@)kHRN3>{FZ$?*KcdSI(#MVj@VY0!o)5-%05AlD00@8p2!KEp5HR+9#-uGF zv7-|TX$@UtkELkOXRb1;a{*mu8G--ln3J>XW8{np*!9_AL@bhYOfsvIU7xWB%H=X? zX2CHTH9kFlyh4H+`4S{YRG`Ms;Xqb)=l#M;V`W$pZBxm;G#boJF_bm-Dq z{!2CO50WD*+4VW}8y|1+8hvix)tY9E#)AL|fB*=900@9UH4uPZpK9nuv;hP_00ck) z1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`;KmY_l00ck)1V8`; zKmY_l00cmw+6hd9U7tONM5&aVK)XI#FZeWTopivFhqXqk(O1~|b?qOK@XZo$-|0GY z6EA$4_IFBnT;lC)FOO$ktNpfwk4U`uz2))t-=qD9KcZcUcFH=H>?4hawH~$6t3G8L zf{L9K0zvkX8ZIx_ub(RWNc%3+r1+l)}jy{ z9&`JWPOcceG--6`FO7?vA zeBZ|r_Izq%z@U{N00JNY0w4eaHA?{Yd}_Aq(Qps|0T2KI5C8!X009sH0T2KI5C8!X z009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2KI5C8!X009sH0T2Lz8AMgqYpfTkfW+E1A!oWKBNDyT%UQW?D?$wPhDc7=l3n1V8`;K%fc;7<)cr(w55heC8@UJr|GyFUtu0PwVoMfgk#us9hf;XH3AZ zPs7oo{BDw2mF)Vo%+~U{s@!q@n2bhOjUVqgNvk?2NRF&z*Jt!RA4k~rsp@fp#(@9` zfB*=900@8p?D}8~fB*=900@8p2!H?xfB*=900@8p2!H?xfB*=900@8p2!H?xfB*=9 z00@8p2!H?xfB*=900@8p2!H?xfB*=900@AniAjqA2k z5VrnAE2bKKh22kU|GpnR*~sXslNQXHr}\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ImageNumberImage_FileName_OrigERImage_FileName_OrigHoechstImage_FileName_OrigMitoImage_FileName_OrigPh_golgiImage_FileName_OrigSytoImage_Metadata_ASSAY_WELL_ROLEImage_Metadata_BROAD_IDImage_Metadata_CPD_MMOL_CONCImage_Metadata_CPD_PLATE_MAP_NAME...Image_Metadata_SOURCE_COMPOUND_NAMEImage_Metadata_SOURCE_NAMEImage_Metadata_SiteImage_Metadata_TimeImage_PathName_OrigERImage_PathName_OrigHoechstImage_PathName_OrigMitoImage_PathName_OrigPh_golgiImage_PathName_OrigSytoIXMtest
41944195IXMtest_D14_s1_w205B05914-92D1-4E66-BF35-812B3...IXMtest_D14_s1_w12F978460-B7F4-4A65-AD72-44C9D...IXMtest_D14_s1_w5886B65E8-9B26-4BEA-98DD-F1534...IXMtest_D14_s1_w4A581954E-41BB-42C2-999D-91231...IXMtest_D14_s1_w32366353B-7500-4A39-8756-39EAD...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.11.267570e+1220586/20586/20586/20586/20586/NaN
41954196IXMtest_D14_s2_w236194DAE-70DA-4769-8A35-04199...IXMtest_D14_s2_w141CECDA9-5A06-42CF-9DF6-CC5A8...IXMtest_D14_s2_w555A0CE74-2554-41FD-8F09-97892...IXMtest_D14_s2_w414ECB73E-8AA0-41FE-9FEA-D9AC1...IXMtest_D14_s2_w387508B7F-F087-4186-AB8B-9E493...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.21.267570e+1220586/20586/20586/20586/20586/NaN
41964197IXMtest_D14_s3_w2F44F7D4D-8FA3-4A2B-AA66-DD476...IXMtest_D14_s3_w191EE929D-654B-4C99-8A0F-3F324...IXMtest_D14_s3_w539399806-E7F8-4A29-B604-04AF2...IXMtest_D14_s3_w4A1B5B202-7323-490B-8074-277FE...IXMtest_D14_s3_w3345CAD66-B1FE-4D98-A6F7-1CEFD...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.31.267570e+1220586/20586/20586/20586/20586/NaN
41974198IXMtest_D14_s4_w255BC67E4-B89E-4491-B11E-FCCEA...IXMtest_D14_s4_w1CE02AEAB-E014-4033-9F97-EF3D4...IXMtest_D14_s4_w532D34840-81B1-4B6C-AC5F-86A4C...IXMtest_D14_s4_w4703401C2-49D5-4ACE-B185-4C058...IXMtest_D14_s4_w3807D770C-80F0-4A17-9310-B7176...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.41.267570e+1220586/20586/20586/20586/20586/NaN
41984199IXMtest_D14_s5_w2C6CB5AD0-FABD-4AF0-8B60-F0B3B...IXMtest_D14_s5_w110875F57-3FD2-4F97-8A15-EEC9E...IXMtest_D14_s5_w5ADC816D6-656A-42B7-B6C0-0DFFC...IXMtest_D14_s5_w4D38124D8-DA3E-43F9-AFA3-2BE59...IXMtest_D14_s5_w3EE367BFF-AE68-4FAC-A6E6-46FAA...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.51.267570e+1220586/20586/20586/20586/20586/NaN
41994200IXMtest_D14_s6_w2065C6DE4-7737-4271-B77F-BF1D5...IXMtest_D14_s6_w11E8295C2-6FB1-4F2B-97AE-A22A7...IXMtest_D14_s6_w5E627B00C-54A0-4611-B7F7-ACA10...IXMtest_D14_s6_w4B39BBA41-9CDC-4508-A16E-B0429...IXMtest_D14_s6_w3EAE55115-EC24-408E-9255-FB4A6...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.61.267570e+1220586/20586/20586/20586/20586/NaN
42004201IXMtest_D14_s7_w2616EE522-D61A-4773-B5F8-A678F...IXMtest_D14_s7_w1A6716324-AE58-4585-975F-CF587...IXMtest_D14_s7_w505211AC8-5229-46DB-9067-D6943...IXMtest_D14_s7_w41A25E2FE-975B-4204-9172-4BF00...IXMtest_D14_s7_w36C760DDA-0715-4243-93DB-53BCD...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.71.267570e+1220586/20586/20586/20586/20586/NaN
42014202IXMtest_D14_s8_w270BA2267-733E-45D5-A0CB-61017...IXMtest_D14_s8_w1FC44A4B1-35C8-4F5F-B46D-6055C...IXMtest_D14_s8_w514B8B702-D565-4763-8F60-A0684...IXMtest_D14_s8_w400FC1631-F57E-4EE9-9E54-F9242...IXMtest_D14_s8_w3ADE3A78D-D781-42C0-A49C-C3BE8...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.81.267570e+1220586/20586/20586/20586/20586/NaN
42024203IXMtest_D14_s9_w2E0E9C5BF-9541-44BA-B7AF-389E4...IXMtest_D14_s9_w1461E6B60-1BE6-4105-880A-93FAD...IXMtest_D14_s9_w58C8EAED5-325B-43A0-AF9E-EF8C6...IXMtest_D14_s9_w47F0DF5C9-8F57-4F71-B113-6A05D...IXMtest_D14_s9_w3C175E6A6-2A41-494A-B82F-AD3E2...compoundBRD-K79905821-001-01-65.000000H-BIOA-003-1...GabazineBiomol International Inc.91.267570e+1220586/20586/20586/20586/20586/NaN
80198020IXMtest_F04_s1_w28B0CE185-B4FE-475A-B5D1-67509...IXMtest_F04_s1_w1747DA47C-A8D7-4435-A17B-3B936...IXMtest_F04_s1_w52D3352E9-FC10-4072-AD9B-49018...IXMtest_F04_s1_w4ACCAED6B-7D92-49CD-846C-C8D0E...IXMtest_F04_s1_w321AF6479-6747-4B0D-91BB-5F47E...compoundBRD-K93280214-004-02-32.708312H-BIOA-004-1...GabazinePrestwick Chemical Inc.11.267580e+1220589/20589/20589/20589/20589/NaN
\n", + "

10 rows × 23 columns

\n", + "" + ], + "text/plain": [ + " ImageNumber Image_FileName_OrigER \\\n", + "4194 4195 IXMtest_D14_s1_w205B05914-92D1-4E66-BF35-812B3... \n", + "4195 4196 IXMtest_D14_s2_w236194DAE-70DA-4769-8A35-04199... \n", + "4196 4197 IXMtest_D14_s3_w2F44F7D4D-8FA3-4A2B-AA66-DD476... \n", + "4197 4198 IXMtest_D14_s4_w255BC67E4-B89E-4491-B11E-FCCEA... \n", + "4198 4199 IXMtest_D14_s5_w2C6CB5AD0-FABD-4AF0-8B60-F0B3B... \n", + "4199 4200 IXMtest_D14_s6_w2065C6DE4-7737-4271-B77F-BF1D5... \n", + "4200 4201 IXMtest_D14_s7_w2616EE522-D61A-4773-B5F8-A678F... \n", + "4201 4202 IXMtest_D14_s8_w270BA2267-733E-45D5-A0CB-61017... \n", + "4202 4203 IXMtest_D14_s9_w2E0E9C5BF-9541-44BA-B7AF-389E4... \n", + "8019 8020 IXMtest_F04_s1_w28B0CE185-B4FE-475A-B5D1-67509... \n", + "\n", + " Image_FileName_OrigHoechst \\\n", + "4194 IXMtest_D14_s1_w12F978460-B7F4-4A65-AD72-44C9D... \n", + "4195 IXMtest_D14_s2_w141CECDA9-5A06-42CF-9DF6-CC5A8... \n", + "4196 IXMtest_D14_s3_w191EE929D-654B-4C99-8A0F-3F324... \n", + "4197 IXMtest_D14_s4_w1CE02AEAB-E014-4033-9F97-EF3D4... \n", + "4198 IXMtest_D14_s5_w110875F57-3FD2-4F97-8A15-EEC9E... \n", + "4199 IXMtest_D14_s6_w11E8295C2-6FB1-4F2B-97AE-A22A7... \n", + "4200 IXMtest_D14_s7_w1A6716324-AE58-4585-975F-CF587... \n", + "4201 IXMtest_D14_s8_w1FC44A4B1-35C8-4F5F-B46D-6055C... \n", + "4202 IXMtest_D14_s9_w1461E6B60-1BE6-4105-880A-93FAD... \n", + "8019 IXMtest_F04_s1_w1747DA47C-A8D7-4435-A17B-3B936... \n", + "\n", + " Image_FileName_OrigMito \\\n", + "4194 IXMtest_D14_s1_w5886B65E8-9B26-4BEA-98DD-F1534... \n", + "4195 IXMtest_D14_s2_w555A0CE74-2554-41FD-8F09-97892... \n", + "4196 IXMtest_D14_s3_w539399806-E7F8-4A29-B604-04AF2... \n", + "4197 IXMtest_D14_s4_w532D34840-81B1-4B6C-AC5F-86A4C... \n", + "4198 IXMtest_D14_s5_w5ADC816D6-656A-42B7-B6C0-0DFFC... \n", + "4199 IXMtest_D14_s6_w5E627B00C-54A0-4611-B7F7-ACA10... \n", + "4200 IXMtest_D14_s7_w505211AC8-5229-46DB-9067-D6943... \n", + "4201 IXMtest_D14_s8_w514B8B702-D565-4763-8F60-A0684... \n", + "4202 IXMtest_D14_s9_w58C8EAED5-325B-43A0-AF9E-EF8C6... \n", + "8019 IXMtest_F04_s1_w52D3352E9-FC10-4072-AD9B-49018... \n", + "\n", + " Image_FileName_OrigPh_golgi \\\n", + "4194 IXMtest_D14_s1_w4A581954E-41BB-42C2-999D-91231... \n", + "4195 IXMtest_D14_s2_w414ECB73E-8AA0-41FE-9FEA-D9AC1... \n", + "4196 IXMtest_D14_s3_w4A1B5B202-7323-490B-8074-277FE... \n", + "4197 IXMtest_D14_s4_w4703401C2-49D5-4ACE-B185-4C058... \n", + "4198 IXMtest_D14_s5_w4D38124D8-DA3E-43F9-AFA3-2BE59... \n", + "4199 IXMtest_D14_s6_w4B39BBA41-9CDC-4508-A16E-B0429... \n", + "4200 IXMtest_D14_s7_w41A25E2FE-975B-4204-9172-4BF00... \n", + "4201 IXMtest_D14_s8_w400FC1631-F57E-4EE9-9E54-F9242... \n", + "4202 IXMtest_D14_s9_w47F0DF5C9-8F57-4F71-B113-6A05D... \n", + "8019 IXMtest_F04_s1_w4ACCAED6B-7D92-49CD-846C-C8D0E... \n", + "\n", + " Image_FileName_OrigSyto \\\n", + "4194 IXMtest_D14_s1_w32366353B-7500-4A39-8756-39EAD... \n", + "4195 IXMtest_D14_s2_w387508B7F-F087-4186-AB8B-9E493... \n", + "4196 IXMtest_D14_s3_w3345CAD66-B1FE-4D98-A6F7-1CEFD... \n", + "4197 IXMtest_D14_s4_w3807D770C-80F0-4A17-9310-B7176... \n", + "4198 IXMtest_D14_s5_w3EE367BFF-AE68-4FAC-A6E6-46FAA... \n", + "4199 IXMtest_D14_s6_w3EAE55115-EC24-408E-9255-FB4A6... \n", + "4200 IXMtest_D14_s7_w36C760DDA-0715-4243-93DB-53BCD... \n", + "4201 IXMtest_D14_s8_w3ADE3A78D-D781-42C0-A49C-C3BE8... \n", + "4202 IXMtest_D14_s9_w3C175E6A6-2A41-494A-B82F-AD3E2... \n", + "8019 IXMtest_F04_s1_w321AF6479-6747-4B0D-91BB-5F47E... \n", + "\n", + " Image_Metadata_ASSAY_WELL_ROLE Image_Metadata_BROAD_ID \\\n", + "4194 compound BRD-K79905821-001-01-6 \n", + "4195 compound BRD-K79905821-001-01-6 \n", + "4196 compound BRD-K79905821-001-01-6 \n", + "4197 compound BRD-K79905821-001-01-6 \n", + "4198 compound BRD-K79905821-001-01-6 \n", + "4199 compound BRD-K79905821-001-01-6 \n", + "4200 compound BRD-K79905821-001-01-6 \n", + "4201 compound BRD-K79905821-001-01-6 \n", + "4202 compound BRD-K79905821-001-01-6 \n", + "8019 compound BRD-K93280214-004-02-3 \n", + "\n", + " Image_Metadata_CPD_MMOL_CONC Image_Metadata_CPD_PLATE_MAP_NAME ... \\\n", + "4194 5.000000 H-BIOA-003-1 ... \n", + "4195 5.000000 H-BIOA-003-1 ... \n", + "4196 5.000000 H-BIOA-003-1 ... \n", + "4197 5.000000 H-BIOA-003-1 ... \n", + "4198 5.000000 H-BIOA-003-1 ... \n", + "4199 5.000000 H-BIOA-003-1 ... \n", + "4200 5.000000 H-BIOA-003-1 ... \n", + "4201 5.000000 H-BIOA-003-1 ... \n", + "4202 5.000000 H-BIOA-003-1 ... \n", + "8019 2.708312 H-BIOA-004-1 ... \n", + "\n", + " Image_Metadata_SOURCE_COMPOUND_NAME Image_Metadata_SOURCE_NAME \\\n", + "4194 Gabazine Biomol International Inc. \n", + "4195 Gabazine Biomol International Inc. \n", + "4196 Gabazine Biomol International Inc. \n", + "4197 Gabazine Biomol International Inc. \n", + "4198 Gabazine Biomol International Inc. \n", + "4199 Gabazine Biomol International Inc. \n", + "4200 Gabazine Biomol International Inc. \n", + "4201 Gabazine Biomol International Inc. \n", + "4202 Gabazine Biomol International Inc. \n", + "8019 Gabazine Prestwick Chemical Inc. \n", + "\n", + " Image_Metadata_Site Image_Metadata_Time Image_PathName_OrigER \\\n", + "4194 1 1.267570e+12 20586/ \n", + "4195 2 1.267570e+12 20586/ \n", + "4196 3 1.267570e+12 20586/ \n", + "4197 4 1.267570e+12 20586/ \n", + "4198 5 1.267570e+12 20586/ \n", + "4199 6 1.267570e+12 20586/ \n", + "4200 7 1.267570e+12 20586/ \n", + "4201 8 1.267570e+12 20586/ \n", + "4202 9 1.267570e+12 20586/ \n", + "8019 1 1.267580e+12 20589/ \n", + "\n", + " Image_PathName_OrigHoechst Image_PathName_OrigMito \\\n", + "4194 20586/ 20586/ \n", + "4195 20586/ 20586/ \n", + "4196 20586/ 20586/ \n", + "4197 20586/ 20586/ \n", + "4198 20586/ 20586/ \n", + "4199 20586/ 20586/ \n", + "4200 20586/ 20586/ \n", + "4201 20586/ 20586/ \n", + "4202 20586/ 20586/ \n", + "8019 20589/ 20589/ \n", + "\n", + " Image_PathName_OrigPh_golgi Image_PathName_OrigSyto IXMtest \n", + "4194 20586/ 20586/ NaN \n", + "4195 20586/ 20586/ NaN \n", + "4196 20586/ 20586/ NaN \n", + "4197 20586/ 20586/ NaN \n", + "4198 20586/ 20586/ NaN \n", + "4199 20586/ 20586/ NaN \n", + "4200 20586/ 20586/ NaN \n", + "4201 20586/ 20586/ NaN \n", + "4202 20586/ 20586/ NaN \n", + "8019 20589/ 20589/ NaN \n", + "\n", + "[10 rows x 23 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "compounds = ['Gabazine', 'CYCLOPIAZONIC ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + "\n", + "\n", + "compound = compounds[0]\n", + "if compound == 'DMSO':\n", + " codf1 = df[df.Image_Metadata_ASSAY_WELL_ROLE == 'mock']\n", + " \n", + " for ind, plate in enumerate(plates) :\n", + " codf2 = codf1[codf1.Image_PathName_OrigHoechst == plate]\n", + " l = len(codf2)\n", + " rows = range(50)\n", + " codf2 = codf2.iloc[rows]\n", + " \n", + " if ind == 0:\n", + " indicies = list(codf2.index)\n", + " continue\n", + " \n", + " indicies += list(codf2.index)\n", + " codf = codf1.loc[indicies]\n", + " \n", + "else:\n", + " codf = df[df.Image_Metadata_SOURCE_COMPOUND_NAME.str.lower() == compound.lower()]\n", + "\n", + "codf.head(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Image_FileName_OrigHoechstImage_PathName_OrigHoechst
4194IXMtest_D14_s1_w12F978460-B7F4-4A65-AD72-44C9D...20586/
4195IXMtest_D14_s2_w141CECDA9-5A06-42CF-9DF6-CC5A8...20586/
4196IXMtest_D14_s3_w191EE929D-654B-4C99-8A0F-3F324...20586/
4197IXMtest_D14_s4_w1CE02AEAB-E014-4033-9F97-EF3D4...20586/
4198IXMtest_D14_s5_w110875F57-3FD2-4F97-8A15-EEC9E...20586/
4199IXMtest_D14_s6_w11E8295C2-6FB1-4F2B-97AE-A22A7...20586/
4200IXMtest_D14_s7_w1A6716324-AE58-4585-975F-CF587...20586/
4201IXMtest_D14_s8_w1FC44A4B1-35C8-4F5F-B46D-6055C...20586/
4202IXMtest_D14_s9_w1461E6B60-1BE6-4105-880A-93FAD...20586/
8019IXMtest_F04_s1_w1747DA47C-A8D7-4435-A17B-3B936...20589/
8020IXMtest_F04_s2_w10201BAB2-89E5-4FE0-83D3-BD7EA...20589/
8021IXMtest_F04_s3_w1A8B037B6-C37D-427D-978F-AF8D9...20589/
8022IXMtest_F04_s4_w14DAC099F-1F8D-47C7-AD97-38B47...20589/
8023IXMtest_F04_s5_w1F60F68FA-DAB0-4E4C-A49F-30C7B...20589/
8024IXMtest_F04_s6_w1CE907BF9-ACB4-4DB5-BBDF-79E82...20589/
8025IXMtest_F04_s7_w17263C8F1-274A-4EDF-959C-6A404...20589/
8026IXMtest_F04_s8_w16B1736BC-5BBD-4397-AE94-B8375...20589/
8027IXMtest_F04_s9_w1D32670CD-6ED3-4A0C-AA76-40F36...20589/
21501IXMtest_D14_s1_w1512BAD45-1E60-4AE4-AD14-A27A2...20593/
21502IXMtest_D14_s2_w14E73B4BE-D4CF-4F69-91CD-01978...20593/
21503IXMtest_D14_s3_w15F807DA3-AB87-4CEB-91D2-07E64...20593/
21504IXMtest_D14_s4_w1C25029E2-F686-4163-80CE-B7940...20593/
21505IXMtest_D14_s5_w1A23530B9-3754-43AB-8029-61B38...20593/
21506IXMtest_D14_s6_w1E23656D9-2F4B-4193-AF3F-F3515...20593/
21507IXMtest_D14_s7_w11689E8B9-379D-4AC0-B9D1-F9333...20593/
21508IXMtest_D14_s8_w19C44A265-8EAA-4168-AE76-77606...20593/
21509IXMtest_D14_s9_w1C4B83189-2CA7-452C-B6B6-86187...20593/
25299IXMtest_F04_s1_w10CC3FCE3-A91B-4584-8A26-6C24F...20594/
25300IXMtest_F04_s2_w128C6F48B-3320-46DB-986C-37286...20594/
25301IXMtest_F04_s3_w1C0CD7839-DD8E-4167-82BC-723C4...20594/
.........
38787IXMtest_D14_s7_w14C4C854C-B04B-4A99-8874-63FB0...20608/
38788IXMtest_D14_s8_w1BB7A43F1-8A71-4886-A2EB-BC05F...20608/
38789IXMtest_D14_s9_w11ECFCC74-1B6C-4D02-9761-17D94...20608/
42579IXMtest_F04_s1_w1FF3BA401-17ED-4855-A3AD-E8800...20625/
42580IXMtest_F04_s2_w1EF21FB51-3E40-413D-8585-4CDCF...20625/
42581IXMtest_F04_s3_w14A32D190-3130-460E-B08E-A127B...20625/
42582IXMtest_F04_s4_w152D1755D-6E2A-4F88-9A8B-346EF...20625/
42583IXMtest_F04_s5_w1C80828CC-724B-459D-8A5F-7BE11...20625/
42584IXMtest_F04_s6_w13EB649CA-087E-4594-83A8-A4303...20625/
42585IXMtest_F04_s7_w187D0B42F-7C48-4D5F-8B73-4AD01...20625/
42586IXMtest_F04_s8_w101040FE6-96BA-4CC0-9658-C45E0...20625/
42587IXMtest_F04_s9_w1F881EC09-D70A-4DC8-B073-F9120...20625/
56061IXMtest_D14_s1_w170277CB7-5995-4B29-AF9C-23D4B...20639/
56062IXMtest_D14_s2_w18F649829-D379-448C-AAC2-9191A...20639/
56063IXMtest_D14_s3_w1EF5FF1B5-A888-47CD-A16A-EAE16...20639/
56064IXMtest_D14_s4_w169755FB9-D420-495D-BA81-A7B54...20639/
56065IXMtest_D14_s5_w11D5DDA76-D483-492B-8B65-737B9...20639/
56066IXMtest_D14_s6_w1CE504357-84F2-4EF4-9D47-F21CC...20639/
56067IXMtest_D14_s7_w164F9AA05-8319-4316-B1E8-CE30A...20639/
56068IXMtest_D14_s8_w152EBED23-2A88-4615-A63E-398F4...20639/
56069IXMtest_D14_s9_w19B0D3466-1016-4A8E-8BC6-BE8BB...20639/
59859IXMtest_F04_s1_w1CD13792A-F9DD-4245-A516-BC9CF...20640/
59860IXMtest_F04_s2_w1D3E8F619-8497-415D-9B8C-FDF34...20640/
59861IXMtest_F04_s3_w1D06D063D-9688-45C8-849B-8BBE4...20640/
59862IXMtest_F04_s4_w1EDA551E9-F578-479F-9AD6-4B6C2...20640/
59863IXMtest_F04_s5_w1E3AFFCB3-6CC1-45C3-99E1-6FD8E...20640/
59864IXMtest_F04_s6_w12BAD78F1-225C-481C-80F8-6679C...20640/
59865IXMtest_F04_s7_w1DB8167FA-93EA-41DE-9B2E-D34B8...20640/
59866IXMtest_F04_s8_w172FC34A8-E906-4B33-948E-EF895...20640/
59867IXMtest_F04_s9_w18F72FCBC-56B7-4DD5-B5A4-5788A...20640/
\n", + "

72 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " Image_FileName_OrigHoechst \\\n", + "4194 IXMtest_D14_s1_w12F978460-B7F4-4A65-AD72-44C9D... \n", + "4195 IXMtest_D14_s2_w141CECDA9-5A06-42CF-9DF6-CC5A8... \n", + "4196 IXMtest_D14_s3_w191EE929D-654B-4C99-8A0F-3F324... \n", + "4197 IXMtest_D14_s4_w1CE02AEAB-E014-4033-9F97-EF3D4... \n", + "4198 IXMtest_D14_s5_w110875F57-3FD2-4F97-8A15-EEC9E... \n", + "4199 IXMtest_D14_s6_w11E8295C2-6FB1-4F2B-97AE-A22A7... \n", + "4200 IXMtest_D14_s7_w1A6716324-AE58-4585-975F-CF587... \n", + "4201 IXMtest_D14_s8_w1FC44A4B1-35C8-4F5F-B46D-6055C... \n", + "4202 IXMtest_D14_s9_w1461E6B60-1BE6-4105-880A-93FAD... \n", + "8019 IXMtest_F04_s1_w1747DA47C-A8D7-4435-A17B-3B936... \n", + "8020 IXMtest_F04_s2_w10201BAB2-89E5-4FE0-83D3-BD7EA... \n", + "8021 IXMtest_F04_s3_w1A8B037B6-C37D-427D-978F-AF8D9... \n", + "8022 IXMtest_F04_s4_w14DAC099F-1F8D-47C7-AD97-38B47... \n", + "8023 IXMtest_F04_s5_w1F60F68FA-DAB0-4E4C-A49F-30C7B... \n", + "8024 IXMtest_F04_s6_w1CE907BF9-ACB4-4DB5-BBDF-79E82... \n", + "8025 IXMtest_F04_s7_w17263C8F1-274A-4EDF-959C-6A404... \n", + "8026 IXMtest_F04_s8_w16B1736BC-5BBD-4397-AE94-B8375... \n", + "8027 IXMtest_F04_s9_w1D32670CD-6ED3-4A0C-AA76-40F36... \n", + "21501 IXMtest_D14_s1_w1512BAD45-1E60-4AE4-AD14-A27A2... \n", + "21502 IXMtest_D14_s2_w14E73B4BE-D4CF-4F69-91CD-01978... \n", + "21503 IXMtest_D14_s3_w15F807DA3-AB87-4CEB-91D2-07E64... \n", + "21504 IXMtest_D14_s4_w1C25029E2-F686-4163-80CE-B7940... \n", + "21505 IXMtest_D14_s5_w1A23530B9-3754-43AB-8029-61B38... \n", + "21506 IXMtest_D14_s6_w1E23656D9-2F4B-4193-AF3F-F3515... \n", + "21507 IXMtest_D14_s7_w11689E8B9-379D-4AC0-B9D1-F9333... \n", + "21508 IXMtest_D14_s8_w19C44A265-8EAA-4168-AE76-77606... \n", + "21509 IXMtest_D14_s9_w1C4B83189-2CA7-452C-B6B6-86187... \n", + "25299 IXMtest_F04_s1_w10CC3FCE3-A91B-4584-8A26-6C24F... \n", + "25300 IXMtest_F04_s2_w128C6F48B-3320-46DB-986C-37286... \n", + "25301 IXMtest_F04_s3_w1C0CD7839-DD8E-4167-82BC-723C4... \n", + "... ... \n", + "38787 IXMtest_D14_s7_w14C4C854C-B04B-4A99-8874-63FB0... \n", + "38788 IXMtest_D14_s8_w1BB7A43F1-8A71-4886-A2EB-BC05F... \n", + "38789 IXMtest_D14_s9_w11ECFCC74-1B6C-4D02-9761-17D94... \n", + "42579 IXMtest_F04_s1_w1FF3BA401-17ED-4855-A3AD-E8800... \n", + "42580 IXMtest_F04_s2_w1EF21FB51-3E40-413D-8585-4CDCF... \n", + "42581 IXMtest_F04_s3_w14A32D190-3130-460E-B08E-A127B... \n", + "42582 IXMtest_F04_s4_w152D1755D-6E2A-4F88-9A8B-346EF... \n", + "42583 IXMtest_F04_s5_w1C80828CC-724B-459D-8A5F-7BE11... \n", + "42584 IXMtest_F04_s6_w13EB649CA-087E-4594-83A8-A4303... \n", + "42585 IXMtest_F04_s7_w187D0B42F-7C48-4D5F-8B73-4AD01... \n", + "42586 IXMtest_F04_s8_w101040FE6-96BA-4CC0-9658-C45E0... \n", + "42587 IXMtest_F04_s9_w1F881EC09-D70A-4DC8-B073-F9120... \n", + "56061 IXMtest_D14_s1_w170277CB7-5995-4B29-AF9C-23D4B... \n", + "56062 IXMtest_D14_s2_w18F649829-D379-448C-AAC2-9191A... \n", + "56063 IXMtest_D14_s3_w1EF5FF1B5-A888-47CD-A16A-EAE16... \n", + "56064 IXMtest_D14_s4_w169755FB9-D420-495D-BA81-A7B54... \n", + "56065 IXMtest_D14_s5_w11D5DDA76-D483-492B-8B65-737B9... \n", + "56066 IXMtest_D14_s6_w1CE504357-84F2-4EF4-9D47-F21CC... \n", + "56067 IXMtest_D14_s7_w164F9AA05-8319-4316-B1E8-CE30A... \n", + "56068 IXMtest_D14_s8_w152EBED23-2A88-4615-A63E-398F4... \n", + "56069 IXMtest_D14_s9_w19B0D3466-1016-4A8E-8BC6-BE8BB... \n", + "59859 IXMtest_F04_s1_w1CD13792A-F9DD-4245-A516-BC9CF... \n", + "59860 IXMtest_F04_s2_w1D3E8F619-8497-415D-9B8C-FDF34... \n", + "59861 IXMtest_F04_s3_w1D06D063D-9688-45C8-849B-8BBE4... \n", + "59862 IXMtest_F04_s4_w1EDA551E9-F578-479F-9AD6-4B6C2... \n", + "59863 IXMtest_F04_s5_w1E3AFFCB3-6CC1-45C3-99E1-6FD8E... \n", + "59864 IXMtest_F04_s6_w12BAD78F1-225C-481C-80F8-6679C... \n", + "59865 IXMtest_F04_s7_w1DB8167FA-93EA-41DE-9B2E-D34B8... \n", + "59866 IXMtest_F04_s8_w172FC34A8-E906-4B33-948E-EF895... \n", + "59867 IXMtest_F04_s9_w18F72FCBC-56B7-4DD5-B5A4-5788A... \n", + "\n", + " Image_PathName_OrigHoechst \n", + "4194 20586/ \n", + "4195 20586/ \n", + "4196 20586/ \n", + "4197 20586/ \n", + "4198 20586/ \n", + "4199 20586/ \n", + "4200 20586/ \n", + "4201 20586/ \n", + "4202 20586/ \n", + "8019 20589/ \n", + "8020 20589/ \n", + "8021 20589/ \n", + "8022 20589/ \n", + "8023 20589/ \n", + "8024 20589/ \n", + "8025 20589/ \n", + "8026 20589/ \n", + "8027 20589/ \n", + "21501 20593/ \n", + "21502 20593/ \n", + "21503 20593/ \n", + "21504 20593/ \n", + "21505 20593/ \n", + "21506 20593/ \n", + "21507 20593/ \n", + "21508 20593/ \n", + "21509 20593/ \n", + "25299 20594/ \n", + "25300 20594/ \n", + "25301 20594/ \n", + "... ... \n", + "38787 20608/ \n", + "38788 20608/ \n", + "38789 20608/ \n", + "42579 20625/ \n", + "42580 20625/ \n", + "42581 20625/ \n", + "42582 20625/ \n", + "42583 20625/ \n", + "42584 20625/ \n", + "42585 20625/ \n", + "42586 20625/ \n", + "42587 20625/ \n", + "56061 20639/ \n", + "56062 20639/ \n", + "56063 20639/ \n", + "56064 20639/ \n", + "56065 20639/ \n", + "56066 20639/ \n", + "56067 20639/ \n", + "56068 20639/ \n", + "56069 20639/ \n", + "59859 20640/ \n", + "59860 20640/ \n", + "59861 20640/ \n", + "59862 20640/ \n", + "59863 20640/ \n", + "59864 20640/ \n", + "59865 20640/ \n", + "59866 20640/ \n", + "59867 20640/ \n", + "\n", + "[72 rows x 2 columns]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "coimages = codf[['Image_FileName_OrigHoechst',\n", + " 'Image_PathName_OrigHoechst']]\n", + "\n", + "coimages" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DNADone
\n", + "
" + ], + "text/plain": [ + "Empty DataFrame\n", + "Columns: [DNA, Done]\n", + "Index: []" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "image_list = pd.DataFrame(columns = ['DNA', 'Done'])\n", + "\n", + "image_list" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
DNADone
0BBBC022_v1_images_20586w1/IXMtest_D14_s1_w12F9...False
1BBBC022_v1_images_20586w1/IXMtest_D14_s2_w141C...False
2BBBC022_v1_images_20586w1/IXMtest_D14_s3_w191E...False
3BBBC022_v1_images_20586w1/IXMtest_D14_s4_w1CE0...False
4BBBC022_v1_images_20586w1/IXMtest_D14_s5_w1108...False
5BBBC022_v1_images_20586w1/IXMtest_D14_s6_w11E8...False
6BBBC022_v1_images_20586w1/IXMtest_D14_s7_w1A67...False
7BBBC022_v1_images_20586w1/IXMtest_D14_s8_w1FC4...False
8BBBC022_v1_images_20586w1/IXMtest_D14_s9_w1461...False
9BBBC022_v1_images_20589w1/IXMtest_F04_s1_w1747...False
10BBBC022_v1_images_20589w1/IXMtest_F04_s2_w1020...False
11BBBC022_v1_images_20589w1/IXMtest_F04_s3_w1A8B...False
12BBBC022_v1_images_20589w1/IXMtest_F04_s4_w14DA...False
13BBBC022_v1_images_20589w1/IXMtest_F04_s5_w1F60...False
14BBBC022_v1_images_20589w1/IXMtest_F04_s6_w1CE9...False
15BBBC022_v1_images_20589w1/IXMtest_F04_s7_w1726...False
16BBBC022_v1_images_20589w1/IXMtest_F04_s8_w16B1...False
17BBBC022_v1_images_20589w1/IXMtest_F04_s9_w1D32...False
18BBBC022_v1_images_20593w1/IXMtest_D14_s1_w1512...False
19BBBC022_v1_images_20593w1/IXMtest_D14_s2_w14E7...False
20BBBC022_v1_images_20593w1/IXMtest_D14_s3_w15F8...False
21BBBC022_v1_images_20593w1/IXMtest_D14_s4_w1C25...False
22BBBC022_v1_images_20593w1/IXMtest_D14_s5_w1A23...False
23BBBC022_v1_images_20593w1/IXMtest_D14_s6_w1E23...False
24BBBC022_v1_images_20593w1/IXMtest_D14_s7_w1168...False
25BBBC022_v1_images_20593w1/IXMtest_D14_s8_w19C4...False
26BBBC022_v1_images_20593w1/IXMtest_D14_s9_w1C4B...False
27BBBC022_v1_images_20594w1/IXMtest_F04_s1_w10CC...False
28BBBC022_v1_images_20594w1/IXMtest_F04_s2_w128C...False
29BBBC022_v1_images_20594w1/IXMtest_F04_s3_w1C0C...False
.........
42BBBC022_v1_images_20608w1/IXMtest_D14_s7_w14C4...False
43BBBC022_v1_images_20608w1/IXMtest_D14_s8_w1BB7...False
44BBBC022_v1_images_20608w1/IXMtest_D14_s9_w11EC...False
45BBBC022_v1_images_20625w1/IXMtest_F04_s1_w1FF3...False
46BBBC022_v1_images_20625w1/IXMtest_F04_s2_w1EF2...False
47BBBC022_v1_images_20625w1/IXMtest_F04_s3_w14A3...False
48BBBC022_v1_images_20625w1/IXMtest_F04_s4_w152D...False
49BBBC022_v1_images_20625w1/IXMtest_F04_s5_w1C80...False
50BBBC022_v1_images_20625w1/IXMtest_F04_s6_w13EB...False
51BBBC022_v1_images_20625w1/IXMtest_F04_s7_w187D...False
52BBBC022_v1_images_20625w1/IXMtest_F04_s8_w1010...False
53BBBC022_v1_images_20625w1/IXMtest_F04_s9_w1F88...False
54BBBC022_v1_images_20639w1/IXMtest_D14_s1_w1702...False
55BBBC022_v1_images_20639w1/IXMtest_D14_s2_w18F6...False
56BBBC022_v1_images_20639w1/IXMtest_D14_s3_w1EF5...False
57BBBC022_v1_images_20639w1/IXMtest_D14_s4_w1697...False
58BBBC022_v1_images_20639w1/IXMtest_D14_s5_w11D5...False
59BBBC022_v1_images_20639w1/IXMtest_D14_s6_w1CE5...False
60BBBC022_v1_images_20639w1/IXMtest_D14_s7_w164F...False
61BBBC022_v1_images_20639w1/IXMtest_D14_s8_w152E...False
62BBBC022_v1_images_20639w1/IXMtest_D14_s9_w19B0...False
63BBBC022_v1_images_20640w1/IXMtest_F04_s1_w1CD1...False
64BBBC022_v1_images_20640w1/IXMtest_F04_s2_w1D3E...False
65BBBC022_v1_images_20640w1/IXMtest_F04_s3_w1D06...False
66BBBC022_v1_images_20640w1/IXMtest_F04_s4_w1EDA...False
67BBBC022_v1_images_20640w1/IXMtest_F04_s5_w1E3A...False
68BBBC022_v1_images_20640w1/IXMtest_F04_s6_w12BA...False
69BBBC022_v1_images_20640w1/IXMtest_F04_s7_w1DB8...False
70BBBC022_v1_images_20640w1/IXMtest_F04_s8_w172F...False
71BBBC022_v1_images_20640w1/IXMtest_F04_s9_w18F7...False
\n", + "

72 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " DNA Done\n", + "0 BBBC022_v1_images_20586w1/IXMtest_D14_s1_w12F9... False\n", + "1 BBBC022_v1_images_20586w1/IXMtest_D14_s2_w141C... False\n", + "2 BBBC022_v1_images_20586w1/IXMtest_D14_s3_w191E... False\n", + "3 BBBC022_v1_images_20586w1/IXMtest_D14_s4_w1CE0... False\n", + "4 BBBC022_v1_images_20586w1/IXMtest_D14_s5_w1108... False\n", + "5 BBBC022_v1_images_20586w1/IXMtest_D14_s6_w11E8... False\n", + "6 BBBC022_v1_images_20586w1/IXMtest_D14_s7_w1A67... False\n", + "7 BBBC022_v1_images_20586w1/IXMtest_D14_s8_w1FC4... False\n", + "8 BBBC022_v1_images_20586w1/IXMtest_D14_s9_w1461... False\n", + "9 BBBC022_v1_images_20589w1/IXMtest_F04_s1_w1747... False\n", + "10 BBBC022_v1_images_20589w1/IXMtest_F04_s2_w1020... False\n", + "11 BBBC022_v1_images_20589w1/IXMtest_F04_s3_w1A8B... False\n", + "12 BBBC022_v1_images_20589w1/IXMtest_F04_s4_w14DA... False\n", + "13 BBBC022_v1_images_20589w1/IXMtest_F04_s5_w1F60... False\n", + "14 BBBC022_v1_images_20589w1/IXMtest_F04_s6_w1CE9... False\n", + "15 BBBC022_v1_images_20589w1/IXMtest_F04_s7_w1726... False\n", + "16 BBBC022_v1_images_20589w1/IXMtest_F04_s8_w16B1... False\n", + "17 BBBC022_v1_images_20589w1/IXMtest_F04_s9_w1D32... False\n", + "18 BBBC022_v1_images_20593w1/IXMtest_D14_s1_w1512... False\n", + "19 BBBC022_v1_images_20593w1/IXMtest_D14_s2_w14E7... False\n", + "20 BBBC022_v1_images_20593w1/IXMtest_D14_s3_w15F8... False\n", + "21 BBBC022_v1_images_20593w1/IXMtest_D14_s4_w1C25... False\n", + "22 BBBC022_v1_images_20593w1/IXMtest_D14_s5_w1A23... False\n", + "23 BBBC022_v1_images_20593w1/IXMtest_D14_s6_w1E23... False\n", + "24 BBBC022_v1_images_20593w1/IXMtest_D14_s7_w1168... False\n", + "25 BBBC022_v1_images_20593w1/IXMtest_D14_s8_w19C4... False\n", + "26 BBBC022_v1_images_20593w1/IXMtest_D14_s9_w1C4B... False\n", + "27 BBBC022_v1_images_20594w1/IXMtest_F04_s1_w10CC... False\n", + "28 BBBC022_v1_images_20594w1/IXMtest_F04_s2_w128C... False\n", + "29 BBBC022_v1_images_20594w1/IXMtest_F04_s3_w1C0C... False\n", + ".. ... ...\n", + "42 BBBC022_v1_images_20608w1/IXMtest_D14_s7_w14C4... False\n", + "43 BBBC022_v1_images_20608w1/IXMtest_D14_s8_w1BB7... False\n", + "44 BBBC022_v1_images_20608w1/IXMtest_D14_s9_w11EC... False\n", + "45 BBBC022_v1_images_20625w1/IXMtest_F04_s1_w1FF3... False\n", + "46 BBBC022_v1_images_20625w1/IXMtest_F04_s2_w1EF2... False\n", + "47 BBBC022_v1_images_20625w1/IXMtest_F04_s3_w14A3... False\n", + "48 BBBC022_v1_images_20625w1/IXMtest_F04_s4_w152D... False\n", + "49 BBBC022_v1_images_20625w1/IXMtest_F04_s5_w1C80... False\n", + "50 BBBC022_v1_images_20625w1/IXMtest_F04_s6_w13EB... False\n", + "51 BBBC022_v1_images_20625w1/IXMtest_F04_s7_w187D... False\n", + "52 BBBC022_v1_images_20625w1/IXMtest_F04_s8_w1010... False\n", + "53 BBBC022_v1_images_20625w1/IXMtest_F04_s9_w1F88... False\n", + "54 BBBC022_v1_images_20639w1/IXMtest_D14_s1_w1702... False\n", + "55 BBBC022_v1_images_20639w1/IXMtest_D14_s2_w18F6... False\n", + "56 BBBC022_v1_images_20639w1/IXMtest_D14_s3_w1EF5... False\n", + "57 BBBC022_v1_images_20639w1/IXMtest_D14_s4_w1697... False\n", + "58 BBBC022_v1_images_20639w1/IXMtest_D14_s5_w11D5... False\n", + "59 BBBC022_v1_images_20639w1/IXMtest_D14_s6_w1CE5... False\n", + "60 BBBC022_v1_images_20639w1/IXMtest_D14_s7_w164F... False\n", + "61 BBBC022_v1_images_20639w1/IXMtest_D14_s8_w152E... False\n", + "62 BBBC022_v1_images_20639w1/IXMtest_D14_s9_w19B0... False\n", + "63 BBBC022_v1_images_20640w1/IXMtest_F04_s1_w1CD1... False\n", + "64 BBBC022_v1_images_20640w1/IXMtest_F04_s2_w1D3E... False\n", + "65 BBBC022_v1_images_20640w1/IXMtest_F04_s3_w1D06... False\n", + "66 BBBC022_v1_images_20640w1/IXMtest_F04_s4_w1EDA... False\n", + "67 BBBC022_v1_images_20640w1/IXMtest_F04_s5_w1E3A... False\n", + "68 BBBC022_v1_images_20640w1/IXMtest_F04_s6_w12BA... False\n", + "69 BBBC022_v1_images_20640w1/IXMtest_F04_s7_w1DB8... False\n", + "70 BBBC022_v1_images_20640w1/IXMtest_F04_s8_w172F... False\n", + "71 BBBC022_v1_images_20640w1/IXMtest_F04_s9_w18F7... False\n", + "\n", + "[72 rows x 2 columns]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for i in range(0, len(coimages.index)):\n", + " row = coimages.index[i]\n", + "# print('\"{}\"'.format(coimages.iloc[i].Image_FileName_OrigHoechst))\n", + " ipath = 'BBBC022_v1_images_' \n", + " ipath += coimages.iloc[i].Image_PathName_OrigHoechst[:-1] \n", + " ipath += 'w' \n", + " ipath += coimages.iloc[i].Image_FileName_OrigHoechst[16]\n", + " ipath += '/'\n", + " ipath += coimages.iloc[i].Image_FileName_OrigHoechst\n", + "# print(\"Path of index {} : {}\".format(row, ipath))\n", + " image_list.loc[i] = [ipath, 'False']\n", + " \n", + "image_list" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Separating the images from the rest" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from shutil import copyfile\n", + "import os.path" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Directory ./../UNET/Gabazine already exists\n", + "Directory ./../UNET/Gabazine/masks Created \n", + "Directory ./../UNET/Gabazine/cp Created \n", + "Directory ./../CP/Gabazine already exists\n", + "Directory ./../UNET/Gabazine/images Created \n" + ] + } + ], + "source": [ + "dirName = './../UNET/' + compound\n", + "try:\n", + " # Create target Directory\n", + " os.mkdir(dirName)\n", + " print(\"Directory \" , dirName , \" Created \") \n", + "except FileExistsError:\n", + " print(\"Directory \" , dirName , \" already exists\")\n", + "\n", + "\n", + "dirName = './../UNET/' + compound + '/masks'\n", + "try:\n", + " # Create target Directory\n", + " os.mkdir(dirName)\n", + " print(\"Directory \" , dirName , \" Created \") \n", + "except FileExistsError:\n", + " print(\"Directory \" , dirName , \" already exists\")\n", + "\n", + "dirName = './../UNET/' + compound + '/cp'\n", + "try:\n", + " # Create target Directory\n", + " os.mkdir(dirName)\n", + " print(\"Directory \" , dirName , \" Created \") \n", + "except FileExistsError:\n", + " print(\"Directory \" , dirName , \" already exists\")\n", + " \n", + " \n", + "dirName = './../CP/' + compound\n", + "try:\n", + " # Create target Directory\n", + " os.mkdir(dirName)\n", + " print(\"Directory \" , dirName , \" Created \") \n", + "except FileExistsError:\n", + " print(\"Directory \" , dirName , \" already exists\")\n", + "\n", + "\n", + "dirName = './../UNET/' + compound + '/images'\n", + "try:\n", + " # Create target Directory\n", + " os.mkdir(dirName)\n", + " print(\"Directory \" , dirName , \" Created \") \n", + "except FileExistsError:\n", + " print(\"Directory \" , dirName , \" already exists\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Need to change the path below : enter the source of your images !" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(0, len(image_list)):\n", + " \n", + " # This path should be the source of the BBBC dataset (not the images, the different folders)\n", + " src = './../../DSBPaper/code/unet4nuclei/data/' + image_list.DNA.iloc[i]\n", + " \n", + " \n", + " dst = dirName + '/' + image_list.DNA.iloc[i][:25] + image_list.DNA.iloc[i][26:]\n", + " if not(os.path.isfile(dst)):\n", + " copyfile(src, dst)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " DNA Done\n", + "0 /BBBC022_v1_images_20586w1IXMtest_D14_s1_w12F9... False\n", + "1 /BBBC022_v1_images_20586w1IXMtest_D14_s2_w141C... False\n", + "2 /BBBC022_v1_images_20586w1IXMtest_D14_s3_w191E... False\n", + "3 /BBBC022_v1_images_20586w1IXMtest_D14_s4_w1CE0... False\n", + "4 /BBBC022_v1_images_20586w1IXMtest_D14_s5_w1108... False\n", + "5 /BBBC022_v1_images_20586w1IXMtest_D14_s6_w11E8... False\n", + "6 /BBBC022_v1_images_20586w1IXMtest_D14_s7_w1A67... False\n", + "7 /BBBC022_v1_images_20586w1IXMtest_D14_s8_w1FC4... False\n", + "8 /BBBC022_v1_images_20586w1IXMtest_D14_s9_w1461... False\n", + "9 /BBBC022_v1_images_20589w1IXMtest_F04_s1_w1747... False\n", + "10 /BBBC022_v1_images_20589w1IXMtest_F04_s2_w1020... False\n", + "11 /BBBC022_v1_images_20589w1IXMtest_F04_s3_w1A8B... False\n", + "12 /BBBC022_v1_images_20589w1IXMtest_F04_s4_w14DA... False\n", + "13 /BBBC022_v1_images_20589w1IXMtest_F04_s5_w1F60... False\n", + "14 /BBBC022_v1_images_20589w1IXMtest_F04_s6_w1CE9... False\n", + "15 /BBBC022_v1_images_20589w1IXMtest_F04_s7_w1726... False\n", + "16 /BBBC022_v1_images_20589w1IXMtest_F04_s8_w16B1... False\n", + "17 /BBBC022_v1_images_20589w1IXMtest_F04_s9_w1D32... False\n", + "18 /BBBC022_v1_images_20593w1IXMtest_D14_s1_w1512... False\n", + "19 /BBBC022_v1_images_20593w1IXMtest_D14_s2_w14E7... False\n", + "20 /BBBC022_v1_images_20593w1IXMtest_D14_s3_w15F8... False\n", + "21 /BBBC022_v1_images_20593w1IXMtest_D14_s4_w1C25... False\n", + "22 /BBBC022_v1_images_20593w1IXMtest_D14_s5_w1A23... False\n", + "23 /BBBC022_v1_images_20593w1IXMtest_D14_s6_w1E23... False\n", + "24 /BBBC022_v1_images_20593w1IXMtest_D14_s7_w1168... False\n", + "25 /BBBC022_v1_images_20593w1IXMtest_D14_s8_w19C4... False\n", + "26 /BBBC022_v1_images_20593w1IXMtest_D14_s9_w1C4B... False\n", + "27 /BBBC022_v1_images_20594w1IXMtest_F04_s1_w10CC... False\n", + "28 /BBBC022_v1_images_20594w1IXMtest_F04_s2_w128C... False\n", + "29 /BBBC022_v1_images_20594w1IXMtest_F04_s3_w1C0C... False\n", + ".. ... ...\n", + "42 /BBBC022_v1_images_20608w1IXMtest_D14_s7_w14C4... False\n", + "43 /BBBC022_v1_images_20608w1IXMtest_D14_s8_w1BB7... False\n", + "44 /BBBC022_v1_images_20608w1IXMtest_D14_s9_w11EC... False\n", + "45 /BBBC022_v1_images_20625w1IXMtest_F04_s1_w1FF3... False\n", + "46 /BBBC022_v1_images_20625w1IXMtest_F04_s2_w1EF2... False\n", + "47 /BBBC022_v1_images_20625w1IXMtest_F04_s3_w14A3... False\n", + "48 /BBBC022_v1_images_20625w1IXMtest_F04_s4_w152D... False\n", + "49 /BBBC022_v1_images_20625w1IXMtest_F04_s5_w1C80... False\n", + "50 /BBBC022_v1_images_20625w1IXMtest_F04_s6_w13EB... False\n", + "51 /BBBC022_v1_images_20625w1IXMtest_F04_s7_w187D... False\n", + "52 /BBBC022_v1_images_20625w1IXMtest_F04_s8_w1010... False\n", + "53 /BBBC022_v1_images_20625w1IXMtest_F04_s9_w1F88... False\n", + "54 /BBBC022_v1_images_20639w1IXMtest_D14_s1_w1702... False\n", + "55 /BBBC022_v1_images_20639w1IXMtest_D14_s2_w18F6... False\n", + "56 /BBBC022_v1_images_20639w1IXMtest_D14_s3_w1EF5... False\n", + "57 /BBBC022_v1_images_20639w1IXMtest_D14_s4_w1697... False\n", + "58 /BBBC022_v1_images_20639w1IXMtest_D14_s5_w11D5... False\n", + "59 /BBBC022_v1_images_20639w1IXMtest_D14_s6_w1CE5... False\n", + "60 /BBBC022_v1_images_20639w1IXMtest_D14_s7_w164F... False\n", + "61 /BBBC022_v1_images_20639w1IXMtest_D14_s8_w152E... False\n", + "62 /BBBC022_v1_images_20639w1IXMtest_D14_s9_w19B0... False\n", + "63 /BBBC022_v1_images_20640w1IXMtest_F04_s1_w1CD1... False\n", + "64 /BBBC022_v1_images_20640w1IXMtest_F04_s2_w1D3E... False\n", + "65 /BBBC022_v1_images_20640w1IXMtest_F04_s3_w1D06... False\n", + "66 /BBBC022_v1_images_20640w1IXMtest_F04_s4_w1EDA... False\n", + "67 /BBBC022_v1_images_20640w1IXMtest_F04_s5_w1E3A... False\n", + "68 /BBBC022_v1_images_20640w1IXMtest_F04_s6_w12BA... False\n", + "69 /BBBC022_v1_images_20640w1IXMtest_F04_s7_w1DB8... False\n", + "70 /BBBC022_v1_images_20640w1IXMtest_F04_s8_w172F... False\n", + "71 /BBBC022_v1_images_20640w1IXMtest_F04_s9_w18F7... False\n", + "\n", + "[72 rows x 2 columns]\n" + ] + } + ], + "source": [ + "for i in range(0, len(image_list)):\n", + " image_list.DNA.iloc[i] = '/' + image_list.DNA.iloc[i][:25] + image_list.DNA.iloc[i][26:]\n", + "\n", + "fname = compound + '_image_list.csv'\n", + "image_list.to_csv(fname)\n", + "print(image_list)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array(['False'], dtype=object)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "numpy.unique(image_list.Done.values)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/cell-cycle/README.md b/cell-cycle/README.md new file mode 100644 index 0000000..93c4d20 --- /dev/null +++ b/cell-cycle/README.md @@ -0,0 +1,8 @@ +# Measuring cell cycle disruption. + +By Mattheui Broisin @mbroisinBI + +The details of this experiment can be found in the `Results.ipynb` notebook. +The results are reported in the paper. + + diff --git a/cell-cycle/Results.ipynb b/cell-cycle/Results.ipynb new file mode 100644 index 0000000..f6a6579 --- /dev/null +++ b/cell-cycle/Results.ipynb @@ -0,0 +1,858 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Results of the Cytometry experiment\n", + "\n", + "### Summary of the experiment\n", + "\n", + "1. Data gathering from [BBBC022](https://data.broadinstitute.org/bbbc/BBBC022/) \n", + "\n", + "2. Compound selection and data conditioning (`Compound_Images_List.ipynb`)\n", + "\n", + "3. Segmentation and DNA content analysis (for UNET)\n", + " * Segmentation (`batch_prediction.py`)\n", + " * DNA content analysis\n", + "\n", + "4. Segmentation and DNA content analysis (for CellProfiler)\n", + " * Segmentation\n", + " * DNA content analysis\n", + "\n", + "5. Results exploitation (`Results.ipynb`)\n", + "\n", + "\n", + "### 1. Data gathering\n", + "\n", + "The dataset is coming from the [Broad Bioimage Benchmark Collection](https://data.broadinstitute.org/bbbc/index.html) and its name is [BBBC022](https://data.broadinstitute.org/bbbc/BBBC022/). The channel we are interested in is the Hoechst 33342 channel, so we only need to download the `.zip` files ending with `w1`. All the files must be decompressed and stored in a `/data` folder.\n", + "\n", + "\n", + "### 2. Compound selection and data conditioning\n", + "\n", + "The list of the available compounds is printed by the first cells of `Compound_Images_List.ipynb`. In the section \"Compound selection\" we can choose which compound to analyse.\n", + "\n", + "Once the compound is selected, we can run the entire notebook `Compound_Images_List.ipynb` for the desired compound. It should output a `.csv` file in the current folder with the list of the images corresponding to the compound. It should also create the structure of the directories in order to use the `batch_prediction.py` code.\n", + "\n", + "It creates a `data/COMPOUND` directory with three subdirectories : \n", + "* `/COMPOUND/images` : folder of the images corresponding to the compound (filled by `Compound_Images_List.ipynb`)\n", + "* `/COMPOUND/masks` : folder of the masks found with UNET (computed by `batch_prediction.py`)\n", + "* `/COMPOUND/cp` : folder of the intermediary output of the DNA content analysis with CellProfiler\n", + "\n", + "It also creates a `/COMPOUND` folder in another location where the results of the CellProfiler segmentation will be stored.\n", + "\n", + "\n", + "### 3. Segmentation and DNA content analysis (for UNET)\n", + "\n", + "#### 3. a) Segmentation\n", + "\n", + "Use the `batch_prediction.py` file to process the images. It should be called by :\n", + "\n", + "```python batch_prediction.py experiment_name image_list.csv input_dir output_dir```\n", + "\n", + "The recommended structure is :\n", + "\n", + "```python3 batch_prediction.py experiment1 ./../../DSB\\ paper/COMPOUND_image_list.csv ./../../UNET/COMPOUND/images/ ./../../UNET/COMPOUND/masks/```\n", + "\n", + "where `COMPOUND` is the selected compound for the compound analysis.\n", + "\n", + "\n", + "*Warnings:* \n", + "* This command should be called in the folder where `batch_prediction.py` is\n", + "* python3 can be used instead of python\n", + "* The experiment name can be chosen by the user\n", + "* The list of images is the output of the compound analysis\n", + "* The `model.hdf5` file should be located in the `experiment/experiment_name` folder\n", + "\n", + "This step takes care of the segmentation using UNET. It outputs the mask corresponding of each image in `COMPOUND_image_list.csv `.\n", + "\n", + "\n", + "#### 3. b) DNA content analysis\n", + "\n", + "Use a pipeline in CellProfiler to compute the integrated intensity of the images, masked by the segmentation masks from the prediction. The pipeline consists of a `MeasureObjectIntensity` layer and some other layers to save the results.\n", + "\n", + "For the several compounds (Gabazine, CYCLOPIAZONIC ACID, ESTRADIOL, DMSO, Proglumide, PIZOTIFEN, CATECHIN, PENTABENZOATE, TRIADIMEFON, 3-HYDROXYCOUMARIN, Etoposide, DACTINOMYCIN, Colchicine, Blebbistatin) the projects are saved into their respective folders (`/data/COMPOUND`). The pipeline for another compound should have exactly the same structure in order to use this notebook. \n", + "\n", + "\n", + "### 4. Segmentation and DNA content analysis (for CellProfiler)\n", + "\n", + "The segmentation and the DNA content analysis for CellProfiler are done using the same pipeline. The pipeline consists of one step of segmentation (`IdentifyPrimaryObjects` layer) and one step of measurement (`MeasureObjectIntensity`) to compute the DNA content of the objects.\n", + "\n", + "\n", + "### 5. Results exploitation\n", + "\n", + "DNA content analysis is the purpose of this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.image as mpimg\n", + "\n", + "import math\n", + "\n", + "import skimage\n", + "from skimage import io\n", + "\n", + "from os import listdir\n", + "from os.path import isfile, join" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Results gathering\n", + "\n", + "\n", + "The following functions load the `.csv` file corresponding to the DNA content analysis for UNET and CellProfiler. If the above structure is respected, they should work perfectly, otherwise the paths may need to be adapted.\n", + "\n", + "They create for each segmentation method (UNET and CP) a dataframe containing the information of the analysis :\n", + "\n", + "| Index | ImageNumber | ObjectNumber | Integrated_Intensity | Plate |\n", + "|:-----:|-------------|--------------|----------------------|-------|\n", + "| **0** | 1 | 1 | 2.245075 | 20589 |\n", + "| **1** | 1 | 2 | 1.579263 | 20589 |\n", + "| ... | ... | ... | ... | ... |\n", + "\n", + "\n", + "Each *ImageNumber* corresponds to an image, each *ObjectNumber* corresponds to one found nucleus. The *Integrated_Intensity* field shows the result of the DNA content analysis, and we keep the information of the plate.\n", + "\n", + "For each compound analysis, we have 4 dataframes of the above type :\n", + "\n", + "| Compound\\Method | UNET | CP |\n", + "|:----------------:|--------------|------------|\n", + "| **Compound X :** | df_unet_cmpd | df_cp_cmpd |\n", + "| **DMSO :** | df_unet_dmso | df_cp_dmso |\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def construct_dataframe_unet(data_path, compound_number):\n", + " compounds = ['Gabazine', 'CYCLOPIAZONIC ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + "\n", + " compounds2 = ['Gabazine', 'CYCLOPIAZONIC_ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN_PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + "\n", + " compound = compounds[compound_number]\n", + " compound2 = compounds2[compound_number]\n", + "\n", + " mypath = data_path + compound\n", + " onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f)) and f not in ['UNET_MASKS.csv', 'UNET_Image.csv']]\n", + " masks_end = 'MASKS.csv'\n", + " image_end = 'Image.csv'\n", + " masks_file = mypath + '/'\n", + " image_file = mypath + '/'\n", + "\n", + " for f in onlyfiles:\n", + " if (len(f) < len(masks_end) or len(f) < len(image_end)):\n", + " continue\n", + "\n", + " if (f[len(f)-len(masks_end):] == masks_end):\n", + " masks_file += f\n", + "\n", + " if (f[len(f)-len(image_end):] == image_end):\n", + " image_file += f\n", + "\n", + " df_masks = pd.read_csv(masks_file)\n", + " df_image = pd.read_csv(image_file)\n", + "\n", + " df1 = df_masks[['ImageNumber', 'ObjectNumber','Intensity_IntegratedIntensity_'+compound2+'_IMAGES']]\n", + " df2 = df_image[['FileName_'+compound2+'_IMAGES']]\n", + "\n", + " ser = []\n", + " for i in range(0, len(df1)):\n", + " ser.append(df2['FileName_'+compound2+'_IMAGES'][df1.ImageNumber.iloc[i]-1][18:23])\n", + "\n", + " ser = np.array(ser)\n", + " df1 = df1.assign(Plate=pd.Series(ser).values)\n", + "\n", + " df1 = df1.rename(index=str, columns={'Intensity_IntegratedIntensity_'+compound2+'_IMAGES': \"Integrated_Intensity\"})\n", + "\n", + " return df1, compound" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def construct_dataframe_unet2(data_path, compound_number):\n", + " compounds = ['Gabazine', 'CYCLOPIAZONIC ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + "\n", + " compound = compounds[compound_number]\n", + "\n", + " mypath = data_path + compound\n", + " masks_end = 'MASKS.csv'\n", + " image_end = 'Image.csv'\n", + " masks_file = mypath + '/UNET_DilateObjects.csv'\n", + " image_file = mypath + '/UNET_Image.csv'\n", + " \n", + " df_masks = pd.read_csv(masks_file)\n", + " df_image = pd.read_csv(image_file)\n", + "\n", + " df1 = df_masks[['ImageNumber', 'ObjectNumber','Intensity_IntegratedIntensity_IMAGES']]\n", + " df2 = df_image[['FileName_IMAGES']]\n", + "\n", + " ser = []\n", + " for i in range(0, len(df1)):\n", + " ser.append(df2['FileName_IMAGES'][df1.ImageNumber.iloc[i]-1][18:23])\n", + "\n", + " ser = np.array(ser)\n", + " df1 = df1.assign(Plate=pd.Series(ser).values)\n", + "\n", + " df1 = df1.rename(index=str, columns={'Intensity_IntegratedIntensity_IMAGES': \"Integrated_Intensity\"})\n", + "\n", + " return df1, compound" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def construct_dataframe_cp(data_path, compound_number):\n", + " compounds = ['Gabazine', 'CYCLOPIAZONIC ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + " compound = compounds[compound_number]\n", + " compounds2 = ['Gabazine', 'CYCLOPIAZONIC_ACID', \n", + " 'ESTRADIOL', 'DMSO', \n", + " 'Proglumide', 'PIZOTIFEN', \n", + " 'CATECHIN_PENTABENZOATE', 'TRIADIMEFON',\n", + " '3-HYDROXYCOUMARIN', 'Etoposide',\n", + " 'DACTINOMYCIN', 'Colchicine',\n", + " 'Blebbistatin']\n", + " compound2 = compounds2[compound_number]\n", + "\n", + " compound_path = data_path + compound + '/'\n", + " \n", + " df1 = pd.read_csv(compound_path + 'Nuclei.csv')\n", + " df2 = pd.read_csv(compound_path + 'Image.csv')\n", + "\n", + " df1 = df1[['ImageNumber', 'ObjectNumber','Intensity_IntegratedIntensity_DNA']]\n", + " df2 = df2[['FileName_DNA']]\n", + " \n", + " ser = []\n", + " for i in range(0, len(df1)):\n", + " ser.append(df2['FileName_DNA'][df1.ImageNumber.iloc[i]-1][18:23])\n", + "\n", + " ser = np.array(ser)\n", + " df1 = df1.assign(Plate=pd.Series(ser).values)\n", + "\n", + " df1 = df1.rename(index=str, columns={'Intensity_IntegratedIntensity_DNA': \"Integrated_Intensity\"})\n", + " \n", + " return df1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Image Plotting \n", + "\n", + "For each of the 2 methods and each compound, one can print an image, its corresponding mask, and the image seen through the mask thanks to the 2 functions below :" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_image_and_mask_unet(data_path, compound, i):\n", + " mypath = data_path + compound\n", + " \n", + " image_path = mypath + '/images/'\n", + " masks_path = mypath + '/masks/'\n", + " \n", + " onlyfiles = [f for f in listdir(image_path) if isfile(join(image_path, f))]\n", + " im_end = '.tif'\n", + " files = [f for f in onlyfiles if f[-4:]==im_end]\n", + " \n", + " file = files[i]\n", + "# print(file)\n", + " \n", + " ipath = image_path + file\n", + " mpath = masks_path + file\n", + " \n", + " im = skimage.io.imread(ipath)\n", + " ma = skimage.io.imread(mpath)\n", + "\n", + " plt.figure(figsize=(15,10))\n", + " plt.subplot(1,2,1)\n", + " plt.imshow(im)\n", + " plt.subplot(1,2,2)\n", + "# plt.imshow(ma>0)\n", + " plt.imshow(ma)\n", + " \n", + " plt.figure(figsize=(7,6))\n", + " plt.imshow(im*(ma>0))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def plot_image_and_mask_cp(data_path_im, data_path_cp, compound, i):\n", + " mypath = data_path_im + compound\n", + " \n", + " image_path = mypath + '/images/'\n", + " \n", + " onlyfiles = [f for f in listdir(image_path) if isfile(join(image_path, f))]\n", + " im_end = '.tif'\n", + " files = [f for f in onlyfiles if f[-4:]==im_end]\n", + " \n", + " file = files[i]\n", + "# print(file)\n", + " ipath = image_path + file\n", + " \n", + " \n", + " mpath = data_path_cp + compound +'/' + file[:-4] + '_Overlay.tiff'\n", + "# print(mpath)\n", + " \n", + " im = skimage.io.imread(ipath)\n", + " ma = skimage.io.imread(mpath)\n", + " plt.figure(figsize=(15,10))\n", + " plt.subplot(1,2,1)\n", + " plt.imshow(im)\n", + " plt.subplot(1,2,2)\n", + " plt.imshow(ma)\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Normalization\n", + "\n", + "Each plate corresponds to different experimental conditions. Hence, we cannot compare the intensity of a cell in plate A with the intensity of a cell in plate B without normalizing them.\n", + "\n", + "To normalize the intensities over the different plates, we use the log-transform of the histograms. \n", + "\n", + "\n", + "### Splitting and log-intensities\n", + "\n", + "We first split the dataframe into a dictionary with each plate as entry (`split_per_plates`) and then, we compute the log10 of each intensity value (`compute_log_intensities`), for each plate.\n", + "\n", + "This gives us a dictionary of dataframes :\n", + "\n", + "{'20589':\n", + "\n", + "| Index | ImageNumber | ObjectNumber | Integrated_Intensity | Plate | Log_Integrated_Intensity |\n", + "|:-----:|-------------|--------------|----------------------|-------|--------------------------|\n", + "| **0** | 1 | 1 | 2.245075 | 20589 | 0.351231 |\n", + "| **1** | 1 | 2 | 1.579263 | 20589 | 0.198454 |\n", + "| ... | ... | ... | ... | ... | ... |\n", + "\n", + "'20590':\n", + "\n", + "| Index | ImageNumber | ObjectNumber | Integrated_Intensity | Plate | Log_Integrated_Intensity |\n", + "|:-----:|-------------|--------------|----------------------|-------|--------------------------|\n", + "| **0** | 10 | 1 | 3.067079 | 20590 | 0.486725 |\n", + "| **1** | 10 | 2 | 2.338338 | 20590 | 0.368907 |\n", + "| ... | ... | ... | ... | ... | ... |\n", + "\n", + "...}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def split_per_plates(df):\n", + " plates = df.Plate.unique()\n", + " dfs = {}\n", + "\n", + " for p in plates:\n", + " dfs[p] = df[df.Plate == p]\n", + " \n", + " return dfs\n", + "\n", + "def compute_log_intensities(dfs):\n", + " for p in dfs:\n", + " LII = np.log10(dfs[p].Integrated_Intensity.values)\n", + " dfs[p] = dfs[p].assign(Log_Integrated_Intensity = pd.Series(LII).values)\n", + " return dfs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Normalization\n", + "\n", + "To normalize the instensities over the plates, we use the log intensities. If we look at the typical DNA content distribution, it should contain the global maximum at position 2N and a local maximum at position 4N. This is exactly what we obtain for each plate, but the position 2N is not the same for all of the plates (thus the position 4N is not neither). By shifting the histogram in the log space, we can easily make the two modes match in the original space. That is why we use the log transform.\n", + "\n", + "Hence, in order to normalize, we compute the histogram of the log intensities. The goal is to find the main peak for each plates and to align these peaks. To find the main peak for each plate, we first smoothen the histogram to avoid the influence of the noise, and we take the maximum.\n", + "\n", + "Once we computed the position of the maximum for each plate, we need to align them so the main peak of each histogram is at the same position. To do so, we take one of the positions and we compute the shifts needed to reach this position from the others. We can now apply those shifts to the log intensities (each shift corresponds to a plate).\n", + "\n", + "Since the compound could have an influence on the cell cycle, we take the DMSO to compute the shifts. Since same plate means same experimental conditions, the shift computed to normalize the DMSO on a plate can be used to normalize the compound on the same plate.\n", + "\n", + "\n", + "Finally, at the end of the function, we remove the plates that are not in the compound dictionary from the dictionary of the DMSO to keep only the plates of interest.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def normalization(dfs_cmpd, dfs_dmso, plot_histograms=False, return_shifts=False):\n", + " dfs = dfs_dmso.copy()\n", + " dfs2 = dfs_cmpd.copy()\n", + " \n", + " nbins = 200\n", + " hrange = (-1.0, 2.5)\n", + " step = (hrange[1]-hrange[0])/nbins\n", + "\n", + " n_mask = 7\n", + " mask = (1/n_mask)*np.ones(n_mask)\n", + "\n", + " maxima = {}\n", + "\n", + " for plate in dfs:\n", + " histogram = np.histogram(\n", + " a=dfs[plate].Log_Integrated_Intensity,\n", + " bins = nbins,\n", + " range = hrange\n", + " )\n", + " \n", + " sig = np.convolve(histogram[0], mask)\n", + " maxima[plate] = np.argmax(sig)\n", + "\n", + " maxis = np.fromiter(maxima.values(), dtype=float)\n", + " max_pos = np.max(maxis)\n", + "\n", + " shifts = {}\n", + " for plate in maxima:\n", + " shifts[plate] = (max_pos-maxima[plate])*step\n", + "\n", + " for plate in dfs2:\n", + " vals = dfs2[plate].Log_Integrated_Intensity.values\n", + " dfs2[plate] = dfs2[plate].assign(normalized_logInt = pd.Series(vals+shifts[plate]).values)\n", + " dfs2[plate] = dfs2[plate].assign(Normalized_Intensity = pd.Series(np.power(10, dfs2[plate].normalized_logInt)).values)\n", + " dfs_cmpd[plate] = dfs2[plate].assign(Normalized_Intensity = pd.Series(dfs2[plate].Normalized_Intensity).values)\n", + " \n", + " vals = dfs[plate].Log_Integrated_Intensity.values\n", + " dfs[plate] = dfs[plate].assign(normalized_logInt = pd.Series(vals+shifts[plate]).values)\n", + " dfs[plate] = dfs[plate].assign(Normalized_Intensity = pd.Series(np.power(10, dfs[plate].normalized_logInt)).values)\n", + " dfs_dmso[plate] = dfs[plate].assign(Normalized_Intensity = pd.Series(dfs[plate].Normalized_Intensity).values)\n", + " \n", + " \n", + " if plot_histograms:\n", + " for plate in dfs_cmpd:\n", + " plt.figure(figsize=(20,8))\n", + " \n", + "# print(\"Plate {} : {}\".format(plate,len(dfs[plate])))\n", + " plt.subplot(1, 2, 1)\n", + " plt.title(plate + ' DMSO')\n", + " p1 = plt.hist(dfs_dmso[plate].Integrated_Intensity, nbins, (0, 30), label='Before normalization')\n", + " p2 = plt.hist(dfs_dmso[plate].Normalized_Intensity, nbins, (0, 30), label='After normalization')\n", + " plt.xlabel('Logarithm intensities')\n", + " plt.ylabel('number of cells')\n", + " plt.legend(frameon = False)\n", + " \n", + " plt.subplot(1, 2, 2)\n", + " plt.title(plate + ' COMPOUND')\n", + " plt.hist(dfs_cmpd[plate].Integrated_Intensity, nbins, (0, 30), label='Before normalization')\n", + " plt.hist(dfs_cmpd[plate].Normalized_Intensity, nbins, (0, 30), label='After normalization')\n", + " plt.xlabel('Intensities')\n", + " plt.ylabel('number of cells')\n", + " plt.legend(frameon = False)\n", + " \n", + " dfs_dmso2 = dfs_dmso.copy()\n", + " for plate in dfs_dmso2:\n", + " if plate not in dfs_cmpd.keys():\n", + " del dfs_dmso[plate]\n", + " \n", + " if return_shifts:\n", + " return dfs_cmpd, dfs_dmso, shifts\n", + " \n", + " return dfs_cmpd, dfs_dmso" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Regrouping the plates\n", + "\n", + "Now that all the plates are normalized, we need to merge them together in order to compute the Z' factor for all the images. That is the purpose of the function below." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def regroup_plates(df, dfs):\n", + " df2 = df.copy()\n", + " pls = df2.Plate.unique()\n", + " pls2 = dfs.keys()\n", + " \n", + " plates = list(set(list(pls))-set(list(pls2)))\n", + " \n", + " for p in plates:\n", + " df2 = df2[df2.Plate != p]\n", + "\n", + " intensities = []\n", + "\n", + " for plate in pls2:\n", + " intensities = intensities + list(dfs[plate].Normalized_Intensity.values)\n", + " \n", + " \n", + " df2 = df2.assign(Normalized_Intensity=pd.Series(np.array(intensities)).values)\n", + " \n", + " return df2" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def regroup_plates2(dfs):\n", + " plates = list(dfs.keys())\n", + " df = pd.DataFrame(columns=dfs[plates[0]].columns)\n", + "\n", + " for plate in plates:\n", + " df = df.append(dfs[plate])\n", + " \n", + " \n", + " return df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Z factor calculation\n", + "\n", + "\n", + "We compute the Z factor to determine if we can see a significant difference between the compound experiment results and the DMSO experiment results. \n", + "\n", + "\n", + "### Percentages computation\n", + "\n", + "Thanks to the function below, we compute for each image the percentage of cell with intensity that is above a given threshold. By choosing the threshold between the 2 modes of the DNA distribution, we compute, for each image, the percentage of cells with intensity close to 4N." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_percentages(df, threshold, display_hist=False, cmpd_name=''):\n", + " images = df.ImageNumber.unique()\n", + " percentages = []\n", + " for im in images:\n", + " vals = df[df.ImageNumber == im].Normalized_Intensity.values\n", + " percentages.append(np.sum(vals>threshold)/vals.shape[0])\n", + " \n", + " if display_hist:\n", + " image_number = 1\n", + " vals = np.array(percentages)\n", + " \n", + " x = np.array([threshold, threshold])\n", + " y = np.array([0, 100])\n", + " plt.figure(figsize=(10,8))\n", + " plt.title(compound + ' percentages')\n", + " plt.hist(vals, 100, (0.0, 1.0), label='Values', normed=True)\n", + " \n", + "# plt.plot(x, y, 'r', label='Threshold')\n", + " plt.xlabel('Proportion of intensity values above threshold')\n", + " plt.ylabel('Number of images')\n", + " plt.legend(frameon = False)\n", + " \n", + " return percentages" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Threshold computation\n", + "\n", + "To compute the threshold, we first find the position of the peak at 2N and 4N, and we take the mean between the two positions." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_threshold(df_DMSO, display=False):\n", + " nbins = 200\n", + " hrange = (-1.0, 30)\n", + " step = (hrange[1]-hrange[0])/nbins\n", + "\n", + " n_mask = 3\n", + " mask = (1/n_mask)*np.ones(n_mask)\n", + "\n", + " histogram2 = np.histogram(\n", + " a=df_DMSO.Normalized_Intensity,\n", + " bins = nbins,\n", + " range = hrange\n", + " )\n", + " sig = np.convolve(histogram2[0], mask)\n", + " maxima = np.argmax(sig)*step\n", + " \n", + " local_maxima = np.r_[True, sig[1:] > sig[:-1]] & np.r_[sig[:-1] > sig[1:], True]\n", + " pos_max = np.squeeze(np.argwhere(local_maxima==True))\n", + " val_max = sig[local_maxima==True]\n", + " \n", + " argmax1 = np.argmax(val_max)\n", + " pmax1 = pos_max[argmax1]\n", + " max1 = val_max[argmax1]\n", + " \n", + " pos_max = pos_max[argmax1+1:]\n", + " val_max = val_max[argmax1+1:]\n", + " \n", + " argmax2 = np.argmax(val_max)\n", + " pmax2 = pos_max[argmax2]\n", + " \n", + " if display:\n", + " height = np.array([0, 1100])\n", + " plt.figure()\n", + " plt.plot(np.array(range(-1, nbins+1))*step, sig)\n", + " plt.plot(np.array([pmax1, pmax1])*step, height, 'r')\n", + " plt.plot(np.array([pmax2, pmax2])*step, height, 'r')\n", + " \n", + " thres = (pmax1+pmax2)/2*step\n", + " if display:\n", + " print(\"Threshold = {}\".format(thres))\n", + " \n", + " return thres" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Z factor computation\n", + "\n", + "\n", + "The formula of the Z factor is :\n", + "\n", + "$$Z' = 1-{3(\\sigma _{p}+\\sigma _{n}) \\over |\\mu _{p}-\\mu _{n}|}$$\n", + "\n", + "where p, n denote the positive and negative control, respectively compound and DMSO. $\\mu$ and $\\sigma$ are the mean and the standard deviation of the percentages" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_Z_factor(percentages_pos, percentages_neg):\n", + " p = np.array(percentages_pos)\n", + " n = np.array(percentages_neg)\n", + " \n", + " mean_p = np.mean(p)\n", + " std_p = np.std(p)\n", + " mean_n = np.mean(n)\n", + " std_n = np.std(n)\n", + " \n", + " return 1 - 3*(std_p+std_n)/abs(mean_p-mean_n) " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Results" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compound : Gabazine\n", + "Z' for UNET = -28.066965940664478\n", + "Z' for CellProfiler = -45.06801417449648\n", + "\n", + "Compound : CYCLOPIAZONIC ACID\n", + "Z' for UNET = -29.996334244753626\n", + "Z' for CellProfiler = -55.24586616428868\n", + "\n", + "Compound : ESTRADIOL\n", + "Z' for UNET = -3.2878556884273085\n", + "Z' for CellProfiler = -5.741775063390136\n", + "\n", + "Compound : Proglumide\n", + "Z' for UNET = -9.799619561853978\n", + "Z' for CellProfiler = -17.22519559743383\n", + "\n", + "Compound : PIZOTIFEN\n", + "Z' for UNET = -39.49409987195662\n", + "Z' for CellProfiler = -56.720938485163025\n", + "\n", + "Compound : CATECHIN PENTABENZOATE\n", + "Z' for UNET = -59.539427374904186\n", + "Z' for CellProfiler = -18.167502634304967\n", + "\n", + "Compound : TRIADIMEFON\n", + "Z' for UNET = -73.22188573510053\n", + "Z' for CellProfiler = -23.432665810567336\n", + "\n", + "Compound : 3-HYDROXYCOUMARIN\n", + "Z' for UNET = -16.548270650652213\n", + "Z' for CellProfiler = -51.00284333300692\n", + "\n", + "Compound : Etoposide\n", + "Z' for UNET = -1.850535951808781\n", + "Z' for CellProfiler = -1.7130831419201842\n", + "\n", + "Compound : DACTINOMYCIN\n", + "Z' for UNET = -23.764863521752513\n", + "Z' for CellProfiler = -301.046409211808\n", + "\n", + "Compound : Colchicine\n", + "Z' for UNET = -13.48708224403214\n", + "Z' for CellProfiler = -0.3252778152575291\n", + "\n" + ] + } + ], + "source": [ + "for cmp_number in range(0, 12):\n", + " if cmp_number == 3:\n", + " continue\n", + " \n", + " data_path = './../UNET/'\n", + "\n", + " df_unet_cmpd, compound = construct_dataframe_unet2(data_path, cmp_number)\n", + " df_unet_dmso, dmso = construct_dataframe_unet2(data_path, 3)\n", + "\n", + "\n", + " df_plates_unet_cmpd, df_plates_unet_dmso = normalization(\n", + " compute_log_intensities(split_per_plates(df_unet_cmpd)),\n", + " compute_log_intensities(split_per_plates(df_unet_dmso)) , \n", + " plot_histograms=False)\n", + "\n", + " df_unet_cmpd = regroup_plates2(df_plates_unet_cmpd)\n", + " df_unet_dmso = regroup_plates2(df_plates_unet_dmso)\n", + "\n", + "\n", + "\n", + "\n", + " data_path_cp = './../CP/'\n", + "\n", + " df_cp_cmpd = construct_dataframe_cp(data_path_cp, cmp_number)\n", + " df_cp_dmso = construct_dataframe_cp(data_path_cp, 3)\n", + "\n", + " df_plates_cp_cmpd, df_plates_cp_dmso = normalization(\n", + " compute_log_intensities(split_per_plates(df_cp_cmpd)),\n", + " compute_log_intensities(split_per_plates(df_cp_dmso)),\n", + " plot_histograms=False)\n", + "\n", + " df_cp_cmpd = regroup_plates2(df_plates_cp_cmpd)\n", + " df_cp_dmso = regroup_plates2(df_plates_cp_dmso)\n", + "\n", + "\n", + "\n", + "\n", + " threshold_unet = compute_threshold(df_unet_dmso, display=False)\n", + " threshold_cp = compute_threshold(df_cp_dmso, display=False)\n", + "\n", + " per_unet_cmpd = compute_percentages(df_unet_cmpd, threshold_unet)\n", + " per_unet_dmso = compute_percentages(df_unet_dmso, threshold_unet)\n", + "\n", + " per_cp_cmpd = compute_percentages(df_cp_cmpd, threshold_cp)\n", + " per_cp_dmso = compute_percentages(df_cp_dmso, threshold_cp)\n", + "\n", + " ZprimeUNET = compute_Z_factor(per_unet_cmpd, per_unet_dmso)\n", + " ZprimeCP = compute_Z_factor(per_cp_cmpd, per_cp_dmso)\n", + "\n", + "\n", + " print(\"Compound : {}\".format(compound))\n", + " print(\"Z' for UNET = {}\".format(ZprimeUNET))\n", + " print(\"Z' for CellProfiler = {}\".format(ZprimeCP))\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/classic-ml/README.md b/classic-ml/README.md new file mode 100644 index 0000000..a4dfbe0 --- /dev/null +++ b/classic-ml/README.md @@ -0,0 +1,48 @@ +# Segmentation of nuclei using Ilastik + +By Molnar Csaba @csmolnar + +A random forrest classifier is trained after loading the training images. Some notes of how to run this project: + +Label images are transformed into sparse pixel samples. Completely random. +Sampling rates: 10% background - 10% foreground - 50% boundaries. +The sampling is run in Matlab. Run for all training images. This produces the following gray scale values: +000000 0,0,0 Nothing +FFFFFF 255,255,255. Boundaries +AAAAAA 170,170,170. Background +616161 97,97,97. Foreground + +1) Create a new project in Ilastik and load the training data (only original images). + By default, it uses Random Forests (more details in the paper). + +2) Features selected for training. + Screenshot in Slack. + +3) Create classes in the project. + There is a skeleton project file with the basic Ilastik configuration. + +4) Run the Python script to generate a new Ilastik project file with the desired labeled images. + Parameters: 1- skeleton project file. + 2- folder of labels + This script has dependencies with Ilastik packages + This generates the new project that can be used for training. + This is a bigger file that can be reproduced following the steps before. + +5) Open and run the new project file in Ilastik + Start training with Live Update. + +6) When done, we can select what to export: + Choose probability maps. + Convert to Data Type: unsigned 16-bits + File format: PNG + +7) Load test images: use batch processing for opening the test images. + Process all files: generates the probability maps for test images. + +8) Postprocessing: use the Jupyter Notebook to transform probability maps into label matrices + The notebook is preconfigured with the right parameters. + Generates outputs in a new folder. + +9) Evaluation: object dilation = 3 + + diff --git a/classic-ml/ilastik-projects/empty_project_3_labels.ilp b/classic-ml/ilastik-projects/empty_project_3_labels.ilp new file mode 100644 index 0000000000000000000000000000000000000000..387e3cc70b2dd54773ea0fd9a8f47f8a217b9f87 GIT binary patch literal 649460 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 V9~n7w_VlsoxpO~LIzk7Ucm%mFfxE30|Qh$ zgCNv=baf#?uC5F~l`!*RG}Jf-0~QDiW)4hzlpYx&;Nj{R0P<-B)Vm5$`UEtcLAeZ> zIf=z3nb~2fMa7x|L&(44>wGYBoe2_c!k{lMR71_oG80U9z0K(L>`3nK#))Rhbj z3e5N&1&#(G9Y$wshZ7nZ+6N>dg5^2-#A%oPmHtqd%!Of40R3=HAL?FRnA;^R?w wjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S17`tz{y08k$v0FC=a82|tP literal 0 HcmV?d00001 diff --git a/classic-ml/src/ilastik/__init__.py b/classic-ml/src/ilastik/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/classic-ml/src/ilastik/absolutize_input_path.py b/classic-ml/src/ilastik/absolutize_input_path.py new file mode 100644 index 0000000..933ef9c --- /dev/null +++ b/classic-ml/src/ilastik/absolutize_input_path.py @@ -0,0 +1,29 @@ +import h5py +import os +import skimage.io as sio +import numpy as np + +""" + Implements the import feature of ilastik from external file +""" + + +INPUT_INFO_PATH = 'Input Data/infos/' +RAW_DATA_PATH = 'Input Data/infos/lane{:04d}/Raw Data' +LABELS_PATH = 'PixelClassification/LabelSets/labels{:03d}' + + +def absolutize_input_path(ilastik_project_file): + project_file_dir = os.path.dirname(ilastik_project_file) + + pf = h5py.File(ilastik_project_file, 'a') + + for num, lane in enumerate(pf.get('Input Data/infos/').keys()): + data_id = int(lane[len('labels'):]) + raw_data_file_path = pf.get(u'Input Data/infos/lane{:04d}/Raw Data/filePath/'.format(data_id)) + raw_data_image_path_string = raw_data_file_path.value.decode() + + new_raw_data_image_path = os.path.abspath(os.path.join(project_file_dir, raw_data_image_path_string)) + raw_data_file_path[...] = new_raw_data_image_path + + pf.close diff --git a/classic-ml/src/ilastik/import_training_from_external.py b/classic-ml/src/ilastik/import_training_from_external.py new file mode 100644 index 0000000..7ea918c --- /dev/null +++ b/classic-ml/src/ilastik/import_training_from_external.py @@ -0,0 +1,36 @@ +import h5py +import os +import skimage.io as sio +import numpy as np + +""" + Implements the import feature of ilastik from external file +""" + + +INPUT_INFO_PATH = 'Input Data/infos/' +RAW_DATA_PATH = 'Input Data/infos/lane{:04d}/Raw Data' +LABELS_PATH = 'PixelClassification/LabelSets/labels{:03d}' + + +def import_training_from_external(ilastik_project_file, trainmap_dir): + pf = h5py.File(ilastik_project_file, 'a') + + for num, lane in enumerate(pf.get(INPUT_INFO_PATH).keys()): + data_id = int(lane[len('lane'):]) + raw_data = pf.get(RAW_DATA_PATH.format(data_id)) + + labels = pf.get(LABELS_PATH.format(data_id)) + + image_name = os.path.basename(raw_data.get(u'filePath').value.decode()) + training_map = np.expand_dims(sio.imread(os.path.join(trainmap_dir, image_name)), axis=2) + + print(num, lane, training_map.shape) + + if len(training_map.shape) > 2: + temp_training_map = np.zeros((training_map.shape[0], training_map.shape[1]), dtype=np.uint16) + + ds = labels.create_dataset('block{:04d}'.format(0), data=training_map) + ds.attrs['blockSlice'] = '[{:d}:{:d},{:d}:{:d},0:1]'.format(0, training_map.shape[0] - 1, 0, + training_map.shape[1] - 1) + pf.close() diff --git a/classic-ml/src/ilastik_import_script.py b/classic-ml/src/ilastik_import_script.py new file mode 100644 index 0000000..9afcb48 --- /dev/null +++ b/classic-ml/src/ilastik_import_script.py @@ -0,0 +1,47 @@ +import ilastik.import_training_from_external as itfe +import ilastik.absolutize_input_path as iaip +import argparse +import sys +from shutil import copy +import os.path + +''' + Import external training set for each image of the input data to a copy of existing ilastik project file. + The script expects 2 arguments: + ilastik_project_file: existing ilastik project file with selected input data, features and labels; + train_data_dir: directory that contains grayscale label images, where each label corresponds to + a label from the ilastik_project_file. +''' + +parser = argparse.ArgumentParser() +parser.add_argument('ilastik_project_file', help="ilastik project file path") +parser.add_argument('train_data_dir', help="directory path of training label images") +args = parser.parse_args() + +old_project_file = args.ilastik_project_file +project_dir = os.path.dirname(old_project_file) +old_project_file_name = os.path.basename(old_project_file) + +train_data_dir = args.train_data_dir +# print(train_data_dir) + +if train_data_dir.endswith('/'): + # print(os.path.split(train_data_dir[:-1])) + test_name = os.path.split(train_data_dir[:-1])[-1] + pass +else: + # print(os.path.split(train_data_dir)) + test_name = os.path.split(train_data_dir)[-1] + pass + +print(test_name) + +new_project_file = os.path.join(project_dir, old_project_file_name.split('.')[0]+ '_' + test_name + '.' + old_project_file_name.split('.')[-1]) + +print('{} -> {}'.format(old_project_file, new_project_file)) + +copy(old_project_file, new_project_file) + +iaip.absolutize_input_path(new_project_file) + +itfe.import_training_from_external(new_project_file, args.train_data_dir) diff --git a/examples/bbbc039_seg_config.py b/examples/bbbc039_seg_config.py new file mode 100644 index 0000000..a816abb --- /dev/null +++ b/examples/bbbc039_seg_config.py @@ -0,0 +1,108 @@ +import os +import utils.dirtools + +config_vars = {} + +# ************ 01 ************ # +# ****** PREPROCESSING ******* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.01 INPUT DIRECTORIES AND FILES + +config_vars["root_directory"] = '/data1/image-segmentation/BBBC022/unet/' + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.02 DATA PARTITION INFO + +## Maximum number of training images (use 0 for all) +config_vars["max_training_images"] = 0 + +## Generate partitions? +## If False, load predefined partitions (training.txt, validation.txt and test.txt) +config_vars["create_split_files"] = False + +## Randomly choose training and validation images. +## The remaining fraction is reserved for test images. +config_vars["training_fraction"] = 0.5 +config_vars["validation_fraction"] = 0.25 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.03 IMAGE STORAGE OPTIONS + +## Transform gray scale TIF images to PNG +config_vars["transform_images_to_PNG"] = True +config_vars["pixel_depth"] = 8 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.04 PRE-PROCESSING OF ANNOTATIONS + +## Area of minimun object in pixels +config_vars["min_nucleus_size"] = 25 + +## Pixels of the boundary (min 2 pixels) +config_vars["boundary_size"] = 2 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.05 DATA AUGMENTATION USING ELASTIC DEFORMATIONS + +## Elastic deformation takes a lot of times to compute. +## It is computed only once in the preprocessing. +config_vars["augment_images"] = False + +## Augmentation parameters. +## Calibrate parameters using the 00-elastic-deformation.ipynb +config_vars["elastic_points"] = 16 +config_vars["elastic_distortion"] = 5 + +## Number of augmented images +config_vars["elastic_augmentations"] = 10 + + +# ************ 02 ************ # +# ********* TRAINING ********* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.01 OPTIMIZATION + +config_vars["learning_rate"] = 1e-4 + +config_vars["epochs"] = 15 + +config_vars["steps_per_epoch"] = 500 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.02 BATCHES + +config_vars["batch_size"] = 10 + +config_vars["val_batch_size"] = 10 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.03 DATA NORMALIZATION + +config_vars["rescale_labels"] = True + +config_vars["crop_size"] = 256 + +# ************ 03 ************ # +# ******** PREDICTION ******** # +# **************************** # + +config_vars["cell_min_size"] = 16 + +config_vars["boundary_boost_factor"] = 1 + +# ************ 04 ************ # +# ******** EVALUATION ******** # +# **************************** # + +config_vars["object_dilation"] = 3 + +# **************************** # +# ******** FINAL SETUP ******* # +# **************************** # + +config_vars = utils.dirtools.setup_working_directories(config_vars) + diff --git a/examples/combined_set_config.py b/examples/combined_set_config.py new file mode 100644 index 0000000..b566485 --- /dev/null +++ b/examples/combined_set_config.py @@ -0,0 +1,108 @@ +import os +import utils.dirtools + +config_vars = {} + +# ************ 01 ************ # +# ****** PREPROCESSING ******* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.01 INPUT DIRECTORIES AND FILES + +config_vars["root_directory"] = '/data1/image-segmentation/combined_set/' + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.02 DATA PARTITION INFO + +## Maximum number of training images (use 0 for all) +config_vars["max_training_images"] = 0 + +## Generate partitions? +## If False, load predefined partitions (training.txt, validation.txt and test.txt) +config_vars["create_split_files"] = False + +## Randomly choose training and validation images. +## The remaining fraction is reserved for test images. +config_vars["training_fraction"] = 0.5 +config_vars["validation_fraction"] = 0.25 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.03 IMAGE STORAGE OPTIONS + +## Transform gray scale TIF images to PNG +config_vars["transform_images_to_PNG"] = True +config_vars["pixel_depth"] = 8 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.04 PRE-PROCESSING OF ANNOTATIONS + +## Area of minimun object in pixels +config_vars["min_nucleus_size"] = 25 + +## Pixels of the boundary (min 2 pixels) +config_vars["boundary_size"] = 2 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.05 DATA AUGMENTATION USING ELASTIC DEFORMATIONS + +## Elastic deformation takes a lot of times to compute. +## It is computed only once in the preprocessing. +config_vars["augment_images"] = True + +## Augmentation parameters. +## Calibrate parameters using the 00-elastic-deformation.ipynb +config_vars["elastic_points"] = 16 +config_vars["elastic_distortion"] = 5 + +## Number of augmented images +config_vars["elastic_augmentations"] = 10 + + +# ************ 02 ************ # +# ********* TRAINING ********* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.01 OPTIMIZATION + +config_vars["learning_rate"] = 1e-4 + +config_vars["epochs"] = 50 + +config_vars["steps_per_epoch"] = 500 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.02 BATCHES + +config_vars["batch_size"] = 10 + +config_vars["val_batch_size"] = 10 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.03 DATA NORMALIZATION + +config_vars["rescale_labels"] = True + +config_vars["crop_size"] = 256 + +# ************ 03 ************ # +# ******** PREDICTION ******** # +# **************************** # + +config_vars["cell_min_size"] = 16 + +config_vars["boundary_boost_factor"] = 1 + +# ************ 04 ************ # +# ******** EVALUATION ******** # +# **************************** # + +config_vars["object_dilation"] = 3 + +# **************************** # +# ******** FINAL SETUP ******* # +# **************************** # + +config_vars = utils.dirtools.setup_working_directories(config_vars) + diff --git a/examples/vanvalen_data_config.py b/examples/vanvalen_data_config.py new file mode 100644 index 0000000..0917293 --- /dev/null +++ b/examples/vanvalen_data_config.py @@ -0,0 +1,108 @@ +import os +import utils.dirtools + +config_vars = {} + +# ************ 01 ************ # +# ****** PREPROCESSING ******* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.01 INPUT DIRECTORIES AND FILES + +config_vars["root_directory"] = '/data1/image-segmentation/DeepCell_data/unet/' + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.02 DATA PARTITION INFO + +## Maximum number of training images (use 0 for all) +config_vars["max_training_images"] = 0 + +## Generate partitions? +## If False, load predefined partitions (training.txt, validation.txt and test.txt) +config_vars["create_split_files"] = False + +## Randomly choose training and validation images. +## The remaining fraction is reserved for test images. +config_vars["training_fraction"] = 0.5 +config_vars["validation_fraction"] = 0.25 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.03 IMAGE STORAGE OPTIONS + +## Transform gray scale TIF images to PNG +config_vars["transform_images_to_PNG"] = True +config_vars["pixel_depth"] = 8 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.04 PRE-PROCESSING OF ANNOTATIONS + +## Area of minimun object in pixels +config_vars["min_nucleus_size"] = 25 + +## Pixels of the boundary (min 2 pixels) +config_vars["boundary_size"] = 2 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.05 DATA AUGMENTATION USING ELASTIC DEFORMATIONS + +## Elastic deformation takes a lot of times to compute. +## It is computed only once in the preprocessing. +config_vars["augment_images"] = True + +## Augmentation parameters. +## Calibrate parameters using the 00-elastic-deformation.ipynb +config_vars["elastic_points"] = 24 +config_vars["elastic_distortion"] = 8 + +## Number of augmented images +config_vars["elastic_augmentations"] = 10 + + +# ************ 02 ************ # +# ********* TRAINING ********* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.01 OPTIMIZATION + +config_vars["learning_rate"] = 1e-4 + +config_vars["epochs"] = 15 + +config_vars["steps_per_epoch"] = 500 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.02 BATCHES + +config_vars["batch_size"] = 10 + +config_vars["val_batch_size"] = 1 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.03 DATA NORMALIZATION + +config_vars["rescale_labels"] = True + +config_vars["crop_size"] = 256 + +# ************ 03 ************ # +# ******** PREDICTION ******** # +# **************************** # + +config_vars["cell_min_size"] = 16 + +config_vars["boundary_boost_factor"] = 1 + +# ************ 04 ************ # +# ******** EVALUATION ******** # +# **************************** # + +config_vars["object_dilation"] = 3 + +# **************************** # +# ******** FINAL SETUP ******* # +# **************************** # + +config_vars = utils.dirtools.setup_working_directories(config_vars) + diff --git a/unet4nuclei/00-download-dataset.ipynb b/unet4nuclei/00-download-dataset.ipynb new file mode 100644 index 0000000..8b796ae --- /dev/null +++ b/unet4nuclei/00-download-dataset.ipynb @@ -0,0 +1,180 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import glob\n", + "import os\n", + "import shutil\n", + "import zipfile\n", + "import requests\n", + "from config import config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Download zip files from BBBC website\n", + "\n", + "The _**root directory**_ for extracting the dataset is defined in the `config.py` file, in the following field: `config_vars[\"root_directory\"]`.\n", + "\n", + "The root directory is used to create sub-directories automatically with the code below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "images = requests.get('https://data.broadinstitute.org/bbbc/BBBC039/images.zip')\n", + "masks = requests.get('https://data.broadinstitute.org/bbbc/BBBC039/masks.zip')\n", + "metadata = requests.get('https://data.broadinstitute.org/bbbc/BBBC039/metadata.zip')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "assert images.ok\n", + "assert masks.ok\n", + "assert metadata.ok\n", + "\n", + "print(\"Data OK\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Extract images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "images_zip = os.path.join(config_vars['raw_images_dir'], 'images.zip')\n", + "os.makedirs(config_vars['raw_images_dir'], exist_ok=True)\n", + "with open(images_zip, 'wb') as f:\n", + " f.write(images.content)\n", + " \n", + "zip_ref = zipfile.ZipFile(images_zip, 'r')\n", + "for file in zip_ref.namelist():\n", + " if file.startswith('images/'):\n", + " zip_ref.extract(file, config_vars['raw_images_dir'])\n", + "\n", + "zip_ref.close()\n", + "os.remove(images_zip)\n", + "\n", + "for file in glob.glob(os.path.join(config_vars['raw_images_dir'], 'images/*')):\n", + " shutil.move(file, config_vars['raw_images_dir'])\n", + "\n", + "shutil.rmtree(os.path.join(config_vars['raw_images_dir'], 'images'))\n", + "\n", + "print(\"Images extracted\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Extract annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "masks_zip = os.path.join(config_vars['raw_annotations_dir'], 'masks.zip')\n", + "os.makedirs(config_vars['raw_annotations_dir'], exist_ok=True)\n", + "with open(masks_zip, 'wb') as f:\n", + " f.write(masks.content)\n", + "\n", + "zip_ref = zipfile.ZipFile(masks_zip, 'r')\n", + "for file in zip_ref.namelist():\n", + " if file.startswith('masks/'):\n", + " zip_ref.extract(file, config_vars['raw_annotations_dir'])\n", + "\n", + "zip_ref.close()\n", + "os.remove(masks_zip)\n", + "\n", + "for file in glob.glob(os.path.join(config_vars['raw_annotations_dir'], 'masks/*')):\n", + " shutil.move(file, config_vars['raw_annotations_dir'])\n", + "shutil.rmtree(os.path.join(config_vars['raw_annotations_dir'], 'masks'))\n", + "\n", + "print(\"Annotations extracted\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Extract metadata" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "metadata_zip = os.path.join(config_vars['root_directory'], 'metadata.zip')\n", + "with open(metadata_zip, 'wb') as f:\n", + " f.write(metadata.content)\n", + "\n", + "zip_ref = zipfile.ZipFile(metadata_zip, 'r')\n", + "for file in zip_ref.namelist():\n", + " if file.startswith('metadata/'):\n", + " zip_ref.extract(file, config_vars['root_directory'])\n", + "\n", + "zip_ref.close()\n", + "os.remove(metadata_zip)\n", + "\n", + "for file in glob.glob(os.path.join(config_vars['root_directory'], 'metadata/*')):\n", + " shutil.move(file, config_vars['root_directory'])\n", + "shutil.rmtree(os.path.join(config_vars['root_directory'], 'metadata'))\n", + "\n", + "print(\"Metadata extracted\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/01-preprocessing.ipynb b/unet4nuclei/01-preprocessing.ipynb new file mode 100644 index 0000000..0992900 --- /dev/null +++ b/unet4nuclei/01-preprocessing.ipynb @@ -0,0 +1,342 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 01\n", + "# Preprocessing input images and annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import random\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import pathlib\n", + "from tqdm import tqdm\n", + "\n", + "import skimage.io\n", + "import skimage.segmentation\n", + "\n", + "import utils.dirtools\n", + "import utils.augmentation\n", + "from config import config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Data partitions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Prepare split files\n", + "\n", + "if config_vars[\"create_split_files\"]:\n", + " file_list = os.listdir(config_vars[\"raw_images_dir\"])\n", + "\n", + " [list_training, list_test, list_validation] = utils.dirtools.create_image_lists(\n", + " config_vars[\"raw_images_dir\"],\n", + " config_vars[\"training_fraction\"],\n", + " config_vars[\"validation_fraction\"]\n", + " )\n", + "\n", + " utils.dirtools.write_path_files(config_vars[\"path_files_training\"], list_training)\n", + " utils.dirtools.write_path_files(config_vars[\"path_files_test\"], list_test)\n", + " utils.dirtools.write_path_files(config_vars[\"path_files_validation\"], list_validation)\n", + " \n", + "data_partitions = utils.dirtools.read_data_partitions(config_vars, load_augmented=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create output directories for transformed data\n", + "\n", + "os.makedirs(config_vars[\"normalized_images_dir\"], exist_ok=True)\n", + "os.makedirs(config_vars[\"boundary_labels_dir\"], exist_ok=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Image Preprocessing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "if config_vars[\"transform_images_to_PNG\"]:\n", + " \n", + " filelist = sorted(os.listdir(config_vars[\"raw_images_dir\"]))\n", + "\n", + " # run over all raw images\n", + " for filename in tqdm(filelist):\n", + "\n", + " # load image and its annotation\n", + " orig_img = skimage.io.imread(config_vars[\"raw_images_dir\"] + filename) \n", + "\n", + " # IMAGE\n", + "\n", + " # normalize to [0,1]\n", + " percentile = 99.9\n", + " high = np.percentile(orig_img, percentile)\n", + " low = np.percentile(orig_img, 100-percentile)\n", + "\n", + " img = np.minimum(high, orig_img)\n", + " img = np.maximum(low, img)\n", + "\n", + " img = (img - low) / (high - low) # gives float64, thus cast to 8 bit later\n", + " img = skimage.img_as_ubyte(img) \n", + "\n", + " skimage.io.imsave(config_vars[\"normalized_images_dir\"] + filename[:-3] + 'png', img) \n", + "else:\n", + " config_vars[\"normalized_images_dir\"] = config_vars[\"raw_images_dir\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "print(img.dtype, img.shape)\n", + "filename = os.listdir(config_vars[\"normalized_images_dir\"])\n", + "img = skimage.io.imread(config_vars[\"normalized_images_dir\"] + filename[-1])\n", + "plt.figure(figsize=(10,10))\n", + "plt.imshow(img)\n", + "plt.show()\n", + "plt.hist(img.flatten(), bins=100)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Create Output Targets: Three Class Boundary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "filelist = sorted(os.listdir(config_vars[\"raw_annotations_dir\"]))\n", + "total_objects = 0\n", + "\n", + "# run over all raw images\n", + "for filename in tqdm(filelist):\n", + " \n", + " # GET ANNOTATION\n", + " annot = skimage.io.imread(config_vars[\"raw_annotations_dir\"] + filename)\n", + " \n", + " # strip the first channel\n", + " if len(annot.shape) == 3:\n", + " annot = annot[:,:,0]\n", + " \n", + " # label the annotations nicely to prepare for future filtering operation\n", + " annot = skimage.morphology.label(annot)\n", + " total_objects += len(np.unique(annot)) - 1\n", + " \n", + " # filter small objects, e.g. micronulcei\n", + " annot = skimage.morphology.remove_small_objects(annot, min_size=config_vars[\"min_nucleus_size\"])\n", + " \n", + " # find boundaries\n", + " boundaries = skimage.segmentation.find_boundaries(annot)\n", + "\n", + " for k in range(2, config_vars[\"boundary_size\"], 2):\n", + " boundaries = skimage.morphology.binary_dilation(boundaries)\n", + " \n", + " # BINARY LABEL\n", + " \n", + " # prepare buffer for binary label\n", + " label_binary = np.zeros((annot.shape + (3,)))\n", + " \n", + " # write binary label\n", + " label_binary[(annot == 0) & (boundaries == 0), 0] = 1\n", + " label_binary[(annot != 0) & (boundaries == 0), 1] = 1\n", + " label_binary[boundaries == 1, 2] = 1\n", + " \n", + " # save it - converts image to range from 0 to 255\n", + " skimage.io.imsave(config_vars[\"boundary_labels_dir\"] + filename, label_binary)\n", + " \n", + "print(\"Total objects: \",total_objects)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Show example image \n", + "print(annot.dtype, annot.shape)\n", + "\n", + "# plot original annotation\n", + "plt.figure(figsize=(10,10))\n", + "plt.imshow(annot, cmap=\"nipy_spectral\")\n", + "plt.show()\n", + "\n", + "# plot boundary labels\n", + "plt.figure(figsize=(10,10))\n", + "plt.imshow(label_binary)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Augment images (optional) \n", + "* data augmentation using affine transformations \n", + "* n_points x n_points data points are equally distributed in the image \n", + "* distort \n", + "* n_augmentations images are calculated for each image \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "def generate_augmented_examples(filelist, n_augmentations, n_points, distort, dir_boundary_labels, dir_images_normalized_8bit):\n", + " \n", + " updated_filelist = []\n", + " \n", + " # run over all raw images\n", + " for filename in tqdm(filelist):\n", + " \n", + " #if filename.startswith(\"IXMtest\"):\n", + " # print(\"Skipping\", filename)\n", + " # continue\n", + " \n", + " # check if boundary labels were calculated \n", + " my_file = pathlib.Path(dir_boundary_labels + filename)\n", + " \n", + " if my_file.is_file():\n", + " \n", + " # load image \n", + " x = skimage.io.imread(dir_images_normalized_8bit + filename)\n", + " \n", + " # load annotation \n", + " y = skimage.io.imread(dir_boundary_labels + filename)\n", + " \n", + " for n in range(1,n_augmentations):\n", + " # augment image and annotation \n", + " x_augmented, y_augmented = utils.augmentation.deform(x, y, points = n_points, distort = distort)\n", + " \n", + " # filename for augmented images\n", + " filename_augmented = os.path.splitext(filename)[0] + '_aug_{:03d}'.format(n) + os.path.splitext(filename)[1]\n", + " skimage.io.imsave(dir_images_normalized_8bit + filename_augmented, x)\n", + " skimage.io.imsave(dir_boundary_labels + filename_augmented, y)\n", + " updated_filelist.append(filename_augmented)\n", + " \n", + " return filelist + updated_filelist \n", + "\n", + "if config_vars[\"augment_images\"]:\n", + " \n", + " tmp_value = config_vars[\"max_training_images\"]\n", + " config_vars[\"max_training_images\"] = 0\n", + " tmp_partitions = utils.dirtools.read_data_partitions(config_vars, load_augmented=False)\n", + " \n", + " training_files = generate_augmented_examples(\n", + " tmp_partitions[\"training\"], \n", + " config_vars[\"elastic_augmentations\"], \n", + " config_vars[\"elastic_points\"], \n", + " config_vars[\"elastic_distortion\"], \n", + " config_vars[\"boundary_labels_dir\"], \n", + " config_vars[\"normalized_images_dir\"]\n", + " )\n", + " \n", + " config_vars[\"max_training_images\"] = tmp_value\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/02-training.ipynb b/unet4nuclei/02-training.ipynb new file mode 100644 index 0000000..05cf930 --- /dev/null +++ b/unet4nuclei/02-training.ipynb @@ -0,0 +1,227 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 02\n", + "# Training a U-Net model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "import os\n", + "\n", + "import numpy as np\n", + "import skimage.io\n", + "\n", + "import tensorflow as tf\n", + "\n", + "import keras.backend\n", + "import keras.callbacks\n", + "import keras.layers\n", + "import keras.models\n", + "import keras.optimizers\n", + "\n", + "import utils.model_builder\n", + "import utils.data_provider\n", + "import utils.metrics\n", + "import utils.objectives\n", + "import utils.dirtools\n", + "\n", + "# Uncomment the following line if you don't have a GPU\n", + "#os.environ['CUDA_VISIBLE_DEVICES'] = ''" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from config import config_vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "experiment_name = '01'\n", + "\n", + "config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name)\n", + "\n", + "data_partitions = utils.dirtools.read_data_partitions(config_vars)\n", + "\n", + "config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Initiate data generators" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#build session running on GPU 1\n", + "configuration = tf.ConfigProto()\n", + "configuration.gpu_options.allow_growth = True\n", + "configuration.gpu_options.visible_device_list = \"1\"\n", + "session = tf.Session(config = configuration)\n", + "\n", + "# apply session\n", + "keras.backend.set_session(session)\n", + "\n", + "train_gen = utils.data_provider.random_sample_generator(\n", + " config_vars[\"normalized_images_dir\"],\n", + " config_vars[\"boundary_labels_dir\"],\n", + " data_partitions[\"training\"],\n", + " config_vars[\"batch_size\"],\n", + " config_vars[\"pixel_depth\"],\n", + " config_vars[\"crop_size\"],\n", + " config_vars[\"crop_size\"],\n", + " config_vars[\"rescale_labels\"]\n", + ")\n", + "\n", + "val_gen = utils.data_provider.single_data_from_images(\n", + " config_vars[\"normalized_images_dir\"],\n", + " config_vars[\"boundary_labels_dir\"],\n", + " data_partitions[\"validation\"],\n", + " config_vars[\"val_batch_size\"],\n", + " config_vars[\"pixel_depth\"],\n", + " config_vars[\"crop_size\"],\n", + " config_vars[\"crop_size\"],\n", + " config_vars[\"rescale_labels\"]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Build model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# build model\n", + "model = utils.model_builder.get_model_3_class(config_vars[\"crop_size\"], config_vars[\"crop_size\"], activation=None)\n", + "model.summary()\n", + "\n", + "#loss = \"categorical_crossentropy\"\n", + "loss = utils.objectives.weighted_crossentropy\n", + "\n", + "metrics = [keras.metrics.categorical_accuracy, \n", + " utils.metrics.channel_recall(channel=0, name=\"background_recall\"), \n", + " utils.metrics.channel_precision(channel=0, name=\"background_precision\"),\n", + " utils.metrics.channel_recall(channel=1, name=\"interior_recall\"), \n", + " utils.metrics.channel_precision(channel=1, name=\"interior_precision\"),\n", + " utils.metrics.channel_recall(channel=2, name=\"boundary_recall\"), \n", + " utils.metrics.channel_precision(channel=2, name=\"boundary_precision\"),\n", + " ]\n", + "\n", + "optimizer = keras.optimizers.RMSprop(lr=config_vars[\"learning_rate\"])\n", + "\n", + "model.compile(loss=loss, metrics=metrics, optimizer=optimizer)\n", + "\n", + "# Performance logging\n", + "callback_csv = keras.callbacks.CSVLogger(filename=config_vars[\"csv_log_file\"])\n", + "\n", + "callbacks=[callback_csv]\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Training " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# TRAIN\n", + "statistics = model.fit_generator(\n", + " generator=train_gen,\n", + " steps_per_epoch=config_vars[\"steps_per_epoch\"],\n", + " epochs=config_vars[\"epochs\"],\n", + " validation_data=val_gen,\n", + " validation_steps=int(len(data_partitions[\"validation\"])/config_vars[\"val_batch_size\"]),\n", + " callbacks=callbacks,\n", + " verbose = 1\n", + ")\n", + "\n", + "model.save_weights(config_vars[\"model_file\"])\n", + "\n", + "print('Done! :)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/03-prediction.ipynb b/unet4nuclei/03-prediction.ipynb new file mode 100644 index 0000000..d5939b2 --- /dev/null +++ b/unet4nuclei/03-prediction.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 03\n", + "# Predict segmentations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import os.path\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "\n", + "import skimage.io\n", + "import skimage.morphology\n", + "\n", + "import tensorflow as tf\n", + "import keras\n", + "\n", + "import utils.metrics\n", + "import utils.model_builder" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from config import config_vars\n", + "\n", + "# Partition of the data to make predictions (test or validation)\n", + "partition = \"validation\"\n", + "\n", + "experiment_name = '01'\n", + "\n", + "config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name)\n", + "\n", + "data_partitions = utils.dirtools.read_data_partitions(config_vars)\n", + "\n", + "config_vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Device configuration\n", + "\n", + "# Use the following configuration if you want to test on CPUs\n", + "# os.environ['CUDA_VISIBLE_DEVICES'] = ''\n", + "# configuration = tf.ConfigProto(\n", + "# intra_op_parallelism_threads=1,\n", + "# inter_op_parallelism_threads=1)\n", + "\n", + "# Configuration to run on GPU\n", + "configuration = tf.ConfigProto()\n", + "configuration.gpu_options.allow_growth = True\n", + "configuration.gpu_options.visible_device_list = \"0\"\n", + "\n", + "session = tf.Session(config = configuration)\n", + "\n", + "# apply session\n", + "keras.backend.set_session(session)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load images and run predictions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "#image_names = [f for f in data_partitions[partition] if f.startswith(\"IXM\")]\n", + "image_names = [os.path.join(config_vars[\"normalized_images_dir\"], f) for f in data_partitions[partition]]\n", + "\n", + "imagebuffer = skimage.io.imread_collection(image_names)\n", + "\n", + "images = imagebuffer.concatenate()\n", + "\n", + "dim1 = images.shape[1]\n", + "dim2 = images.shape[2]\n", + "\n", + "images = images.reshape((-1, dim1, dim2, 1))\n", + "\n", + "# preprocess (assuming images are encoded as 8-bits in the preprocessing step)\n", + "images = images / 255\n", + "\n", + "# build model and load weights\n", + "model = utils.model_builder.get_model_3_class(dim1, dim2)\n", + "model.load_weights(config_vars[\"model_file\"])\n", + "\n", + "# Normal prediction time\n", + "predictions = model.predict(images, batch_size=1)\n", + "\n", + "model.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Transform predictions to label matrices" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "for i in range(len(images)):\n", + "\n", + " filename = imagebuffer.files[i]\n", + " filename = os.path.basename(filename)\n", + " print(filename)\n", + " \n", + " probmap = predictions[i].squeeze()\n", + " \n", + " plt.imshow(probmap)\n", + " plt.show()\n", + " \n", + " skimage.io.imsave(config_vars[\"probmap_out_dir\"] + filename, probmap)\n", + " \n", + " pred = utils.metrics.probmap_to_pred(probmap, config_vars[\"boundary_boost_factor\"])\n", + "\n", + " plt.imshow(pred)\n", + " plt.show()\n", + " \n", + " label = utils.metrics.pred_to_label(pred, config_vars[\"cell_min_size\"])\n", + " \n", + " plt.imshow(label)\n", + " plt.show()\n", + " \n", + " skimage.io.imsave(config_vars[\"labels_out_dir\"] + filename, label)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/04-evaluation.ipynb b/unet4nuclei/04-evaluation.ipynb new file mode 100644 index 0000000..749eac8 --- /dev/null +++ b/unet4nuclei/04-evaluation.ipynb @@ -0,0 +1,335 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Step 04\n", + "# Evaluation of performance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "import seaborn as sb\n", + "\n", + "import skimage.io\n", + "import skimage.morphology\n", + "import skimage.segmentation\n", + "\n", + "import utils.evaluation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from config import config_vars\n", + "\n", + "# Partition of the data to make predictions (test or validation)\n", + "partition = \"validation\"\n", + "\n", + "experiment_name = '01'\n", + "\n", + "config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name)\n", + "\n", + "data_partitions = utils.dirtools.read_data_partitions(config_vars)\n", + "\n", + "config_vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# To evaluate segmentations produced by other segmentation algorithms (e.g. CellProfiler), \n", + "# manually modify the following config var:\n", + "\n", + "# config_vars[\"labels_out_dir\"] = \"/data1/image-segmentation/cp3_output_2018_02_02/mask/\"\n", + "# config_vars[\"object_dilation\"] = 3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Auxiliary visualization function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Display prediction along with segmentation to visualize errors\n", + "\n", + "def show(ground_truth, prediction, threshold=0.5, image_name=\"N\"):\n", + " \n", + " # Compute Intersection over Union\n", + " IOU = utils.evaluation.intersection_over_union(ground_truth, prediction)\n", + " \n", + " # Create diff map\n", + " diff = np.zeros(ground_truth.shape + (3,))\n", + " A = ground_truth.copy()\n", + " B = prediction.copy()\n", + " A[A > 0] = 1\n", + " B[B > 0] = 1\n", + " D = A - B\n", + " \n", + " # Object-level errors\n", + " C = IOU.copy()\n", + " C[C>=threshold] = 1\n", + " C[C 0:\n", + " struct = skimage.morphology.square(config_vars[\"object_dilation\"])\n", + " prediction = skimage.morphology.dilation(prediction, struct)\n", + " elif config_vars[\"object_dilation\"] < 0:\n", + " struct = skimage.morphology.square(-config_vars[\"object_dilation\"])\n", + " prediction = skimage.morphology.erosion(prediction, struct)\n", + " \n", + " # Relabel objects (cut margin of 30 pixels to make a fair comparison with DeepCell)\n", + " ground_truth = skimage.segmentation.relabel_sequential(ground_truth)[0] #[30:-30,30:-30])[0]\n", + " prediction = skimage.segmentation.relabel_sequential(prediction)[0] #[30:-30,30:-30])[0]\n", + " \n", + " # Compute evaluation metrics\n", + " results = utils.evaluation.compute_af1_results(\n", + " ground_truth, \n", + " prediction, \n", + " results, \n", + " image_name\n", + " )\n", + " \n", + " false_negatives = utils.evaluation.get_false_negatives(\n", + " ground_truth, \n", + " prediction, \n", + " false_negatives, \n", + " image_name\n", + " )\n", + " \n", + " splits_merges = utils.evaluation.get_splits_and_merges(\n", + " ground_truth, \n", + " prediction, \n", + " splits_merges, \n", + " image_name\n", + " )\n", + " \n", + " # Display an example image\n", + " if image_name == all_images[0]:\n", + " show(ground_truth, prediction, image_name=image_name)\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Report of results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "# Display accuracy results\n", + "\n", + "results = results[results[\"Threshold\"] < 0.95]\n", + "\n", + "average_performance = results.groupby(\"Threshold\").mean().reset_index()\n", + "\n", + "R = results.groupby(\"Image\").mean().reset_index()\n", + "g = sb.jointplot(data=R[R[\"F1\"] > 0.4], x=\"Jaccard\", y=\"F1\", xlim=[0.8,1], ylim=[0.6,1])\n", + "g.fig.set_figwidth(4)\n", + "g.fig.set_figheight(8)\n", + "\n", + "average_performance\n", + "R.sort_values(by=\"F1\",ascending=False).loc[12, \"Image\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Plot accuracy results\n", + "\n", + "sb.regplot(data=average_performance, x=\"Threshold\", y=\"F1\", order=3, ci=None)\n", + "average_performance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Compute and print Average F1\n", + "\n", + "average_F1_score = average_performance[\"F1\"].mean()\n", + "jaccard_index = average_performance[\"Jaccard\"].mean()\n", + "print(\"Average F1 score:\", average_F1_score)\n", + "print(\"Jaccard index:\", jaccard_index)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Summarize False Negatives by area\n", + "\n", + "false_negatives = false_negatives[false_negatives[\"False_Negative\"] == 1]\n", + "\n", + "false_negatives.groupby(\n", + " pd.cut(\n", + " false_negatives[\"Area\"], \n", + " [0,250,625,900,10000], # Area intervals\n", + " labels=[\"Tiny nuclei\",\"Small nuclei\",\"Normal nuclei\",\"Large nuclei\"],\n", + " )\n", + ")[\"False_Negative\"].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Summarize splits and merges\n", + "\n", + "print(\"Splits:\",np.sum(splits_merges[\"Splits\"]))\n", + "print(\"Merges:\",np.sum(splits_merges[\"Merges\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Report false positives\n", + "\n", + "print(\"Extra objects (false postives):\",results[results[\"Threshold\"].round(3) == 0.7].sum()[\"FP\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/batch_prediction.py b/unet4nuclei/batch_prediction.py new file mode 100644 index 0000000..13126e3 --- /dev/null +++ b/unet4nuclei/batch_prediction.py @@ -0,0 +1,176 @@ + +# coding: utf-8 + +# # Step 03 +# # Predict segmentations + +import sys +import os +import os.path + +import numpy as np +import pandas as pd + +import skimage.io +import skimage.exposure +import skimage.morphology +import skimage.measure +import skimage.transform +import skimage.segmentation + +import tensorflow as tf +import keras + +import utils.metrics +import utils.model_builder + +# # Configuration +PI = 3.1415926539 +HALF_SIDE = 96 # pixels +SAVE_OUTPUT = "locations" +IMG_EXT = ".tif" + +from config import config_vars + +if len(sys.argv) < 5: + print("Use: python batch_prediction.py experiment_name image_list.csv input_dir output_dir") + sys.exit() +else: + experiment_name = sys.argv[1] + image_list = pd.read_csv(sys.argv[2]) + input_dir = sys.argv[3] + output_dir = sys.argv[4] + +# Partition of the data to make predictions (test or validation) + +config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name) + +# Configuration to run on GPU +configuration = tf.ConfigProto() +configuration.gpu_options.allow_growth = True +configuration.gpu_options.visible_device_list = "0" + +session = tf.Session(config = configuration) + +# apply session +keras.backend.set_session(session) + + +# Load model +image = skimage.io.imread(input_dir + image_list.loc[0,"DNA"]) +dim1 = image.shape[0] +dim2 = image.shape[1] + +# build model and load weights +model = utils.model_builder.get_model_3_class(dim1, dim2) +model.load_weights(config_vars["model_file"]) +model.summary() + +# + +# # Load images and run predictions + +total_num_images = len(image_list) +# The current logic of the code breaks if batch_size > 1 +batch_size = 1 + + +# Check that images have not been processed before +print("Verifying image list") +image_list["Done"] = False +for k,r in image_list.iterrows(): + img_name = image_list.loc[k,"DNA"] + outfile = output_dir + img_name.replace(IMG_EXT, ".csv") + if os.path.isfile(outfile): + image_list.loc[k,"Done"] = True + +print("Total images:",image_list.shape[0]) +image_list = image_list[~image_list["Done"]] +print("Pending processing:", image_list.shape[0]) + + +i = 0 +while i < total_num_images: + batch = image_list.loc[i:i+batch_size-1, "DNA"].values + if i + batch_size < total_num_images: + i += batch_size + else: + batch_size = total_num_images - i + i += batch_size + + image_names = [input_dir + b for b in batch] + + # Check that images exist + missing = [k for k in range(len(image_names)) if not os.path.isfile(image_names[k])] + + # Filter images missing + good_to_go = [] + for j in range(len(batch)): + if j in missing: + print("Image missing:", batch[j]) + else: + good_to_go.append(j) + + image_names = [image_names[k] for k in good_to_go] + batch = [batch[k] for k in good_to_go] + + if len(batch) == 0: + continue + + # Load images + imagebuffer = skimage.io.imread_collection(image_names) + images = imagebuffer.concatenate() + images = images.reshape((-1, dim1, dim2, 1)) + + # Normalize pixels + images = images / np.max(np.max(np.max(images, axis=-1), axis=-1), axis=-1) + + # Normal prediction time + predictions = model.predict(images, batch_size=len(batch)) + + # # Transform predictions to label matrices + + for j in range(len(batch)): + # Determine whether the image has been processed before + filename = output_dir + batch[j].replace(IMG_EXT,".csv") + if os.path.isfile(filename): + print("Image", batch[j], "already done") + continue + + print("Image",batch[j]) + + os.makedirs("/".join(filename.split("/")[0:-1]), exist_ok=True) + probmap = predictions[j].squeeze() + pred = utils.metrics.probmap_to_pred(probmap, config_vars["boundary_boost_factor"]) + label = utils.metrics.pred_to_label(pred, config_vars["cell_min_size"]) + + # Apply object dilation + if config_vars["object_dilation"] > 0: + struct = skimage.morphology.square(config_vars["object_dilation"]) + label = skimage.morphology.dilation(label, struct) + elif config_vars["object_dilation"] < 0: + struct = skimage.morphology.square(-config_vars["object_dilation"]) + label = skimage.morphology.erosion(label, struct) + + label = label.astype(np.int16) + if SAVE_OUTPUT == "masks" or SAVE_OUTPUT == "both": + skimage.io.imsave(filename, label) + + # Save object properties + if SAVE_OUTPUT == "locations" or SAVE_OUTPUT == "both": + os.makedirs("/".join(filename.split("/")[0:-1]), exist_ok=True) + nuclei_df = pd.DataFrame(columns=["Nuclei_Location_Center_X","Nuclei_Location_Center_Y","Orientation"]) + regions = skimage.measure.regionprops(label) + + idx = 0 + for region in regions: + row = int(region.centroid[0]) + col = int(region.centroid[1]) + if row - HALF_SIDE > 0 and row + HALF_SIDE < dim1 and col - HALF_SIDE > 0 and col + HALF_SIDE < dim2: + angle = int(90 - (180 * region.orientation) / PI) + nuclei_df.loc[idx] = {"Nuclei_Location_Center_X": col, "Nuclei_Location_Center_Y": row, "Orientation": angle} + idx += 1 + + nuclei_df.to_csv(filename, index=False) + + diff --git a/unet4nuclei/config.py b/unet4nuclei/config.py new file mode 100644 index 0000000..80d2b05 --- /dev/null +++ b/unet4nuclei/config.py @@ -0,0 +1,108 @@ +import os +import utils.dirtools + +config_vars = {} + +# ************ 01 ************ # +# ****** PREPROCESSING ******* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.01 INPUT DIRECTORIES AND FILES + +config_vars["root_directory"] = '/home/jccaicedo/nucleus_segmentation/' + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.02 DATA PARTITION INFO + +## Maximum number of training images (use 0 for all) +config_vars["max_training_images"] = 0 + +## Generate partitions? +## If False, load predefined partitions (training.txt, validation.txt and test.txt) +config_vars["create_split_files"] = False + +## Randomly choose training and validation images. +## The remaining fraction is reserved for test images. +config_vars["training_fraction"] = 0.5 +config_vars["validation_fraction"] = 0.25 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.03 IMAGE STORAGE OPTIONS + +## Transform gray scale TIF images to PNG +config_vars["transform_images_to_PNG"] = True +config_vars["pixel_depth"] = 8 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.04 PRE-PROCESSING OF ANNOTATIONS + +## Area of minimun object in pixels +config_vars["min_nucleus_size"] = 25 + +## Pixels of the boundary (min 2 pixels) +config_vars["boundary_size"] = 2 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 01.05 DATA AUGMENTATION USING ELASTIC DEFORMATIONS + +## Elastic deformation takes a lot of times to compute. +## It is computed only once in the preprocessing. +config_vars["augment_images"] = False + +## Augmentation parameters. +## Calibrate parameters using the 00-elastic-deformation.ipynb +config_vars["elastic_points"] = 16 +config_vars["elastic_distortion"] = 5 + +## Number of augmented images +config_vars["elastic_augmentations"] = 10 + + +# ************ 02 ************ # +# ********* TRAINING ********* # +# **************************** # + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.01 OPTIMIZATION + +config_vars["learning_rate"] = 1e-4 + +config_vars["epochs"] = 15 + +config_vars["steps_per_epoch"] = 500 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.02 BATCHES + +config_vars["batch_size"] = 10 + +config_vars["val_batch_size"] = 10 + +# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +# 02.03 DATA NORMALIZATION + +config_vars["rescale_labels"] = True + +config_vars["crop_size"] = 256 + +# ************ 03 ************ # +# ******** PREDICTION ******** # +# **************************** # + +config_vars["cell_min_size"] = 16 + +config_vars["boundary_boost_factor"] = 1 + +# ************ 04 ************ # +# ******** EVALUATION ******** # +# **************************** # + +config_vars["object_dilation"] = 3 + +# **************************** # +# ******** FINAL SETUP ******* # +# **************************** # + +config_vars = utils.dirtools.setup_working_directories(config_vars) + diff --git a/unet4nuclei/elastic-deformation.ipynb b/unet4nuclei/elastic-deformation.ipynb new file mode 100644 index 0000000..8b1bdb0 --- /dev/null +++ b/unet4nuclei/elastic-deformation.ipynb @@ -0,0 +1,186 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Optional Step\n", + "# Explore elastic deformation augmentations\n", + "\n", + "Load an example image with the corresponding annotations and change parameters to determine which configuration makes realistic deformations for your dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import skimage.io\n", + "\n", + "import utils.augmentation\n", + "from config import config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Configuration\n", + "Select one image with the corresponding expected output (boundary labels)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "config_vars" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "test_image = \"IXMtest_P23_s7_w13B627CB9-6C57-4049-AAD7-6468A051DD24.png\"\n", + "input_image_filename = config_vars[\"normalized_images_dir\"] + test_image\n", + "output_image_filename = config_vars[\"boundary_labels_dir\"] + test_image" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Auxiliary visualization function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def display(im1, im2, p, d, params=None):\n", + " fig, ax = plt.subplots(1,2, figsize=(18,12))\n", + " ax[0].imshow(im1)\n", + " ax[1].imshow(im2)\n", + " ax[0].set_title('Points {} Distort {}'.format(p, d))\n", + " if params is not None:\n", + " ax[0].plot(params[\"tform\"].inverse(params[\"src\"])[:, 0], params[\"tform\"].inverse(params[\"src\"])[:, 1], '.y')\n", + " ax[0].axis((0, params[\"out_cols\"], params[\"out_rows\"], 0))\n", + " ax[1].plot(params[\"tform\"].inverse(params[\"src\"])[:, 0], params[\"tform\"].inverse(params[\"src\"])[:, 1], '.y')\n", + " ax[1].axis((0, params[\"out_cols\"], params[\"out_rows\"], 0))\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Show original images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "x = skimage.io.imread(input_image_filename)\n", + "y = skimage.io.imread(output_image_filename)\n", + "\n", + "fig, ax = plt.subplots(1,2, figsize=(18,12))\n", + "ax[0].imshow(x)\n", + "ax[1].imshow(y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generate example augmented images\n", + "Change the four parameters below to explore different configurations " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "points_from = 14\n", + "points_to = 18\n", + "distortion_from = 4\n", + "distortion_to = 6\n", + "\n", + "for i in range(points_from, points_to, 2):\n", + " for j in range(distortion_from, distortion_to, 1):\n", + " out1, out2 = utils.augmentation.deform(x, y, points=i, distort=j)\n", + " display(out1, out2, i, j)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Estimate computational cost of augmentations\n", + "Elastic deformations are not computed online during training. They are pre-computed and stored." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%timeit out1, out2 = utils.augmentation.deform(x, y, points=20, distort=6)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/full-experiment.ipynb b/unet4nuclei/full-experiment.ipynb new file mode 100644 index 0000000..4d5c50e --- /dev/null +++ b/unet4nuclei/full-experiment.ipynb @@ -0,0 +1,166 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Run multiple repetitions of a full segmentation experiment\n", + "This notebook is useful to test variables that may influence the result. In this example, we evaluated the impact of using variable number of images for training a U-Net" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import utils.experiment\n", + "import utils.dirtools\n", + "import pandas as pd\n", + "import seaborn as sb\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from config import config_vars\n", + "\n", + "experiment_name = 'impact_of_augmented_dataset_size'\n", + "\n", + "partition = \"validation\"\n", + "\n", + "total_repetitions = 10\n", + "\n", + "config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name)\n", + "\n", + "config_vars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Main loop\n", + "Run repetitions for each number of images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "results = pd.DataFrame(columns=[\"Samples\", \"Repeat\", \"Average_F1\", \"Jaccard\", \"Missed\", \"Merges\", \"Splits\"])\n", + "idx = 0\n", + "\n", + "for max_samples in [2, 4, 6, 8, 10, 20, 40, 60, 80, 100]:\n", + " for repetition in range(total_repetitions):\n", + " print(\"Experiment\", idx, \"- max_samples:\", max_samples, \"- repetition:\", repetition)\n", + " \n", + " # Modify settings\n", + " config_vars[\"max_training_images\"] = max_samples\n", + " \n", + " # Reconfigure variables and data partitions\n", + " config_vars = utils.dirtools.setup_experiment(config_vars, experiment_name)\n", + " data_partitions = utils.dirtools.read_data_partitions(config_vars)\n", + " \n", + " # Run experiment\n", + " output = utils.experiment.run(config_vars, data_partitions, experiment_name, partition, GPU=\"0\")\n", + " \n", + " # Collect outputs\n", + " record = {\n", + " \"Samples\": max_samples,\n", + " \"Repeat\": repetition,\n", + " \"Average_F1\": output[\"Average_F1\"],\n", + " \"Jaccard\": output[\"Jaccard\"],\n", + " \"Missed\": output[\"Missed\"].sum(),\n", + " \"Merges\": output[\"Merges\"],\n", + " \"Splits\": output[\"Splits\"]\n", + " }\n", + " results.loc[idx] = record\n", + " idx += 1\n", + " \n", + " # Clean up directories\n", + " experiment_dir = config_vars[\"root_directory\"] + \"/experiments/\" + experiment_name\n", + " if os.path.exists(experiment_dir):\n", + " os.system(\"rm -Rf \" + experiment_dir)\n", + " \n", + " # Save results\n", + " results.to_csv(config_vars[\"root_directory\"] + \"/experiments/\" + experiment_name + \".csv\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualize results\n", + "After all repetitions are done, load results and visualize" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Load all the results and repetitions for analysis\n", + "\n", + "results = pd.read_csv(config_vars[\"root_directory\"] + \"/experiments/\" + experiment_name + \".csv\")\n", + "mean = results.groupby(\"Samples\").mean().reset_index()\n", + "sem = results.groupby(\"Samples\").sem().reset_index()\n", + "sem.columns = [c+\"_se\" for c in sem.columns]\n", + "data = pd.concat([mean, sem], axis=1).drop([\"Samples_se\", \"Repeat\", \"Repeat_se\"], axis=1)\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Change the column for visualizing other metrics:\n", + "results_column = \"Average_F1\"\n", + "\n", + "plt.figure(figsize=(8,8))\n", + "plt.errorbar(x=data[\"Samples\"], y=data[results_column], yerr=data[results_column + \"_se\"])\n", + "plt.xscale(\"log\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/utils/__init__.py b/unet4nuclei/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unet4nuclei/utils/augmentation.py b/unet4nuclei/utils/augmentation.py new file mode 100644 index 0000000..a03ad3d --- /dev/null +++ b/unet4nuclei/utils/augmentation.py @@ -0,0 +1,46 @@ +import numpy as np +import skimage.transform + +# Based on example code from: +# http://scikit-image.org/docs/dev/auto_examples/transform/plot_piecewise_affine.html + +def deform(image1, image2, points=10, distort=5.0): + + # create deformation grid + rows, cols = image1.shape[0], image1.shape[1] + src_cols = np.linspace(0, cols, points) + src_rows = np.linspace(0, rows, points) + src_rows, src_cols = np.meshgrid(src_rows, src_cols) + src = np.dstack([src_cols.flat, src_rows.flat])[0] + + # add distortion to coordinates + s = src[:, 1].shape + dst_rows = src[:, 1] + np.random.normal(size=s)*np.random.uniform(0.0, distort, size=s) + dst_cols = src[:, 0] + np.random.normal(size=s)*np.random.uniform(0.0, distort, size=s) + + dst = np.vstack([dst_cols, dst_rows]).T + + tform = skimage.transform.PiecewiseAffineTransform() + tform.estimate(src, dst) + + out_rows = rows + out_cols = cols + out1 = skimage.transform.warp(image1, tform, output_shape=(out_rows, out_cols), mode="symmetric") + out2 = skimage.transform.warp(image2, tform, output_shape=(out_rows, out_cols), mode="symmetric") + + return out1, out2 + + +def resize(x, y): + wf = 1 + np.random.uniform(-0.25, 0.25) + hf = 1 + np.random.uniform(-0.25, 0.25) + + w,h = x.shape[0:2] + + wt, ht = int(wf*w), int(hf*h) + + new_x = skimage.transform.resize(x, (wt,ht)) + new_y = skimage.transform.resize(y, (wt,ht)) + + return new_x, new_y + diff --git a/unet4nuclei/utils/contours_to_labels.ipynb b/unet4nuclei/utils/contours_to_labels.ipynb new file mode 100644 index 0000000..5141105 --- /dev/null +++ b/unet4nuclei/utils/contours_to_labels.ipynb @@ -0,0 +1,316 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## This notebook shall help to understand the process how we get from contours to a segmentation.\n", + "\n", + "Let's describe the process:\n", + "- We threshold the input which shall contain the probability that a pixel belongs to a contour by 0.5.\n", + "- In the resulting binary images, we search for connected components with treating 1 as the background color.\n", + "- To ensure that we only use connected components, that belong to parts of the image that depict nuclei, for our further analysis, we only use bright (high DNA content) connected components. This filters out connected components that belong to the background.\n", + "- We define bright as the mean intensity of the pixels in the connected component to be higher than 50.\n", + "- The number 50 is derived by analyzing all training images. The histogram distributions is clearly composed by the distribution of the dark background pixels and the distribution of the bright pixels depicting DNA stain. The minimum of the histogram best splitting both distributions is around 50 as we show in the appendix below." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "\n", + "import skimage.io\n", + "import skimage.morphology\n", + "\n", + "import numpy as np\n", + "\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load sample image" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "image = skimage.io.imread('/home/jr0th/github/segmentation/data/BBBC022/test/x/all/IXMtest_A09_s1_w1CE70AD49-290D-4312-82E6-CDC717F32637_0_0.png')\n", + "boundary = skimage.io.imread('/home/jr0th/github/segmentation/data/BBBC022/test/y_boundary_2/all/IXMtest_A09_s1_w1CE70AD49-290D-4312-82E6-CDC717F32637_0_0.png')\n", + "correct_labels = skimage.io.imread('/home/jr0th/github/segmentation/data/BBBC022/test/y_label_binary_2/all/IXMtest_A09_s1_w1CE70AD49-290D-4312-82E6-CDC717F32637_0_0.png')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that there a certain regions of the image that are not connected to the \"big connected background\". These are small connected components that should be filtered out." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\nB7vEy7q6v2RbAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(boundary)\n", + "plt.colorbar()\n", + "plt.show()\n", + "\n", + "plt.imshow(image)\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Find connected components in outline image" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "labels = skimage.morphology.label(boundary, background=1)\n", + "\n", + "plt.imshow(labels)\n", + "plt.colorbar()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Prepare loop over all connected components to filter out those which correspond to background regions" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of connected components: 29\n" + ] + } + ], + "source": [ + "n_ccs = np.max(labels)\n", + "print('Number of connected components:', n_ccs)\n", + "\n", + "# buffer label image\n", + "filtered_labels = np.zeros_like(labels, dtype=np.uint16)\n", + "\n", + "# relabel as we don't know what connected component the background has been given before\n", + "label_index = 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Loop over all components" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "# start at 1 (0 is contours), end at number of connected components\n", + "for i in range(1, n_ccs + 1):\n", + " \n", + " # get mask of connected compoenents\n", + " mask = labels == i\n", + " \n", + " # get mean\n", + " mean = np.mean(np.take(image.flatten(),np.nonzero(mask.flatten())))\n", + "\n", + " if(mean > 50):\n", + " filtered_labels[mask] = label_index\n", + " label_index = label_index + 1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot to see what we did" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\nmv8HpDSbFHrZJ+kAAAAASUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\nTkSuQmCC\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.imshow(labels)\n", + "plt.colorbar()\n", + "plt.show()\n", + "plt.imshow(filtered_labels)\n", + "plt.colorbar()\n", + "plt.show()\n", + "plt.imshow(correct_labels)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Appendix: Threshold for defining a bright region" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# load all images\n", + "images = skimage.io.imread_collection('/home/jr0th/github/segmentation/data/BBBC022/training/x/all/*')\n", + "all_data = images.concatenate()\n", + "\n", + "# plot histogram\n", + "plt.hist(all_data.flatten(),bins=50,normed=True)\n", + "plt.title('Histogram of Training Images', size = 12)\n", + "plt.ylabel('Relative Frequency', size = 12)\n", + "plt.xlabel('Intensity', size = 12)\n", + "plt.savefig('training_set_hist.eps')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the minimum separating the two subdistributions is at 50 roughly, this determined the choice above." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/unet4nuclei/utils/data_provider.py b/unet4nuclei/utils/data_provider.py new file mode 100644 index 0000000..9c71ff3 --- /dev/null +++ b/unet4nuclei/utils/data_provider.py @@ -0,0 +1,157 @@ +import os +import os.path +import numpy as np + +import skimage.io +import keras.preprocessing.image + +import utils.augmentation + + +def setup_working_directories(config_vars): + + ## Expected raw data directories: + config_vars["raw_images_dir"] = os.path.join(config_vars["root_directory"], 'raw_images/') + config_vars["raw_annotations_dir"] = os.path.join(config_vars["root_directory"], 'raw_annotations/') + + ## Split files + config_vars["path_files_training"] = os.path.join(config_vars["root_directory"], 'training.txt') + config_vars["path_files_validation"] = os.path.join(config_vars["root_directory"], 'validation.txt') + config_vars["path_files_test"] = os.path.join(config_vars["root_directory"], 'test.txt') + + ## Transformed data directories: + config_vars["normalized_images_dir"] = os.path.join(config_vars["root_directory"], 'norm_images/') + config_vars["boundary_labels_dir"] = os.path.join(config_vars["root_directory"], 'boundary_labels/') + + return config_vars + +def single_data_from_images(x_dir, y_dir, image_names, batch_size, bit_depth, dim1, dim2, rescale_labels): + + ## Prepare image names + x_image_names = [os.path.join(x_dir, f) for f in image_names] + y_image_names = [os.path.join(y_dir, f) for f in image_names] + + ## Load all images in memory + x = skimage.io.imread_collection(x_image_names).concatenate() + y = skimage.io.imread_collection(y_image_names).concatenate() + + ## Crop the desired size + x = x[:, 0:dim1, 0:dim2] + x = x.reshape(-1, dim1, dim2, 1) + y = y[:, 0:dim1, 0:dim2, :] + + ## Setup Keras Generators + rescale_factor = 1./(2**bit_depth - 1) + + if(rescale_labels): + rescale_factor_labels = rescale_factor + else: + rescale_factor_labels = 1 + + gen_x = keras.preprocessing.image.ImageDataGenerator(rescale=rescale_factor) + gen_y = keras.preprocessing.image.ImageDataGenerator(rescale=rescale_factor_labels) + + seed = 42 + + stream_x = gen_x.flow( + x, + batch_size=batch_size, + seed=seed + ) + stream_y = gen_y.flow( + y, + batch_size=batch_size, + seed=seed + ) + + flow = zip(stream_x, stream_y) + + return flow + + +def random_sample_generator(x_dir, y_dir, image_names, batch_size, bit_depth, dim1, dim2, rescale_labels): + + do_augmentation = True + + # get image names + print('Training with',len(image_names), 'images.') + + # get dimensions right -- understand data set + n_images = len(image_names) + ref_img = skimage.io.imread(os.path.join(y_dir, image_names[0])) + + if(len(ref_img.shape) == 2): + gray = True + else: + gray = False + + # rescale images + rescale_factor = 1./(2**bit_depth - 1) + if(rescale_labels): + rescale_factor_labels = rescale_factor + else: + rescale_factor_labels = 1 + + while(True): + + if(gray): + y_channels = 1 + else: + y_channels = 3 + + # buffers for a batch of data + x = np.zeros((batch_size, dim1, dim2, 1)) + y = np.zeros((batch_size, dim1, dim2, y_channels)) + + # get one image at a time + for i in range(batch_size): + + # get random image + img_index = np.random.randint(low=0, high=n_images) + + # open images + x_big = skimage.io.imread(os.path.join(x_dir, image_names[img_index])) * rescale_factor + y_big = skimage.io.imread(os.path.join(y_dir, image_names[img_index])) * rescale_factor_labels + + # resizing + #x_big, y_big = utils.augmentation.resize(patch_x, patch_y) + + + # get random crop + start_dim1 = np.random.randint(low=0, high=x_big.shape[0] - dim1) + start_dim2 = np.random.randint(low=0, high=x_big.shape[1] - dim2) + + patch_x = x_big[start_dim1:start_dim1 + dim1, start_dim2:start_dim2 + dim2] #* rescale_factor + patch_y = y_big[start_dim1:start_dim1 + dim1, start_dim2:start_dim2 + dim2] #* rescale_factor_labels + + if(do_augmentation): + + rand_flip = np.random.randint(low=0, high=2) + rand_rotate = np.random.randint(low=0, high=4) + + # flip + if(rand_flip == 0): + patch_x = np.flip(patch_x, 0) + patch_y = np.flip(patch_y, 0) + + # rotate + for rotate_index in range(rand_rotate): + patch_x = np.rot90(patch_x) + patch_y = np.rot90(patch_y) + + # illumination + ifactor = 1 + np.random.uniform(-0.75, 0.75) + patch_x *= ifactor + + # save image to buffer + x[i, :, :, 0] = patch_x + + if(gray): + y[i, :, :, 0] = patch_y + else: + y[i, :, :, 0:y_channels] = patch_y + + # return the buffer + yield(x, y) + + diff --git a/unet4nuclei/utils/dirtools.py b/unet4nuclei/utils/dirtools.py new file mode 100644 index 0000000..e31e4e4 --- /dev/null +++ b/unet4nuclei/utils/dirtools.py @@ -0,0 +1,103 @@ +import os +import glob +import random + +def create_image_lists(dir_raw_images, fraction_train = 0.5, fraction_validation = 0.25): + file_list = os.listdir(dir_raw_images) + + if (fraction_train + fraction_validation >= 1): + print("fraction_train + fraction_validation is > 1!") + print("setting fraction_train = 0.5, fraction_validation = 0.25") + fraction_train = 0.5 + fraction_validation = 0.25 + + fraction_test = 1 - fraction_train - fraction_validation + + image_list = [x for x in file_list if x.endswith("png") ] + + random.shuffle(image_list) + + index_train_end = int( len(image_list) * fraction_train) + index_validation_end = index_train_end + int(len(image_list) * fraction_validation) + + # split into two parts for training and testing + image_list_train = image_list[0:index_train_end] + image_list_test = image_list[index_train_end:(index_validation_end)] + image_list_validation = image_list[index_validation_end:] + return(image_list_train, image_list_test, image_list_validation) + + +def write_path_files(file_path, list): + with open(file_path, 'w') as myfile: + for line in list: myfile.write(line + '\n') + + +def setup_working_directories(config_vars): + + ## Expected raw data directories: + config_vars["raw_images_dir"] = os.path.join(config_vars["root_directory"], 'raw_images/') + config_vars["raw_annotations_dir"] = os.path.join(config_vars["root_directory"], 'raw_annotations/') + + ## Split files + config_vars["path_files_training"] = os.path.join(config_vars["root_directory"], 'training.txt') + config_vars["path_files_validation"] = os.path.join(config_vars["root_directory"], 'validation.txt') + config_vars["path_files_test"] = os.path.join(config_vars["root_directory"], 'test.txt') + + ## Transformed data directories: + config_vars["normalized_images_dir"] = os.path.join(config_vars["root_directory"], 'norm_images/') + config_vars["boundary_labels_dir"] = os.path.join(config_vars["root_directory"], 'boundary_labels/') + + return config_vars + + +def read_data_partitions(config_vars, load_augmented=True): + with open(config_vars["path_files_training"]) as f: + training_files = f.read().splitlines() + if config_vars["max_training_images"] > 0: + random.shuffle(training_files) + training_files = training_files[0:config_vars["max_training_images"]] + + with open(config_vars["path_files_validation"]) as f: + validation_files = f.read().splitlines() + + with open(config_vars["path_files_test"]) as f: + test_files = f.read().splitlines() + + # Add augmented images to the training list + if load_augmented: + files = glob.glob(config_vars["root_directory"] + "norm_images/*_aug_*.png") + files = [f.split("/")[-1] for f in files] + if config_vars["max_training_images"] > 0: + augmented = [] + for trf in training_files: + augmented += [f for f in files if f.startswith(trf.split(".")[0])] + training_files += augmented + else: + training_files += files + + partitions = { + "training": training_files, + "validation": validation_files, + "test": test_files + } + + return partitions + +def setup_experiment(config_vars, tag): + + # Output dirs + config_vars["experiment_dir"] = os.path.join(config_vars["root_directory"], "experiments/" + tag + "/out/") + config_vars["probmap_out_dir"] = os.path.join(config_vars["experiment_dir"], "prob/") + config_vars["labels_out_dir"] = os.path.join(config_vars["experiment_dir"], "segm/") + + # Files + config_vars["model_file"] = config_vars["root_directory"] + "experiments/" + tag + "/model.hdf5" + config_vars["csv_log_file"] = config_vars["root_directory"] + "experiments/" + tag + "/log.csv" + + # Make output directories + os.makedirs(config_vars["experiment_dir"], exist_ok=True) + os.makedirs(config_vars["probmap_out_dir"], exist_ok=True) + os.makedirs(config_vars["labels_out_dir"], exist_ok=True) + + return config_vars + diff --git a/unet4nuclei/utils/evaluation.py b/unet4nuclei/utils/evaluation.py new file mode 100644 index 0000000..9ffeae9 --- /dev/null +++ b/unet4nuclei/utils/evaluation.py @@ -0,0 +1,114 @@ +import numpy as np +import pandas as pd + + +def intersection_over_union(ground_truth, prediction): + + # Count objects + true_objects = len(np.unique(ground_truth)) + pred_objects = len(np.unique(prediction)) + + # Compute intersection + h = np.histogram2d(ground_truth.flatten(), prediction.flatten(), bins=(true_objects,pred_objects)) + intersection = h[0] + + # Area of objects + area_true = np.histogram(ground_truth, bins=true_objects)[0] + area_pred = np.histogram(prediction, bins=pred_objects)[0] + + # Calculate union + area_true = np.expand_dims(area_true, -1) + area_pred = np.expand_dims(area_pred, 0) + union = area_true + area_pred - intersection + + # Exclude background from the analysis + intersection = intersection[1:,1:] + union = union[1:,1:] + + # Compute Intersection over Union + union[union == 0] = 1e-9 + IOU = intersection/union + + return IOU + + + +def measures_at(threshold, IOU): + + matches = IOU > threshold + + true_positives = np.sum(matches, axis=1) == 1 # Correct objects + false_positives = np.sum(matches, axis=0) == 0 # Extra objects + false_negatives = np.sum(matches, axis=1) == 0 # Missed objects + + assert np.all(np.less_equal(true_positives, 1)) + assert np.all(np.less_equal(false_positives, 1)) + assert np.all(np.less_equal(false_negatives, 1)) + + TP, FP, FN = np.sum(true_positives), np.sum(false_positives), np.sum(false_negatives) + + f1 = 2*TP / (2*TP + FP + FN + 1e-9) + + return f1, TP, FP, FN + +# Compute Average Precision for all IoU thresholds + +def compute_af1_results(ground_truth, prediction, results, image_name): + + # Compute IoU + IOU = intersection_over_union(ground_truth, prediction) + if IOU.shape[0] > 0: + jaccard = np.max(IOU, axis=0).mean() + else: + jaccard = 0.0 + + # Calculate F1 score at all thresholds + for t in np.arange(0.5, 0.95, 0.05): + f1, tp, fp, fn = measures_at(t, IOU) + res = {"Image": image_name, "Threshold": t, "F1": f1, "Jaccard": jaccard, "TP": tp, "FP": fp, "FN": fn} + row = len(results) + results.loc[row] = res + + return results + +# Count number of False Negatives at 0.7 IoU + +def get_false_negatives(ground_truth, prediction, results, image_name, threshold=0.7): + + # Compute IoU + IOU = intersection_over_union(ground_truth, prediction) + + true_objects = len(np.unique(ground_truth)) + if true_objects <= 1: + return results + + area_true = np.histogram(ground_truth, bins=true_objects)[0][1:] + true_objects -= 1 + + # Identify False Negatives + matches = IOU > threshold + false_negatives = np.sum(matches, axis=1) == 0 # Missed objects + + data = np.asarray([ + area_true.copy(), + np.array(false_negatives, dtype=np.int32) + ]) + + results = pd.concat([results, pd.DataFrame(data=data.T, columns=["Area", "False_Negative"])], sort=False) + + return results + +# Count the number of splits and merges + +def get_splits_and_merges(ground_truth, prediction, results, image_name): + + # Compute IoU + IOU = intersection_over_union(ground_truth, prediction) + + matches = IOU > 0.1 + merges = np.sum(matches, axis=0) > 1 + splits = np.sum(matches, axis=1) > 1 + r = {"Image_Name":image_name, "Merges":np.sum(merges), "Splits":np.sum(splits)} + results.loc[len(results)+1] = r + return results + diff --git a/unet4nuclei/utils/experiment.py b/unet4nuclei/utils/experiment.py new file mode 100644 index 0000000..d9a4e3a --- /dev/null +++ b/unet4nuclei/utils/experiment.py @@ -0,0 +1,220 @@ +import sys +import os +import os.path + +import numpy as np +import pandas as pd + +import tensorflow as tf + +import keras.backend +import keras.callbacks +import keras.layers +import keras.models +import keras.optimizers + +import utils.model_builder +import utils.data_provider +import utils.metrics +import utils.objectives +import utils.dirtools +import utils.evaluation + +import skimage.io +import skimage.morphology +import skimage.segmentation + + +def run(config_vars, data_partitions, experiment_name, partition, GPU="2"): + + # Device configuration + configuration = tf.ConfigProto() + configuration.gpu_options.allow_growth = True + configuration.gpu_options.visible_device_list = GPU + session = tf.Session(config = configuration) + + # apply session + keras.backend.set_session(session) + + # # Step 02 + # # Training a U-Net model + + train_gen = utils.data_provider.random_sample_generator( + config_vars["normalized_images_dir"], + config_vars["boundary_labels_dir"], + data_partitions["training"], + config_vars["batch_size"], + config_vars["pixel_depth"], + config_vars["crop_size"], + config_vars["crop_size"], + config_vars["rescale_labels"] + ) + + val_gen = utils.data_provider.single_data_from_images( + config_vars["normalized_images_dir"], + config_vars["boundary_labels_dir"], + data_partitions["validation"], + config_vars["val_batch_size"], + config_vars["pixel_depth"], + config_vars["crop_size"], + config_vars["crop_size"], + config_vars["rescale_labels"] + ) + + model = utils.model_builder.get_model_3_class(config_vars["crop_size"], config_vars["crop_size"], activation=None) + + loss = utils.objectives.weighted_crossentropy + + metrics = [keras.metrics.categorical_accuracy, + utils.metrics.channel_recall(channel=0, name="background_recall"), + utils.metrics.channel_precision(channel=0, name="background_precision"), + utils.metrics.channel_recall(channel=1, name="interior_recall"), + utils.metrics.channel_precision(channel=1, name="interior_precision"), + utils.metrics.channel_recall(channel=2, name="boundary_recall"), + utils.metrics.channel_precision(channel=2, name="boundary_precision"), + ] + + optimizer = keras.optimizers.RMSprop(lr=config_vars["learning_rate"]) + + model.compile(loss=loss, metrics=metrics, optimizer=optimizer) + + callback_csv = keras.callbacks.CSVLogger(filename=config_vars["csv_log_file"]) + + callbacks=[callback_csv] + + # TRAIN + statistics = model.fit_generator( + generator=train_gen, + steps_per_epoch=config_vars["steps_per_epoch"], + epochs=config_vars["epochs"], + validation_data=val_gen, + validation_steps=int(len(data_partitions["validation"])/config_vars["val_batch_size"]), + callbacks=callbacks, + verbose = 1 + ) + + model.save_weights(config_vars["model_file"]) + + print('Training Done! :)') + + + # # Step 03 + # # Predict segmentations + + image_names = [f for f in data_partitions[partition] if f.startswith("IXM")] + image_names = [os.path.join(config_vars["normalized_images_dir"], f) for f in image_names]#data_partitions[partition]] + + imagebuffer = skimage.io.imread_collection(image_names) + + images = imagebuffer.concatenate() + + dim1 = images.shape[1] + dim2 = images.shape[2] + + images = images.reshape((-1, dim1, dim2, 1)) + + images = images / 255 + + model = utils.model_builder.get_model_3_class(dim1, dim2) + model.load_weights(config_vars["model_file"]) + + predictions = model.predict(images, batch_size=1) + + for i in range(len(images)): + + filename = imagebuffer.files[i] + filename = os.path.basename(filename) + + probmap = predictions[i].squeeze() + + skimage.io.imsave(config_vars["probmap_out_dir"] + filename, probmap) + + pred = utils.metrics.probmap_to_pred(probmap, config_vars["boundary_boost_factor"]) + + label = utils.metrics.pred_to_label(pred, config_vars["cell_min_size"]) + + skimage.io.imsave(config_vars["labels_out_dir"] + filename, label) + + + # # Step 04 + # # Evaluation of performance + + all_images = data_partitions[partition] + #all_images = [f for f in data_partitions[partition] if f.startswith("IXM")] + + + #results = pd.DataFrame(columns=["Image", "Threshold", "Precision"]) + #false_negatives = pd.DataFrame(columns=["False_Negative", "Area"]) + #splits_merges = pd.DataFrame(columns=["Image_Name", "Merges","Splits"]) + results = pd.DataFrame(columns=["Image", "Threshold", "F1", "Jaccard", "TP", "FP", "FN"]) + false_negatives = pd.DataFrame(columns=["False_Negative", "Area"]) + splits_merges = pd.DataFrame(columns=["Image_Name", "Merges", "Splits"]) + + for image_name in all_images: + img_filename = os.path.join(config_vars["raw_annotations_dir"], image_name) + ground_truth = skimage.io.imread(img_filename) + if len(ground_truth.shape) == 3: + ground_truth = ground_truth[:,:,0] + + ground_truth = skimage.morphology.label(ground_truth) + + pred_filename = os.path.join(config_vars["labels_out_dir"], image_name) + prediction = skimage.io.imread(pred_filename) #.replace(".png",".tiff")) + + if config_vars["object_dilation"] > 0: + struct = skimage.morphology.square(config_vars["object_dilation"]) + prediction = skimage.morphology.dilation(prediction, struct) + elif config_vars["object_dilation"] < 0: + struct = skimage.morphology.square(-config_vars["object_dilation"]) + prediction = skimage.morphology.erosion(prediction, struct) + + ground_truth = skimage.segmentation.relabel_sequential(ground_truth[30:-30,30:-30])[0] # )[0] # + prediction = skimage.segmentation.relabel_sequential(prediction[30:-30,30:-30])[0] # )[0] # + + results = utils.evaluation.compute_af1_results( + ground_truth, + prediction, + results, + image_name + ) + + false_negatives = utils.evaluation.get_false_negatives( + ground_truth, + prediction, + false_negatives, + image_name + ) + + splits_merges = utils.evaluation.get_splits_and_merges( + ground_truth, + prediction, + splits_merges, + image_name + ) + + + # # Report of results + + output = {} + + results = results[results["Threshold"] < 0.95] + average_performance = results.groupby("Threshold").mean().reset_index() + output["Average_F1"] = average_performance["F1"].mean() + output["Jaccard"] = average_performance["Jaccard"].mean() + + false_negatives = false_negatives[false_negatives["False_Negative"] == 1] + + missed = false_negatives.groupby( + pd.cut( + false_negatives["Area"], + [0,250,625,900,10000], # Area intervals + labels=["Tiny nuclei","Small nuclei","Normal nuclei","Large nuclei"], + ) + )["False_Negative"].sum() + + output["Missed"] = missed + output["Splits"] = np.sum(splits_merges["Splits"]) + output["Merges"] = np.sum(splits_merges["Merges"]) + + return output + diff --git a/unet4nuclei/utils/metrics.py b/unet4nuclei/utils/metrics.py new file mode 100644 index 0000000..fd90c74 --- /dev/null +++ b/unet4nuclei/utils/metrics.py @@ -0,0 +1,180 @@ +import numpy as np +import skimage.segmentation +import skimage.io +import keras.backend as K +import tensorflow as tf + +debug = False + +def channel_precision(channel, name): + def precision_func(y_true, y_pred): + y_pred_tmp = K.cast(tf.equal( K.argmax(y_pred, axis=-1), channel), "float32") + true_positives = K.sum(K.round(K.clip(y_true[:,:,:,channel] * y_pred_tmp, 0, 1))) + predicted_positives = K.sum(K.round(K.clip(y_pred_tmp, 0, 1))) + precision = true_positives / (predicted_positives + K.epsilon()) + + return precision + precision_func.__name__ = name + return precision_func + + +def channel_recall(channel, name): + def recall_func(y_true, y_pred): + y_pred_tmp = K.cast(tf.equal( K.argmax(y_pred, axis=-1), channel), "float32") + true_positives = K.sum(K.round(K.clip(y_true[:,:,:,channel] * y_pred_tmp, 0, 1))) + possible_positives = K.sum(K.round(K.clip(y_true[:,:,:,channel], 0, 1))) + recall = true_positives / (possible_positives + K.epsilon()) + + return recall + recall_func.__name__ = name + return recall_func + + +## PROBMAP TO CONTOURS TO LABEL + +def probmap_to_contour(probmap, threshold = 0.5): + # assume 2D input + outline = probmap >= threshold + + return outline + +def contour_to_label(outline, image): + # see notebook contours_to_labels for why we do what we do here + + # get connected components + labels = skimage.morphology.label(outline, background=1) + skimage.morphology.remove_small_objects(labels, min_size = 100, in_place = True) + + n_ccs = np.max(labels) + + # buffer label image + filtered_labels = np.zeros_like(labels, dtype=np.uint16) + + # relabel as we don't know what connected component the background has been given before + label_index = 1 + + # start at 1 (0 is contours), end at number of connected components + for i in range(1, n_ccs + 1): + + # get mask of connected compoenents + mask = labels == i + + # get mean + mean = np.mean(np.take(image.flatten(),np.nonzero(mask.flatten()))) + + if(mean > 50/255): + filtered_labels[mask] = label_index + label_index = label_index + 1 + + return filtered_labels + + +## PROBMAP TO PRED TO LABEL + +def probmap_to_pred(probmap, boundary_boost_factor): + # we need to boost the boundary class to make it more visible + # this shrinks the cells a little bit but avoids undersegmentation + pred = np.argmax(probmap * [1, 1, boundary_boost_factor], -1) + + return pred + + +def pred_to_label(pred, cell_min_size, cell_label=1): + + cell = (pred == cell_label) + # fix cells + cell = skimage.morphology.remove_small_holes(cell, min_size=cell_min_size) + cell = skimage.morphology.remove_small_objects(cell, min_size=cell_min_size) + + # label cells only + [label, num] = skimage.morphology.label(cell, return_num=True) + return label + + +def compare_two_labels(label_model, label_gt, return_IoU_matrix): + + # get number of detected nuclei + nb_nuclei_gt = np.max(label_gt) + nb_nuclei_model = np.max(label_model) + + # catch the case of an empty picture in model and gt + if nb_nuclei_gt == 0 and nb_nuclei_model == 0: + if(return_IoU_matrix): + return [0, 0, 1, np.empty(0)] + else: + return [0, 0, 1] + + # catch the case of empty picture in model + if nb_nuclei_model == 0: + if(return_IoU_matrix): + return [0, nb_nuclei_gt, 0, np.empty(0)] + else: + return [0, nb_nuclei_gt, 0] + + # catch the case of empty picture in gt + if nb_nuclei_gt == 0: + if(return_IoU_matrix): + return [nb_nuclei_model, 0, 0, np.empty(0)] + else: + return [nb_nuclei_model, 0, 0] + + # build IoU matrix + IoUs = np.full((nb_nuclei_gt, nb_nuclei_model), -1, dtype = np.float32) + + # calculate IoU for each nucleus index_gt in GT and nucleus index_pred in prediction + # TODO improve runtime of this algorithm + for index_gt in range(1,nb_nuclei_gt+1): + + nucleus_gt = label_gt == index_gt + number_gt = np.sum(nucleus_gt) + + for index_model in range(1,nb_nuclei_model+1): + + if debug: + print(index_gt, "/", index_model) + + nucleus_model = label_model == index_model + number_model = np.sum(nucleus_model) + + same_and_1 = np.sum((nucleus_gt == nucleus_model) * nucleus_gt) + + IoUs[index_gt-1,index_model-1] = same_and_1 / (number_gt + number_model - same_and_1) + + # get matches and errors + detection_map = (IoUs > 0.5) + nb_matches = np.sum(detection_map) + + detection_rate = IoUs * detection_map + + nb_overdetection = nb_nuclei_model - nb_matches + nb_underdetection = nb_nuclei_gt - nb_matches + + mean_IoU = np.mean(np.sum(detection_rate, axis = 1)) + + if(return_IoU_matrix): + result = [nb_overdetection, nb_underdetection, mean_IoU, IoUs] + else: + result = [nb_overdetection, nb_underdetection, mean_IoU] + return result + +def splits_and_merges_3_class(y_model_pred, y_gt_pred): + + # get segmentations + label_gt = pred_to_label(y_gt_pred, cell_min_size=2) + label_model = pred_to_label(y_model_pred, cell_min_size=2) + + # compare labels + result = compare_two_labels(label_model, label_gt, False) + + return result + +def splits_and_merges_boundary(y_model_outline, y_gt_outline, image): + + # get segmentations + label_gt = contour_to_label(y_gt_outline, image) + label_model = contour_to_label(y_model_outline, image) + + # compare labels + result = compare_two_labels(label_model, label_gt, False) + + return result diff --git a/unet4nuclei/utils/model_builder.py b/unet4nuclei/utils/model_builder.py new file mode 100644 index 0000000..08cd18c --- /dev/null +++ b/unet4nuclei/utils/model_builder.py @@ -0,0 +1,98 @@ +import keras.layers +import keras.models +import tensorflow as tf + +CONST_DO_RATE = 0.5 + +option_dict_conv = {"activation": "relu", "padding": "same"} +option_dict_bn = {"axis": -1, "momentum" : 0.9} + + +# returns a core model from gray input to 64 channels of the same size +def get_core(dim1, dim2): + + x = keras.layers.Input(shape=(dim1, dim2, 1)) + + a = keras.layers.Conv2D(64, (3, 3), **option_dict_conv)(x) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + a = keras.layers.Conv2D(64, (3, 3), **option_dict_conv)(a) + a = keras.layers.BatchNormalization(**option_dict_bn)(a) + + + y = keras.layers.MaxPooling2D()(a) + + b = keras.layers.Conv2D(128, (3, 3), **option_dict_conv)(y) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + b = keras.layers.Conv2D(128, (3, 3), **option_dict_conv)(b) + b = keras.layers.BatchNormalization(**option_dict_bn)(b) + + + y = keras.layers.MaxPooling2D()(b) + + c = keras.layers.Conv2D(256, (3, 3), **option_dict_conv)(y) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + c = keras.layers.Conv2D(256, (3, 3), **option_dict_conv)(c) + c = keras.layers.BatchNormalization(**option_dict_bn)(c) + + + y = keras.layers.MaxPooling2D()(c) + + d = keras.layers.Conv2D(512, (3, 3), **option_dict_conv)(y) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + d = keras.layers.Conv2D(512, (3, 3), **option_dict_conv)(d) + d = keras.layers.BatchNormalization(**option_dict_bn)(d) + + + # UP + + d = keras.layers.UpSampling2D()(d) + + y = keras.layers.merge.concatenate([d, c], axis=3) + + e = keras.layers.Conv2D(256, (3, 3), **option_dict_conv)(y) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.Conv2D(256, (3, 3), **option_dict_conv)(e) + e = keras.layers.BatchNormalization(**option_dict_bn)(e) + + e = keras.layers.UpSampling2D()(e) + + + y = keras.layers.merge.concatenate([e, b], axis=3) + + f = keras.layers.Conv2D(128, (3, 3), **option_dict_conv)(y) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.Conv2D(128, (3, 3), **option_dict_conv)(f) + f = keras.layers.BatchNormalization(**option_dict_bn)(f) + + f = keras.layers.UpSampling2D()(f) + + + y = keras.layers.merge.concatenate([f, a], axis=3) + + y = keras.layers.Conv2D(64, (3, 3), **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + y = keras.layers.Conv2D(64, (3, 3), **option_dict_conv)(y) + y = keras.layers.BatchNormalization(**option_dict_bn)(y) + + return [x, y] + + +def get_model_3_class(dim1, dim2, activation="softmax"): + + [x, y] = get_core(dim1, dim2) + + y = keras.layers.Conv2D(3, (1, 1), **option_dict_conv)(y) + + if activation is not None: + y = keras.layers.Activation(activation)(y) + + model = keras.models.Model(x, y) + + return model diff --git a/unet4nuclei/utils/objectives.py b/unet4nuclei/utils/objectives.py new file mode 100644 index 0000000..1a50636 --- /dev/null +++ b/unet4nuclei/utils/objectives.py @@ -0,0 +1,18 @@ +import keras.metrics +import tensorflow as tf + + +def weighted_crossentropy(y_true, y_pred): + + class_weights = tf.constant([[[[1., 1., 10.]]]]) + + unweighted_losses = tf.nn.softmax_cross_entropy_with_logits_v2(labels=y_true, logits=y_pred) + + weights = tf.reduce_sum(class_weights * y_true, axis=-1) + + weighted_losses = weights * unweighted_losses + + loss = tf.reduce_mean(weighted_losses) + + return loss + diff --git a/unet4nuclei/utils/preprocessing.py b/unet4nuclei/utils/preprocessing.py new file mode 100644 index 0000000..85d7df0 --- /dev/null +++ b/unet4nuclei/utils/preprocessing.py @@ -0,0 +1,20 @@ +import os + +def setup_working_directories(config_vars): + + ## Expected raw data directories: + config_vars["raw_images_dir"] = os.path.join(config_vars["root_directory"], 'raw_images/') + config_vars["raw_annotations_dir"] = os.path.join(config_vars["root_directory"], 'raw_annotations/') + + ## Split files + config_vars["path_files_training"] = os.path.join(config_vars["root_directory"], 'training.txt') + config_vars["path_files_validation"] = os.path.join(config_vars["root_directory"], 'validation.txt') + config_vars["path_files_test"] = os.path.join(config_vars["root_directory"], 'test.txt') + + ## Transformed data directories: + config_vars["normalized_images_dir"] = os.path.join(config_vars["root_directory"], 'norm_images/') + config_vars["boundary_labels_dir"] = os.path.join(config_vars["root_directory"], 'boundary_labels/') + + return config_vars + + diff --git a/unet4nuclei/utils/visualize.py b/unet4nuclei/utils/visualize.py new file mode 100644 index 0000000..390beb8 --- /dev/null +++ b/unet4nuclei/utils/visualize.py @@ -0,0 +1,274 @@ +import matplotlib +matplotlib.use('Agg') +import matplotlib.pyplot as plt + +import skimage.io +import sklearn.metrics + +import numpy as np + +out_format = 'eps' + +def visualize(pred_y, true_x, true_y, out_dir='./', label=''): + + # TODO + skimage.io.imsave(out_dir + label + '_' + 'img_probmap_boundary_test.png', pred_y[1,:,:,2]) + + plt.figure() + plt.hist(pred_y[1,:,:,2].flatten()) + plt.savefig(out_dir + label + '_' + 'hist_probmap_boundary' + '.' + out_format, format=out_format) + + # print all samples for visual inspection + nSamples = pred_y.shape[0] + + for sampleIndex in range(nSamples): + nCols = 2 + figure, axes = plt.subplots(ncols=nCols, nrows=2, figsize=(nCols*5, nCols*5)) + + predFig = axes[0,0] + trueFig = axes[0,1] + compFig = axes[1,0] + cmatFig = axes[1,1] + + pred_prob_map = pred_y[sampleIndex,:,:,:] + pred_prob_map_vec = pred_prob_map.reshape((-1, 3)) + pred = np.argmax(pred_prob_map, axis=2) + + true_prob_map = true_y[sampleIndex,:,:,:] + true_prob_map_vec = true_prob_map.reshape((-1, 3)) + true = np.argmax(true_prob_map, axis=2) + + comp = pred != true + + cmat = sklearn.metrics.confusion_matrix(y_true = true.flatten(), y_pred = pred.flatten(), labels=[0,1,2]) + + predFig.imshow(skimage.color.label2rgb(pred, image=true_x[sampleIndex,:,:,0])) + trueFig.imshow(skimage.color.label2rgb(true, image=true_x[sampleIndex,:,:,0])) + compFig.imshow(skimage.color.label2rgb(comp, image=true_x[sampleIndex,:,:,0])) + cmatFig.matshow(cmat, cmap = "cool") + + predFig.set_title('Prediction', fontsize=16) + trueFig.set_title('Truth', fontsize=16) + compFig.set_title('Errors', fontsize=16) + + + predFig.axis('off') + trueFig.axis('off') + compFig.axis('off') + + cmatFig.set_ylabel('truth', fontsize=16) + cmatFig.set_xlabel('prediction', fontsize=16) + + cmatFig.tick_params(axis='both', which='major', labelsize=12) + cmatFig.set_title('Confusion Matrix', fontsize=16, y=1.08) + + # annotate share of pred + for x in range(3): + for y in range(3): + cmatFig.annotate(str(np.round(cmat[x,y],2)), xy=(y, x), + horizontalalignment='center', + verticalalignment='center', fontsize = 15) + plt.tight_layout(pad = 1) + plt.savefig(out_dir + label + '_' + str(sampleIndex) + '_vis' + '.' + out_format, format=out_format) + classNames = ['background', 'interior', 'boundary'] + f = open(out_dir + '/' + label + '_' + str(sampleIndex) + '.txt', 'w') + f.write(sklearn.metrics.classification_report(pred.flatten(), true.flatten(), target_names=classNames) + '\n') + f.write('Jaccard: ' + + str(sklearn.metrics.jaccard_similarity_score(y_pred = pred.flatten(), y_true = true.flatten())) + + '\n') + f.write('Cross Entropy: ' + + str(sklearn.metrics.log_loss(y_pred = pred_prob_map_vec, y_true = true_prob_map_vec)) + + '\n') + f.close() + +def visualize_boundary_hard(pred_y, true_x, true_y, out_dir='./', label=''): + + print('VISUALIZE', pred_y.shape, true_y.shape) + plt.figure() + plt.hist(pred_y.flatten()) + plt.savefig(out_dir + label + '_' + 'hist_probmap_boundary' + '.' + out_format, format=out_format) + + # print all samples for visual inspection + nSamples = pred_y.shape[0] + + for sampleIndex in range(nSamples): + + nCols = 3 + nRows = 2 + figure, axes = plt.subplots(ncols=nCols, nrows=2, figsize=(nCols*5+2, nRows*5+2)) + + origFig = axes[0,0] + trueFig = axes[0,1] + + predProbMapFig = axes[1,0] + predFig = axes[1,1] + + compFig = axes[0,2] + cmatFig = axes[1,2] + + pred_prob_map = pred_y[sampleIndex,:,:,0] + pred = pred_prob_map >= 0.5 + + true = true_y[sampleIndex,:,:,0] + true = true.astype(np.bool) + + print('TRUE MEAN', np.mean(true)) + print('PRED_PROB_MAP MEAN', np.mean(pred_prob_map)) + print('PRED MEAN', np.mean(pred)) + + comp = pred != true + + cmat = sklearn.metrics.confusion_matrix(y_true = true.flatten(), y_pred = pred.flatten()) #, labels=[0,1]) + + mappable = origFig.imshow(true_x[sampleIndex,:,:,0]) + # figure.colorbar(mappable, ax=origFig) + + trueFig.imshow(skimage.color.label2rgb(true, image=true_x[sampleIndex,:,:,0])) + + mappable = predProbMapFig.imshow(pred_prob_map) + cbar = figure.colorbar(mappable, ax=predProbMapFig) + cbar.ax.tick_params(labelsize=18) + + predFig.imshow(skimage.color.label2rgb(pred, image=true_x[sampleIndex,:,:,0])) + + compFig.imshow(skimage.color.label2rgb(comp, image=true_x[sampleIndex,:,:,0])) + cmatFig.matshow(cmat, cmap = "cool") + + predProbMapFig.set_title('Prediction (not thresholded)', fontsize=18) + origFig.set_title('Image', fontsize=18) + predFig.set_title('Prediction (thresholded)', fontsize=18) + trueFig.set_title('Ground Truth', fontsize=18) + compFig.set_title('Pixelwise Errors', fontsize=18) + cmatFig.set_title('Confusion Matrix', fontsize=18) + + predProbMapFig.axis('off') + origFig.axis('off') + predFig.axis('off') + trueFig.axis('off') + compFig.axis('off') + + cmatFig.set_ylabel('truth', fontsize=18) + cmatFig.set_xlabel('prediction', fontsize=18) + + cmatFig.tick_params(axis='both', which='major', labelsize=18) + + # annotate share of pred + for x in range(2): + for y in range(2): + cmatFig.annotate(str(np.round(cmat[x,y],2)), xy=(y, x), + horizontalalignment='center', + verticalalignment='center', fontsize = 15) + figure.tight_layout(pad = 1) + plt.savefig(out_dir + label + '_' + str(sampleIndex) + '_vis' + '.' + out_format, format=out_format) + classNames = ['background', 'boundary'] + + # write cross entropy + ce = sklearn.metrics.log_loss(y_pred = pred.flatten(), y_true = true.flatten()) + + f = open(out_dir + '/' + label + '_' + str(sampleIndex) + '.txt', 'w') + f.write('Cross Entropy: ' + str(ce) + '\n') + f.close() + +def visualize_boundary_soft(pred_y, true_x, true_y, out_dir='./', label=''): + + plt.figure() + plt.hist(pred_y.flatten()) + plt.savefig(out_dir + label + '_' + 'hist_probmap_boundary' + '.' + out_format, format=out_format) + + # print all samples for visual inspection + nSamples = pred_y.shape[0] + + for sampleIndex in range(nSamples): + + nCols = 4 + figure, axes = plt.subplots(ncols=nCols, nrows=1, figsize=(nCols*5+2, 5+2)) + + origFig = axes[0] + predFig = axes[1] + trueFig = axes[2] + compFig = axes[3] + + pred_prob_map = pred_y[sampleIndex,:,:,0] + + true_prob_map = true_y[sampleIndex,:,:,0] + + comp = pred_prob_map - true_prob_map + + origFig.imshow(true_x[sampleIndex,:,:,0]) + predFig.imshow(pred_prob_map) + trueFig.imshow(true_prob_map) + compFig.imshow(comp) + + origFig.set_title('Image', fontsize=18) + predFig.set_title('Prediction', fontsize=18) + trueFig.set_title('Truth', fontsize=18) + compFig.set_title('Errors (MSE)', fontsize=18) + + predFig.axis('off') + trueFig.axis('off') + compFig.axis('off') + + plt.savefig(out_dir + label + '_' + str(sampleIndex) + '_vis' + '.' + out_format, format=out_format) + classNames = ['background', 'boundary'] + + # write mean squared error + ce = sklearn.metrics.mean_squared_error(y_pred = pred_prob_map.flatten(), y_true = true_prob_map.flatten()) + + f = open(out_dir + '/' + label + '_' + str(sampleIndex) + '.txt', 'w') + f.write('Mean Squared Error: ' + str(ce) + '\n') + f.close() + +def visualize_learning_stats(statistics, out_dir, metrics): + plt.figure() + + plt.xlabel("Epoch") + plt.ylabel("Loss") + plt.plot(statistics.history["loss"]) + plt.plot(statistics.history["val_loss"]) + plt.legend(["Training", "Validation"]) + + plt.savefig(out_dir + "plot_loss" + '.' + out_format, format=out_format) + + plt.figure() + + plt.xlabel("Epoch") + plt.ylabel("Accuracy") + plt.plot(statistics.history["categorical_accuracy"]) + plt.plot(statistics.history["val_categorical_accuracy"]) + plt.legend(["Training", "Validation"]) + + plt.savefig(out_dir + "plot_accuracy" + '.' + out_format, format=out_format) + +def visualize_learning_stats_boundary_hard(statistics, out_dir, metrics): + plt.figure() + + plt.xlabel("Epoch") + plt.ylabel("Loss") + plt.plot(statistics.history["loss"]) + plt.plot(statistics.history["val_loss"]) + plt.legend(["Training", "Validation"]) + + plt.savefig(out_dir + "plot_loss" + '.' + out_format, format=out_format) + + plt.figure() + + plt.xlabel("Epoch") + plt.ylabel("Accuracy") + plt.plot(statistics.history["binary_accuracy"]) + plt.plot(statistics.history["val_binary_accuracy"]) + plt.legend(["Training", "Validation"]) + + plt.savefig(out_dir + "plot_accuracy" + '.' + out_format, format=out_format) + +def visualize_learning_stats_boundary_soft(statistics, out_dir, metrics): + plt.figure() + + plt.xlabel("Epoch") + plt.ylabel("Loss") + plt.plot(statistics.history["loss"]) + plt.plot(statistics.history["val_loss"]) + plt.legend(["Training", "Validation"]) + + plt.savefig(out_dir + "plot_loss" + '.' + out_format, format=out_format) + + plt.figure() \ No newline at end of file