From d1da652799ca24d500f7d69818be516b4eadc3e9 Mon Sep 17 00:00:00 2001 From: Jirka Borovec Date: Thu, 12 Apr 2018 09:46:14 +0200 Subject: [PATCH] update segmentations (#6) * rename internal function * update manifest * rename local _wrapper * code cleaning * unify segm. models * move images -> data_images * add Langer. islets images * optionally missing params * add using segm. config * update annots * fix segm. debug show * check segm. features * regroup general images * update colors for visual @segm * update arg params @segm * add label boundary * fix classif. stat * add unsuper. model * update (c) * add mv Otsu threshold * update compute stat @segm * add color mGrad * update doc --- .shippable.yml | 16 +- MANIFEST.in | 1 + README.md | 71 ++--- circle.yml | 20 +- .../drosophila_disc/annot/img_12.png | Bin .../drosophila_disc/annot/img_14.png | Bin .../drosophila_disc/annot/img_15.png | Bin .../drosophila_disc/annot/img_19.png | Bin .../drosophila_disc/annot/img_20.png | Bin .../drosophila_disc/annot/img_24.png | Bin .../drosophila_disc/annot/img_26.png | Bin .../drosophila_disc/annot/img_43.png | Bin .../drosophila_disc/annot/img_5.png | Bin .../drosophila_disc/annot/img_6.png | Bin .../drosophila_disc/annot_rgb/img_12.png | Bin .../drosophila_disc/annot_rgb/img_14.png | Bin .../drosophila_disc/annot_rgb/img_15.png | Bin .../drosophila_disc/annot_rgb/img_19.png | Bin .../drosophila_disc/annot_rgb/img_20.png | Bin .../drosophila_disc/annot_rgb/img_24.png | Bin .../drosophila_disc/annot_rgb/img_26.png | Bin .../drosophila_disc/annot_rgb/img_43.png | Bin .../drosophila_disc/annot_rgb/img_5.png | Bin .../drosophila_disc/annot_rgb/img_6.png | Bin .../drosophila_disc/image/img_12.jpg | Bin .../drosophila_disc/image/img_14.jpg | Bin .../drosophila_disc/image/img_15.jpg | Bin .../drosophila_disc/image/img_19.jpg | Bin .../drosophila_disc/image/img_20.jpg | Bin .../drosophila_disc/image/img_24.jpg | Bin .../drosophila_disc/image/img_26.jpg | Bin .../drosophila_disc/image/img_43.jpg | Bin .../drosophila_disc/image/img_5.jpg | Bin .../drosophila_disc/image/img_6.jpg | Bin .../drosophila_disc/list_imaginal-disks.csv | 11 + .../list_imaginal-disks_short.csv | 3 + .../drosophila_ovary_3D/AU10-13_f0011.tif | Bin .../annot_eggs/insitu4174.png | Bin .../annot_eggs/insitu4358.png | Bin .../annot_eggs/insitu7331.png | Bin .../annot_eggs/insitu7544.png | Bin .../annot_eggs/insitu7545.png | Bin .../annot_struct/insitu4174.png | Bin .../annot_struct/insitu4358.png | Bin .../annot_struct/insitu7331.png | Bin .../annot_struct/insitu7544.png | Bin .../annot_struct/insitu7545.png | Bin .../center_levels/insitu4174.csv | 0 .../center_levels/insitu4174.png | Bin .../center_levels/insitu4358.csv | 0 .../center_levels/insitu4358.png | Bin .../center_levels/insitu7331.csv | 0 .../center_levels/insitu7331.png | Bin .../center_levels/insitu7544.csv | 0 .../center_levels/insitu7544.png | Bin .../center_levels/insitu7545.csv | 0 .../center_levels/insitu7545.png | Bin .../drosophila_ovary_slice/egg_ray_shapes.csv | 0 .../ellipse_fitting/insitu4174.csv | 0 .../ellipse_fitting/insitu4358.csv | 0 .../ellipse_fitting/insitu7331.csv | 0 .../ellipse_fitting/insitu7544.csv | 0 .../ellipse_fitting/insitu7545.csv | 0 .../image/insitu4174.jpg | Bin .../image/insitu4358.jpg | Bin .../image/insitu7331.jpg | Bin .../image/insitu7544.jpg | Bin .../image/insitu7545.jpg | Bin .../image/insitu7545.tif | Bin .../image_cut-stage-2/insitu4174.png | Bin .../image_cut-stage-2/insitu4358.png | Bin .../image_cut-stage-2/insitu7331.png | Bin .../image_cut-stage-2/insitu7544.png | Bin .../image_cut-stage-2/insitu7545.png | Bin .../info_ovary_images.txt | 0 .../info_ovary_images_ellipses.csv | 0 .../list_imgs-annot-struct.csv | 11 + .../list_imgs-annot-struct_short.csv | 3 + .../list_imgs-segm-center-levels.csv | 6 + .../list_imgs-segm-center-levels_short.csv | 3 + .../list_imgs-segm-center-points.csv | 6 + .../list_imgs-segm-center-points_short.csv | 3 + .../segm/insitu4174.png | Bin .../segm/insitu4358.png | Bin .../segm/insitu7331.png | Bin .../segm/insitu7544.png | Bin .../segm/insitu7545.png | Bin .../segm_rgb/insitu4174.png | Bin .../segm_rgb/insitu4358.png | Bin .../segm_rgb/insitu7331.png | Bin .../segm_rgb/insitu7544.png | Bin .../segm_rgb/insitu7545.png | Bin .../29-041-Izd2-w35-CD31-3-les1.jpg | Bin .../29-041-Izd2-w35-CD31-3-les3.jpg | Bin .../29-041-Izd2-w35-He-les1.jpg | Bin .../29-041-Izd2-w35-He-les3.jpg | Bin .../29-041-Izd2-w35-proSPC-4-les1.jpg | Bin .../29-041-Izd2-w35-proSPC-4-les3.jpg | Bin .../Case001_Cytokeratin.jpg | Bin .../histology_Flagship/Case001_HE.jpg | Bin .../histology_Flagship/Case001_Ki67.jpg | Bin .../Rat_Kidney_Section02_HE.jpg | Bin .../Rat_Kidney_Section04_Podocin.jpg | Bin .../Rat_Kidney_Section06_PanCytokeratin.jpg | Bin .../langerhans_islets/annot/Lh05-04.png | Bin 0 -> 3312 bytes .../langerhans_islets/annot/Lh05-09.png | Bin 0 -> 4648 bytes .../langerhans_islets/annot/Lh09-07.png | Bin 0 -> 4909 bytes .../langerhans_islets/annot/Lh10-03.png | Bin 0 -> 7948 bytes .../langerhans_islets/annot/gtExoIsl_13.png | Bin .../langerhans_islets/annot/gtExoIsl_21.png | Bin .../langerhans_islets/annot/gtExoIsl_27.png | Bin .../langerhans_islets/image/Lh05-04.jpg | Bin 0 -> 60734 bytes .../langerhans_islets/image/Lh05-09.jpg | Bin 0 -> 76211 bytes .../langerhans_islets/image/Lh09-07.jpg | Bin 0 -> 46491 bytes .../langerhans_islets/image/Lh10-03.jpg | Bin 0 -> 103663 bytes .../langerhans_islets/image/gtExoIsl_13.jpg | Bin .../langerhans_islets/image/gtExoIsl_21.jpg | Bin .../langerhans_islets/image/gtExoIsl_27.jpg | Bin .../list_lang-isl_imgs-annot-2.csv | 5 + .../list_lang-isl_imgs-annot.csv | 4 + {images => data_images}/others/industry.jpg | Bin {images => data_images}/others/lena.png | Bin {images => data_images}/others/sample.zvi | Bin .../others/sea_starfish-1.jpg | Bin .../others/sea_starfish-2.jpg | Bin .../synthetic/moving-affine.jpg | Bin .../synthetic/moving-elastic.jpg | Bin .../synthetic/reference.jpg | Bin .../synthetic/texture-1.jpg | Bin .../synthetic/texture-2_gray.jpg | Bin .../synthetic/texture-3-small_annot.png | Bin .../synthetic/texture-3-small_gray.jpg | Bin .../synthetic/texture_rgb_3cls.jpg | Bin .../gui_annot_center_correction.py | 2 +- .../run_center_candidate_training.py | 39 +-- .../run_center_clustering.py | 8 +- .../run_center_evaluation.py | 26 +- .../run_center_prediction.py | 12 +- .../run_create_annotation.py | 9 +- .../run_RG2Sp_estim_shape-models.py | 2 +- .../run_cut_segmented_objects.py | 12 +- .../run_egg_swap_orientation.py | 9 +- .../run_ellipse_annot_match.py | 8 +- .../run_ellipse_cut_scale.py | 8 +- .../run_export_user-annot-segm.py | 8 +- .../run_ovary_egg-segmentation.py | 8 +- .../run_ovary_segm_evaluation.py | 10 +- ...segm.py => run_compute_stat_annot_segm.py} | 52 ++-- .../run_eval_superpixels.py | 18 +- .../run_segm_slic_classif_graphcut.py | 218 +++++++++------ .../run_segm_slic_model_graphcut.py | 179 +++++++----- experiments_segmentation/sample_config.json | 13 + .../run_image_color_quantization.py | 21 +- .../run_image_convert_label_color.py | 19 +- .../run_overlap_images_segms.py | 5 +- .../run_segm_annot_inpaint.py | 16 +- .../run_segm_annot_relabel.py | 13 +- .../drosophila_disc/list_imaginal-disks.csv | 11 - .../list_imaginal-disks_short.csv | 3 - .../list_imgs-annot-struct.csv | 11 - .../list_imgs-annot-struct_short.csv | 3 - .../list_imgs-segm-center-levels.csv | 6 - .../list_imgs-segm-center-levels_short.csv | 3 - .../list_imgs-segm-center-points.csv | 6 - .../list_imgs-segm-center-points_short.csv | 3 - .../list_lang-isl_imgs-annot.csv | 4 - imsegm/annotation.py | 8 +- imsegm/classification.py | 92 ++++--- imsegm/descriptors.py | 100 ++++--- imsegm/ellipse_fitting.py | 14 +- imsegm/features_cython.pyx | 2 +- imsegm/graph_cuts.py | 182 +++++++++++-- imsegm/labeling.py | 48 +++- imsegm/pipelines.py | 257 ++++++++---------- imsegm/region_growing.py | 8 +- imsegm/superpixels.py | 4 +- imsegm/tests/test-classification.py | 2 +- imsegm/tests/test-descriptors.py | 2 +- imsegm/tests/test-ellipse_fitting.py | 4 +- imsegm/tests/test-graph_cut.py | 2 +- imsegm/tests/test-labels.py | 2 +- imsegm/tests/test-pipelines.py | 17 +- imsegm/tests/test-region_growing.py | 4 +- imsegm/tests/test-superpixels.py | 2 +- imsegm/utils/data_io.py | 71 ++--- imsegm/utils/data_samples.py | 12 +- imsegm/utils/drawing.py | 36 ++- imsegm/utils/experiments.py | 37 ++- imsegm/utils/read_zvi.py | 2 +- ...owing.ipynb => RG2Sp_region-growing.ipynb} | 4 +- notebooks/RG2Sp_shape-models.ipynb | 4 +- .../egg-center_candidates-clustering.ipynb | 2 +- notebooks/egg-detect_ellipse-fitting.ipynb | 2 +- notebooks/egg_segment_graphcut.ipynb | 2 +- .../segment-2d_slic-fts-classif-gc.ipynb | 4 +- notebooks/segment-2d_slic-fts-model-gc.ipynb | 10 +- .../transform-img-plane_inter-circle.ipynb | 2 +- setup.cfg | 4 +- setup.py | 6 +- 199 files changed, 1115 insertions(+), 761 deletions(-) rename {images => data_images}/drosophila_disc/annot/img_12.png (100%) rename {images => data_images}/drosophila_disc/annot/img_14.png (100%) rename {images => data_images}/drosophila_disc/annot/img_15.png (100%) rename {images => data_images}/drosophila_disc/annot/img_19.png (100%) rename {images => data_images}/drosophila_disc/annot/img_20.png (100%) rename {images => data_images}/drosophila_disc/annot/img_24.png (100%) rename {images => data_images}/drosophila_disc/annot/img_26.png (100%) rename {images => data_images}/drosophila_disc/annot/img_43.png (100%) rename {images => data_images}/drosophila_disc/annot/img_5.png (100%) rename {images => data_images}/drosophila_disc/annot/img_6.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_12.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_14.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_15.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_19.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_20.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_24.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_26.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_43.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_5.png (100%) rename {images => data_images}/drosophila_disc/annot_rgb/img_6.png (100%) rename {images => data_images}/drosophila_disc/image/img_12.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_14.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_15.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_19.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_20.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_24.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_26.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_43.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_5.jpg (100%) rename {images => data_images}/drosophila_disc/image/img_6.jpg (100%) create mode 100644 data_images/drosophila_disc/list_imaginal-disks.csv create mode 100644 data_images/drosophila_disc/list_imaginal-disks_short.csv rename {images => data_images}/drosophila_ovary_3D/AU10-13_f0011.tif (100%) rename {images => data_images}/drosophila_ovary_slice/annot_eggs/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_eggs/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_eggs/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_eggs/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_eggs/insitu7545.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_struct/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_struct/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_struct/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_struct/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/annot_struct/insitu7545.png (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu4174.csv (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu4358.csv (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7331.csv (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7544.csv (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7545.csv (100%) rename {images => data_images}/drosophila_ovary_slice/center_levels/insitu7545.png (100%) rename {images => data_images}/drosophila_ovary_slice/egg_ray_shapes.csv (100%) rename {images => data_images}/drosophila_ovary_slice/ellipse_fitting/insitu4174.csv (100%) rename {images => data_images}/drosophila_ovary_slice/ellipse_fitting/insitu4358.csv (100%) rename {images => data_images}/drosophila_ovary_slice/ellipse_fitting/insitu7331.csv (100%) rename {images => data_images}/drosophila_ovary_slice/ellipse_fitting/insitu7544.csv (100%) rename {images => data_images}/drosophila_ovary_slice/ellipse_fitting/insitu7545.csv (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu4174.jpg (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu4358.jpg (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu7331.jpg (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu7544.jpg (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu7545.jpg (100%) rename {images => data_images}/drosophila_ovary_slice/image/insitu7545.tif (100%) rename {images => data_images}/drosophila_ovary_slice/image_cut-stage-2/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/image_cut-stage-2/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/image_cut-stage-2/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/image_cut-stage-2/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/image_cut-stage-2/insitu7545.png (100%) rename {images => data_images}/drosophila_ovary_slice/info_ovary_images.txt (100%) rename {images => data_images}/drosophila_ovary_slice/info_ovary_images_ellipses.csv (100%) create mode 100644 data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv create mode 100644 data_images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv create mode 100644 data_images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv create mode 100644 data_images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv create mode 100644 data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv create mode 100644 data_images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv rename {images => data_images}/drosophila_ovary_slice/segm/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm/insitu7545.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm_rgb/insitu4174.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm_rgb/insitu4358.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm_rgb/insitu7331.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm_rgb/insitu7544.png (100%) rename {images => data_images}/drosophila_ovary_slice/segm_rgb/insitu7545.png (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-CD31-3-les1.jpg (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-CD31-3-les3.jpg (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-He-les1.jpg (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-He-les3.jpg (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-proSPC-4-les1.jpg (100%) rename {images => data_images}/histology_CIMA/29-041-Izd2-w35-proSPC-4-les3.jpg (100%) rename {images => data_images}/histology_Flagship/Case001_Cytokeratin.jpg (100%) rename {images => data_images}/histology_Flagship/Case001_HE.jpg (100%) rename {images => data_images}/histology_Flagship/Case001_Ki67.jpg (100%) rename {images => data_images}/histology_Flagship/Rat_Kidney_Section02_HE.jpg (100%) rename {images => data_images}/histology_Flagship/Rat_Kidney_Section04_Podocin.jpg (100%) rename {images => data_images}/histology_Flagship/Rat_Kidney_Section06_PanCytokeratin.jpg (100%) create mode 100644 data_images/langerhans_islets/annot/Lh05-04.png create mode 100644 data_images/langerhans_islets/annot/Lh05-09.png create mode 100644 data_images/langerhans_islets/annot/Lh09-07.png create mode 100644 data_images/langerhans_islets/annot/Lh10-03.png rename {images => data_images}/langerhans_islets/annot/gtExoIsl_13.png (100%) rename {images => data_images}/langerhans_islets/annot/gtExoIsl_21.png (100%) rename {images => data_images}/langerhans_islets/annot/gtExoIsl_27.png (100%) create mode 100644 data_images/langerhans_islets/image/Lh05-04.jpg create mode 100644 data_images/langerhans_islets/image/Lh05-09.jpg create mode 100644 data_images/langerhans_islets/image/Lh09-07.jpg create mode 100644 data_images/langerhans_islets/image/Lh10-03.jpg rename {images => data_images}/langerhans_islets/image/gtExoIsl_13.jpg (100%) rename {images => data_images}/langerhans_islets/image/gtExoIsl_21.jpg (100%) rename {images => data_images}/langerhans_islets/image/gtExoIsl_27.jpg (100%) create mode 100644 data_images/langerhans_islets/list_lang-isl_imgs-annot-2.csv create mode 100644 data_images/langerhans_islets/list_lang-isl_imgs-annot.csv rename {images => data_images}/others/industry.jpg (100%) rename {images => data_images}/others/lena.png (100%) rename {images => data_images}/others/sample.zvi (100%) rename images/see_starfish/star_nb1.jpg => data_images/others/sea_starfish-1.jpg (100%) rename images/see_starfish/stars_nb2.jpg => data_images/others/sea_starfish-2.jpg (100%) rename {images => data_images}/synthetic/moving-affine.jpg (100%) rename {images => data_images}/synthetic/moving-elastic.jpg (100%) rename {images => data_images}/synthetic/reference.jpg (100%) rename images/textures/sample-1.jpg => data_images/synthetic/texture-1.jpg (100%) rename images/textures/sample-2_gray.jpg => data_images/synthetic/texture-2_gray.jpg (100%) rename images/textures/sample-3-small_annot.png => data_images/synthetic/texture-3-small_annot.png (100%) rename images/textures/sample-3-small_gray.jpg => data_images/synthetic/texture-3-small_gray.jpg (100%) rename images/textures/sample_rgb_3cls.jpg => data_images/synthetic/texture_rgb_3cls.jpg (100%) rename experiments_segmentation/{run_compute-stat_annot-segm.py => run_compute_stat_annot_segm.py} (75%) create mode 100755 experiments_segmentation/sample_config.json delete mode 100644 images/drosophila_disc/list_imaginal-disks.csv delete mode 100644 images/drosophila_disc/list_imaginal-disks_short.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-annot-struct.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-segm-center-points.csv delete mode 100644 images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv delete mode 100644 images/langerhans_islets/list_lang-isl_imgs-annot.csv rename notebooks/{RG2SP_region-growing.ipynb => RG2Sp_region-growing.ipynb} (99%) diff --git a/.shippable.yml b/.shippable.yml index 9c73785b..a230cf26 100755 --- a/.shippable.yml +++ b/.shippable.yml @@ -49,17 +49,17 @@ script: - nosetests -v --exe --with-doctest --with-xunit --with-coverage --cover-package=imsegm --xunit-file=$CI_REPORTS/nosetests.xml # ANNOTATION section - - python handling_annotations/run_image_color_quantization.py -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" - - python handling_annotations/run_image_convert_label_color.py -imgs "images/drosophila_ovary_slice/segm/*.png" -out images/drosophila_ovary_slice/segm_rgb - - python handling_annotations/run_overlap_images_segms.py -imgs "images/drosophila_ovary_slice/image/*.jpg" -segs images/drosophila_ovary_slice/segm -out results/overlap_ovary_segment - - python handling_annotations/run_segm_annot_inpaint.py -imgs "images/drosophila_ovary_slice/segm/*.png" --label 0 - - python handling_annotations/run_segm_annot_relabel.py -imgs "images/drosophila_ovary_slice/center_levels/*.png" -out results/relabel_center_levels + - python handling_annotations/run_image_color_quantization.py -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" + - python handling_annotations/run_image_convert_label_color.py -imgs "data_images/drosophila_ovary_slice/segm/*.png" -out data_images/drosophila_ovary_slice/segm_rgb + - python handling_annotations/run_overlap_images_segms.py -imgs "data_images/drosophila_ovary_slice/image/*.jpg" -segs data_images/drosophila_ovary_slice/segm -out results/overlap_ovary_segment + - python handling_annotations/run_segm_annot_inpaint.py -imgs "data_images/drosophila_ovary_slice/segm/*.png" --label 0 + - python handling_annotations/run_segm_annot_relabel.py -imgs "data_images/drosophila_ovary_slice/center_levels/*.png" -out results/relabel_center_levels # SEGMENTATION section - rm -r -f results && mkdir results - - python experiments_segmentation/run_compute-stat_annot-segm.py - - python experiments_segmentation/run_segm_slic_model_graphcut.py --nb_jobs 1 - - python experiments_segmentation/run_segm_slic_classif_graphcut.py --nb_jobs 1 + - python experiments_segmentation/run_compute_stat_annot_segm.py --visual + - python experiments_segmentation/run_segm_slic_model_graphcut.py --path_config experiments_segmentation/sample_config.json --nb_jobs 1 + - python experiments_segmentation/run_segm_slic_classif_graphcut.py --path_config experiments_segmentation/sample_config.json --nb_jobs 1 # CENTER DETECT. section - rm -r -f results && mkdir results diff --git a/MANIFEST.in b/MANIFEST.in index 3d387c34..e149bfc7 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.md +include LICENSE include requirements.txt \ No newline at end of file diff --git a/README.md b/README.md index cf1a078a..b2e24e0e 100755 --- a/README.md +++ b/README.md @@ -118,35 +118,34 @@ Short description of our three sets of experiments that together compose single We introduce some useful tools for work with image annotation and segmentation. -* In case you have some smooth color labeling in your images you can remove them with following quantization script. +* **Quantization:** in case you have some smooth color labeling in your images you can remove them with following quantization script. ```bash python handling_annotations/run_image_color_quantization.py \ - -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" \ + -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" \ -m position -thr 0.01 --nb_jobs 2 ``` -* Concerting image labels into colour space and other way around. +* **Paint labels:** concerting image labels into colour space and other way around. ```bash python handling_annotations/run_image_convert_label_color.py \ - -imgs "images/drosophila_ovary_slice/segm/*.png" \ - -out images/drosophila_ovary_slice/segm_rgb + -imgs "data_images/drosophila_ovary_slice/segm/*.png" \ + -out data_images/drosophila_ovary_slice/segm_rgb ``` -* Having input image and its segmentation we can use simple visualisation which overlap the segmentation over input image. +* **Visualisation:** having input image and its segmentation we can use simple visualisation which overlap the segmentation over input image. ```bash python handling_annotations/run_overlap_images_segms.py \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -segs images/drosophila_ovary_slice/segm \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -segs data_images/drosophila_ovary_slice/segm \ -out results/overlap_ovary_segment ``` -* Inpainting selected labels in segmentation. +* **Inpainting** selected labels in segmentation. ```bash python handling_annotations/run_segm_annot_inpaint.py \ - -imgs "images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/segm/*.png" \ --label 4 ``` -* Change labels in input segmentation into another set of lables in 1:1 schema. +* **Replace labels:** change labels in input segmentation into another set of lables in 1:1 schema. ```bash python handling_annotations/run_segm_annot_relabel.py \ - -imgs "images/drosophila_ovary_slice/center_levels/*.png" \ -out results/relabel_center_levels \ --label_old 2 3 --label_new 1 1 ``` @@ -159,31 +158,39 @@ We utilize (un)supervised segmentation according to given training examples or s * Evaluate superpixels (with given SLIC parameters) quality against given segmentation. It helps find out best SLIC configuration. ```bash python experiments_segmentation/run_eval_superpixels.py \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -segm "images/drosophila_ovary_slice/annot_eggs/*.png" \ - --img_type 2d_gray \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -segm "data_images/drosophila_ovary_slice/annot_eggs/*.png" \ + --img_type 2d_split \ --slic_size 20 --slic_regul 0.25 --slico 0 ``` -* Perform **Unsupervised** segmentation. +* Perform **Unsupervised** segmentation in images given in CSV ```bash python experiments_segmentation/run_segm_slic_model_graphcut.py \ - -list images/langerhans_islets/list_lang-isl_imgs-annot.csv \ - -imgs "images/langerhans_islets/image/*.jpg" \ - -out results -n langIsl --nb_classes 3 --visual --nb_jobs 2 + -l data_images/langerhans_islets/list_lang-isl_imgs-annot.csv -i "" \ + --path_config experiments_segmentation/sample_config.json \ + -o results -n langIsl --nb_classes 3 --visual --nb_jobs 2 + ``` + OR specified on particuler path: + ```bash + python experiments_segmentation/run_segm_slic_model_graphcut.py \ + -l "" -i "data_images/langerhans_islets/image/*.jpg" \ + --path_config experiments_segmentation/sample_config.json \ + -o results -n langIsl --nb_classes 3 --visual --nb_jobs 2 ``` * Perform **Supervised** segmentation with afterwards evaluation. ```bash python experiments_segmentation/run_segm_slic_classif_graphcut.py \ - -list images/drosophila_ovary_slice/list_imgs-annot-struct.csv \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -out results -n Ovary --img_type 2d_gray --visual --nb_jobs 2 + -l data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv \ + -i "data_images/drosophila_ovary_slice/image/*.jpg" \ + --path_config experiments_segmentation/sample_config.json \ + -o results -n Ovary --img_type 2d_split --visual --nb_jobs 2 ``` * For both experiment you can evaluate segmentation results. ```bash python experiments_segmentation/run_compute-stat_annot-segm.py \ - -annot "images/drosophila_ovary_slice/annot_struct/*.png" \ + -annot "data_images/drosophila_ovary_slice/annot_struct/*.png" \ -segm "results/experiment_segm-supervise_ovary/*.png" \ - -img "images/drosophila_ovary_slice/image/*.jpg" \ + -img "data_images/drosophila_ovary_slice/image/*.jpg" \ -out results/evaluation ``` @@ -203,16 +210,16 @@ In general, the input is a formatted list (CSV file) of input images and annotat 1. With zone annotation, we train a classifier for center candidate prediction. The annotation can be a CSV file with annotated centers as points, and the zone of positive examples is set uniformly as the circular neighborhood around these points. Another way (preferable) is to use annotated image with marked zones for positive, negative and neutral examples. ```bash python experiments_ovary_centres/run_center_candidate_training.py -list none \ - -segs "images/drosophila_ovary_slice/segm/*.png" \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -centers "images/drosophila_ovary_slice/center_levels/*.png" \ + -segs "data_images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -centers "data_images/drosophila_ovary_slice/center_levels/*.png" \ -out results -n ovary ``` 1. Having trained classifier we perfom center prediction composed from two steps: i. center candidate clustering and candidate clustering. ```bash python experiments_ovary_centres/run_center_prediction.py -list none \ - -segs "images/drosophila_ovary_slice/segm/*.png" \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ + -segs "data_images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ -centers results/detect-centers-train_ovary/classifier_RandForest.pkl \ -out results -n ovary ``` @@ -269,7 +276,7 @@ python setup.py install 1. Run several segmentation techniques on each image. ```bash python experiments_ovary_detect/run_ovary_egg-segmentation.py \ - -list images/drosophila_ovary_slice/list_imgs-segm-center-points.csv \ + -list data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv \ -out output -n ovary_image --nb_jobs 1 \ -m ellipse_moments \ ellipse_ransac_mmt \ @@ -289,8 +296,8 @@ python setup.py install 1. In the end, cut individual segmented objects comes as minimal bounding box. ```bash python experiments_ovary_detect/run_cut_segmented_objects.py \ - -annot "images/drosophila_ovary_slice/annot_eggs/*.png" \ - -img "images/drosophila_ovary_slice/segm/*.png" \ + -annot "data_images/drosophila_ovary_slice/annot_eggs/*.png" \ + -img "data_images/drosophila_ovary_slice/segm/*.png" \ -out results/cut_images --padding 50 ``` 1. Finally, performing visualisation of segmentation results toghter with expert annotation. diff --git a/circle.yml b/circle.yml index 499578d8..f4347dfd 100755 --- a/circle.yml +++ b/circle.yml @@ -35,18 +35,18 @@ test: - coverage report && coverage xml -o $CIRCLE_TEST_REPORTS/coverage.xml # ANNOTATION section - - unset DISPLAY && python handling_annotations/run_image_color_quantization.py -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" - - unset DISPLAY && python handling_annotations/run_image_color_quantization.py -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" -m position - - unset DISPLAY && python handling_annotations/run_image_convert_label_color.py -imgs "images/drosophila_ovary_slice/segm/*.png" -out images/drosophila_ovary_slice/segm_rgb - - unset DISPLAY && python handling_annotations/run_image_convert_label_color.py -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" -out images/drosophila_ovary_slice/segm - - unset DISPLAY && python handling_annotations/run_overlap_images_segms.py -imgs "images/drosophila_ovary_slice/image/*.jpg" -segs images/drosophila_ovary_slice/segm -out results/overlap_ovary_segment - - unset DISPLAY && python handling_annotations/run_segm_annot_inpaint.py -imgs "images/drosophila_ovary_slice/segm/*.png" --label 0 - - unset DISPLAY && python handling_annotations/run_segm_annot_relabel.py -imgs "images/drosophila_ovary_slice/center_levels/*.png" -out results/relabel_center_levels + - unset DISPLAY && python handling_annotations/run_image_color_quantization.py -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" + - unset DISPLAY && python handling_annotations/run_image_color_quantization.py -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" -m position + - unset DISPLAY && python handling_annotations/run_image_convert_label_color.py -imgs "data_images/drosophila_ovary_slice/segm/*.png" -out data_images/drosophila_ovary_slice/segm_rgb + - unset DISPLAY && python handling_annotations/run_image_convert_label_color.py -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" -out data_images/drosophila_ovary_slice/segm + - unset DISPLAY && python handling_annotations/run_overlap_images_segms.py -imgs "data_images/drosophila_ovary_slice/image/*.jpg" -segs data_images/drosophila_ovary_slice/segm -out results/overlap_ovary_segment + - unset DISPLAY && python handling_annotations/run_segm_annot_inpaint.py -imgs "data_images/drosophila_ovary_slice/segm/*.png" --label 0 + - unset DISPLAY && python handling_annotations/run_segm_annot_relabel.py -imgs "data_images/drosophila_ovary_slice/center_levels/*.png" -out results/relabel_center_levels # SEGMENTATION section - - unset DISPLAY && python experiments_segmentation/run_compute-stat_annot-segm.py - - unset DISPLAY && python experiments_segmentation/run_segm_slic_model_graphcut.py --visual - - unset DISPLAY && python experiments_segmentation/run_segm_slic_classif_graphcut.py --visual + - unset DISPLAY && python experiments_segmentation/run_compute_stat_annot_segm.py --visual + - unset DISPLAY && python experiments_segmentation/run_segm_slic_model_graphcut.py --path_config experiments_segmentation/sample_config.json --visual + - unset DISPLAY && python experiments_segmentation/run_segm_slic_classif_graphcut.py --path_config experiments_segmentation/sample_config.json --visual # CENTER DETECT. section - unset DISPLAY && python experiments_ovary_centres/run_create_annotation.py diff --git a/images/drosophila_disc/annot/img_12.png b/data_images/drosophila_disc/annot/img_12.png similarity index 100% rename from images/drosophila_disc/annot/img_12.png rename to data_images/drosophila_disc/annot/img_12.png diff --git a/images/drosophila_disc/annot/img_14.png b/data_images/drosophila_disc/annot/img_14.png similarity index 100% rename from images/drosophila_disc/annot/img_14.png rename to data_images/drosophila_disc/annot/img_14.png diff --git a/images/drosophila_disc/annot/img_15.png b/data_images/drosophila_disc/annot/img_15.png similarity index 100% rename from images/drosophila_disc/annot/img_15.png rename to data_images/drosophila_disc/annot/img_15.png diff --git a/images/drosophila_disc/annot/img_19.png b/data_images/drosophila_disc/annot/img_19.png similarity index 100% rename from images/drosophila_disc/annot/img_19.png rename to data_images/drosophila_disc/annot/img_19.png diff --git a/images/drosophila_disc/annot/img_20.png b/data_images/drosophila_disc/annot/img_20.png similarity index 100% rename from images/drosophila_disc/annot/img_20.png rename to data_images/drosophila_disc/annot/img_20.png diff --git a/images/drosophila_disc/annot/img_24.png b/data_images/drosophila_disc/annot/img_24.png similarity index 100% rename from images/drosophila_disc/annot/img_24.png rename to data_images/drosophila_disc/annot/img_24.png diff --git a/images/drosophila_disc/annot/img_26.png b/data_images/drosophila_disc/annot/img_26.png similarity index 100% rename from images/drosophila_disc/annot/img_26.png rename to data_images/drosophila_disc/annot/img_26.png diff --git a/images/drosophila_disc/annot/img_43.png b/data_images/drosophila_disc/annot/img_43.png similarity index 100% rename from images/drosophila_disc/annot/img_43.png rename to data_images/drosophila_disc/annot/img_43.png diff --git a/images/drosophila_disc/annot/img_5.png b/data_images/drosophila_disc/annot/img_5.png similarity index 100% rename from images/drosophila_disc/annot/img_5.png rename to data_images/drosophila_disc/annot/img_5.png diff --git a/images/drosophila_disc/annot/img_6.png b/data_images/drosophila_disc/annot/img_6.png similarity index 100% rename from images/drosophila_disc/annot/img_6.png rename to data_images/drosophila_disc/annot/img_6.png diff --git a/images/drosophila_disc/annot_rgb/img_12.png b/data_images/drosophila_disc/annot_rgb/img_12.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_12.png rename to data_images/drosophila_disc/annot_rgb/img_12.png diff --git a/images/drosophila_disc/annot_rgb/img_14.png b/data_images/drosophila_disc/annot_rgb/img_14.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_14.png rename to data_images/drosophila_disc/annot_rgb/img_14.png diff --git a/images/drosophila_disc/annot_rgb/img_15.png b/data_images/drosophila_disc/annot_rgb/img_15.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_15.png rename to data_images/drosophila_disc/annot_rgb/img_15.png diff --git a/images/drosophila_disc/annot_rgb/img_19.png b/data_images/drosophila_disc/annot_rgb/img_19.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_19.png rename to data_images/drosophila_disc/annot_rgb/img_19.png diff --git a/images/drosophila_disc/annot_rgb/img_20.png b/data_images/drosophila_disc/annot_rgb/img_20.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_20.png rename to data_images/drosophila_disc/annot_rgb/img_20.png diff --git a/images/drosophila_disc/annot_rgb/img_24.png b/data_images/drosophila_disc/annot_rgb/img_24.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_24.png rename to data_images/drosophila_disc/annot_rgb/img_24.png diff --git a/images/drosophila_disc/annot_rgb/img_26.png b/data_images/drosophila_disc/annot_rgb/img_26.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_26.png rename to data_images/drosophila_disc/annot_rgb/img_26.png diff --git a/images/drosophila_disc/annot_rgb/img_43.png b/data_images/drosophila_disc/annot_rgb/img_43.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_43.png rename to data_images/drosophila_disc/annot_rgb/img_43.png diff --git a/images/drosophila_disc/annot_rgb/img_5.png b/data_images/drosophila_disc/annot_rgb/img_5.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_5.png rename to data_images/drosophila_disc/annot_rgb/img_5.png diff --git a/images/drosophila_disc/annot_rgb/img_6.png b/data_images/drosophila_disc/annot_rgb/img_6.png similarity index 100% rename from images/drosophila_disc/annot_rgb/img_6.png rename to data_images/drosophila_disc/annot_rgb/img_6.png diff --git a/images/drosophila_disc/image/img_12.jpg b/data_images/drosophila_disc/image/img_12.jpg similarity index 100% rename from images/drosophila_disc/image/img_12.jpg rename to data_images/drosophila_disc/image/img_12.jpg diff --git a/images/drosophila_disc/image/img_14.jpg b/data_images/drosophila_disc/image/img_14.jpg similarity index 100% rename from images/drosophila_disc/image/img_14.jpg rename to data_images/drosophila_disc/image/img_14.jpg diff --git a/images/drosophila_disc/image/img_15.jpg b/data_images/drosophila_disc/image/img_15.jpg similarity index 100% rename from images/drosophila_disc/image/img_15.jpg rename to data_images/drosophila_disc/image/img_15.jpg diff --git a/images/drosophila_disc/image/img_19.jpg b/data_images/drosophila_disc/image/img_19.jpg similarity index 100% rename from images/drosophila_disc/image/img_19.jpg rename to data_images/drosophila_disc/image/img_19.jpg diff --git a/images/drosophila_disc/image/img_20.jpg b/data_images/drosophila_disc/image/img_20.jpg similarity index 100% rename from images/drosophila_disc/image/img_20.jpg rename to data_images/drosophila_disc/image/img_20.jpg diff --git a/images/drosophila_disc/image/img_24.jpg b/data_images/drosophila_disc/image/img_24.jpg similarity index 100% rename from images/drosophila_disc/image/img_24.jpg rename to data_images/drosophila_disc/image/img_24.jpg diff --git a/images/drosophila_disc/image/img_26.jpg b/data_images/drosophila_disc/image/img_26.jpg similarity index 100% rename from images/drosophila_disc/image/img_26.jpg rename to data_images/drosophila_disc/image/img_26.jpg diff --git a/images/drosophila_disc/image/img_43.jpg b/data_images/drosophila_disc/image/img_43.jpg similarity index 100% rename from images/drosophila_disc/image/img_43.jpg rename to data_images/drosophila_disc/image/img_43.jpg diff --git a/images/drosophila_disc/image/img_5.jpg b/data_images/drosophila_disc/image/img_5.jpg similarity index 100% rename from images/drosophila_disc/image/img_5.jpg rename to data_images/drosophila_disc/image/img_5.jpg diff --git a/images/drosophila_disc/image/img_6.jpg b/data_images/drosophila_disc/image/img_6.jpg similarity index 100% rename from images/drosophila_disc/image/img_6.jpg rename to data_images/drosophila_disc/image/img_6.jpg diff --git a/data_images/drosophila_disc/list_imaginal-disks.csv b/data_images/drosophila_disc/list_imaginal-disks.csv new file mode 100644 index 00000000..3e134e25 --- /dev/null +++ b/data_images/drosophila_disc/list_imaginal-disks.csv @@ -0,0 +1,11 @@ +,path_image,path_annot +1,data_images/drosophila_disc/image/img_5.jpg,data_images/drosophila_disc/annot/img_5.png +2,data_images/drosophila_disc/image/img_6.jpg,data_images/drosophila_disc/annot/img_6.png +3,data_images/drosophila_disc/image/img_12.jpg,data_images/drosophila_disc/annot/img_12.png +4,data_images/drosophila_disc/image/img_14.jpg,data_images/drosophila_disc/annot/img_14.png +5,data_images/drosophila_disc/image/img_15.jpg,data_images/drosophila_disc/annot/img_15.png +6,data_images/drosophila_disc/image/img_19.jpg,data_images/drosophila_disc/annot/img_19.png +7,data_images/drosophila_disc/image/img_20.jpg,data_images/drosophila_disc/annot/img_20.png +8,data_images/drosophila_disc/image/img_24.jpg,data_images/drosophila_disc/annot/img_24.png +9,data_images/drosophila_disc/image/img_26.jpg,data_images/drosophila_disc/annot/img_26.png +10,data_images/drosophila_disc/image/img_43.jpg,data_images/drosophila_disc/annot/img_43.png diff --git a/data_images/drosophila_disc/list_imaginal-disks_short.csv b/data_images/drosophila_disc/list_imaginal-disks_short.csv new file mode 100644 index 00000000..8c9248d4 --- /dev/null +++ b/data_images/drosophila_disc/list_imaginal-disks_short.csv @@ -0,0 +1,3 @@ +,path_image,path_annot +1,data_images/drosophila_disc/image/img_6.jpg,data_images/drosophila_disc/annot/img_6.png +2,data_images/drosophila_disc/image/img_43.jpg,data_images/drosophila_disc/annot/img_43.png diff --git a/images/drosophila_ovary_3D/AU10-13_f0011.tif b/data_images/drosophila_ovary_3D/AU10-13_f0011.tif similarity index 100% rename from images/drosophila_ovary_3D/AU10-13_f0011.tif rename to data_images/drosophila_ovary_3D/AU10-13_f0011.tif diff --git a/images/drosophila_ovary_slice/annot_eggs/insitu4174.png b/data_images/drosophila_ovary_slice/annot_eggs/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/annot_eggs/insitu4174.png rename to data_images/drosophila_ovary_slice/annot_eggs/insitu4174.png diff --git a/images/drosophila_ovary_slice/annot_eggs/insitu4358.png b/data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/annot_eggs/insitu4358.png rename to data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png diff --git a/images/drosophila_ovary_slice/annot_eggs/insitu7331.png b/data_images/drosophila_ovary_slice/annot_eggs/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/annot_eggs/insitu7331.png rename to data_images/drosophila_ovary_slice/annot_eggs/insitu7331.png diff --git a/images/drosophila_ovary_slice/annot_eggs/insitu7544.png b/data_images/drosophila_ovary_slice/annot_eggs/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/annot_eggs/insitu7544.png rename to data_images/drosophila_ovary_slice/annot_eggs/insitu7544.png diff --git a/images/drosophila_ovary_slice/annot_eggs/insitu7545.png b/data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/annot_eggs/insitu7545.png rename to data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png diff --git a/images/drosophila_ovary_slice/annot_struct/insitu4174.png b/data_images/drosophila_ovary_slice/annot_struct/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/annot_struct/insitu4174.png rename to data_images/drosophila_ovary_slice/annot_struct/insitu4174.png diff --git a/images/drosophila_ovary_slice/annot_struct/insitu4358.png b/data_images/drosophila_ovary_slice/annot_struct/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/annot_struct/insitu4358.png rename to data_images/drosophila_ovary_slice/annot_struct/insitu4358.png diff --git a/images/drosophila_ovary_slice/annot_struct/insitu7331.png b/data_images/drosophila_ovary_slice/annot_struct/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/annot_struct/insitu7331.png rename to data_images/drosophila_ovary_slice/annot_struct/insitu7331.png diff --git a/images/drosophila_ovary_slice/annot_struct/insitu7544.png b/data_images/drosophila_ovary_slice/annot_struct/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/annot_struct/insitu7544.png rename to data_images/drosophila_ovary_slice/annot_struct/insitu7544.png diff --git a/images/drosophila_ovary_slice/annot_struct/insitu7545.png b/data_images/drosophila_ovary_slice/annot_struct/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/annot_struct/insitu7545.png rename to data_images/drosophila_ovary_slice/annot_struct/insitu7545.png diff --git a/images/drosophila_ovary_slice/center_levels/insitu4174.csv b/data_images/drosophila_ovary_slice/center_levels/insitu4174.csv similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu4174.csv rename to data_images/drosophila_ovary_slice/center_levels/insitu4174.csv diff --git a/images/drosophila_ovary_slice/center_levels/insitu4174.png b/data_images/drosophila_ovary_slice/center_levels/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu4174.png rename to data_images/drosophila_ovary_slice/center_levels/insitu4174.png diff --git a/images/drosophila_ovary_slice/center_levels/insitu4358.csv b/data_images/drosophila_ovary_slice/center_levels/insitu4358.csv similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu4358.csv rename to data_images/drosophila_ovary_slice/center_levels/insitu4358.csv diff --git a/images/drosophila_ovary_slice/center_levels/insitu4358.png b/data_images/drosophila_ovary_slice/center_levels/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu4358.png rename to data_images/drosophila_ovary_slice/center_levels/insitu4358.png diff --git a/images/drosophila_ovary_slice/center_levels/insitu7331.csv b/data_images/drosophila_ovary_slice/center_levels/insitu7331.csv similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7331.csv rename to data_images/drosophila_ovary_slice/center_levels/insitu7331.csv diff --git a/images/drosophila_ovary_slice/center_levels/insitu7331.png b/data_images/drosophila_ovary_slice/center_levels/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7331.png rename to data_images/drosophila_ovary_slice/center_levels/insitu7331.png diff --git a/images/drosophila_ovary_slice/center_levels/insitu7544.csv b/data_images/drosophila_ovary_slice/center_levels/insitu7544.csv similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7544.csv rename to data_images/drosophila_ovary_slice/center_levels/insitu7544.csv diff --git a/images/drosophila_ovary_slice/center_levels/insitu7544.png b/data_images/drosophila_ovary_slice/center_levels/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7544.png rename to data_images/drosophila_ovary_slice/center_levels/insitu7544.png diff --git a/images/drosophila_ovary_slice/center_levels/insitu7545.csv b/data_images/drosophila_ovary_slice/center_levels/insitu7545.csv similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7545.csv rename to data_images/drosophila_ovary_slice/center_levels/insitu7545.csv diff --git a/images/drosophila_ovary_slice/center_levels/insitu7545.png b/data_images/drosophila_ovary_slice/center_levels/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/center_levels/insitu7545.png rename to data_images/drosophila_ovary_slice/center_levels/insitu7545.png diff --git a/images/drosophila_ovary_slice/egg_ray_shapes.csv b/data_images/drosophila_ovary_slice/egg_ray_shapes.csv similarity index 100% rename from images/drosophila_ovary_slice/egg_ray_shapes.csv rename to data_images/drosophila_ovary_slice/egg_ray_shapes.csv diff --git a/images/drosophila_ovary_slice/ellipse_fitting/insitu4174.csv b/data_images/drosophila_ovary_slice/ellipse_fitting/insitu4174.csv similarity index 100% rename from images/drosophila_ovary_slice/ellipse_fitting/insitu4174.csv rename to data_images/drosophila_ovary_slice/ellipse_fitting/insitu4174.csv diff --git a/images/drosophila_ovary_slice/ellipse_fitting/insitu4358.csv b/data_images/drosophila_ovary_slice/ellipse_fitting/insitu4358.csv similarity index 100% rename from images/drosophila_ovary_slice/ellipse_fitting/insitu4358.csv rename to data_images/drosophila_ovary_slice/ellipse_fitting/insitu4358.csv diff --git a/images/drosophila_ovary_slice/ellipse_fitting/insitu7331.csv b/data_images/drosophila_ovary_slice/ellipse_fitting/insitu7331.csv similarity index 100% rename from images/drosophila_ovary_slice/ellipse_fitting/insitu7331.csv rename to data_images/drosophila_ovary_slice/ellipse_fitting/insitu7331.csv diff --git a/images/drosophila_ovary_slice/ellipse_fitting/insitu7544.csv b/data_images/drosophila_ovary_slice/ellipse_fitting/insitu7544.csv similarity index 100% rename from images/drosophila_ovary_slice/ellipse_fitting/insitu7544.csv rename to data_images/drosophila_ovary_slice/ellipse_fitting/insitu7544.csv diff --git a/images/drosophila_ovary_slice/ellipse_fitting/insitu7545.csv b/data_images/drosophila_ovary_slice/ellipse_fitting/insitu7545.csv similarity index 100% rename from images/drosophila_ovary_slice/ellipse_fitting/insitu7545.csv rename to data_images/drosophila_ovary_slice/ellipse_fitting/insitu7545.csv diff --git a/images/drosophila_ovary_slice/image/insitu4174.jpg b/data_images/drosophila_ovary_slice/image/insitu4174.jpg similarity index 100% rename from images/drosophila_ovary_slice/image/insitu4174.jpg rename to data_images/drosophila_ovary_slice/image/insitu4174.jpg diff --git a/images/drosophila_ovary_slice/image/insitu4358.jpg b/data_images/drosophila_ovary_slice/image/insitu4358.jpg similarity index 100% rename from images/drosophila_ovary_slice/image/insitu4358.jpg rename to data_images/drosophila_ovary_slice/image/insitu4358.jpg diff --git a/images/drosophila_ovary_slice/image/insitu7331.jpg b/data_images/drosophila_ovary_slice/image/insitu7331.jpg similarity index 100% rename from images/drosophila_ovary_slice/image/insitu7331.jpg rename to data_images/drosophila_ovary_slice/image/insitu7331.jpg diff --git a/images/drosophila_ovary_slice/image/insitu7544.jpg b/data_images/drosophila_ovary_slice/image/insitu7544.jpg similarity index 100% rename from images/drosophila_ovary_slice/image/insitu7544.jpg rename to data_images/drosophila_ovary_slice/image/insitu7544.jpg diff --git a/images/drosophila_ovary_slice/image/insitu7545.jpg b/data_images/drosophila_ovary_slice/image/insitu7545.jpg similarity index 100% rename from images/drosophila_ovary_slice/image/insitu7545.jpg rename to data_images/drosophila_ovary_slice/image/insitu7545.jpg diff --git a/images/drosophila_ovary_slice/image/insitu7545.tif b/data_images/drosophila_ovary_slice/image/insitu7545.tif similarity index 100% rename from images/drosophila_ovary_slice/image/insitu7545.tif rename to data_images/drosophila_ovary_slice/image/insitu7545.tif diff --git a/images/drosophila_ovary_slice/image_cut-stage-2/insitu4174.png b/data_images/drosophila_ovary_slice/image_cut-stage-2/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/image_cut-stage-2/insitu4174.png rename to data_images/drosophila_ovary_slice/image_cut-stage-2/insitu4174.png diff --git a/images/drosophila_ovary_slice/image_cut-stage-2/insitu4358.png b/data_images/drosophila_ovary_slice/image_cut-stage-2/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/image_cut-stage-2/insitu4358.png rename to data_images/drosophila_ovary_slice/image_cut-stage-2/insitu4358.png diff --git a/images/drosophila_ovary_slice/image_cut-stage-2/insitu7331.png b/data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/image_cut-stage-2/insitu7331.png rename to data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7331.png diff --git a/images/drosophila_ovary_slice/image_cut-stage-2/insitu7544.png b/data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/image_cut-stage-2/insitu7544.png rename to data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7544.png diff --git a/images/drosophila_ovary_slice/image_cut-stage-2/insitu7545.png b/data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/image_cut-stage-2/insitu7545.png rename to data_images/drosophila_ovary_slice/image_cut-stage-2/insitu7545.png diff --git a/images/drosophila_ovary_slice/info_ovary_images.txt b/data_images/drosophila_ovary_slice/info_ovary_images.txt similarity index 100% rename from images/drosophila_ovary_slice/info_ovary_images.txt rename to data_images/drosophila_ovary_slice/info_ovary_images.txt diff --git a/images/drosophila_ovary_slice/info_ovary_images_ellipses.csv b/data_images/drosophila_ovary_slice/info_ovary_images_ellipses.csv similarity index 100% rename from images/drosophila_ovary_slice/info_ovary_images_ellipses.csv rename to data_images/drosophila_ovary_slice/info_ovary_images_ellipses.csv diff --git a/data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv b/data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv new file mode 100644 index 00000000..b5645329 --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv @@ -0,0 +1,11 @@ +,path_image,path_annot +1,data_images/drosophila_ovary_slice/image/insitu4174.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu4174.png +2,data_images/drosophila_ovary_slice/image/insitu4358.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu4358.png +3,data_images/drosophila_ovary_slice/image/insitu7331.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu7331.png +4,data_images/drosophila_ovary_slice/image/insitu7544.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu7544.png +5,data_images/drosophila_ovary_slice/image/insitu7545.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu7545.png +6,data_images/drosophila_ovary_slice/image/insitu4174.tif,data_images/drosophila_ovary_slice/annot_struct/insitu4174.png +7,data_images/drosophila_ovary_slice/image/insitu4358.tif,data_images/drosophila_ovary_slice/annot_struct/insitu4358.png +8,data_images/drosophila_ovary_slice/image/insitu7331.tif,data_images/drosophila_ovary_slice/annot_struct/insitu7331.png +9,data_images/drosophila_ovary_slice/image/insitu7544.tif,data_images/drosophila_ovary_slice/annot_struct/insitu7544.png +10,data_images/drosophila_ovary_slice/image/insitu7545.tif,data_images/drosophila_ovary_slice/annot_struct/insitu7545.png diff --git a/data_images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv b/data_images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv new file mode 100644 index 00000000..67427076 --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv @@ -0,0 +1,3 @@ +,path_image,path_annot +1,data_images/drosophila_ovary_slice/image/insitu4174.jpg,data_images/drosophila_ovary_slice/annot_struct/insitu4174.png +2,data_images/drosophila_ovary_slice/image/insitu7545.tif,data_images/drosophila_ovary_slice/annot_struct/insitu7545.png diff --git a/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv b/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv new file mode 100644 index 00000000..24cc4dec --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv @@ -0,0 +1,6 @@ +,path_image,path_centers,path_annot,path_segm +1,data_images/drosophila_ovary_slice/image/insitu4174.tif,data_images/drosophila_ovary_slice/center_levels/insitu4174.png,data_images/drosophila_ovary_slice/annot_eggs/insitu4174.png,data_images/drosophila_ovary_slice/segm/insitu4174.png +2,data_images/drosophila_ovary_slice/image/insitu4358.tif,data_images/drosophila_ovary_slice/center_levels/insitu4358.png,data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png,data_images/drosophila_ovary_slice/segm/insitu4358.png +3,data_images/drosophila_ovary_slice/image/insitu7331.tif,data_images/drosophila_ovary_slice/center_levels/insitu7331.png,data_images/drosophila_ovary_slice/annot_eggs/insitu7331.png,data_images/drosophila_ovary_slice/segm/insitu7331.png +4,data_images/drosophila_ovary_slice/image/insitu7544.tif,data_images/drosophila_ovary_slice/center_levels/insitu7544.png,data_images/drosophila_ovary_slice/annot_eggs/insitu7544.png,data_images/drosophila_ovary_slice/segm/insitu7544.png +5,data_images/drosophila_ovary_slice/image/insitu7545.tif,data_images/drosophila_ovary_slice/center_levels/insitu7545.png,data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png,data_images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv b/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv new file mode 100644 index 00000000..97455de1 --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv @@ -0,0 +1,3 @@ +,path_image,path_centers,path_annot,path_segm +1,data_images/drosophila_ovary_slice/image/insitu4358.jpg,data_images/drosophila_ovary_slice/center_levels/insitu4358.png,data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png,data_images/drosophila_ovary_slice/segm/insitu4358.png +2,data_images/drosophila_ovary_slice/image/insitu7545.tif,data_images/drosophila_ovary_slice/center_levels/insitu7545.png,data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png,data_images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv b/data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv new file mode 100644 index 00000000..4f01a95e --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv @@ -0,0 +1,6 @@ +,path_image,path_centers,path_annot,path_segm +1,data_images/drosophila_ovary_slice/image/insitu4174.jpg,data_images/drosophila_ovary_slice/center_levels/insitu4174.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu4174.png,data_images/drosophila_ovary_slice/segm/insitu4174.png +2,data_images/drosophila_ovary_slice/image/insitu4358.jpg,data_images/drosophila_ovary_slice/center_levels/insitu4358.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png,data_images/drosophila_ovary_slice/segm/insitu4358.png +3,data_images/drosophila_ovary_slice/image/insitu7331.jpg,data_images/drosophila_ovary_slice/center_levels/insitu7331.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu7331.png,data_images/drosophila_ovary_slice/segm/insitu7331.png +4,data_images/drosophila_ovary_slice/image/insitu7544.jpg,data_images/drosophila_ovary_slice/center_levels/insitu7544.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu7544.png,data_images/drosophila_ovary_slice/segm/insitu7544.png +5,data_images/drosophila_ovary_slice/image/insitu7545.jpg,data_images/drosophila_ovary_slice/center_levels/insitu7545.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png,data_images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/data_images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv b/data_images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv new file mode 100644 index 00000000..bed54caf --- /dev/null +++ b/data_images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv @@ -0,0 +1,3 @@ +,path_image,path_centers,path_annot,path_segm +1,data_images/drosophila_ovary_slice/image/insitu4358.jpg,data_images/drosophila_ovary_slice/center_levels/insitu4358.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu4358.png,data_images/drosophila_ovary_slice/segm/insitu4358.png +2,data_images/drosophila_ovary_slice/image/insitu7545.tif,data_images/drosophila_ovary_slice/center_levels/insitu7545.csv,data_images/drosophila_ovary_slice/annot_eggs/insitu7545.png,data_images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/drosophila_ovary_slice/segm/insitu4174.png b/data_images/drosophila_ovary_slice/segm/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/segm/insitu4174.png rename to data_images/drosophila_ovary_slice/segm/insitu4174.png diff --git a/images/drosophila_ovary_slice/segm/insitu4358.png b/data_images/drosophila_ovary_slice/segm/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/segm/insitu4358.png rename to data_images/drosophila_ovary_slice/segm/insitu4358.png diff --git a/images/drosophila_ovary_slice/segm/insitu7331.png b/data_images/drosophila_ovary_slice/segm/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/segm/insitu7331.png rename to data_images/drosophila_ovary_slice/segm/insitu7331.png diff --git a/images/drosophila_ovary_slice/segm/insitu7544.png b/data_images/drosophila_ovary_slice/segm/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/segm/insitu7544.png rename to data_images/drosophila_ovary_slice/segm/insitu7544.png diff --git a/images/drosophila_ovary_slice/segm/insitu7545.png b/data_images/drosophila_ovary_slice/segm/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/segm/insitu7545.png rename to data_images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/drosophila_ovary_slice/segm_rgb/insitu4174.png b/data_images/drosophila_ovary_slice/segm_rgb/insitu4174.png similarity index 100% rename from images/drosophila_ovary_slice/segm_rgb/insitu4174.png rename to data_images/drosophila_ovary_slice/segm_rgb/insitu4174.png diff --git a/images/drosophila_ovary_slice/segm_rgb/insitu4358.png b/data_images/drosophila_ovary_slice/segm_rgb/insitu4358.png similarity index 100% rename from images/drosophila_ovary_slice/segm_rgb/insitu4358.png rename to data_images/drosophila_ovary_slice/segm_rgb/insitu4358.png diff --git a/images/drosophila_ovary_slice/segm_rgb/insitu7331.png b/data_images/drosophila_ovary_slice/segm_rgb/insitu7331.png similarity index 100% rename from images/drosophila_ovary_slice/segm_rgb/insitu7331.png rename to data_images/drosophila_ovary_slice/segm_rgb/insitu7331.png diff --git a/images/drosophila_ovary_slice/segm_rgb/insitu7544.png b/data_images/drosophila_ovary_slice/segm_rgb/insitu7544.png similarity index 100% rename from images/drosophila_ovary_slice/segm_rgb/insitu7544.png rename to data_images/drosophila_ovary_slice/segm_rgb/insitu7544.png diff --git a/images/drosophila_ovary_slice/segm_rgb/insitu7545.png b/data_images/drosophila_ovary_slice/segm_rgb/insitu7545.png similarity index 100% rename from images/drosophila_ovary_slice/segm_rgb/insitu7545.png rename to data_images/drosophila_ovary_slice/segm_rgb/insitu7545.png diff --git a/images/histology_CIMA/29-041-Izd2-w35-CD31-3-les1.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-CD31-3-les1.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-CD31-3-les1.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-CD31-3-les1.jpg diff --git a/images/histology_CIMA/29-041-Izd2-w35-CD31-3-les3.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-CD31-3-les3.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-CD31-3-les3.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-CD31-3-les3.jpg diff --git a/images/histology_CIMA/29-041-Izd2-w35-He-les1.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-He-les1.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-He-les1.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-He-les1.jpg diff --git a/images/histology_CIMA/29-041-Izd2-w35-He-les3.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-He-les3.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-He-les3.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-He-les3.jpg diff --git a/images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les1.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les1.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les1.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les1.jpg diff --git a/images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les3.jpg b/data_images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les3.jpg similarity index 100% rename from images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les3.jpg rename to data_images/histology_CIMA/29-041-Izd2-w35-proSPC-4-les3.jpg diff --git a/images/histology_Flagship/Case001_Cytokeratin.jpg b/data_images/histology_Flagship/Case001_Cytokeratin.jpg similarity index 100% rename from images/histology_Flagship/Case001_Cytokeratin.jpg rename to data_images/histology_Flagship/Case001_Cytokeratin.jpg diff --git a/images/histology_Flagship/Case001_HE.jpg b/data_images/histology_Flagship/Case001_HE.jpg similarity index 100% rename from images/histology_Flagship/Case001_HE.jpg rename to data_images/histology_Flagship/Case001_HE.jpg diff --git a/images/histology_Flagship/Case001_Ki67.jpg b/data_images/histology_Flagship/Case001_Ki67.jpg similarity index 100% rename from images/histology_Flagship/Case001_Ki67.jpg rename to data_images/histology_Flagship/Case001_Ki67.jpg diff --git a/images/histology_Flagship/Rat_Kidney_Section02_HE.jpg b/data_images/histology_Flagship/Rat_Kidney_Section02_HE.jpg similarity index 100% rename from images/histology_Flagship/Rat_Kidney_Section02_HE.jpg rename to data_images/histology_Flagship/Rat_Kidney_Section02_HE.jpg diff --git a/images/histology_Flagship/Rat_Kidney_Section04_Podocin.jpg b/data_images/histology_Flagship/Rat_Kidney_Section04_Podocin.jpg similarity index 100% rename from images/histology_Flagship/Rat_Kidney_Section04_Podocin.jpg rename to data_images/histology_Flagship/Rat_Kidney_Section04_Podocin.jpg diff --git a/images/histology_Flagship/Rat_Kidney_Section06_PanCytokeratin.jpg b/data_images/histology_Flagship/Rat_Kidney_Section06_PanCytokeratin.jpg similarity index 100% rename from images/histology_Flagship/Rat_Kidney_Section06_PanCytokeratin.jpg rename to data_images/histology_Flagship/Rat_Kidney_Section06_PanCytokeratin.jpg diff --git a/data_images/langerhans_islets/annot/Lh05-04.png b/data_images/langerhans_islets/annot/Lh05-04.png new file mode 100644 index 0000000000000000000000000000000000000000..5f381e5237fe724fdd3c52f2f95eec000cadd79f GIT binary patch literal 3312 zcmeHJ`!`f;A0L!vTrw`FX_cZOGDAX19S)l_R8x#0x*g};Go5w*hO^#v_78il@Auiy^Zk52&*%F*J3Bbg zM_a>C1Bb(D`}xvBaJa<~4!6h4%|L2aJ4(?mZFqW8wdUn_(y6N&aYovn9wyq5}^4oK&Z#aWWe%&Pl;p{r+hJn3{idwTf81%LH8 zxaQD1&yB4e*HXjOy5yBQx!WKnUrx228n-@FdDXjkdH||PW@g@-qpmbz_qu+* zHGI5pRSK-t6$Y%+0|lA5l$RiXaoi6N+zA0!n8W^b4rg0YqAEE+nw_k5Eq07I9<_XV9{(qeS)zka zhO+~BJQ~py3qD0G4V5qC92zp{VwRuEBe@-((bzi0B`s9L&xxR*hMZ~+Tl6tL1u7N$ z7Ux>cl_4vO`5MhhXJQytosFhv^GNCMs%@MMK_;Ye2Vysq=JqZ||6(O1fq8-I#|%raz(Ex>@$YbO&8Q}27L zqP!I0F-f@>LbrT%qJ;?RX+L9xYg-UO$TOwGRH@)*wZRQ*#R68vX59cPwW#9l3iwN;i83yvWRn=-e!6cPYGZ=ml@cfLPD z*PD8b{(w?CWJahgs9-aq`EuvmMt`WoZo$oI!QLWIhHP{I*Xx4~ZEfOMM3b}{2qyrm zX#`LSTHzRlt;mtnh>_Aiiilv@$UOf$Q!fb0b_^(};#^sT&F{=l7%@b7Vip`ZBYnH% zA^SQ0$N+>o1JuY>N>aPexeu7bk9rvc6lEN|3>>h6X}t_5cV&%?wJ@8jRBLo6gc4Co z?j&NTteG8+@-n%6Q=z600kAtnTKZj3ye43bE%1!tv=z!8dHDe${EaYjB8Nxk^6^FK z4OLB%-))I2S`V|&$dWEkBC8S2@WxTeDwKDf^(E2*9(0tBJ4$E99TW)|OJFuu91ckA zulHG#Mo!)BYKzXhxjogb#u&LB1*-7?Ef@@xEzAB&i;q~Al|9CS2dkaL0_YFa38fsD zBV>fWNW#E;<`HI19jLYbK=t{F{p~hHt2VQ0wA+uzVRGEQInGOSO?St3#|co6-*R4=3~$l z{_u+%`W9J_1s!$?DJV{f?dokAl7P@pa+;($d^IkAkx+9A1705$v<0_3D~-RG1wXnt zU!{U^R&+7uJF^@caH$@MztUt(fw>ny-nkcX*>OTySV2~?MJGP7^D2~tJ+w4|tiQ*& zGi64|L zF@|6UvwM8l;X{%Qo}Q=!u2DN3CUbeb(Pzbr$TLa{4C zUv~HeiOb*6dM;(a${Xvq?(z)7S?2a9T4xgAx;4ph4PC@D`)|)WQwOo@fwlMSz8mqa zo?QN+U5ld#z-RR}Q|>{$B_-zDw32$HFcC4`{(H-8`nS+J(B-`%oX%a}Cv70MngHF; zY%ML}%~xCHM?MuWi)3lDeeTSAW&TDCQ^t%-w zW}R)gcK#bGhvuBuJUZA;-_c*k`!VgUnmM)q?nLqjM$5OMJEaDyWGlnFH^p;D9e%W~ z%k;3Aq0MmlJq=^LG$Gwu&Fkw5kYl2ElAmlYd**ci*%%Ri?Pg;-9i@pTso3X^*NE9$4ApI@7hX@nO9wpHN{n-1H?m zT}>nnF6c4=TqA}T_KS|GjgqNf?yQTNA~pXZb}`1UId89?Yz3cI+vsmt(HIIY$=EiY zhnB9Z^K{?1@|Ioc$FFtu1E0rgbkrA+XLhyukLTODiBkUA0m6t%+iAjnarB(ZKls1s fUugeN3QT??MP$DljcK21Qqc1A3Z&ig*nQ%EG{-k) literal 0 HcmV?d00001 diff --git a/data_images/langerhans_islets/annot/Lh05-09.png b/data_images/langerhans_islets/annot/Lh05-09.png new file mode 100644 index 0000000000000000000000000000000000000000..2421184f8f1ca20ed5a25a45ebb4ff551271a41d GIT binary patch literal 4648 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 literal 0 HcmV?d00001 diff --git a/images/langerhans_islets/annot/gtExoIsl_13.png b/data_images/langerhans_islets/annot/gtExoIsl_13.png similarity index 100% rename from images/langerhans_islets/annot/gtExoIsl_13.png rename to data_images/langerhans_islets/annot/gtExoIsl_13.png diff --git a/images/langerhans_islets/annot/gtExoIsl_21.png b/data_images/langerhans_islets/annot/gtExoIsl_21.png similarity index 100% rename from images/langerhans_islets/annot/gtExoIsl_21.png rename to data_images/langerhans_islets/annot/gtExoIsl_21.png diff --git a/images/langerhans_islets/annot/gtExoIsl_27.png b/data_images/langerhans_islets/annot/gtExoIsl_27.png similarity index 100% rename from images/langerhans_islets/annot/gtExoIsl_27.png rename to data_images/langerhans_islets/annot/gtExoIsl_27.png diff --git a/data_images/langerhans_islets/image/Lh05-04.jpg b/data_images/langerhans_islets/image/Lh05-04.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5af201ffef64a13035f4b8a603cbf14dcd6df37f GIT binary patch literal 60734 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 literal 0 HcmV?d00001 diff --git a/data_images/langerhans_islets/image/Lh05-09.jpg b/data_images/langerhans_islets/image/Lh05-09.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e9380e21b57747daf5df016527112ef0b7980ac GIT binary patch literal 76211 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

I;*vHT+cBfgZrHz#gl5>gapb*_Z=S zn|w+Q3yIwO#0*-JHY>aC(1>>VaNo4)%g92c{f7C)1k0RPL|6dAQ}d&G;Il=@_1rua z9G>iwD=5XE(twM9+x0lK1VGa}(C}o-JR>rm_G+CzLDt_cuXkiQ)m$$TNK!~G@O}ji z=F#;IY?b5 zuGmdu0SgcRFA?+zbh~p-gXR21?|r->^;BxIo|uSa%vV6*4>PGhW1L9QQR2Xp&5FQ+ z(AXQ^h=TM5=+GVxo=8lhNm7z5eq)LRnrA?tVxuDgV#NR&JzNbml;>oiLH4?z77=#oQ9|IB<13*)TU&jC_-zcBziUl9@qVh5q6PI;-Z(W{#ZTp3~D zOGWEk0a?}nIDxxGw-AFZ1gx0&f(ijG_UI8=vBbKt>_F6zdWVsKdd1lmVwHU-{9k$|9L)6qsx$N5TrbaWWND?P4g6-z>VLQG?>v_e z{}spy$Bte;0bbjy)SoSmYm#x3z5**@)C1?XJquULS*B$7E_rvz#g%u)%SfH4p546L6EI!Y%hDs`47(c%w?0e z2#(CJ>t*Vb=f39wHd+0ckKy}&w-#Ise7aaGxQ64s7-v)Q@Nj5a18EK6Nj2D-bt>yDA+(e?-P^*OfaK6}7x4 znoJ$2{UlJ7wa?FjnK8jK;3hJbXGuWzr><+9{&9vZ=O%;kJ!quN@Xy*M(D$s1_Sq;- zeqwL&I&oz@)%(T`^A_|0*ENplr1XxqMPheO3bqC))@BAVd}xCyq@D!vSGxp7`nny=bqP1O?H+OCJ)BkBEO z49_??_)BbAtCYP!^Wm`FTUzfpH<*P13g{1q`CEHU*Z{5l>?>mowt7d?cWbam3cx7= z%+I2&(c7`UZ1Bgcm;n}Q%VZcmjE2M#WBZhSi6mYXS&w#6Qd#Up!5)I|h%BzC-4!=* zTonM)m5z3U8SDq$Vs{v@YFmtDHp#~lW(o>-{n@Xkqr6DB762MF0IH2tSQsxOm&tysVa(i{& zDR8ZqrnCh)x`Zq)BjlCg_}+9NDG&ia#R5%EyWxQVai~Mw@Zzf#k3<%)`(&Mhm!|Zv zg-{g0Hfiy21;qV_Sj8?#F1W;{D1=LKV5+zbPN1=boV{v(r<{eC@oT{8N9PI*Y(#R& zE_oKeEQOvnbksqrEVKF<-y&x)mXM%IMO4yy>=M<=wBP2)djasso$8z#>B}V{Gx4fz_8Lu2Xem4$*pn5c{v|}f zt3vkt)`b6(v(N(}1^wvd_KcHq<;q#wvcU0cCh;~y|AoCV1}v6PFvD*=eX%rI(oOyv z3eHn|@V+@+AIoz@Bws_XvYrd_(OI`yS-ss7>oO=$i;uOfuWCIdpVjDj8}-1XFH|gb zsi;IQ^%$eXp9nt+-(Qf5!&kpFRrZdl5vsGksmfH})xB3dW1)oa9mj>atM;X79TmGD z#Sj!Tjc)7wE^0De1GsBnT7wDS371mL3;VxTteDF1DNjqQ=piiROEpJhdL$NVFAm!j z;q34pea3%!&#rpbUy3*VfZH0~(L|}?b?CAnxWsf0eY#(<0@_r=`n(?L%9U~iw*$$T zE7+>l{>u7-lz0jEmqXhMDkMLJH50(ctG(2iM{&>UPn#WJUm1E##?ui^0Dq;;M|HlI z(8?3@e;C|^BmDM4vQJ_`kA9tQeGs&UX{a3n7WmRl4)qpFF zo=-*vRm((W2p#`g^zWkdRL1}-?e zYPuj8<=0>4B7@#J3d&IJd?iR`MvBSrB2&R$Kj|9{sQL+RKh78eZdv}`AOD9rb;tCtYb!f-r;$TSm(@?C(Y2ylYRjV4LU%1ObnQsf%jzFO9 zAqF^T@4QnAPV419hpWr|D(VE26Jr2)#1bo_+eS5*OHOGO8|NPZ3FDe3l$JkMt=Nj>3L#v+L91a);VzsQ04_AL9$E5==NFzzG3 zW<|C?8;9m;DGCTIm|{6|?}Wf(?N8%5;nP_ptwSZy%?l=ch1mkI(

tTmH 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 literal 0 HcmV?d00001 diff --git a/data_images/langerhans_islets/image/Lh10-03.jpg b/data_images/langerhans_islets/image/Lh10-03.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c0cdc32b6ff35e2b6a787724134eb9da87f6d61b GIT binary patch literal 103663 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 python run_center_candidate_training.py -list none \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -segs "images/drosophila_ovary_slice/segm/*.png" \ - -centers "images/drosophila_ovary_slice/center_levels/*.png" \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -segs "data_images/drosophila_ovary_slice/segm/*.png" \ + -centers "data_images/drosophila_ovary_slice/center_levels/*.png" \ -out results -n ovary Copyright (C) 2016-2017 Jiri Borovec @@ -103,7 +103,7 @@ 'center_dist_thr': 50, # distance to from annotated center as a point } -PATH_IMAGES = os.path.join(tl_data.update_path('images'), +PATH_IMAGES = os.path.join(tl_data.update_path('data_images'), 'drosophila_ovary_slice') PATH_RESULTS = tl_data.update_path('results', absolute=True) CENTER_PARAMS.update({ @@ -270,12 +270,12 @@ def load_image_segm_center(idx_row, path_out=None, dict_relabel=None): centers = np.array(LUT_ANNOT_CENTER_RELABEL)[centers] else: logging.warning('not supported file format %s', ext) + centers = None else: centers = None if is_drawing(path_out): - export_visual_input_image_segm(path_out, idx_name, img_rgb, segm, - centers) + export_visual_input_image_segm(path_out, idx_name, img_rgb, segm, centers) return idx_name, img_rgb, segm, centers @@ -459,10 +459,11 @@ def dataset_load_images_segms_compute_features(params, df_paths, dict_imgs, dict_segms, dict_center = dict(), dict(), dict() logging.info('loading input data (images, segmentation and centers)') path_show_in = os.path.join(params['path_expt'], FOLDER_INPUT) - wrapper_load_data = partial(load_image_segm_center, path_out=path_show_in, - dict_relabel=params['dict_relabel']) - iterate = tl_expt.WrapExecuteSequence(wrapper_load_data, df_paths.iterrows(), - nb_jobs=nb_jobs, desc='loading input data') + _wrapper_load = partial(load_image_segm_center, path_out=path_show_in, + dict_relabel=params['dict_relabel']) + iterate = tl_expt.WrapExecuteSequence(_wrapper_load, df_paths.iterrows(), + nb_jobs=nb_jobs, + desc='loading input data') for name, img, seg, center in iterate: dict_imgs[name] = img dict_segms[name] = seg @@ -472,10 +473,10 @@ def dataset_load_images_segms_compute_features(params, df_paths, logging.info('estimate candidate points and compute features') gene_name_img_seg = ((name, dict_imgs[name], dict_segms[name]) for name in dict_imgs) - wrapper_points_features = partial(wrapper_estim_points_compute_features, - params=params) + _wrapper_pnt_features = partial(wrapper_estim_points_compute_features, + params=params) feature_names = None - iterate = tl_expt.WrapExecuteSequence(wrapper_points_features, + iterate = tl_expt.WrapExecuteSequence(_wrapper_pnt_features, gene_name_img_seg, nb_jobs=nb_jobs, desc='estimate candidates & features') for name, slic, points, features, feature_names in iterate: @@ -572,14 +573,14 @@ def detect_center_candidates(name, image, segm, centers_gt, slic, points, :param str name: :param ndarray image: - :param ndarray seg: + :param ndarray segm: :param centers_gt: :param slic: np.array :param [(int, int)] points: :param features: :param [str] feature_names: :param {} params: - :param paths: path + :param str path_out: :param classif: obj :return {}: """ @@ -650,11 +651,11 @@ def experiment_loo(classif, dict_imgs, dict_segms, dict_centers, dict_slics, gener_data = ((n, dict_imgs[n], dict_segms[n], dict_centers[n], dict_slics[n], dict_points[n], dict_features[n], feature_names) for n in dict_imgs) - wrapper_detection = partial(wrapper_detect_center_candidates, - params=params, classif=classif, - path_output=params['path_expt']) + _wrapper_detection = partial(wrapper_detect_center_candidates, + params=params, classif=classif, + path_output=params['path_expt']) df_stat = pd.DataFrame() - iterate = tl_expt.WrapExecuteSequence(wrapper_detection, + iterate = tl_expt.WrapExecuteSequence(_wrapper_detection, gener_data, nb_jobs=params['nb_jobs']) for dict_stat in iterate: df_stat = df_stat.append(dict_stat, ignore_index=True) diff --git a/experiments_ovary_centres/run_center_clustering.py b/experiments_ovary_centres/run_center_clustering.py index 253b0a1c..b195eff9 100755 --- a/experiments_ovary_centres/run_center_clustering.py +++ b/experiments_ovary_centres/run_center_clustering.py @@ -211,10 +211,10 @@ def main(params): logging.info('run clustering...') df_paths_new = pd.DataFrame() - wrapper_clustering = partial(cluster_points_draw_export, params=params, - path_out=params['path_expt']) - iterate = tl_expt.WrapExecuteSequence(wrapper_clustering, - (dict(row) for idx, row in df_paths.iterrows()), + _wrapper_clustering = partial(cluster_points_draw_export, params=params, + path_out=params['path_expt']) + rows = (dict(row) for idx, row in df_paths.iterrows()) + iterate = tl_expt.WrapExecuteSequence(_wrapper_clustering, rows, nb_jobs=params['nb_jobs']) for dict_center in iterate: df_paths_new = df_paths_new.append(dict_center, ignore_index=True) diff --git a/experiments_ovary_centres/run_center_evaluation.py b/experiments_ovary_centres/run_center_evaluation.py index d475082a..df626c3d 100755 --- a/experiments_ovary_centres/run_center_evaluation.py +++ b/experiments_ovary_centres/run_center_evaluation.py @@ -4,8 +4,8 @@ SAMPLE run: >> python run_center_evaluation.py -list none \ - -segs "images/drosophila_ovary_slice/segm/*.png" \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ + -segs "data_images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ -centers "results/detect-centers-predict_ovary/centers/*.csv" \ -out results/detect-centers-predict_ovary @@ -79,7 +79,8 @@ def estimate_eggs_from_info(row_slice, mask_shape): """ finds all eggs for particular slice and mask them by ellipse annotated by ant, post and lat in the all info table - :param str path_img: + :param row_slice: + :param mask_shape: :return ndarray: ndarray """ pos_ant, pos_lat, pos_post = tl_visu.parse_annot_rectangles(row_slice) @@ -138,9 +139,10 @@ def load_center_evaluate(idx_row, df_annot, path_annot, path_visu=None, generate points, compute features and using given classifier predict labels :param (int, DF:row) idx_row: - :param {str: ...} params: - :param {str: str} paths: - :param classif: + :param df_annot: + :param str path_annot: + :param str path_visu: + :param str col_prefix: :return {str: float}: """ idx, row = idx_row @@ -223,10 +225,11 @@ def evaluate_detection_stage(df_paths, stage, path_info, path_out, nb_jobs=1): # perfom on new images stage_prefix = '[stage-%s] ' % str_stage logging.info('start section %s - load_center_evaluate ...', stage_prefix) - wrapper_detection = partial(load_center_evaluate, df_annot=df_slices_info, - path_annot=path_annot, path_visu=path_visu, - col_prefix=stage_prefix) - iterate = tl_expt.WrapExecuteSequence(wrapper_detection, df_paths.iterrows(), + _wrapper_detection = partial(load_center_evaluate, df_annot=df_slices_info, + path_annot=path_annot, path_visu=path_visu, + col_prefix=stage_prefix) + iterate = tl_expt.WrapExecuteSequence(_wrapper_detection, + df_paths.iterrows(), nb_jobs=nb_jobs) for dict_eval in iterate: df_eval = df_eval.append(dict_eval, ignore_index=True) @@ -238,8 +241,7 @@ def evaluate_detection_stage(df_paths, stage, path_info, path_out, nb_jobs=1): def main(params): """ PIPELINE for new detections - :param {str: str} paths: - :param int nb_jobs: + :param {str: ...} params: """ logging.info('running...') diff --git a/experiments_ovary_centres/run_center_prediction.py b/experiments_ovary_centres/run_center_prediction.py index 4a6803f6..96af52d0 100644 --- a/experiments_ovary_centres/run_center_prediction.py +++ b/experiments_ovary_centres/run_center_prediction.py @@ -4,8 +4,8 @@ SAMPLE run: >> python run_center_prediction.py -list none \ - -segs "images/drosophila_ovary_slice/segm/*.png" \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ + -segs "data_images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ -centers results/detect-centers-train_ovary/classifier_RandForest.pkl \ -out results -n ovary @@ -155,10 +155,10 @@ def main(params): # perform on new images df_stat = pd.DataFrame() - wrapper_detection = partial(load_compute_detect_centers, params=params_clf, - path_classif=params['path_classif'], - path_output=params['path_expt']) - iterate = tl_expt.WrapExecuteSequence(wrapper_detection, df_paths.iterrows(), + _wrapper_detection = partial(load_compute_detect_centers, params=params_clf, + path_classif=params['path_classif'], + path_output=params['path_expt']) + iterate = tl_expt.WrapExecuteSequence(_wrapper_detection, df_paths.iterrows(), nb_jobs=params['nb_jobs']) for dict_center in iterate: df_stat = df_stat.append(dict_center, ignore_index=True) diff --git a/experiments_ovary_centres/run_create_annotation.py b/experiments_ovary_centres/run_create_annotation.py index ab4f3402..4cff110c 100644 --- a/experiments_ovary_centres/run_create_annotation.py +++ b/experiments_ovary_centres/run_create_annotation.py @@ -157,7 +157,6 @@ def main(path_segs, path_out, nb_jobs): :param str path_segs: path with image pattern of images - obj segmentation :param str path_out: :param int nb_jobs: number of processes in parallel - :return ndarray: """ logging.info('running...') @@ -171,10 +170,10 @@ def main(path_segs, path_out, nb_jobs): 'missing: %s' % path_out os.mkdir(path_out) - wrapper_create_annot_centers = partial(create_annot_centers, - path_out_seg=path_out, - path_out_csv=path_out) - iterate = tl_expt.WrapExecuteSequence(wrapper_create_annot_centers, + _wrapper_create_annot_centers = partial(create_annot_centers, + path_out_seg=path_out, + path_out_csv=path_out) + iterate = tl_expt.WrapExecuteSequence(_wrapper_create_annot_centers, list_imgs, nb_jobs=nb_jobs, desc='annotating images') list(iterate) diff --git a/experiments_ovary_detect/run_RG2Sp_estim_shape-models.py b/experiments_ovary_detect/run_RG2Sp_estim_shape-models.py index dfad44e9..b7dc5f03 100644 --- a/experiments_ovary_detect/run_RG2Sp_estim_shape-models.py +++ b/experiments_ovary_detect/run_RG2Sp_estim_shape-models.py @@ -24,7 +24,7 @@ import imsegm.region_growing as tl_rg PATH_DATA = tl_data.update_path('data', absolute=True) -PATH_IMAGES = os.path.join(tl_data.update_path('images'), 'drosophila_ovary_slice') +PATH_IMAGES = os.path.join(tl_data.update_path('data_images'), 'drosophila_ovary_slice') PATH_ANNOT = os.path.join(PATH_IMAGES, 'annot_eggs', '*.png') RAY_STEP = 10 # names of default files for models diff --git a/experiments_ovary_detect/run_cut_segmented_objects.py b/experiments_ovary_detect/run_cut_segmented_objects.py index 2eab5688..5e930676 100644 --- a/experiments_ovary_detect/run_cut_segmented_objects.py +++ b/experiments_ovary_detect/run_cut_segmented_objects.py @@ -3,8 +3,8 @@ SAMPLE run: >> python run_cut_segmented_objects.py \ - -annot "images/drosophila_ovary_slice/annot_eggs/*.png" \ - -img "images/drosophila_ovary_slice/segm/*.png" \ + -annot "data_images/drosophila_ovary_slice/annot_eggs/*.png" \ + -img "data_images/drosophila_ovary_slice/segm/*.png" \ -out results/cut_images --padding 20 """ @@ -23,7 +23,7 @@ import imsegm.utils.experiments as tl_expt NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) PATHS = { 'annot': os.path.join(PATH_IMAGES, 'annot_eggs', '*.png'), @@ -113,9 +113,9 @@ def main(dict_paths, padding=0, use_mask=False, bg_color=None, df_paths = tl_data.find_files_match_names_across_dirs(list_dirs) logging.info('start cutting images') - wrapper_cutting = partial(export_cut_objects, path_out=dict_paths['output'], - padding=padding, use_mask=use_mask, bg_color=bg_color) - iterate = tl_expt.WrapExecuteSequence(wrapper_cutting, + _wrapper_cutting = partial(export_cut_objects, path_out=dict_paths['output'], + padding=padding, use_mask=use_mask, bg_color=bg_color) + iterate = tl_expt.WrapExecuteSequence(_wrapper_cutting, (row for idx, row in df_paths.iterrows()), nb_jobs=nb_jobs) list(iterate) diff --git a/experiments_ovary_detect/run_egg_swap_orientation.py b/experiments_ovary_detect/run_egg_swap_orientation.py index 1db5c74c..2a3cde15 100644 --- a/experiments_ovary_detect/run_egg_swap_orientation.py +++ b/experiments_ovary_detect/run_egg_swap_orientation.py @@ -16,7 +16,6 @@ import multiprocessing as mproc from functools import partial -import tqdm import numpy as np sys.path += [os.path.abspath('.'), os.path.abspath('..')] # Add path to root @@ -27,7 +26,7 @@ IMAGE_CHANNEL = 0 # image channel for mass extraction NB_THREADS = max(1, int(mproc.cpu_count() * 0.8)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) PARAMS = { @@ -77,10 +76,10 @@ def main(params): if not os.path.isdir(params['path_output']): os.mkdir(params['path_output']) - wrapper_object = partial(perform_orientation_swap, - path_out=params['path_output']) + _wrapper_object = partial(perform_orientation_swap, + path_out=params['path_output']) dir_name = os.path.dirname(params['path_images']) - iterate = tl_expt.WrapExecuteSequence(wrapper_object, list_imgs, + iterate = tl_expt.WrapExecuteSequence(_wrapper_object, list_imgs, nb_jobs=params['nb_jobs'], desc=dir_name) list(iterate) diff --git a/experiments_ovary_detect/run_ellipse_annot_match.py b/experiments_ovary_detect/run_ellipse_annot_match.py index 9df390bd..6c98fbbb 100644 --- a/experiments_ovary_detect/run_ellipse_annot_match.py +++ b/experiments_ovary_detect/run_ellipse_annot_match.py @@ -33,7 +33,7 @@ OVERLAP_THRESHOLD = 0. NB_THREADS = max(1, int(mproc.cpu_count() * 0.8)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PARAMS = { 'path_ellipses': os.path.join(PATH_IMAGES, 'ellipse_fitting', '*.csv'), @@ -155,9 +155,9 @@ def main(params): list_evals = [] # get the folder path_dir_csv = os.path.dirname(params['path_ellipses']) - wrapper_match = partial(select_optimal_ellipse, - path_dir_csv=path_dir_csv) - iterate = tl_expt.WrapExecuteSequence(wrapper_match, df_info.iterrows(), + _wrapper_match = partial(select_optimal_ellipse, + path_dir_csv=path_dir_csv) + iterate = tl_expt.WrapExecuteSequence(_wrapper_match, df_info.iterrows(), nb_jobs=params['nb_jobs']) for i, dict_row in enumerate(iterate): list_evals.append(dict_row) diff --git a/experiments_ovary_detect/run_ellipse_cut_scale.py b/experiments_ovary_detect/run_ellipse_cut_scale.py index 829ff429..99fbfd0d 100644 --- a/experiments_ovary_detect/run_ellipse_cut_scale.py +++ b/experiments_ovary_detect/run_ellipse_cut_scale.py @@ -34,7 +34,7 @@ NORM_FUNC = np.median # other options - mean, max, ... NB_THREADS = max(1, int(mproc.cpu_count() * 0.8)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) PARAMS = { @@ -93,10 +93,10 @@ def perform_stage(df_group, stage, path_images, path_out): if not os.path.isdir(path_out_stage): os.mkdir(path_out_stage) - wrapper_object = partial(extract_ellipse_object, path_images=path_images, - path_out=path_out_stage, norm_size=norm_size) + _wrapper_object = partial(extract_ellipse_object, path_images=path_images, + path_out=path_out_stage, norm_size=norm_size) desc = 'stage %i - size %s' % (stage, norm_size) - iterate = tl_expt.WrapExecuteSequence(wrapper_object, df_group.iterrows(), + iterate = tl_expt.WrapExecuteSequence(_wrapper_object, df_group.iterrows(), nb_jobs=params['nb_jobs'], desc=desc) list(iterate) diff --git a/experiments_ovary_detect/run_export_user-annot-segm.py b/experiments_ovary_detect/run_export_user-annot-segm.py index 3664ea03..5e244e4e 100644 --- a/experiments_ovary_detect/run_export_user-annot-segm.py +++ b/experiments_ovary_detect/run_export_user-annot-segm.py @@ -40,7 +40,7 @@ import imsegm.annotation as seg_annot NB_THREADS = max(1, int(mproc.cpu_count() * 0.8)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) PARAMS = { 'path_images': os.path.join(PATH_IMAGES, 'image', '*.jpg'), @@ -215,9 +215,9 @@ def main(params): df_slices_info = seg_annot.load_info_group_by_slices(params['path_infofile'], params['stages']) - wrapper_export = partial(export_figure, df_slices_info=df_slices_info, - path_out=params['path_output']) - iterate = tl_expt.WrapExecuteSequence(wrapper_export, df_paths.iterrows(), + _wrapper_export = partial(export_figure, df_slices_info=df_slices_info, + path_out=params['path_output']) + iterate = tl_expt.WrapExecuteSequence(_wrapper_export, df_paths.iterrows(), nb_jobs=params['nb_jobs']) list(iterate) logging.info('DONE') diff --git a/experiments_ovary_detect/run_ovary_egg-segmentation.py b/experiments_ovary_detect/run_ovary_egg-segmentation.py index 017f12fb..218391fa 100755 --- a/experiments_ovary_detect/run_ovary_egg-segmentation.py +++ b/experiments_ovary_detect/run_ovary_egg-segmentation.py @@ -9,7 +9,7 @@ SAMPLE run: >> python run_ovary_egg-segmentation.py \ - -list images/drosophila_ovary_slice/list_imgs-segm-center-points.csv \ + -list data_images/drosophila_ovary_slice/list_imgs-segm-center-points.csv \ -out results -n ovary_slices --nb_jobs 1 \ -m ellipse_moments \ ellipse_ransac_mmt \ @@ -96,7 +96,7 @@ } PATH_DATA = tl_data.update_path('data', absolute=True) -PATH_IMAGES = os.path.join(tl_data.update_path('images'), +PATH_IMAGES = os.path.join(tl_data.update_path('data_images'), 'drosophila_ovary_slice') # sample segmentation methods LIST_SAMPLE_METHODS = ( @@ -807,8 +807,8 @@ def main(params, debug_export=DEBUG_EXPORT): list_dirs = [n + DIR_DEBUG_POSIX for n in dict_segment if 'rg2sp' in n] tl_expt.create_subfolders(params['path_exp'], list_dirs) - wrapper_segment = partial(image_segmentation, params=params) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, df_paths.iterrows(), + _wrapper_segment = partial(image_segmentation, params=params) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, df_paths.iterrows(), nb_jobs=params['nb_jobs']) list(iterate) diff --git a/experiments_ovary_detect/run_ovary_segm_evaluation.py b/experiments_ovary_detect/run_ovary_segm_evaluation.py index e007d694..057d6ce5 100755 --- a/experiments_ovary_detect/run_ovary_segm_evaluation.py +++ b/experiments_ovary_detect/run_ovary_segm_evaluation.py @@ -46,7 +46,7 @@ SKIP_DIRS = ['input', 'simple', NAME_DIR_VISUAL_1, NAME_DIR_VISUAL_2, NAME_DIR_VISUAL_3] NAME_CSV_STAT = 'segmented-eggs_%s.csv' -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) PATHS = { 'images': os.path.join(PATH_IMAGES, 'image', '*.jpg'), @@ -245,7 +245,7 @@ def evaluate_folder(path_dir, dict_paths, export_visual=EXPORT_VUSIALISATION): for n in ['mean', 'std']: names = ['%s (%s)' % (c, n) for c in cols] dict_eval.update(zip(names, df_summary.T[n].values.tolist())) - dict_eval.update(zip(['%s (median)' % (c) for c in cols], + dict_eval.update(zip(['%s (median)' % c for c in cols], df_eval.median(axis=0).values.tolist())) return dict_eval @@ -271,9 +271,9 @@ def main(dict_paths, export_visual=EXPORT_VUSIALISATION, nb_jobs=NB_THREADS): [NAME_DIR_VISUAL_1, NAME_DIR_VISUAL_2, NAME_DIR_VISUAL_3]) df_all = pd.DataFrame() - wrapper_eval = partial(evaluate_folder, dict_paths=dict_paths, - export_visual=export_visual) - iterate = tl_expt.WrapExecuteSequence(wrapper_eval, list_results, + _wrapper_eval = partial(evaluate_folder, dict_paths=dict_paths, + export_visual=export_visual) + iterate = tl_expt.WrapExecuteSequence(_wrapper_eval, list_results, nb_jobs=nb_jobs) for dict_eval in iterate: df_all = df_all.append(dict_eval, ignore_index=True) diff --git a/experiments_segmentation/run_compute-stat_annot-segm.py b/experiments_segmentation/run_compute_stat_annot_segm.py similarity index 75% rename from experiments_segmentation/run_compute-stat_annot-segm.py rename to experiments_segmentation/run_compute_stat_annot_segm.py index aa433631..e02f3d12 100644 --- a/experiments_segmentation/run_compute-stat_annot-segm.py +++ b/experiments_segmentation/run_compute_stat_annot_segm.py @@ -2,12 +2,12 @@ With two given folder find image match and compute segmentation statistic >> python run_compute_stat_annot_segm.py \ - -annot "images/drosophila_ovary_slice/annot_struct/*.png" \ + -annot "data_images/drosophila_ovary_slice/annot_struct/*.png" \ -segm "results/experiment_segm-supervise_ovary/*.png" \ - -img "images/drosophila_ovary_slice/image/*.jpg" \ - -out results/evaluation + -img "data_images/drosophila_ovary_slice/image/*.jpg" \ + -out results/evaluation --visual -Copyright (C) 2016-2017 Jiri Borovec +Copyright (C) 2016-2018 Jiri Borovec """ import os @@ -30,8 +30,9 @@ NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) NAME_CVS_OVERALL = 'segm-STATISTIC_%s_stat-overall.csv' NAME_CVS_PER_IMAGE = 'segm-STATISTIC_%s_stat-per-images.csv' -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) +SUFFIX_VISUAL = '__visual' PATHS = { 'annot': os.path.join(PATH_IMAGES, 'annot_struct', '*.png'), 'segm': os.path.join(PATH_IMAGES, 'segm', '*.png'), @@ -58,6 +59,11 @@ def aparse_params(dict_paths): parser.add_argument('-out', '--path_out', type=str, required=False, help='path to the output directory', default=dict_paths['output']) + parser.add_argument('--nb_jobs', type=int, required=False, + default=NB_THREADS, + help='number of processes in parallel') + parser.add_argument('--visual', required=False, action='store_true', + help='export visualisations', default=False) args = parser.parse_args() logging.info('ARG PARAMETERS: \n %s', repr(args)) dict_paths = { @@ -106,7 +112,7 @@ def wrapper_relabel_segm(annot_segm): return segm -def main(dict_paths, nb_jobs=NB_THREADS, relabel=True): +def main(dict_paths, nb_jobs=NB_THREADS, visual=True, relabel=True): """ main evaluation :param {str: str} dict_paths: @@ -127,35 +133,45 @@ def main(dict_paths, nb_jobs=NB_THREADS, relabel=True): path_csv = os.path.join(dict_paths['output'], NAME_CVS_PER_IMAGE % name) df_paths.to_csv(path_csv) + assert len(df_paths) > 0, 'nothing to compare' + annots, _ = tl_data.load_images_list(df_paths['path_1'].values.tolist()) segms, names = tl_data.load_images_list(df_paths['path_2'].values.tolist()) logging.info('loaded %i annots and %i segms', len(annots), len(segms)) if relabel: + logging.info('reabel annotations and segmentations') annots = [relabel_sequential(annot)[0] for annot in annots] - segms = list(map(wrapper_relabel_segm, zip(annots, segms))) + iterate = tl_expt.WrapExecuteSequence(wrapper_relabel_segm, + zip(annots, segms), + nb_jobs=nb_jobs, ordered=True) + segms = list(iterate) + logging.info('compute statistic per image') path_csv = os.path.join(dict_paths['output'], NAME_CVS_PER_IMAGE % name) logging.debug('export to "%s"', path_csv) df_stat = seg_clf.compute_stat_per_image(segms, annots, names, nb_jobs) df_stat.to_csv(path_csv) + logging.info('sumarise statistic') path_csv = os.path.join(dict_paths['output'], NAME_CVS_OVERALL % name) logging.debug('export to "%s"', path_csv) df_desc = df_stat.describe() logging.info(df_desc.T[['count', 'mean', 'std']]) df_desc.to_csv(path_csv) - path_visu = os.path.join(dict_paths['output'], '%s__visual' % name) - if not os.path.isdir(path_visu): - os.mkdir(path_visu) - # for idx, row in df_paths.iterrows(): - # export_visual(row, path_visu) - wrapper_visual = partial(export_visual, path_out=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_visual, - (row for idx, row in df_paths.iterrows()), - nb_jobs=nb_jobs) - list(iterate) + if visual: + path_visu = os.path.join(dict_paths['output'], + '%s%s' % (name, SUFFIX_VISUAL)) + if not os.path.isdir(path_visu): + os.mkdir(path_visu) + # for idx, row in df_paths.iterrows(): + # export_visual(row, path_visu) + _wrapper_visual = partial(export_visual, path_out=path_visu) + iterate = tl_expt.WrapExecuteSequence(_wrapper_visual, + (row for idx, row in df_paths.iterrows()), + nb_jobs=nb_jobs) + list(iterate) logging.info('DONE') @@ -163,4 +179,4 @@ def main(dict_paths, nb_jobs=NB_THREADS, relabel=True): if __name__ == '__main__': logging.basicConfig(level=logging.INFO) dict_paths, args = aparse_params(PATHS) - main(dict_paths) + main(dict_paths, nb_jobs=args.nb_jobs, visual=args.visual) diff --git a/experiments_segmentation/run_eval_superpixels.py b/experiments_segmentation/run_eval_superpixels.py index 6cff50ad..2b6ea7d6 100644 --- a/experiments_segmentation/run_eval_superpixels.py +++ b/experiments_segmentation/run_eval_superpixels.py @@ -3,9 +3,9 @@ SAMPLE run: >> python run_eval_superpixels.py \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -segm "images/drosophila_ovary_slice/annot_eggs/*.png" \ - --img_type 2d_gray \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -segm "data_images/drosophila_ovary_slice/annot_eggs/*.png" \ + --img_type 2d_split \ --slic_size 20 --slic_regul 0.25 --slico 0 Copyright (C) 2017 Jiri Borovec @@ -32,14 +32,14 @@ NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) -PATH_IMAGES = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice')) +PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice')) PATH_RESULTS = tl_data.update_path('results', absolute=True) NAME_CSV_DISTANCES = 'measured_boundary_distances.csv' PARAMS = { 'path_images': os.path.join(PATH_IMAGES, 'image', '*.jpg'), 'path_segms': os.path.join(PATH_IMAGES, 'annot_eggs', '*.png'), 'path_out': os.path.join(PATH_RESULTS, 'compute_boundary_distances'), - 'img_type': '2d_gray', + 'img_type': '2d_split', } @@ -93,7 +93,7 @@ def compute_boundary_distance(idx_row, params, path_out=''): _, row = idx_row name = os.path.splitext(os.path.basename(row['path_image']))[0] img = load_image(row['path_image'], params['img_type']) - segm = load_image(row['path_segm'], 'segm') + segm = load_image(row['path_segm'], '2d_segm') logging.debug('segment SLIC...') slic = seg_spx.segment_slic_img2d(img, @@ -126,9 +126,9 @@ def main(params): df_dist = pd.DataFrame() - wrapper_eval = partial(compute_boundary_distance, params=params, - path_out=params['path_out']) - iterate = tl_expt.WrapExecuteSequence(wrapper_eval, df_paths.iterrows(), + _wrapper_eval = partial(compute_boundary_distance, params=params, + path_out=params['path_out']) + iterate = tl_expt.WrapExecuteSequence(_wrapper_eval, df_paths.iterrows(), nb_jobs=params['nb_jobs'], desc='evaluate SLIC') for name, dist in iterate: diff --git a/experiments_segmentation/run_segm_slic_classif_graphcut.py b/experiments_segmentation/run_segm_slic_classif_graphcut.py index e45bab69..d9275dee 100644 --- a/experiments_segmentation/run_segm_slic_classif_graphcut.py +++ b/experiments_segmentation/run_segm_slic_classif_graphcut.py @@ -18,11 +18,12 @@ SAMPLE run: >> python run_segm_slic_classif_graphcut.py \ - -list images/langerhans_islets/list_lang-isl_imgs-annot.csv \ - -imgs "images/langerhans_islets/image/*.jpg" \ - -out results -n LangIsl --img_type 2d_rgb --visual 1 --nb_jobs 2 + -l data_images/drosophila_ovary_slice/list_imgs-annot-struct.csv \ + -i "data_images/drosophila_ovary_slice/image/*.jpg" \ + --path_config experiments_segmentation/sample_config.json \ + -o results -n Ovary --img_type 2d_split --visual -Copyright (C) 2016-2017 Jiri Borovec +Copyright (C) 2016-2018 Jiri Borovec """ import os @@ -47,6 +48,7 @@ import matplotlib.pyplot as plt # from llvmpy._api.llvm.CmpInst import FCMP_OLE from skimage import segmentation +import skimage.color as sk_color from sklearn import metrics sys.path += [os.path.abspath('.'), os.path.abspath('..')] # Add path to root @@ -65,7 +67,7 @@ NAME_EXPERIMENT = 'experiment_segm-Supervised' NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) -TYPES_LOAD_IMAGE = ['2d_rgb', '2d_gray'] +TYPES_LOAD_IMAGE = ['2d_rgb', '2d_split'] NAME_FIG_LABEL_HISTO = 'fig_histogram_annot_segments.png' NAME_CSV_SEGM_STAT_SLIC_ANNOT = 'statistic_segm_slic_annot.csv' NAME_CSV_SEGM_STAT_RESULT_LOO = 'statistic_segm_LOO.csv' @@ -81,11 +83,12 @@ FOLDER_SLIC = 'slic' FOLDER_SLIC_ANNOT = 'annot_slic' FOLDER_SEGM = 'segmentation_trained' -FOLDER_SEGM_VISU = FOLDER_SEGM + '___visual' +SUFFIX_VISUAL = '___visual' +FOLDER_SEGM_VISU = FOLDER_SEGM + SUFFIX_VISUAL FOLDER_LOO = 'segmentation_leave-one-out' -FOLDER_LOO_VISU = FOLDER_LOO + '___visual' +FOLDER_LOO_VISU = FOLDER_LOO + SUFFIX_VISUAL FOLDER_LPO = 'segmentation_leave-P-out' -FOLDER_LPO_VISU = FOLDER_LPO + '___visual' +FOLDER_LPO_VISU = FOLDER_LPO + SUFFIX_VISUAL LIST_FOLDERS_BASE = (FOLDER_IMAGE, FOLDER_ANNOT, FOLDER_SLIC, FOLDER_SLIC_ANNOT, FOLDER_SEGM, FOLDER_LOO, FOLDER_LPO) LIST_FOLDERS_DEBUG = (FOLDER_SEGM_VISU, FOLDER_LOO_VISU, FOLDER_LPO_VISU) @@ -93,15 +96,15 @@ # unique experiment means adding timestemp on the end of folder name EACH_UNIQUE_EXPERIMENT = False # showing some intermediate debug images from segmentation -SHOW_DEBUG_IMAGES = False +SHOW_DEBUG_IMAGES = True # relabel annotation such that labels are in sequence no gaps in between them ANNOT_RELABEL_SEQUENCE = False # whether skip loading config from previous fun -FORCE_RELOAD = False +FORCE_RELOAD = True # even you have dumped data from previous time, all wil be recomputed -FORCE_RECOMP_DATA = False +FORCE_RECOMP_DATA = True # even you have saved classif. data from previous time, all wil be retrained -FORCE_RETRAIN_CLASSIF = False +FORCE_RETRAIN_CLASSIF = True # ration of fold size for LPO for hyper-parameter search CROSS_VAL_LEAVE_OUT_SEARCH = 0.2 # ration of fold size for LPO for evaluation @@ -112,20 +115,20 @@ RUN_CROSS_VAL_LPO = True -FEATURES_SET_COLOR = {'color': ('mean', 'std', 'eng')} -FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'eng')} +FEATURES_SET_COLOR = {'color': ('mean', 'std', 'energy')} +FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'energy')} FEATURES_SET_ALL = {'color': ('mean', 'std', 'median'), - 'tLM': ('mean', 'std', 'eng', 'mG')} + 'tLM': ('mean', 'std', 'energy', 'meanGrad')} FEATURES_SET_MIN = {'color': ('mean', 'std', 'energy'), 'tLM_s': ('mean', )} -FEATURES_SET_MIX = {'color': ('mean', 'std', 'eng', 'median'), +FEATURES_SET_MIX = {'color': ('mean', 'std', 'energy', 'median'), 'tLM': ('mean', 'std')} # Default parameter configuration SEGM_PARAMS = { 'name': 'ovary', 'nb_classes': None, 'clr_space': 'rgb', - 'img_type': '2d_gray', + 'img_type': '2d_split', 'slic_size': 35, 'slic_regul': 0.3, # 'spacing': (12, 1, 1), @@ -139,7 +142,7 @@ 'gc_edge_type': 'model', 'gc_use_trans': False, } -PATH_IMAGES = os.path.join(tl_data.update_path('images'), +PATH_IMAGES = os.path.join(tl_data.update_path('data_images'), 'drosophila_ovary_slice') PATH_RESULTS = tl_data.update_path('results', absolute=True) SEGM_PARAMS.update({ @@ -165,6 +168,17 @@ def visu_histogram_labels(params, dict_label_hist, fig_name=NAME_FIG_LABEL_HISTO plt.close(fig) +def use_rgb_image(img, clr='rgb'): + # clr = params.get('clr_space', 'rgb').lower() + if img.ndim == 3 and img.shape[-1] in (3, 4): + img_rgb = seg_pipe.convert_img_color_to_rgb(img, clr) + elif img.ndim == 2: + img_rgb = sk_color.gray2rgb(img) + else: + img_rgb = img.copy() + return img_rgb + + def load_image_annot_compute_features_labels(idx_row, params, show_debug_imgs=SHOW_DEBUG_IMAGES): """ load image and annotation, and compute superpixel features and labels @@ -174,42 +188,47 @@ def load_image_annot_compute_features_labels(idx_row, params, :param bool show_debug_imgs: whether show debug images :return (...): """ - def path_out_img(params, dir_name, name): + def _path_out_img(params, dir_name, name): return os.path.join(params['path_exp'], dir_name, name + '.png') idx, row = idx_row idx_name = get_idx_name(idx, row['path_image']) img = load_image(row['path_image'], params['img_type']) - annot = load_image(row['path_annot'], 'segm') + annot = load_image(row['path_annot'], '2d_segm') logging.debug('.. processing: %s', idx_name) assert img.shape[:2] == annot.shape[:2], \ 'individual size of image %s and seg_pipe %s for "%s" - "%s"' % \ (repr(img.shape), repr(annot.shape), row['path_image'], row['path_annot']) if show_debug_imgs: - plt.imsave(path_out_img(params, FOLDER_IMAGE, idx_name), img, + plt.imsave(_path_out_img(params, FOLDER_IMAGE, idx_name), img, cmap=plt.cm.gray) - plt.imsave(path_out_img(params, FOLDER_ANNOT, idx_name), annot) + plt.imsave(_path_out_img(params, FOLDER_ANNOT, idx_name), annot) # duplicate gray band to be as rgb # if img.ndim == 2: # img = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3) slic = seg_spx.segment_slic_img2d(img, sp_size=params['slic_size'], rltv_compact=params['slic_regul']) - img = seg_pipe.convert_img_color_space(img, params.get('clr_space', 'rgb')) + img = seg_pipe.convert_img_color_from_rgb(img, params.get('clr_space', 'rgb')) logging.debug('computed SLIC with %i labels', slic.max()) if show_debug_imgs: - img_slic = segmentation.mark_boundaries(img / float(img.max()), slic, - color=(1, 0, 0), mode='subpixel') - plt.imsave(path_out_img(params, FOLDER_SLIC, idx_name), img_slic) - features, ft_names = seg_fts.compute_selected_features_img2d(img, slic, - params['features']) - - label_hist = seg_label.histogram_regions_labels_norm(slic, annot) - labels = np.argmax(label_hist, axis=1) + img_rgb = use_rgb_image(img, params.get('clr_space', 'rbgb').lower()) + img_slic = segmentation.mark_boundaries(img_rgb, slic, + color=(1, 0, 0), + mode='subpixel') + plt.imsave(_path_out_img(params, FOLDER_SLIC, idx_name), + np.clip(img_slic, 0, 1)) + slic_label_hist = seg_label.histogram_regions_labels_norm(slic, annot) + labels = np.argmax(slic_label_hist, axis=1) slic_annot = labels[slic] if show_debug_imgs: - plt.imsave(path_out_img(params, FOLDER_SLIC_ANNOT, idx_name), slic_annot) - return idx_name, img, annot, slic, features, labels, label_hist, ft_names + plt.imsave(_path_out_img(params, FOLDER_SLIC_ANNOT, idx_name), + np.clip(slic_annot, 0, slic_annot.max())) + + features, feature_names = seg_fts.compute_selected_features_img2d( + img, slic, params['features']) + return idx_name, img, annot, slic, features, labels, \ + slic_label_hist, feature_names def dataset_load_images_annot_compute_features(params, @@ -231,9 +250,9 @@ def dataset_load_images_annot_compute_features(params, df_paths = pd.read_csv(params['path_train_list'], index_col=0) assert all(n in df_paths.columns for n in ['path_image', 'path_annot']), \ 'missing required columns in loaded csv file' - wrapper_load_compute = partial(load_image_annot_compute_features_labels, - params=params, show_debug_imgs=show_debug_imgs) - iterate = tl_expt.WrapExecuteSequence(wrapper_load_compute, df_paths.iterrows(), + _wrapper_load_compute = partial(load_image_annot_compute_features_labels, + params=params, show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_load_compute, df_paths.iterrows(), nb_jobs=params['nb_jobs'], desc='extract training data') for name, img, annot, slic, features, labels, label_hist, feature_names in iterate: @@ -307,15 +326,17 @@ def segment_image(imgs_idx_path, params, classif, path_out, path_visu=None, :param obj classif: trained classifier :param str path_out: path for output :param str path_visu: the existing patch means export also visualisation + :param bool show_debug_imgs: whether show debug images :return (str, ndarray, ndarray): """ idx, path_img = parse_imgs_idx_path(imgs_idx_path) logging.debug('segmenting image: "%s"', path_img) idx_name = get_idx_name(idx, path_img) - img = load_image(path_img, params['img_type']) - slic = seg_spx.segment_slic_img2d(img, sp_size=params['slic_size'], - rltv_compact=params['slic_regul']) - img = seg_pipe.convert_img_color_space(img, params.get('clr_space', 'rgb')) + img_rgb = load_image(path_img, params['img_type']) + slic = seg_spx.segment_slic_img2d(img_rgb, sp_size=params['slic_size'], + rltv_compact=params['slic_regul']) + img = seg_pipe.convert_img_color_from_rgb(img_rgb, params.get('clr_space', + 'rgb')) features, _ = seg_fts.compute_selected_features_img2d(img, slic, params['features']) labels = classif.predict(features) @@ -328,7 +349,7 @@ def segment_image(imgs_idx_path, params, classif, path_out, path_visu=None, # plt.imsave(os.path.join(path_out, idx_name + '_rgb.png'), seg_pipe) if path_visu is not None and os.path.isdir(path_visu): - export_draw_image_segm_contour(img, segm, path_visu, idx_name) + export_draw_image_segm_contour(img_rgb, segm, path_visu, idx_name) try: # in case some classiefier do not support predict_proba proba = classif.predict_proba(features) @@ -336,7 +357,7 @@ def segment_image(imgs_idx_path, params, classif, path_out, path_visu=None, path_npz = os.path.join(path_out, idx_name + '.npz') np.savez_compressed(path_npz, segm_soft) except Exception: - logging.warning('classif: %s not support predict_proba(.)', + logging.warning('classif: %s not support predict_proba(...)', repr(classif)) proba = None segm_soft = None @@ -362,9 +383,8 @@ def segment_image(imgs_idx_path, params, classif, path_out, path_visu=None, # io.imsave(path_img, segm_gc) if path_visu is not None and os.path.isdir(path_visu): - export_draw_image_segm_contour(img, segm_gc, path_visu, + export_draw_image_segm_contour(img_rgb, segm_gc, path_visu, idx_name, '_gc') - if show_debug_imgs: labels_map = np.argmax(proba, axis=1) plt.imsave(os.path.join(path_visu, idx_name + '_map.png'), @@ -415,14 +435,16 @@ def eval_segment_with_annot(params, dict_annot, dict_segm, dict_label_hist=None, def retrain_loo_segment_image(imgs_idx_path, path_classif, path_dump, - path_out, path_visu): + path_out, path_visu, + show_debug_imgs=SHOW_DEBUG_IMAGES): """ load the classifier, and dumped data, subtract the image, retrain the classif. without it and do the segmentation - :param str path_img: path to input image + :param () imgs_idx_path: path to input image :param str path_classif: path to saved classifier :param str path_dump: path to dumped data :param, str path_out: path to segmentation outputs + :param bool show_debug_imgs: whether show debug images :return (str, ndarray, ndarray): """ idx, path_img = parse_imgs_idx_path(imgs_idx_path) @@ -443,20 +465,23 @@ def retrain_loo_segment_image(imgs_idx_path, path_classif, path_dump, classif.fit(features, labels) idx_name, segm, segm_gc = segment_image(imgs_idx_path, params, classif, - path_out, path_visu) + path_out, path_visu, + show_debug_imgs=show_debug_imgs) # gc.collect(), time.sleep(1) return idx_name, segm, segm_gc def retrain_lpo_segment_image(list_imgs_idx_path, path_classif, path_dump, - path_out, path_visu): + path_out, path_visu, + show_debug_imgs=SHOW_DEBUG_IMAGES): """ load the classifier, and dumped data, subtract the image, retrain the classif without it and do the segmentation - :param str path_img: path to input image + :param [str] list_imgs_idx_path: path to input image :param str path_classif: path to saved classifier :param str path_dump: path to dumped data :param, str path_out: path to segmentation outputs + :param bool show_debug_imgs: whether show debug images :return (str, ndarray, ndarray): """ dict_imgs, _, _, dict_features, dict_labels, _, _ = \ @@ -481,7 +506,8 @@ def retrain_lpo_segment_image(list_imgs_idx_path, path_classif, path_dump, dict_segm, dict_segm_gc = {}, {} for imgs_idx_path in list_imgs_idx_path: idx_name, segm, segm_gc = segment_image(imgs_idx_path, params, classif, - path_out, path_visu) + path_out, path_visu, + show_debug_imgs=show_debug_imgs) dict_segm[idx_name] = segm dict_segm_gc[idx_name] = segm_gc # gc.collect(), time.sleep(1) @@ -509,16 +535,18 @@ def get_summary(df, name, list_stat=('mean', 'std', 'median')): return dict_state -def perform_predictions(params, paths_img, classif): +def perform_predictions(params, paths_img, classif, + show_debug_imgs=SHOW_DEBUG_IMAGES): logging.info('run prediction on training images...') imgs_idx_path = list(zip(range(1, len(paths_img) + 1), paths_img)) dict_segms, dict_segms_gc = dict(), dict() path_out = os.path.join(params['path_exp'], FOLDER_SEGM) path_visu = os.path.join(params['path_exp'], FOLDER_SEGM_VISU) - wrapper_segment = partial(segment_image, params=params, classif=classif, - path_out=path_out, path_visu=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, imgs_idx_path, + _wrapper_segment = partial(segment_image, params=params, classif=classif, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, imgs_idx_path, nb_jobs=params['nb_jobs'], desc='image segm: prediction') for name, segm, segm_gc in iterate: @@ -528,16 +556,28 @@ def perform_predictions(params, paths_img, classif): def experiment_loo(params, df_stat, dict_annot, paths_img, path_classif, - path_dump): + path_dump, show_debug_imgs=SHOW_DEBUG_IMAGES): + """ experiment Leave-One-Out + + :param {str: ...} params: + :param DF df_stat: + :param {str: ndarray} dict_annot: + :param [str] paths_img: + :param str path_classif: + :param str path_dump: + :param bool show_debug_imgs: whether show debug images + :return {}: + """ imgs_idx_path = list(zip(range(1, len(paths_img) + 1), paths_img)) logging.info('run prediction on training images as Leave-One-Out...') dict_segms, dict_segms_gc = dict(), dict() path_out = os.path.join(params['path_exp'], FOLDER_LOO) path_visu = os.path.join(params['path_exp'], FOLDER_LOO_VISU) - wrapper_segment = partial(retrain_loo_segment_image, - path_classif=path_classif, path_dump=path_dump, - path_out=path_out, path_visu=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, imgs_idx_path, + _wrapper_segment = partial(retrain_loo_segment_image, + path_classif=path_classif, path_dump=path_dump, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, imgs_idx_path, nb_jobs=params['nb_jobs'], desc='experiment LOO') for name, segm, segm_gc in iterate: @@ -562,7 +602,19 @@ def experiment_loo(params, df_stat, dict_annot, paths_img, path_classif, def experiment_lpo(params, df_stat, dict_annot, paths_img, path_classif, - path_dump, nb_holdout): + path_dump, nb_holdout, show_debug_imgs=SHOW_DEBUG_IMAGES): + """ experiment Leave-P-samples-Out + + :param {str: ...} params: + :param DF df_stat: + :param {str: ndarray} dict_annot: + :param [str] paths_img: + :param str path_classif: + :param str path_dump: + :param int nb_holdout: + :param bool show_debug_imgs: whether show debug images + :return {}: + """ imgs_idx_path = list(zip(range(1, len(paths_img) + 1), paths_img)) logging.info('run prediction on training images as Leave-%i-Out...', nb_holdout) @@ -571,10 +623,11 @@ def experiment_lpo(params, df_stat, dict_annot, paths_img, path_classif, test_imgs_idx_path = [[imgs_idx_path[i] for i in ids] for _, ids in cv] path_out = os.path.join(params['path_exp'], FOLDER_LPO) path_visu = os.path.join(params['path_exp'], FOLDER_LPO_VISU) - wrapper_segment = partial(retrain_lpo_segment_image, - path_classif=path_classif, path_dump=path_dump, - path_out=path_out, path_visu=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, test_imgs_idx_path, + _wrapper_segment = partial(retrain_lpo_segment_image, + path_classif=path_classif, path_dump=path_dump, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, test_imgs_idx_path, nb_jobs=params['nb_jobs'], desc='experiment LPO') for dict_seg, dict_seg_gc in iterate: @@ -620,7 +673,7 @@ def load_train_classifier(params, features, labels, feature_names, sizes, classif, path_classif = seg_clf.create_classif_train_export( params['classif'], features, labels, cross_val=cv, params=params, feature_names=feature_names, - nb_search_iter=params['nb_classif_search'], + nb_search_iter=params.get('nb_classif_search', 1), nb_jobs=params['nb_jobs'], pca_coef=params['pca_coef'], path_out=params['path_exp']) params['path_classif'] = path_classif @@ -640,19 +693,21 @@ def main_train(params): 4) perform Leave-One-Out and Leave-P-Out experiments on images :param {str: ...} params: - :return{str: ...} : + :return {str: ...}: """ logging.getLogger().setLevel(logging.DEBUG) logging.info('running TRAINING...') + show_debug_imgs = params.get('visual', False) or SHOW_DEBUG_IMAGES - reload_dir_config = (os.path.isfile(params['path_config']) or FORCE_RELOAD) + reload_dir_config = (os.path.isfile(params.get('path_config', '')) + or FORCE_RELOAD) params = tl_expt.create_experiment_folder(params, dir_name=NAME_EXPERIMENT, - stamp_unique=EACH_UNIQUE_EXPERIMENT, + stamp_unique=params.get('unique', EACH_UNIQUE_EXPERIMENT), skip_load=reload_dir_config) tl_expt.set_experiment_logger(params['path_exp']) logging.info(tl_expt.string_dict(params, desc='PARAMETERS')) tl_expt.create_subfolders(params['path_exp'], LIST_FOLDERS_BASE) - if params['visual']: + if params.get('visual', False): tl_expt.create_subfolders(params['path_exp'], LIST_FOLDERS_DEBUG) df_stat = pd.DataFrame() @@ -663,7 +718,8 @@ def main_train(params): else: dict_imgs, dict_annot, dict_slics, dict_features, dict_labels, \ dict_label_hist, feature_names = \ - dataset_load_images_annot_compute_features(params) + dataset_load_images_annot_compute_features(params, + show_debug_imgs) save_dump_data(path_dump, dict_imgs, dict_annot, dict_slics, dict_features, dict_labels, dict_label_hist, feature_names) @@ -704,18 +760,22 @@ def main_train(params): # test classif on images df_paths = pd.read_csv(params['path_train_list'], index_col=0) paths_img = df_paths['path_image'].tolist() - perform_predictions(params, paths_img, classif) + perform_predictions(params, paths_img, classif, + show_debug_imgs=show_debug_imgs) # LEAVE ONE OUT if RUN_CROSS_VAL_LOO: df_stat = experiment_loo(params, df_stat, dict_annot, paths_img, - path_classif, path_dump) + path_classif, path_dump, + show_debug_imgs=show_debug_imgs) # LEAVE P OUT if RUN_CROSS_VAL_LPO: df_stat = experiment_lpo(params, df_stat, dict_annot, paths_img, - path_classif, path_dump, nb_holdout) + path_classif, path_dump, nb_holdout, + show_debug_imgs=show_debug_imgs) + logging.info('Statistic: \n %s', repr(df_stat.describe())) logging.info('training DONE') return params @@ -734,14 +794,14 @@ def prepare_output_dir(path_pattern_imgs, path_out, name): path_out = os.path.join(path_out, name) if not os.path.isdir(path_out): os.mkdir(path_out) - path_visu = path_out + '___visual' + path_visu = path_out + SUFFIX_VISUAL if not os.path.isdir(path_visu): os.mkdir(path_visu) return path_out, path_visu def try_segment_image(img_idx_path, params, classif, path_out, path_visu, - show_debug_imgs=False): + show_debug_imgs=SHOW_DEBUG_IMAGES): try: return segment_image(img_idx_path, params, classif, path_out, path_visu, @@ -759,7 +819,6 @@ def main_predict(path_classif, path_pattern_imgs, path_out, name='segment_', :param str path_pattern_imgs: :param str path_out: :param str name: - :return: """ logging.getLogger().setLevel(logging.INFO) logging.info('running PREDICTION...') @@ -780,10 +839,11 @@ def main_predict(path_classif, path_pattern_imgs, path_out, name='segment_', path_pattern_imgs) logging.debug('run prediction...') - wrapper_segment = partial(try_segment_image, params=params, classif=classif, - path_out=path_out, path_visu=path_visu) + _wrapper_segment = partial(try_segment_image, params=params, classif=classif, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=SHOW_DEBUG_IMAGES) list_img_path = list(zip([None] * len(paths_img), paths_img)) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, list_img_path, + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, list_img_path, nb_jobs=params['nb_jobs'], desc='segmenting images') for _ in iterate: diff --git a/experiments_segmentation/run_segm_slic_model_graphcut.py b/experiments_segmentation/run_segm_slic_model_graphcut.py index 23a5718a..c16eed31 100644 --- a/experiments_segmentation/run_segm_slic_model_graphcut.py +++ b/experiments_segmentation/run_segm_slic_model_graphcut.py @@ -12,16 +12,17 @@ SAMPLE run: >> python run_segm_slic_model_graphcut.py \ - -list images/langerhans_islets/list_lang-isl_imgs-annot.csv \ - -imgs "images/langerhans_islets/image/*.jpg" \ - -out results -n LangIsl --nb_classes 3 --visual 1 --nb_jobs 2 + -l data_images/langerhans_islets/list_lang-isl_imgs-annot.csv \ + -i "data_images/langerhans_islets/image/*.jpg" \ + -o results -n LangIsl --nb_classes 3 --visual --nb_jobs 2 -Copyright (C) 2016-2017 Jiri Borovec +Copyright (C) 2016-2018 Jiri Borovec """ import os import sys import json +import glob import pickle import argparse import logging @@ -36,7 +37,6 @@ logging.warning('No display found. Using non-interactive Agg backend.') matplotlib.use('Agg') -import tqdm from PIL import Image import numpy as np import pandas as pd @@ -50,20 +50,21 @@ import imsegm.utils.experiments as tl_expt import imsegm.utils.drawing as tl_visu import imsegm.pipelines as seg_pipe +import imsegm.labeling as seg_lbs import imsegm.descriptors as seg_fts # sometimes it freeze in "Cython: computing Colour means for image" seg_fts.USE_CYTHON = False NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) -TYPES_LOAD_IMAGE = ['2d_rgb', '2d_gray'] +TYPES_LOAD_IMAGE = ['2d_rgb', '2d_split'] NAME_DUMP_MODEL = 'estimated_model.npz' NAME_CSV_ARS_CORES = 'metric_ARS.csv' # setting experiment sub-folders FOLDER_IMAGE = 'images' FOLDER_ANNOT = 'annotations' -FOLDER_SEGM_GMM = 'segmentation_GaussMixModel' +FOLDER_SEGM_GMM = 'segmentation_MixtureModel' FOLDER_SEGM_GMM_VISU = FOLDER_SEGM_GMM + '___visual' -FOLDER_SEGM_GROUP = 'segmentation_GroupGMM' +FOLDER_SEGM_GROUP = 'segmentation_GroupMM' FOLDER_SEGM_GROUP_VISU = FOLDER_SEGM_GROUP + '___visual' LIST_FOLDERS_BASE = (FOLDER_IMAGE, FOLDER_SEGM_GMM, FOLDER_SEGM_GROUP) LIST_FOLDERS_DEBUG = (FOLDER_SEGM_GMM_VISU, FOLDER_SEGM_GROUP_VISU) @@ -71,23 +72,22 @@ # unique experiment means adding timestemp on the end of folder name EACH_UNIQUE_EXPERIMENT = False # showing some intermediate debug images from segmentation -SHOW_DEBUG_IMAGES = False +SHOW_DEBUG_IMAGES = True # relabel annotation such that labels are in sequence no gaps in between them ANNOT_RELABEL_SEQUENCE = False # whether skip loading config from previous fun -FORCE_RELOAD = False +FORCE_RELOAD = True # even you have dumped data from previous time, all wil be recomputed -FORCE_RECOMP_DATA = False +FORCE_RECOMP_DATA = True -FEATURES_SET_COLOR = {'color': ('mean', 'std', 'eng')} -FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'eng')} +FEATURES_SET_COLOR = {'color': ('mean', 'std', 'energy')} +FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'energy')} FEATURES_SET_ALL = {'color': ('mean', 'std', 'median'), - 'tLM': ('mean', 'std', 'eng', 'mG')} + 'tLM': ('mean', 'std', 'energy', 'meanGrad')} FEATURES_SET_MIN = {'color': ('mean', 'std', 'energy'), 'tLM_s': ('mean', )} -FEATURES_SET_MIX = {'color': ('mean', 'std', 'eng', 'median'), +FEATURES_SET_MIX = {'color': ('mean', 'std', 'energy', 'median'), 'tLM': ('mean', 'std')} -TYPE_GMM = ('GMM', 'Group') # Default parameter configuration SEGM_PARAMS = { 'name': 'imgDisk', @@ -98,15 +98,14 @@ 'slic_regul': 0.2, # 'spacing': (12, 1, 1), 'features': FEATURES_SET_COLOR, - 'prob_type': 'GMM', + 'estim_model': 'GMM', 'pca_coef': None, 'gc_regul': 2.0, 'gc_edge_type': 'model', 'gc_use_trans': False, - 'estimate': TYPE_GMM[0], } -PATH_IMAGES = os.path.join(tl_data.update_path('images'), 'drosophila_disc') -# PATH_IMAGES = tl_data.update_path(os.path.join('images', 'langerhans_islets')) +PATH_IMAGES = os.path.join(tl_data.update_path('data_images'), 'drosophila_disc') +# PATH_IMAGES = tl_data.update_path(os.path.join('data_images', 'langerhans_islets')) PATH_RESULTS = tl_data.update_path('results', absolute=True) NAME_EXPERIMENT = 'experiment_segm-unSupervised' SEGM_PARAMS.update({ @@ -124,13 +123,13 @@ def arg_parse_params(params): :return {str: ...}: """ parser = argparse.ArgumentParser() - parser.add_argument('-list', '--path_train_list', type=str, required=False, + parser.add_argument('-l', '--path_train_list', type=str, required=False, help='path to the list of image', default=params['path_train_list']) - parser.add_argument('-imgs', '--path_predict_imgs', type=str, + parser.add_argument('-i', '--path_predict_imgs', type=str, help='path to folder & name pattern with new image', required=False, default=params['path_predict_imgs']) - parser.add_argument('-out', '--path_out', type=str, required=False, + parser.add_argument('-o', '--path_out', type=str, required=False, help='path to the output directory', default=params['path_out']) parser.add_argument('-n', '--name', type=str, required=False, @@ -148,6 +147,9 @@ def arg_parse_params(params): help='number of processes in parallel') parser.add_argument('--visual', required=False, action='store_true', help='export debug visualisations', default=False) + parser.add_argument('--unique', required=False, action='store_true', + help='each experiment has uniques stamp', + default=EACH_UNIQUE_EXPERIMENT) args = vars(parser.parse_args()) logging.info('ARG PARAMETERS: \n %s', repr(args)) for k in (k for k in args if 'path' in k): @@ -157,7 +159,7 @@ def arg_parse_params(params): assert os.path.exists(p), 'missing: (%s) "%s"' % (k, p) # args['visual'] = bool(args['visual']) # if the config path is set load the it otherwise use default - if os.path.isfile(args['path_config']): + if os.path.isfile(args.get('path_config', '')): with open(args['path_config'], 'r') as fd: config = json.load(fd) params.update(config) @@ -174,7 +176,7 @@ def load_image(path_img, img_type=TYPES_LOAD_IMAGE[0]): """ path_img = tl_data.update_path(path_img) assert os.path.isfile(path_img), 'missing: "%s"' % path_img - if img_type == '2d_gray': + if img_type == '2d_split': img, _ = tl_data.load_img_double_band_split(path_img) assert img.ndim == 2, 'image dims: %s' % repr(img.shape) # img = np.rollaxis(np.tile(img, (3, 1, 1)), 0, 3) @@ -184,8 +186,10 @@ def load_image(path_img, img_type=TYPES_LOAD_IMAGE[0]): img, _ = tl_data.load_image_2d(path_img) # if img.max() > 1: # img = (img / 255.) - elif img_type == 'segm': + elif img_type == '2d_segm': img, _ = tl_data.load_image_2d(path_img) + if img.ndim == 3: + img = img[:, :, 0] if ANNOT_RELABEL_SEQUENCE: img, _, _ = segmentation.relabel_sequential(img) else: @@ -204,15 +208,13 @@ def load_model(path_model): with open(path_model, 'rb') as f: dict_data = pickle.load(f) # npz_file = np.load(path_model) - scaler = dict_data['scaler'] - pca = dict_data['pca'] model = dict_data['model'] params = dict_data['params'] feature_names = dict_data['feature_names'] - return scaler, pca, model, params, feature_names + return model, params, feature_names -def save_model(path_model, scaler, pca, model, params=None, feature_names=None): +def save_model(path_model, model, params=None, feature_names=None): """ save model on specific destination :param str path_model: @@ -225,7 +227,7 @@ def save_model(path_model, scaler, pca, model, params=None, feature_names=None): logging.info('save (dump) model to "%s"', path_model) # np.savez_compressed(path_model, scaler=scaler, pca=pca, # model=model, params=params, feature_names=feature_names) - dict_data = dict(scaler=scaler, pca=pca, model=model, params=params, + dict_data = dict(model=model, params=params, feature_names=feature_names) with open(path_model, 'wb') as f: pickle.dump(dict_data, f) @@ -265,7 +267,7 @@ def export_visual(idx_name, img, segm, dict_debug_imgs=None, path_out=None, path_visu=None): """ export visualisations - :param (int, str) idx_name: + :param str idx_name: :param ndarray img: input image :param ndarray segm: resulting segmentation :param dict_debug_imgs: dictionary with debug images @@ -273,6 +275,9 @@ def export_visual(idx_name, img, segm, dict_debug_imgs=None, :param str path_visu: path to dir with debug images """ logging.info('export results and visualization...') + if set(np.unique(segm)) <= set([0, 1]): + segm *= 255 + path_img = os.path.join(path_out, str(idx_name) + '.png') logging.debug('exporting segmentation: %s', path_img) im_seg = Image.fromarray(segm.astype(np.uint8)) @@ -295,7 +300,8 @@ def export_visual(idx_name, img, segm, dict_debug_imgs=None, plt.close(fig) -def segment_image_independent(img_idx_path, params, path_out, path_visu=None): +def segment_image_independent(img_idx_path, params, path_out, path_visu=None, + show_debug_imgs=SHOW_DEBUG_IMAGES): """ segment image indecently (estimate model just for this model) :param (int, str) img_idx_path: @@ -312,12 +318,12 @@ def segment_image_independent(img_idx_path, params, path_out, path_visu=None): path_img = os.path.join(params['path_exp'], FOLDER_IMAGE, idx_name + '.png') tl_data.io_imsave(path_img, img.astype(np.uint8)) - dict_debug_imgs = dict() if SHOW_DEBUG_IMAGES else None + dict_debug_imgs = dict() if show_debug_imgs else None try: segm = seg_pipe.pipe_color2d_slic_features_gmm_graphcut( img, nb_classes=params['nb_classes'], clr_space=params['clr_space'], sp_size=params['slic_size'], sp_regul=params['slic_regul'], - dict_features=params['features'], proba_type=params['prob_type'], + dict_features=params['features'], estim_model=params['estim_model'], pca_coef=params['pca_coef'], gc_regul=params['gc_regul'], gc_edge_type=params['gc_edge_type'], dict_debug_imgs=dict_debug_imgs) @@ -325,14 +331,18 @@ def segment_image_independent(img_idx_path, params, path_out, path_visu=None): logging.error(traceback.format_exc()) segm = np.zeros(img.shape[:2]) + boundary_size = int(np.sqrt(np.prod(segm.shape)) * 0.01) + segm = seg_lbs.assume_bg_on_boundary(segm, bg_label=0, + boundary_size=boundary_size) + export_visual(idx_name, img, segm, dict_debug_imgs, path_out, path_visu) # gc.collect(), time.sleep(1) return idx_name, segm -def segment_image_model(imgs_idx_path, params, scaler, pca, model, path_out=None, - path_visu=None): +def segment_image_model(imgs_idx_path, params, model, path_out=None, + path_visu=None, show_debug_imgs=SHOW_DEBUG_IMAGES): """ segment image with already estimated model :param (int, str) imgs_idx_path: @@ -342,6 +352,7 @@ def segment_image_model(imgs_idx_path, params, scaler, pca, model, path_out=None :param obj model: :param str path_out: path to dir with segmentation :param str path_visu: path to dir with debug images + :param bool show_debug_imgs: whether show debug images :return (str, ndarray): """ idx, path_img = parse_imgs_idx_path(imgs_idx_path) @@ -352,11 +363,11 @@ def segment_image_model(imgs_idx_path, params, scaler, pca, model, path_out=None path_img = os.path.join(params['path_exp'], FOLDER_IMAGE, idx_name + '.png') tl_data.io_imsave(path_img, img.astype(np.uint8)) - dict_debug_imgs = dict() if SHOW_DEBUG_IMAGES else None + dict_debug_imgs = dict() if show_debug_imgs else None try: segm = seg_pipe.segment_color2d_slic_features_model_graphcut( - img, scaler, pca, model, clr_space=params['clr_space'], + img, model, clr_space=params['clr_space'], sp_size=params['slic_size'], sp_regul=params['slic_regul'], dict_features=params['features'], gc_regul=params['gc_regul'], gc_edge_type=params['gc_edge_type'], @@ -365,6 +376,10 @@ def segment_image_model(imgs_idx_path, params, scaler, pca, model, path_out=None logging.error(traceback.format_exc()) segm = np.zeros(img.shape[:2]) + boundary_size = int(np.sqrt(np.prod(segm.shape)) * 0.01) + segm = seg_lbs.assume_bg_on_boundary(segm, bg_label=0, + boundary_size=boundary_size) + export_visual(idx_name, img, segm, dict_debug_imgs, path_out, path_visu) # gc.collect(), time.sleep(1) @@ -393,21 +408,25 @@ def compare_segms_metric_ars(dict_segm_a, dict_segm_b, suffix=''): return df_ars -def experiment_single_gmm(params, paths_img, path_out, path_visu): +def experiment_single_gmm(params, paths_img, path_out, path_visu, + show_debug_imgs=SHOW_DEBUG_IMAGES): imgs_idx_path = list(zip([None] * len(paths_img), paths_img)) logging.info('Perform image segmentation as single image in each time') - dict_segms_gmm = {} - wrapper_segment = partial(segment_image_independent, params=params, - path_out=path_out, path_visu=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, imgs_idx_path, + _wrapper_segment = partial(segment_image_independent, params=params, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, imgs_idx_path, nb_jobs=params['nb_jobs'], desc='experiment single GMM') - for name, segm in iterate: - dict_segms_gmm[name] = segm + # dict_segms_gmm = {} + # for name, segm in iterate: + # dict_segms_gmm[name] = segm + dict_segms_gmm = dict(iterate) return dict_segms_gmm -def experiment_group_gmm(params, paths_img, path_out, path_visu): +def experiment_group_gmm(params, paths_img, path_out, path_visu, + show_debug_imgs=SHOW_DEBUG_IMAGES): logging.info('load all images') list_images = [load_image(path_img, params['img_type']) for path_img in paths_img] @@ -415,28 +434,45 @@ def experiment_group_gmm(params, paths_img, path_out, path_visu): logging.info('Estimate image segmentation from whole sequence of images') params['path_model'] = os.path.join(params['path_exp'], NAME_DUMP_MODEL) if os.path.isfile(params['path_model']) and not FORCE_RECOMP_DATA: - scaler, pca, model, _, _ = load_model(params['path_model']) + model, _, _ = load_model(params['path_model']) else: - scaler, pca, model = seg_pipe.estim_model_classes_group( + model, _ = seg_pipe.estim_model_classes_group( list_images, nb_classes=params['nb_classes'], clr_space=params['clr_space'], sp_size=params['slic_size'], sp_regul=params['slic_regul'], dict_features=params['features'], - proba_type=params['prob_type'], pca_coef=params['pca_coef']) - save_model(params['path_model'], scaler, pca, model) + proba_type=params['estim_model'], pca_coef=params['pca_coef']) + save_model(params['path_model'], model) logging.info('Perform image segmentation from group model') - dict_segms_group = {} - wrapper_segment = partial(segment_image_model, params=params, - scaler=scaler, pca=pca, model=model, - path_out=path_out, path_visu=path_visu) - iterate = tl_expt.WrapExecuteSequence(wrapper_segment, imgs_idx_path, + _wrapper_segment = partial(segment_image_model, params=params, model=model, + path_out=path_out, path_visu=path_visu, + show_debug_imgs=show_debug_imgs) + iterate = tl_expt.WrapExecuteSequence(_wrapper_segment, imgs_idx_path, nb_jobs=params['nb_jobs'], desc='experiment group GMM') - for name, segm in iterate: - dict_segms_group[name] = segm + # dict_segms_group = {} + # for name, segm in iterate: + # dict_segms_group[name] = segm + dict_segms_group = dict(iterate) return dict_segms_group +def load_path_images(params): + if os.path.isfile(params.get('path_train_list', '')): + logging.info('loading images from CSV: %s', params['path_train_list']) + df_paths = pd.read_csv(params['path_train_list'], index_col=0) + paths_img = df_paths['path_image'].tolist() + elif 'path_predict_imgs' in params: + logging.info('loading images from path: %s', params['path_predict_imgs']) + paths_img = glob.glob(params['path_predict_imgs']) + if len(paths_img) == 0: + logging.warning('no images found on given path...') + else: + logging.warning('no images to load!') + paths_img = [] + return paths_img + + def main(params): """ the main body containgn two approches: 1) segment each image indecently @@ -447,42 +483,43 @@ def main(params): """ logging.getLogger().setLevel(logging.DEBUG) logging.info('running...') + show_debug_imgs = params.get('visual', False) or SHOW_DEBUG_IMAGES reload_dir_config = (os.path.isfile(params['path_config']) or FORCE_RELOAD) params = tl_expt.create_experiment_folder(params, dir_name=NAME_EXPERIMENT, - stamp_unique=EACH_UNIQUE_EXPERIMENT, + stamp_unique=params.get('unique', EACH_UNIQUE_EXPERIMENT), skip_load=reload_dir_config) tl_expt.set_experiment_logger(params['path_exp']) logging.info(tl_expt.string_dict(params, desc='PARAMETERS')) tl_expt.create_subfolders(params['path_exp'], LIST_FOLDERS_BASE) - if params['visual']: + if show_debug_imgs: tl_expt.create_subfolders(params['path_exp'], LIST_FOLDERS_DEBUG) - assert os.path.isfile(params['path_train_list']), \ - 'missing %s' % params['path_train_list'] - dict_segms_gmm, dict_segms_group = {}, {} - df_paths = pd.read_csv(params['path_train_list'], index_col=0) - paths_img = df_paths['path_image'].tolist() + paths_img = load_path_images(params) + assert len(paths_img) > 0, 'missing images' - def path_expt(n): + def _path_expt(n): return os.path.join(params['path_exp'], n) # Segment as single model per image dict_segms_gmm = experiment_single_gmm(params, paths_img, - path_expt(FOLDER_SEGM_GMM), - path_expt(FOLDER_SEGM_GMM_VISU)) + _path_expt(FOLDER_SEGM_GMM), + _path_expt(FOLDER_SEGM_GMM_VISU), + show_debug_imgs=show_debug_imgs) gc.collect() time.sleep(1) + # Segment as model ober set of images dict_segms_group = experiment_group_gmm(params, paths_img, - path_expt(FOLDER_SEGM_GROUP), - path_expt(FOLDER_SEGM_GROUP_VISU)) + _path_expt(FOLDER_SEGM_GROUP), + _path_expt(FOLDER_SEGM_GROUP_VISU), + show_debug_imgs=show_debug_imgs) gc.collect() time.sleep(1) df_ars = compare_segms_metric_ars(dict_segms_gmm, dict_segms_group, suffix='_gmm-group') - df_ars.to_csv(path_expt(NAME_CSV_ARS_CORES)) + df_ars.to_csv(_path_expt(NAME_CSV_ARS_CORES)) logging.info(df_ars.describe()) logging.info('DONE') diff --git a/experiments_segmentation/sample_config.json b/experiments_segmentation/sample_config.json new file mode 100755 index 00000000..1a84c051 --- /dev/null +++ b/experiments_segmentation/sample_config.json @@ -0,0 +1,13 @@ +{ + "clr_space": "hsv", + "slic_size": 35, + "slic_regul": 0.2, + "features": {"color": ["mean", "std", "eng"]}, + "pca_coef": null, + "estim_model": "GMM", + "balance": "unique", + "classif": "RandForest", + "nb_classif_search": 250, + "gc_edge_type": "model", + "gc_regul": 3.0 +} \ No newline at end of file diff --git a/handling_annotations/run_image_color_quantization.py b/handling_annotations/run_image_color_quantization.py index f4296f91..bc8192aa 100644 --- a/handling_annotations/run_image_color_quantization.py +++ b/handling_annotations/run_image_color_quantization.py @@ -7,7 +7,7 @@ SAMPLE run: >> python run_image_color_quantization.py \ - -imgs "images/drosophila_ovary_slice/segm_rgb/*.png" \ + -imgs "data_images/drosophila_ovary_slice/segm_rgb/*.png" \ -m position Copyright (C) 2014-2016 Jiri Borovec @@ -29,7 +29,7 @@ import imsegm.utils.experiments as tl_expt import imsegm.annotation as seg_annot -PATH_IMAGES = os.path.join('images', 'drosophila_ovary_slice', 'segm_rgb', '*.png') +PATH_IMAGES = os.path.join('data_images', 'drosophila_ovary_slice', 'segm_rgb', '*.png') NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) THRESHOLD_INVALID_PIXELS = 5e-3 @@ -61,8 +61,7 @@ def parse_arg_params(): def see_images_color_info(path_images, px_thr=THRESHOLD_INVALID_PIXELS): """ look to the folder on all images and estimate most frequent colours - :param path_dir: str - :param im_pattern: str + :param [str] path_images: list of images :param px_th: float, percentage of nb clr pixels to be assumed as important :return {}: """ @@ -83,7 +82,9 @@ def perform_quantize_image(path_image, list_colors, method='color'): """ logging.debug('quantize img: "%s"', path_image) im = tl_data.io_imread(path_image) - assert im.ndim == 3, 'not valid color image of dims %s' % repr(im.shape) + if not im.ndim == 3: + logging.warning('not valid color image of dims %s', repr(im.shape)) + return im = im[:, :, :3] # im = io.imread(path_image)[:, :, :3] if method == 'color': @@ -92,6 +93,7 @@ def perform_quantize_image(path_image, list_colors, method='color'): im_q = seg_annot.quantize_image_nearest_pixel(im, list_colors) else: logging.error('not implemented method "%s"', method) + im_q = np.zeros(im.shape) path_image = os.path.splitext(path_image)[0] + '.png' tl_data.io_imsave(path_image, im_q.astype(np.uint8)) # io.imsave(path_image, im_q) @@ -118,10 +120,11 @@ def quantize_folder_images(path_images, list_colors=None, method='color', dict_colors = see_images_color_info(path_images, px_thr=px_threshold) list_colors = [c for c in dict_colors] - wrapper_quantize_img = partial(perform_quantize_image, - method=method, list_colors=list_colors) - iterate = tl_expt.WrapExecuteSequence(wrapper_quantize_img, path_imgs, - nb_jobs=nb_jobs, desc='quantize images') + _wrapper_quantize_img = partial(perform_quantize_image, + method=method, list_colors=list_colors) + iterate = tl_expt.WrapExecuteSequence(_wrapper_quantize_img, path_imgs, + nb_jobs=nb_jobs, + desc='quantize images') list(iterate) diff --git a/handling_annotations/run_image_convert_label_color.py b/handling_annotations/run_image_convert_label_color.py index 77ef24b8..1631c538 100644 --- a/handling_annotations/run_image_convert_label_color.py +++ b/handling_annotations/run_image_convert_label_color.py @@ -3,9 +3,9 @@ SAMPLE run: >> python run_image_convert_label_color.py \ - -imgs "images/drosophila_ovary_slice/segm/*.png" \ - -out images/drosophila_ovary_slice/segm_rgb \ - -clrs images/drosophila_ovary_slice/segm_rgb/dict_label-color.json + -imgs "data_images/drosophila_ovary_slice/segm/*.png" \ + -out data_images/drosophila_ovary_slice/segm_rgb \ + -clrs data_images/drosophila_ovary_slice/segm_rgb/dict_label-color.json Copyright (C) 2014-2016 Jiri Borovec """ @@ -27,8 +27,8 @@ import imsegm.utils.experiments as tl_expt import imsegm.annotation as seg_annot -PATH_INPUT = os.path.join('images', 'drosophila_ovary_slice', 'segm', '*.png') -PATH_OUTPUT = os.path.join('images', 'drosophila_ovary_slice', 'segm_rgb') +PATH_INPUT = os.path.join('data_images', 'drosophila_ovary_slice', 'segm', '*.png') +PATH_OUTPUT = os.path.join('data_images', 'drosophila_ovary_slice', 'segm_rgb') NAME_JSON_DICT = 'dictionary_label-color.json' NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) @@ -143,10 +143,11 @@ def convert_folder_images(path_images, path_out, path_json=None, nb_jobs=1): dict_colors = load_dict_colours(path_json) logging.debug('loaded dictionary %s', repr(dict_colors)) - wrapper_img_convert = partial(perform_img_convert, path_out=path_out, - dict_colors=dict_colors) - iterate = tl_expt.WrapExecuteSequence(wrapper_img_convert, path_imgs, - nb_jobs=nb_jobs, desc='convert images') + _wrapper_img_convert = partial(perform_img_convert, path_out=path_out, + dict_colors=dict_colors) + iterate = tl_expt.WrapExecuteSequence(_wrapper_img_convert, path_imgs, + nb_jobs=nb_jobs, + desc='convert images') list(iterate) diff --git a/handling_annotations/run_overlap_images_segms.py b/handling_annotations/run_overlap_images_segms.py index 9b6fd4bb..88b6a2d5 100644 --- a/handling_annotations/run_overlap_images_segms.py +++ b/handling_annotations/run_overlap_images_segms.py @@ -4,8 +4,8 @@ SAMPLE run: >> python run_overlap_images_segms.py \ - -imgs "images/drosophila_ovary_slice/image/*.jpg" \ - -segs images/drosophila_ovary_slice/segm \ + -imgs "data_images/drosophila_ovary_slice/image/*.jpg" \ + -segs data_images/drosophila_ovary_slice/segm \ -out results/overlap_ovary_segment Copyright (C) 2014-2016 Jiri Borovec @@ -48,7 +48,6 @@ def parse_arg_params(): """ create simple arg parser with default values (input, output, dataset) - :param dict_params: {str: ...} :return obj: object argparse """ parser = argparse.ArgumentParser() diff --git a/handling_annotations/run_segm_annot_inpaint.py b/handling_annotations/run_segm_annot_inpaint.py index e0337154..57183d2e 100644 --- a/handling_annotations/run_segm_annot_inpaint.py +++ b/handling_annotations/run_segm_annot_inpaint.py @@ -3,7 +3,7 @@ SAMPLE run: >> python run_image_annot_inpaint.py \ - -imgs "images/drosophila_ovary_slice/segm/*.png" \ + -imgs "data_images/drosophila_ovary_slice/segm/*.png" \ --label 4 --nb_jobs 2 Copyright (C) 2014-2016 Jiri Borovec @@ -25,7 +25,7 @@ import imsegm.utils.experiments as tl_expt import imsegm.annotation as seg_annot -PATH_IMAGES = os.path.join('images', 'drosophila_ovary_slice', 'segm', '*.png') +PATH_IMAGES = os.path.join('data_images', 'drosophila_ovary_slice', 'segm', '*.png') NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) @@ -74,18 +74,18 @@ def perform_img_inpaint(path_img, labels): def quantize_folder_images(path_images, label, nb_jobs=1): """ perform single or multi thread image quantisation - :param path_dir: str, input directory - :param im_pattern: str, image pattern for loading - :param nb_jobs: int + :param [str] path_images: list of image paths + :param int nb_jobs: """ assert os.path.isdir(os.path.dirname(path_images)), \ 'input folder does not exist: %s' % os.path.dirname(path_images) path_imgs = sorted(glob.glob(path_images)) logging.info('found %i images', len(path_imgs)) - wrapper_img_inpaint = partial(perform_img_inpaint, labels=label) - iterate = tl_expt.WrapExecuteSequence(wrapper_img_inpaint, path_imgs, - nb_jobs=nb_jobs, desc='quantise images') + _wrapper_img_inpaint = partial(perform_img_inpaint, labels=label) + iterate = tl_expt.WrapExecuteSequence(_wrapper_img_inpaint, path_imgs, + nb_jobs=nb_jobs, + desc='quantise images') list(iterate) diff --git a/handling_annotations/run_segm_annot_relabel.py b/handling_annotations/run_segm_annot_relabel.py index c702ba44..78888ea5 100644 --- a/handling_annotations/run_segm_annot_relabel.py +++ b/handling_annotations/run_segm_annot_relabel.py @@ -3,7 +3,7 @@ SAMPLE run: >> python run_segm_annot_relabel.py \ - -imgs "images/drosophila_ovary_slice/center_levels/*.png" \ + -imgs "data_images/drosophila_ovary_slice/center_levels/*.png" \ -out results/relabel_center_levels \ --label_old 2 3 --label_new 1 1 --nb_jobs 2 @@ -25,7 +25,7 @@ import imsegm.utils.data_io as tl_data import imsegm.utils.experiments as tl_expt -PATH_IMAGES = os.path.join('images', 'drosophila_ovary_slice', 'center_levels', '*.png') +PATH_IMAGES = os.path.join('data_images', 'drosophila_ovary_slice', 'center_levels', '*.png') PATH_OUTPUT = os.path.join('results', 'relabel_center_levels') NB_THREADS = max(1, int(mproc.cpu_count() * 0.9)) @@ -106,10 +106,11 @@ def relabel_folder_images(path_images, path_out, labels_old, labels_new, path_imgs = sorted(glob.glob(path_images)) logging.info('found %i images', len(path_imgs)) - wrapper_img_relabel = partial(perform_image_relabel, path_out=path_out, - labels_old=labels_old, labels_new=labels_new) - iterate = tl_expt.WrapExecuteSequence(wrapper_img_relabel, path_imgs, - nb_jobs=nb_jobs, desc='relabel images') + _wrapper_img_relabel = partial(perform_image_relabel, path_out=path_out, + labels_old=labels_old, labels_new=labels_new) + iterate = tl_expt.WrapExecuteSequence(_wrapper_img_relabel, path_imgs, + nb_jobs=nb_jobs, + desc='relabel images') list(iterate) diff --git a/images/drosophila_disc/list_imaginal-disks.csv b/images/drosophila_disc/list_imaginal-disks.csv deleted file mode 100644 index 55eb5c45..00000000 --- a/images/drosophila_disc/list_imaginal-disks.csv +++ /dev/null @@ -1,11 +0,0 @@ -,path_image,path_annot -1,images/drosophila_disc/image/img_5.jpg,images/drosophila_disc/annot/img_5.png -2,images/drosophila_disc/image/img_6.jpg,images/drosophila_disc/annot/img_6.png -3,images/drosophila_disc/image/img_12.jpg,images/drosophila_disc/annot/img_12.png -4,images/drosophila_disc/image/img_14.jpg,images/drosophila_disc/annot/img_14.png -5,images/drosophila_disc/image/img_15.jpg,images/drosophila_disc/annot/img_15.png -6,images/drosophila_disc/image/img_19.jpg,images/drosophila_disc/annot/img_19.png -7,images/drosophila_disc/image/img_20.jpg,images/drosophila_disc/annot/img_20.png -8,images/drosophila_disc/image/img_24.jpg,images/drosophila_disc/annot/img_24.png -9,images/drosophila_disc/image/img_26.jpg,images/drosophila_disc/annot/img_26.png -10,images/drosophila_disc/image/img_43.jpg,images/drosophila_disc/annot/img_43.png diff --git a/images/drosophila_disc/list_imaginal-disks_short.csv b/images/drosophila_disc/list_imaginal-disks_short.csv deleted file mode 100644 index 19f60844..00000000 --- a/images/drosophila_disc/list_imaginal-disks_short.csv +++ /dev/null @@ -1,3 +0,0 @@ -,path_image,path_annot -1,images/drosophila_disc/image/img_6.jpg,images/drosophila_disc/annot/img_6.png -2,images/drosophila_disc/image/img_43.jpg,images/drosophila_disc/annot/img_43.png diff --git a/images/drosophila_ovary_slice/list_imgs-annot-struct.csv b/images/drosophila_ovary_slice/list_imgs-annot-struct.csv deleted file mode 100644 index b621b29b..00000000 --- a/images/drosophila_ovary_slice/list_imgs-annot-struct.csv +++ /dev/null @@ -1,11 +0,0 @@ -,path_image,path_annot -1,images/drosophila_ovary_slice/image/insitu4174.jpg,images/drosophila_ovary_slice/annot_struct/insitu4174.png -2,images/drosophila_ovary_slice/image/insitu4358.jpg,images/drosophila_ovary_slice/annot_struct/insitu4358.png -3,images/drosophila_ovary_slice/image/insitu7331.jpg,images/drosophila_ovary_slice/annot_struct/insitu7331.png -4,images/drosophila_ovary_slice/image/insitu7544.jpg,images/drosophila_ovary_slice/annot_struct/insitu7544.png -5,images/drosophila_ovary_slice/image/insitu7545.jpg,images/drosophila_ovary_slice/annot_struct/insitu7545.png -6,images/drosophila_ovary_slice/image/insitu4174.tif,images/drosophila_ovary_slice/annot_struct/insitu4174.png -7,images/drosophila_ovary_slice/image/insitu4358.tif,images/drosophila_ovary_slice/annot_struct/insitu4358.png -8,images/drosophila_ovary_slice/image/insitu7331.tif,images/drosophila_ovary_slice/annot_struct/insitu7331.png -9,images/drosophila_ovary_slice/image/insitu7544.tif,images/drosophila_ovary_slice/annot_struct/insitu7544.png -10,images/drosophila_ovary_slice/image/insitu7545.tif,images/drosophila_ovary_slice/annot_struct/insitu7545.png diff --git a/images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv b/images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv deleted file mode 100644 index d14ddd56..00000000 --- a/images/drosophila_ovary_slice/list_imgs-annot-struct_short.csv +++ /dev/null @@ -1,3 +0,0 @@ -,path_image,path_annot -1,images/drosophila_ovary_slice/image/insitu4174.jpg,images/drosophila_ovary_slice/annot_struct/insitu4174.png -2,images/drosophila_ovary_slice/image/insitu7545.tif,images/drosophila_ovary_slice/annot_struct/insitu7545.png diff --git a/images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv b/images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv deleted file mode 100644 index 4e0818fc..00000000 --- a/images/drosophila_ovary_slice/list_imgs-segm-center-levels.csv +++ /dev/null @@ -1,6 +0,0 @@ -,path_image,path_centers,path_annot,path_segm -1,images/drosophila_ovary_slice/image/insitu4174.tif,images/drosophila_ovary_slice/center_levels/insitu4174.png,images/drosophila_ovary_slice/annot_eggs/insitu4174.png,images/drosophila_ovary_slice/segm/insitu4174.png -2,images/drosophila_ovary_slice/image/insitu4358.tif,images/drosophila_ovary_slice/center_levels/insitu4358.png,images/drosophila_ovary_slice/annot_eggs/insitu4358.png,images/drosophila_ovary_slice/segm/insitu4358.png -3,images/drosophila_ovary_slice/image/insitu7331.tif,images/drosophila_ovary_slice/center_levels/insitu7331.png,images/drosophila_ovary_slice/annot_eggs/insitu7331.png,images/drosophila_ovary_slice/segm/insitu7331.png -4,images/drosophila_ovary_slice/image/insitu7544.tif,images/drosophila_ovary_slice/center_levels/insitu7544.png,images/drosophila_ovary_slice/annot_eggs/insitu7544.png,images/drosophila_ovary_slice/segm/insitu7544.png -5,images/drosophila_ovary_slice/image/insitu7545.tif,images/drosophila_ovary_slice/center_levels/insitu7545.png,images/drosophila_ovary_slice/annot_eggs/insitu7545.png,images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv b/images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv deleted file mode 100644 index 40cae6a5..00000000 --- a/images/drosophila_ovary_slice/list_imgs-segm-center-levels_short.csv +++ /dev/null @@ -1,3 +0,0 @@ -,path_image,path_centers,path_annot,path_segm -1,images/drosophila_ovary_slice/image/insitu4358.jpg,images/drosophila_ovary_slice/center_levels/insitu4358.png,images/drosophila_ovary_slice/annot_eggs/insitu4358.png,images/drosophila_ovary_slice/segm/insitu4358.png -2,images/drosophila_ovary_slice/image/insitu7545.tif,images/drosophila_ovary_slice/center_levels/insitu7545.png,images/drosophila_ovary_slice/annot_eggs/insitu7545.png,images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/drosophila_ovary_slice/list_imgs-segm-center-points.csv b/images/drosophila_ovary_slice/list_imgs-segm-center-points.csv deleted file mode 100644 index 109e15a2..00000000 --- a/images/drosophila_ovary_slice/list_imgs-segm-center-points.csv +++ /dev/null @@ -1,6 +0,0 @@ -,path_image,path_centers,path_annot,path_segm -1,images/drosophila_ovary_slice/image/insitu4174.jpg,images/drosophila_ovary_slice/center_levels/insitu4174.csv,images/drosophila_ovary_slice/annot_eggs/insitu4174.png,images/drosophila_ovary_slice/segm/insitu4174.png -2,images/drosophila_ovary_slice/image/insitu4358.jpg,images/drosophila_ovary_slice/center_levels/insitu4358.csv,images/drosophila_ovary_slice/annot_eggs/insitu4358.png,images/drosophila_ovary_slice/segm/insitu4358.png -3,images/drosophila_ovary_slice/image/insitu7331.jpg,images/drosophila_ovary_slice/center_levels/insitu7331.csv,images/drosophila_ovary_slice/annot_eggs/insitu7331.png,images/drosophila_ovary_slice/segm/insitu7331.png -4,images/drosophila_ovary_slice/image/insitu7544.jpg,images/drosophila_ovary_slice/center_levels/insitu7544.csv,images/drosophila_ovary_slice/annot_eggs/insitu7544.png,images/drosophila_ovary_slice/segm/insitu7544.png -5,images/drosophila_ovary_slice/image/insitu7545.jpg,images/drosophila_ovary_slice/center_levels/insitu7545.csv,images/drosophila_ovary_slice/annot_eggs/insitu7545.png,images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv b/images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv deleted file mode 100644 index 8fe908d8..00000000 --- a/images/drosophila_ovary_slice/list_imgs-segm-center-points_short.csv +++ /dev/null @@ -1,3 +0,0 @@ -,path_image,path_centers,path_annot,path_segm -1,images/drosophila_ovary_slice/image/insitu4358.jpg,images/drosophila_ovary_slice/center_levels/insitu4358.csv,images/drosophila_ovary_slice/annot_eggs/insitu4358.png,images/drosophila_ovary_slice/segm/insitu4358.png -2,images/drosophila_ovary_slice/image/insitu7545.tif,images/drosophila_ovary_slice/center_levels/insitu7545.csv,images/drosophila_ovary_slice/annot_eggs/insitu7545.png,images/drosophila_ovary_slice/segm/insitu7545.png diff --git a/images/langerhans_islets/list_lang-isl_imgs-annot.csv b/images/langerhans_islets/list_lang-isl_imgs-annot.csv deleted file mode 100644 index f100e541..00000000 --- a/images/langerhans_islets/list_lang-isl_imgs-annot.csv +++ /dev/null @@ -1,4 +0,0 @@ -,path_image,path_annot -1,images/langerhans_islets/image/gtExoIsl_13.jpg,images/langerhans_islets/annot/gtExoIsl_13.png -2,images/langerhans_islets/image/gtExoIsl_21.jpg,images/langerhans_islets/annot/gtExoIsl_21.png -3,images/langerhans_islets/image/gtExoIsl_27.jpg,images/langerhans_islets/annot/gtExoIsl_27.png diff --git a/imsegm/annotation.py b/imsegm/annotation.py index 589d1375..eaed9755 100755 --- a/imsegm/annotation.py +++ b/imsegm/annotation.py @@ -1,17 +1,17 @@ """ Framework for handling annotations -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ -import os, sys +import os import logging import tqdm import numpy as np import pandas as pd from PIL import Image -from skimage import io +# from skimage import io from scipy import interpolate # sys.path += [os.path.abspath('.'), os.path.abspath('..')] # Add path to root @@ -321,7 +321,7 @@ def load_info_group_by_slices(path_txt, stages, pos_columns=COLUMNS_POSITION, :param [str] pos_columns: :return: DF - >>> path_txt = os.path.join(tl_data.update_path('images'), + >>> path_txt = os.path.join(tl_data.update_path('data_images'), ... 'drosophila_ovary_slice', 'info_ovary_images.txt') >>> load_info_group_by_slices(path_txt, [4]) # doctest: +NORMALIZE_WHITESPACE ant_x ant_y lat_x lat_y post_x post_y diff --git a/imsegm/classification.py b/imsegm/classification.py index c70f8a12..f446ea01 100755 --- a/imsegm/classification.py +++ b/imsegm/classification.py @@ -2,7 +2,7 @@ Supporting file to create and set parameters for scikit-learn classifiers and some prepossessing functions that support classification -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os @@ -11,8 +11,7 @@ import random import collections import traceback -# import gc -# import time +import itertools # import multiprocessing as mproc import numpy as np @@ -261,8 +260,8 @@ def compute_classif_metrics(y_true, y_pred, metric_averages=METRIC_AVERAGES): :return {str: float}: >>> np.random.seed(0) - >>> y_true = np.random.randint(0, 3, 25) - >>> y_pred = np.random.randint(0, 2, 25) + >>> y_true = np.random.randint(0, 3, 25) * 2 + >>> y_pred = np.random.randint(0, 2, 25) * 2 >>> d = compute_classif_metrics(y_true, y_true) >>> d['accuracy'] # doctest: +ELLIPSIS 1.0 @@ -273,6 +272,9 @@ def compute_classif_metrics(y_true, y_pred, metric_averages=METRIC_AVERAGES): 0.32... >>> d['confusion'] [[3, 7, 0], [5, 5, 0], [1, 4, 0]] + >>> d = compute_classif_metrics(y_pred, y_pred) + >>> d['accuracy'] # doctest: +ELLIPSIS + 1.0 """ y_true = np.array(y_true) y_pred = np.array(y_pred) @@ -282,18 +284,13 @@ def compute_classif_metrics(y_true, y_pred, metric_averages=METRIC_AVERAGES): logging.debug('unique lbs true: %s, predict %s', repr(np.unique(y_true)), repr(np.unique(y_pred))) - uq_y_true = np.unique(y_true) - # in case the are just two classes relabel them as [0, 1] only - # solving sklearn error: + uq_labels = np.unique(np.hstack((y_true, y_pred))) + # in case there are just two classes, relabel them as [0, 1], sklearn error: # "ValueError: pos_label=1 is not a valid label: array([ 0, 255])" - if np.array_equal(sorted(uq_y_true), sorted(np.unique(y_pred))) \ - and len(uq_y_true) <= 2: - logging.debug('relabeling original %s to [0, 1]', repr(uq_y_true)) - lut = np.zeros(uq_y_true.max() + 1) - if len(uq_y_true) == 2: - lut[uq_y_true[1]] = 1 - y_true = lut[y_true] - y_pred = lut[y_pred] + if len(uq_labels) <= 2: + # NOTE, this is temporal just for purposes of computing statistic + y_true = relabel_sequential(y_true, uq_labels) + y_pred = relabel_sequential(y_pred, uq_labels) # http://scikit-learn.org/stable/modules/generated/sklearn.metrics.precision_recall_fscore_support.html EVAL_STR = 'EVALUATION: {:<2} PRE: {:.3f} REC: {:.3f} F1: {:.3f} S: {:>6}' @@ -538,6 +535,27 @@ def export_results_clf_search(path_out, clf_name, clf_search): f.write('\n'.join(rows)) +def relabel_sequential(labels, uq_lbs=None): + """ relabel sequantila vetor staring from 0 + + :param [] labels: + :return []: + + >>> relabel_sequential([0, 0, 0, 5, 5, 5, 0, 5]) + [0, 0, 0, 1, 1, 1, 0, 1] + """ + labels = np.asarray(labels) + if uq_lbs is None: + uq_lbs = np.unique(labels) + lut = np.zeros(np.max(uq_lbs) + 1) + logging.debug('relabeling original %s to %s', repr(uq_lbs), + range(len(uq_lbs))) + for i, lb in enumerate(uq_lbs): + lut[lb] = i + labesl_new = lut[labels].astype(labels.dtype).tolist() + return labesl_new + + def create_classif_train_export(clf_name, features, labels, cross_val=10, nb_search_iter=1, search_type='random', nb_jobs=NB_JOBS_CLASSIF_SEARCH, @@ -600,7 +618,9 @@ def create_classif_train_export(clf_name, features, labels, cross_val=10, clf_search = create_classif_search(clf_name, clf_pipeline, nb_labels, search_type, cross_val, nb_search_iter, nb_jobs) - clf_search.fit(features, labels) + + # NOTE, this is temporal just for purposes of computing statistic + clf_search.fit(features, relabel_sequential(labels)) logging.info('Best score: %s', repr(clf_search.best_score_)) clf_pipeline = clf_search.best_estimator_ @@ -609,9 +629,9 @@ def create_classif_train_export(clf_name, features, labels, cross_val=10, logging.info('Best parameters set: \n %s', repr(best_parameters)) if path_out is not None and os.path.isdir(path_out): export_results_clf_search(path_out, clf_name, clf_search) - else: - # while there is no search, just train the best one - clf_pipeline.fit(features, labels) + + # while there is no search, just train the best one + clf_pipeline.fit(features, labels) if path_out is not None and os.path.isdir(path_out): path_classif = save_classifier(path_out, clf_pipeline, clf_name, @@ -673,7 +693,11 @@ def eval_classif_cross_val_scores(clf_name, classif, features, labels, df_scoring = pd.DataFrame() for scoring in scorings: try: + uq_labels = np.unique(labels) # ValueError: pos_label=1 is not a valid label: array([0, 2]) + if len(uq_labels) <= 2: + # NOTE, this is temporal just for purposes of computing stat. + labels = relabel_sequential(labels, uq_labels) scores = model_selection.cross_val_score(classif, features, labels, cv=cross_val, scoring=scoring) @@ -682,7 +706,6 @@ def eval_classif_cross_val_scores(clf_name, classif, features, labels, df_scoring[scoring] = scores except Exception: logging.error(traceback.format_exc()) - df_stat = df_scoring.describe() if path_out is not None: assert os.path.exists(path_out), 'missing: "%s"' % path_out @@ -690,10 +713,16 @@ def eval_classif_cross_val_scores(clf_name, classif, features, labels, path_csv = os.path.join(path_out, name_csv) df_scoring.to_csv(path_csv) - name_csv = NAME_CSV_CLASSIF_CV_SCORES.format(clf_name, 'statistic') - path_csv = os.path.join(path_out, name_csv) - df_stat.to_csv(path_csv) - logging.info('cross_val scores: \n %s', repr(df_stat)) + if len(df_scoring) > 1: + df_stat = df_scoring.describe() + logging.info('cross_val scores: \n %s', repr(df_stat)) + if path_out is not None: + assert os.path.exists(path_out), 'missing: "%s"' % path_out + name_csv = NAME_CSV_CLASSIF_CV_SCORES.format(clf_name, 'statistic') + path_csv = os.path.join(path_out, name_csv) + df_stat.to_csv(path_csv) + else: + logging.warning('no statistic collected') return df_scoring @@ -709,6 +738,7 @@ def eval_classif_cross_val_roc(clf_name, classif, features, labels, :param [int] labels: annotation for samples :param object cross_val: :param str path_out: path for exporting statistic + :param int nb_thr: number of thresholds :return: >>> np.random.seed(0) @@ -1136,7 +1166,7 @@ class HoldOut: Parameters ---------- - n : total number of samples + nb : total number of samples hold_idx : int index where the test starts random_state : Seed for the random number generator. @@ -1328,12 +1358,10 @@ def __iter__(self): """ for i in range(0, len(self.set_sizes), self.nb_hold_out): test = self.sets_order[i:i + self.nb_hold_out] - inds_train, inds_test = [], [] - for i in self.sets_order: - if i in test: - inds_test += self.set_indexes[i] - else: - inds_train += self.set_indexes[i] + inds_train = list(itertools.chain.from_iterable( + self.set_indexes[i] for i in self.sets_order if i not in test)) + inds_test = list(itertools.chain.from_iterable( + self.set_indexes[i] for i in self.sets_order if i in test)) yield inds_train, inds_test def __len__(self): diff --git a/imsegm/descriptors.py b/imsegm/descriptors.py index 9220d676..f175744b 100755 --- a/imsegm/descriptors.py +++ b/imsegm/descriptors.py @@ -5,7 +5,7 @@ * Ray features * label histogram -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging @@ -28,13 +28,14 @@ logging.warning('descriptors: using pure python libraries') USE_CYTHON = False +NAMES_FEATURE_FLAGS = ('mean', 'std', 'energy', 'median', 'meanGrad') DEFAULT_FILTERS_SIGMAS = (np.sqrt(2), 2, 2 * np.sqrt(2), 4) SHORT_FILTERS_SIGMAS = (np.sqrt(2), 2, 4) -FEATURES_SET_ALL = {'color': ('mean', 'std', 'eng', 'median'), - 'tLM': ('mean', 'std', 'eng', 'mG')} -FEATURES_SET_COLOR = {'color': ('mean', 'std', 'eng')} -FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'eng')} -FEATURES_SET_TEXTURE_SHORT = {'tLM_s': ('mean', 'std', 'eng')} +FEATURES_SET_ALL = {'color': ('mean', 'std', 'energy', 'median', 'meanGrad'), + 'tLM': ('mean', 'std', 'energy', 'median', 'meanGrad')} +FEATURES_SET_COLOR = {'color': ('mean', 'std', 'energy')} +FEATURES_SET_TEXTURE = {'tLM': ('mean', 'std', 'energy')} +FEATURES_SET_TEXTURE_SHORT = {'tLM_s': ('mean', 'std', 'energy')} HIST_CIRCLE_DIAGONALS = (10, 20, 30, 40, 50) # maxila reposnse is bounded by fix number to preven overflowing MAX_SIGNAL_RESPONSE = 1.e6 @@ -134,6 +135,21 @@ def _check_color_image(image): return True +def _check_unrecognised_feature_group(dict_feature_flags): + unknown = [k for k in dict_feature_flags + if k not in ('color', 'tLM', 'tLM_s')] + if len(unknown) > 0: + logging.warning('unrecognised following feature groups: %s', + repr(unknown)) + + +def _check_unrecognised_feature_names(list_feature_flags): + unknown = [k for k in list_feature_flags if k not in NAMES_FEATURE_FLAGS] + if len(unknown) > 0: + logging.warning('unrecognised following feature names: %s', + repr(unknown)) + + def cython_img2d_color_mean(im, seg): """ wrapper for fast implementation of colour features @@ -609,8 +625,7 @@ def numpy_img3d_gray_median(im, seg): def compute_image3d_gray_statistic(image, segm, - list_feature_flags=('mean', 'std', 'eng', - 'median', 'mG'), + list_feature_flags=NAMES_FEATURE_FLAGS, ch_name='gray'): """ compute complete descriptors / statistic on gray (3D) images @@ -669,7 +684,7 @@ def compute_image3d_gray_statistic(image, segm, features.append(std) names += ['%s_std' % ch_name] # ENERGY - if 'eng' in list_feature_flags: + if 'energy' in list_feature_flags: if USE_CYTHON: energy = cython_img3d_gray_energy(image, segm) else: @@ -682,7 +697,7 @@ def compute_image3d_gray_statistic(image, segm, features.append(median) names += ['%s_median' % ch_name] # mean Gradient - if 'mG' in list_feature_flags: + if 'meanGrad' in list_feature_flags: grad_matrix = np.zeros_like(image) for i in range(image.shape[0]): grad_matrix[i, :, :] = np.sum(np.gradient(image[i]), axis=0) @@ -692,6 +707,7 @@ def compute_image3d_gray_statistic(image, segm, grad = numpy_img3d_gray_mean(grad_matrix, segm) features.append(grad) names += ['%s_meanGrad' % ch_name] + _check_unrecognised_feature_names(list_feature_flags) features = np.concatenate(tuple([fts] for fts in features), axis=0) features = np.nan_to_num(features).T # normalise +/- zeros as set all as positive @@ -702,8 +718,7 @@ def compute_image3d_gray_statistic(image, segm, def compute_image2d_color_statistic(image, segm, - list_feature_flags=('mean', 'std', 'eng', - 'median'), + list_feature_flags=NAMES_FEATURE_FLAGS, ch_name='color'): """ compute complete descriptors / statistic on color (2D) images @@ -718,18 +733,17 @@ def compute_image2d_color_statistic(image, segm, >>> image[:, 4:9, 2] = 2 >>> segm = np.array([[0, 0, 0, 0, 0, 1, 1, 1, 1, 1], ... [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]]) - >>> features, names = compute_image2d_color_statistic(image, segm, - ... ['mean', 'std', 'eng', 'median']) + >>> features, names = compute_image2d_color_statistic(image, segm) >>> names # doctest: +NORMALIZE_WHITESPACE ['color-ch1_mean', 'color-ch2_mean', 'color-ch3_mean', 'color-ch1_std', 'color-ch2_std', 'color-ch3_std', 'color-ch1_energy', 'color-ch2_energy', 'color-ch3_energy', - 'color-ch1_median', 'color-ch2_median', 'color-ch3_median'] + 'color-ch1_median', 'color-ch2_median', 'color-ch3_median', + 'color-ch1_meanGrad', 'color-ch2_meanGrad', 'color-ch3_meanGrad'] >>> features.shape - (2, 12) + (2, 15) >>> np.round(features, 1).tolist() # doctest: +NORMALIZE_WHITESPACE - [[0.6, 1.2, 0.4, 0.5, 1.5, 0.8, 0.6, 3.6, 0.8, 1.0, 0.0, 0.0], - [0.2, 1.2, 1.6, 0.4, 1.5, 0.8, 0.2, 3.6, 3.2, 0.0, 0.0, 2.0]] + [[0.6, 1.2, 0.4, 0.5, 1.5, 0.8, 0.6, 3.6, 0.8, 1.0, 0.0, 0.0, 0.2, 0.6, 0.4], [0.2, 1.2, 1.6, 0.4, 1.5, 0.8, 0.2, 3.6, 3.2, 0.0, 0.0, 2.0, -0.2, -0.6, -0.6]] """ _check_color_image(image) _check_color_image_segm(image, segm) @@ -757,7 +771,7 @@ def compute_image2d_color_statistic(image, segm, features = np.hstack((features, std)) names += ['%s_std' % n for n in ch_names] # ENERGY - if 'eng' in list_feature_flags: + if 'energy' in list_feature_flags: if USE_CYTHON: energy = cython_img2d_color_energy(image, segm) else: @@ -770,6 +784,18 @@ def compute_image2d_color_statistic(image, segm, features = np.hstack((features, median)) names += ['%s_median' % n for n in ch_names] # mean Gradient + if 'meanGrad' in list_feature_flags: + grad_matrix = np.zeros_like(image) + for i in range(image.shape[-1]): + grad_matrix[:, :, i] = np.sum(np.gradient(image[:, :, i]), axis=0) + if USE_CYTHON: + grad = cython_img2d_color_mean(grad_matrix, segm) + else: + grad = numpy_img2d_color_mean(grad_matrix, segm) + features = np.hstack((features, grad)) + names += ['%s_meanGrad' % n for n in ch_names] + _check_unrecognised_feature_names(list_feature_flags) + # mean Gradient # G = np.zeros_like(image) # for i in range(image.shape[0]): # G[i,:,:] = np.sum(np.gradient(image[i]), axis=0) @@ -1053,9 +1079,9 @@ def compute_selected_features_gray3d(img, segments, >>> names # doctest: +NORMALIZE_WHITESPACE ['gray_mean', 'gray_std', 'gray_median'] >>> _ = compute_selected_features_gray3d(img, slic, - ... {'tLM': ['median', 'std', 'eng']}) + ... {'tLM': ['median', 'std', 'energy']}) >>> fts, names = compute_selected_features_gray3d(img, slic, - ... {'tLM_s': ['mean', 'std', 'eng']}) + ... {'tLM_s': ['mean', 'std', 'energy']}) >>> fts.shape (4, 45) >>> names # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE @@ -1082,6 +1108,8 @@ def compute_selected_features_gray3d(img, segments, 'short') features.append(fts) names += n + _check_unrecognised_feature_group(dict_feature_flags) + if len(features) == 0: logging.error('not supported features: %s', repr(dict_feature_flags)) features = np.concatenate(tuple(features), axis=1) @@ -1095,7 +1123,7 @@ def compute_selected_features_gray3d(img, segments, def compute_selected_features_gray2d(img, segments, dict_features_flags=FEATURES_SET_ALL): - """ + """ compute selected features for gray image 2D :param ndarray img: :param ndarray segments: @@ -1113,14 +1141,14 @@ def compute_selected_features_gray2d(img, segments, array([[ 0.9 , 1.136, 0.5 ], [ 0.7 , 1.187, 0. ]]) >>> _ = compute_selected_features_gray2d(image, segm, - ... {'tLM': ['mean', 'std', 'median']}) + ... {'tLM': ['mean', 'std', 'median']}) >>> features, names = compute_selected_features_gray2d(image, segm, - ... {'tLM_s': ['mean', 'std', 'eng']}) + ... {'tLM_s': ['mean', 'std', 'energy']}) >>> features.shape (2, 45) >>> features, names = compute_selected_features_gray2d(image, segm) >>> features.shape - (2, 84) + (2, 105) """ _check_gray_image_segm(img, segments) @@ -1134,7 +1162,7 @@ def compute_selected_features_gray2d(img, segments, def compute_selected_features_color2d(img, segments, dict_feature_flags=FEATURES_SET_ALL): - """ compute selected features color2d + """ compute selected features color image 2D :param ndarray img: :param ndarray segments: @@ -1153,14 +1181,14 @@ def compute_selected_features_color2d(img, segments, array([[ 0.6 , 1.2 , 0.4 , 0.49, 1.47, 0.8 , 1. , 0. , 0. ], [ 0.2 , 1.2 , 1.6 , 0.4 , 1.47, 0.8 , 0. , 0. , 2. ]]) >>> _ = compute_selected_features_color2d(image, segm, - ... {'tLM': ['mean', 'std', 'eng']}) + ... {'tLM': ['mean', 'std', 'energy']}) >>> features, names = compute_selected_features_color2d(image, segm, - ... {'tLM_s': ['mean', 'std', 'eng']}) + ... {'tLM_s': ['mean', 'std', 'energy']}) >>> features.shape (2, 135) >>> features, names = compute_selected_features_color2d(image, segm) >>> features.shape - (2, 192) + (2, 315) """ _check_color_image(img) features = np.empty((np.max(segments) + 1, 0)) @@ -1181,6 +1209,8 @@ def compute_selected_features_color2d(img, segments, 'short') features = np.concatenate((features, fts), axis=1) names += n + _check_unrecognised_feature_group(dict_feature_flags) + features = np.nan_to_num(features) # normalise +/- zeros as set all as positive features[features == 0] = 0 @@ -1636,7 +1666,7 @@ def compute_ray_features_positions(segm, list_positions, angle_step=5., if isinstance(segm_open, int): seg_binary = morphology.opening(seg_binary, morphology.disk(segm_open)) - pos_rays, pos_shift = list(), list() + pos_rays, pos_shift, ray_dist = [], [], [] for pos in list_positions: # logging.debug('position %s', repr(pos)) ray_dist = compute_ray_features_segm_2d(seg_binary, pos, angle_step, @@ -1701,18 +1731,18 @@ def interpolate_ray_dist(ray_dists, order='spline'): y_train_ext) ray_dists[missing] = uinterp_us(x_space[missing]) elif order == 'cos': - def fn_cos(x, t): + def _fn_cos(x, t): return x[0] + x[1] * np.sin(x[2] + x[3] * t) - def fn_cos_residual(x, t, y): - return fn_cos(x, t) - y + def _fn_cos_residual(x, t, y): + return _fn_cos(x, t) - y x0 = np.array([np.mean(y_train), (y_train.max() - y_train.min()) / 2., 0, len(x_space) / np.pi]) - lsm_res = optimize.least_squares(fn_cos_residual, x0, gtol=1e-1, + lsm_res = optimize.least_squares(_fn_cos_residual, x0, gtol=1e-1, # loss='soft_l1', f_scale=0.1, args=(x_train, y_train)) - ray_dists[missing] = fn_cos(lsm_res.x, x_space[missing]) + ray_dists[missing] = _fn_cos(lsm_res.x, x_space[missing]) return ray_dists diff --git a/imsegm/ellipse_fitting.py b/imsegm/ellipse_fitting.py index d9381a79..4ecaf7c8 100755 --- a/imsegm/ellipse_fitting.py +++ b/imsegm/ellipse_fitting.py @@ -1,7 +1,7 @@ """ Framework for ellipse fitting -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import numpy as np @@ -42,11 +42,6 @@ class EllipseModelSegm(sk_fit.EllipseModel): xc, yc, a, b, theta - Attributes - ---------- - params : tuple - Ellipse model parameters `xc`, `yc`, `a`, `b`, `theta`. - Example ------- >>> params = 20, 30, 12, 16, np.deg2rad(30) @@ -157,7 +152,7 @@ def ransac_segm(points, model_class, points_all, weights, labels, table_prob, ``is_model_valid(model, *random_data)`` and ``is_data_valid(*random_data)`` must all take each points array as separate arguments. - model_class : object + model_class : class Object with the following object methods: * ``success = estimate(*points)`` @@ -171,11 +166,6 @@ def ransac_segm(points, model_class, points_all, weights, labels, table_prob, Maximum distance for a points point to be classified as an inlier. max_trials : int, optional Maximum number of iterations for random sample selection. - stop_sample_num : int, optional - Stop iteration if at least this number of inliers are found. - stop_residuals_sum : float, optional - Stop iteration if sum of residuals is less than or equal to this - threshold. Returns diff --git a/imsegm/features_cython.pyx b/imsegm/features_cython.pyx index 7ac6b34b..30ff6b76 100755 --- a/imsegm/features_cython.pyx +++ b/imsegm/features_cython.pyx @@ -1,6 +1,6 @@ """ -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ cimport cython diff --git a/imsegm/graph_cuts.py b/imsegm/graph_cuts.py index 69bfdb45..12316141 100755 --- a/imsegm/graph_cuts.py +++ b/imsegm/graph_cuts.py @@ -1,14 +1,16 @@ """ Framework for GraphCut -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging import numpy as np from gco import cut_general_graph -from sklearn import metrics, mixture, cluster, preprocessing +from skimage import filters +from sklearn import metrics, preprocessing +from sklearn import pipeline, cluster, mixture, decomposition import imsegm.utils.drawing as tl_visu import imsegm.superpixels as seg_spx @@ -29,10 +31,10 @@ def estim_gmm_params(features, prob): >>> np.random.seed(0) >>> prob = np.array([[1, 0]] * 30 + [[0, 1]] * 40) >>> fts = prob + np.random.random(prob.shape) - >>> gmm = estim_gmm_params(fts, prob) - >>> gmm['weights'] + >>> mm = estim_gmm_params(fts, prob) + >>> mm['weights'] [0.42857142857142855, 0.5714285714285714] - >>> gmm['means'] + >>> mm['means'] array([[ 1.49537196, 0.53745455], [ 0.54199936, 1.42606497]]) """ @@ -49,24 +51,136 @@ def estim_gmm_params(features, prob): return gmm_params -def estim_class_model(features, nb_classes, proba_type): - """ wrapper over several options how to cluster samples +def estim_class_model(features, nb_classes, estim_model='GMM', pca_coef=None, + scaler=True, max_iter=99): + """ create pipeline (scaler, PCA, model) over several options how + to cluster samples and fit it on data :param ndarray features: - :param int nb_classes: - :param str proba_type: + :param int nb_classes: number of expected classes + :param str proba_type: tyre of used model + :param float pca_coef: range (0, 1) or None + :param bool scaler: wheter use a scaler + :param str init_type: initialsi of :return: + + >>> np.random.seed(0) + >>> fts = np.row_stack([np.random.random((50, 3)) - 1, + ... np.random.random((50, 3)) + 1]) + >>> mm = estim_class_model(fts, 2) + >>> mm.predict_proba(fts).shape + (100, 2) + >>> mm = estim_class_model(fts, 2, estim_model='GMM_kmeans', + ... pca_coef=0.95, max_iter=3) + >>> mm.predict_proba(fts).shape + (100, 2) + >>> mm = estim_class_model(fts, 2, estim_model='GMM_Otsu', max_iter=3) + >>> mm.predict_proba(fts).shape + (100, 2) + >>> mm = estim_class_model(fts, 2, estim_model='kmeans_quantiles', + ... scaler=False, max_iter=3) + >>> mm.predict_proba(fts).shape + (100, 2) + >>> mm = estim_class_model(fts, 2, estim_model='BGM', max_iter=3) + >>> mm.predict_proba(fts).shape + (100, 2) + >>> mm = estim_class_model(fts, 2, estim_model='Otsu', max_iter=3) + >>> mm.predict_proba(fts).shape + (100, 2) """ - if proba_type == 'GMM': - model = estim_class_model_gmm(features, nb_classes) - elif proba_type == 'quantiles': - model = estim_class_model_kmeans(features, nb_classes, - init_type='quantiles') + components = [] + if scaler: + components += [('scaler', preprocessing.StandardScaler())] + if pca_coef is not None: + components += [('reduce_dim', decomposition.PCA(pca_coef))] + + nb_inits = max(1, int(np.sqrt(max_iter))) + # http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GMM.html + mm = mixture.GaussianMixture(n_components=nb_classes, covariance_type='full', + n_init=nb_inits, max_iter=max_iter) + + # split the model and used initilaisation + if '_' in estim_model: + init_type = estim_model.split('_')[-1] + estim_model = estim_model.split('_')[0] else: - model = estim_class_model_kmeans(features, nb_classes) + init_type = '' + + y = None + if estim_model == 'GMM': + # model = estim_class_model_gmm(features, nb_classes) + if init_type == 'kmeans': + mm.set_params(n_init=1) + # http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html + kmeans = cluster.KMeans(n_clusters=nb_classes, init='k-means++', + n_jobs=-1) + y = kmeans.fit_predict(features) + elif init_type == 'Otsu': + mm.set_params(n_init=1) + y = compute_multivarian_otsu(features) + + elif estim_model == 'kmeans': + # http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GMM.html + mm.set_params(max_iter=1) + init_type = 'quantiles' if init_type == 'quantiles' else 'k-means++' + _, y = estim_class_model_kmeans(features, nb_classes, + init_type=init_type, max_iter=max_iter) + + logging.info('compute probability of each feature to all component') + + elif estim_model == 'BGM': + mm = mixture.BayesianGaussianMixture(n_components=nb_classes, + covariance_type='full', + n_init=nb_inits, max_iter=max_iter) + + elif estim_model == 'Otsu' and nb_classes == 2: + mm.set_params(max_iter=1, n_init=1) + y = compute_multivarian_otsu(features) + + components += [('model', mm)] + # compose the pipeline + model = pipeline.Pipeline(components) + + if y is not None: + # fit with examples + model.fit(features, y) + else: + # fit from scrach + model.fit(features) return model +def compute_multivarian_otsu(features): + """ compute otsu individually over each sample dimension + WARNING: this compute only localy and since it does compare all + combinations of orienting the asign for tight cases it may not decide + + :param ndarray features: + :return [bool]: + + >>> np.random.seed(0) + >>> fts = np.row_stack([np.random.random((5, 3)) - 1, + ... np.random.random((5, 3)) + 1]) + >>> fts[:, 1] = - fts[:, 1] + >>> compute_multivarian_otsu(fts).astype(int) + array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]) + """ + ys = np.zeros(features.shape) + for i in range(features.shape[-1]): + thr = filters.threshold_otsu(features[:, i]) + asign = features[:, i] > thr + if i > 0: + m = np.mean(ys[:, :i], axis=1) + d1 = np.mean(np.abs(asign - m)) + d2 = np.mean(np.abs(~asign - m)) + # check if for this dimension it wount be better to swap it + if d2 < d1: + asign = ~asign + ys[:, i] = asign + y = np.mean(ys, axis=1) > 0.5 + return y + + # def estim_class_model_gmm(features, nb_classes, init='kmeans'): # """ from all features estimate Gaussian Mixture Model and assuming # each cluster is a single class compute probability that each feature @@ -79,16 +193,16 @@ def estim_class_model(features, nb_classes, proba_type): # logging.debug('estimate GMM for all given features %s and %i component', # repr(features.shape), nb_classes) # # http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GMM.html -# gmm = mixture.GMM(n_components=nb_classes, covariance_type='full', n_iter=999) +# mm = mixture.GMM(n_components=nb_classes, covariance_type='full', n_iter=999) # if init == 'kmeans': # # http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html # kmeans = cluster.KMeans(n_clusters=nb_classes, init='k-means++', n_jobs=-1) # y = kmeans.fit_predict(features) -# gmm.fit(features, y) +# mm.fit(features, y) # else: -# gmm.fit(features) +# mm.fit(features) # logging.info('compute probability of each feature to all component') -# return gmm +# return mm def estim_class_model_gmm(features, nb_classes, init='kmeans'): @@ -103,8 +217,8 @@ def estim_class_model_gmm(features, nb_classes, init='kmeans'): >>> np.random.seed(0) >>> fts = np.row_stack([np.random.random((50, 3)) - 1, ... np.random.random((50, 3)) + 1]) - >>> gmm = estim_class_model_gmm(fts, 2) - >>> gmm.predict_proba(fts).shape + >>> mm = estim_class_model_gmm(fts, 2) + >>> mm.predict_proba(fts).shape (100, 2) """ logging.debug('estimate GMM for all given features %s and %i component', @@ -124,7 +238,8 @@ def estim_class_model_gmm(features, nb_classes, init='kmeans'): return gmm -def estim_class_model_kmeans(features, nb_classes, init_type='k-means++'): +def estim_class_model_kmeans(features, nb_classes, init_type='k-means++', + max_iter=99): """ from all features estimate Gaussian from k-means clustering :param [[float]] features: list of features per segment @@ -134,26 +249,29 @@ def estim_class_model_kmeans(features, nb_classes, init_type='k-means++'): >>> np.random.seed(0) >>> fts = np.row_stack([np.random.random((50, 3)) - 1, ... np.random.random((50, 3)) + 1]) - >>> gmm = estim_class_model_kmeans(fts, 2) - >>> gmm.predict_proba(fts).shape + >>> mm, y = estim_class_model_kmeans(fts, 2, max_iter=9) + >>> y.shape + (100,) + >>> mm.predict_proba(fts).shape (100, 2) """ logging.debug('estimate Gaussian from k-means clustering for all given ' - 'features %s and %i component', repr(features.shape), nb_classes) + 'features %s and %i components', repr(features.shape), + nb_classes) # http://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html if init_type == 'quantiles': quntiles = np.linspace(5, 95, nb_classes).tolist() init_perc = np.array(np.percentile(features, quntiles, axis=0)) kmeans = cluster.KMeans(nb_classes, init=init_perc, max_iter=2, n_jobs=-1) else: - kmeans = cluster.KMeans(nb_classes, init=init_type, n_init=25, n_jobs=-1) + nb_inits = max(1, int(np.sqrt(max_iter))) + kmeans = cluster.KMeans(nb_classes, init=init_type, max_iter=max_iter, + n_init=nb_inits, n_jobs=-1) y = kmeans.fit_predict(features) - logging.info('compute probability of each feature to all component') - # http://scikit-learn.org/stable/modules/generated/sklearn.mixture.GMM.html gmm = mixture.GaussianMixture(n_components=nb_classes, covariance_type='full', max_iter=1) gmm.fit(features, y) - return gmm + return gmm, y def get_vertexes_edges(segments): @@ -263,7 +381,8 @@ def compute_edge_model(edges, proba, metric='l_T'): and so we take the min valus :param [(int, int)] edges: - :param [[float]] features: + :param [[float]] proba: + :param str metric: :return [float]: @@ -302,6 +421,9 @@ def compute_edge_model(edges, proba, metric='l_T'): # setting min weight ~ max difference in proba as weight dist = np.max(diff, axis=1) edge_weights = np.exp(- dist / (2 * np.std(dist) ** 2)) + else: + logging.error('not implemented for: %s', metric) + edge_weights = np.ones(len(edges)) return edge_weights diff --git a/imsegm/labeling.py b/imsegm/labeling.py index 1773f03d..062d60ee 100755 --- a/imsegm/labeling.py +++ b/imsegm/labeling.py @@ -1,7 +1,7 @@ """ Framework for labeling -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging @@ -10,6 +10,8 @@ from scipy import ndimage import skimage.segmentation as sk_segm +import imsegm.utils.data_io as tl_data + def contour_binary_map(seg, label=1, include_boundary=False): """ get object boundaries @@ -104,8 +106,8 @@ def contour_coords(seg, label=1, include_boundary=False): def binary_image_from_coords(coords, size): """ create binary image just from point contours - :param ndarray seg: integer images, typically a segmentation - :param int label: selected singe label in segmentation + :param ndarray coords: + :param (int, int) size: :return ndarray: >>> img = np.zeros((6, 6), dtype=int) @@ -156,7 +158,7 @@ def compute_distance_map(seg, label=1): def segm_labels_assignment(segm, segm_gt): """ create labels assign to the particular regions - :param ndarray seg: input segmentation + :param ndarray segm: input segmentation :param ndarray segm_gt: true segmentation :return: @@ -673,3 +675,41 @@ def compute_boundary_distances(segm_ref, segm): assert len(points) == len(dist), \ 'number of points and disntances should be equal' return points, dist + + +def assume_bg_on_boundary(segm, bg_label=0, boundary_size=1): + """ swap labels such that the bacround label will be mostly on image boundary + + :param ndarray segm: + :param int bg_label: + :return: + + >>> segm = np.zeros((6, 12), dtype=int) + >>> segm[1:4, 4:] = 2 + >>> assume_bg_on_boundary(segm, boundary_size=1) + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + >>> segm[segm == 0] = 1 + >>> assume_bg_on_boundary(segm, boundary_size=1) + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) + """ + boundary_lb = tl_data.get_image2d_boundary_color(segm, size=boundary_size) + used_lbs = np.unique(segm) + if boundary_lb not in used_lbs: + segm[segm == boundary_lb] = bg_label + else: + lut = list(range(used_lbs.max() + 1)) + lut[boundary_lb] = bg_label + lut[bg_label] = boundary_lb + segm = np.array(lut)[segm] + return segm + diff --git a/imsegm/pipelines.py b/imsegm/pipelines.py index c2964351..14602d1c 100755 --- a/imsegm/pipelines.py +++ b/imsegm/pipelines.py @@ -1,7 +1,7 @@ """ Pipelines for supervised and unsupervised segmentation -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging @@ -10,7 +10,7 @@ import numpy as np import skimage.color as sk_color -from sklearn import preprocessing, mixture, decomposition +# from sklearn import mixture import imsegm.utils.experiments as tl_expt import imsegm.graph_cuts as seg_gc @@ -26,25 +26,51 @@ CROSS_VAL_LEAVE_OUT = 2 NB_THREADS = max(1, int(mproc.cpu_count() * 0.6)) -DICT_CONVERT_COLOR = { +DICT_CONVERT_COLOR_FROM_RGB = { 'hsv': sk_color.rgb2hsv, 'luv': sk_color.rgb2luv, 'lab': sk_color.rgb2lab, 'hed': sk_color.rgb2hed, 'xyz': sk_color.rgb2xyz } +DICT_CONVERT_COLOR_TO_RGB = { + 'hsv': sk_color.hsv2rgb, + 'luv': sk_color.luv2rgb, + 'lab': sk_color.lab2rgb, + 'hed': sk_color.hed2rgb, + 'xyz': sk_color.xyz2rgb +} -def convert_img_color_space(image, clr_space): +def convert_img_color_from_rgb(image, clr_space): """ convert image colour space from RGB to xxx - :param image: rgb image - :param clr_space: str - :return: image + :param ndarray image: rgb image + :param str clr_space: + :return ndarray: image + + >>> convert_img_color_from_rgb(np.ones((50, 75, 3)), 'hsv').shape + (50, 75, 3) + """ + if image.ndim == 3 and image.shape[-1] in (3, 4) \ + and clr_space in DICT_CONVERT_COLOR_FROM_RGB: + image = DICT_CONVERT_COLOR_FROM_RGB[clr_space](image) + return image + + +def convert_img_color_to_rgb(image, clr_space): + """ convert image colour space to RGB to xxx + + :param ndarray image: rgb image + :param str clr_space: + :return ndarray: image + + >>> convert_img_color_to_rgb(np.ones((50, 75, 3)), 'hsv').shape + (50, 75, 3) """ - if image.ndim == 3 and image.shape[2] == 3 \ - and clr_space in DICT_CONVERT_COLOR: - image = DICT_CONVERT_COLOR[clr_space](image) + if image.ndim == 3 and image.shape[-1] == 3 \ + and clr_space in DICT_CONVERT_COLOR_TO_RGB: + image = DICT_CONVERT_COLOR_TO_RGB[clr_space](image) return image @@ -53,7 +79,7 @@ def pipe_color2d_slic_features_gmm_graphcut(image, nb_classes=3, sp_size=30, sp_regul=0.2, gc_regul=1., dict_features=FTS_SET_SIMPLE, - proba_type='GMM', + estim_model='GMM', gc_edge_type='model_lT', pca_coef=None, dict_debug_imgs=None): @@ -66,9 +92,10 @@ def pipe_color2d_slic_features_gmm_graphcut(image, nb_classes=3, :param float sp_regul: regularisation in range(0;1) where "0" gives elastic and "1" nearly square slic :param int nb_classes: number of classes to be segmented(indexing from 0) - :param dict_features: {clr: [str], ...} + :param {} dict_features: {clr: [str]} :param str clr_space: use color space :param float gc_regul: GC regularisation + :param str estim_model: estimating model :param str gc_edge_type: graphCut edge type :param float pca_coef: range (0, 1) or None :param dict_debug_imgs: {str: ...} @@ -94,18 +121,14 @@ def pipe_color2d_slic_features_gmm_graphcut(image, nb_classes=3, dict_debug_imgs['slic_mean'] = sk_color.label2rgb(slic, image, kind='avg') - if pca_coef is not None: - pca = decomposition.PCA(pca_coef) - features = pca.fit_transform(features) - - model = seg_gc.estim_class_model(features, nb_classes, proba_type) + model = seg_gc.estim_class_model(features, nb_classes, estim_model, pca_coef) proba = model.predict_proba(features) logging.debug('list of probabilities: %s', repr(proba.shape)) - gmm = mixture.GaussianMixture(n_components=nb_classes, - covariance_type='full', max_iter=1) - gmm.fit(features, np.argmax(proba, axis=1)) - proba = gmm.predict_proba(features) + # gmm = mixture.GaussianMixture(n_components=nb_classes, + # covariance_type='full', max_iter=1) + # gmm.fit(features, np.argmax(proba, axis=1)) + # proba = gmm.predict_proba(features) graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, features, gc_regul, gc_edge_type, dict_debug_imgs=dict_debug_imgs) @@ -116,7 +139,7 @@ def pipe_color2d_slic_features_gmm_graphcut(image, nb_classes=3, def estim_model_classes_group(list_images, nb_classes=4, clr_space='rgb', sp_size=30, sp_regul=0.2, dict_features=FTS_SET_SIMPLE, - pca_coef=None, proba_type='GMM', + pca_coef=None, scaler=True, proba_type='GMM', nb_jobs=NB_THREADS): """ estimate a model from sequence of input images and return it as result @@ -128,16 +151,17 @@ def estim_model_classes_group(list_images, nb_classes=4, clr_space='rgb', and "1" nearly square slic :param {str: [str]} dict_features: list of features to be extracted :param float pca_coef: range (0, 1) or None + :param bool scaler: wheter use a scaler :param str proba_type: model type :param int nb_jobs: number of jobs running in parallel :return: """ list_slic, list_features = list(), list() - wrapper_compute = partial(compute_color2d_superpixels_features, - sp_size=sp_size, sp_regul=sp_regul, - dict_features=dict_features, - clr_space=clr_space, fts_norm=False) - iterate = tl_expt.WrapExecuteSequence(wrapper_compute, list_images, + _wrapper_compute = partial(compute_color2d_superpixels_features, + sp_size=sp_size, sp_regul=sp_regul, + dict_features=dict_features, + clr_space=clr_space, fts_norm=False) + iterate = tl_expt.WrapExecuteSequence(_wrapper_compute, list_images, nb_jobs=nb_jobs) for slic, features in iterate: list_slic.append(slic) @@ -152,21 +176,13 @@ def estim_model_classes_group(list_images, nb_classes=4, clr_space='rgb', features = np.concatenate(tuple(list_features), axis=0) features = np.nan_to_num(features) - # scaling - scaler = preprocessing.StandardScaler() - scaler.fit(features) - features = scaler.transform(features) - - pca = None - if pca_coef is not None: - pca = decomposition.PCA(pca_coef) - features = pca.fit_transform(features) + model = seg_gc.estim_class_model(features, nb_classes, proba_type, + pca_coef, scaler) - model = seg_gc.estim_class_model(features, nb_classes, proba_type) - return scaler, pca, model + return model, list_features -def segment_color2d_slic_features_model_graphcut(image, scaler, pca, model, +def segment_color2d_slic_features_model_graphcut(image, model_pipeline, clr_space='rgb', sp_size=30, sp_regul=0.2, gc_regul=1., @@ -176,7 +192,8 @@ def segment_color2d_slic_features_model_graphcut(image, scaler, pca, model, """ complete pipe-line for segmentation using superpixels, extracting features and graphCut segmentation - :param ndarry img: input RGB image + :param ndarry image: input RGB image + :param obj model_pipeline: :param str clr_space: chose the color space :param int sp_size: initial size of a superpixel(meaning edge lenght) :param float sp_regul: regularisation in range(0;1) where "0" gives elastic @@ -184,15 +201,28 @@ def segment_color2d_slic_features_model_graphcut(image, scaler, pca, model, :param {str: [str]} dict_features: list of features to be extracted :param float gc_regul: GC regularisation :param str gc_edge_type: select the GC edge type - :param float pca_coef: range (0, 1) or None :param dict_debug_imgs: {str: ...} :return [[int]]: segmentation matrix mapping each pixel into a class + UnSupervised: >>> np.random.seed(0) + >>> seg_fts.USE_CYTHON = False >>> image = np.random.random((125, 150, 3)) / 2. >>> image[:, :75] += 0.5 - >>> sc, pca, model = estim_model_classes_group([image], nb_classes=2) - >>> segm = segment_color2d_slic_features_model_graphcut(image, sc, pca, model) + >>> model, _ = estim_model_classes_group([image], nb_classes=2) + >>> segm = segment_color2d_slic_features_model_graphcut(image, model) + >>> segm.shape + (125, 150) + + Supervised: + >>> np.random.seed(0) + >>> seg_fts.USE_CYTHON = False + >>> image = np.random.random((125, 150, 3)) / 2. + >>> image[:, 75:] += 0.5 + >>> annot = np.zeros(image.shape[:2], dtype=int) + >>> annot[:, 75:] = 1 + >>> clf, _, _, _ = train_classif_color2d_slic_features([image], [annot]) + >>> segm = segment_color2d_slic_features_model_graphcut(image, clf) >>> segm.shape (125, 150) """ @@ -207,23 +237,25 @@ def segment_color2d_slic_features_model_graphcut(image, scaler, pca, model, image = np.rollaxis(np.tile(image, (3, 1, 1)), 0, 3) dict_debug_imgs['image'] = image dict_debug_imgs['slic'] = slic - dict_debug_imgs['slic_mean'] = sk_color.label2rgb(slic, image, kind='avg') - - features = scaler.transform(features) - if pca is not None: - features = pca.fit_transform(features) + dict_debug_imgs['slic_mean'] = sk_color.label2rgb(slic, image, + kind='avg') - proba = model.predict_proba(features) + proba = model_pipeline.predict_proba(features) logging.debug('list of probabilities: %s', repr(proba.shape)) - gmm = mixture.GaussianMixture(n_components=proba.shape[1], - covariance_type='full', max_iter=1) - gmm.fit(features, np.argmax(proba, axis=1)) - proba = gmm.predict_proba(features) + # gmm = mixture.GaussianMixture(n_components=proba.shape[1], + # covariance_type='full', max_iter=1) + # gmm.fit(features, np.argmax(proba, axis=1)) + # proba = gmm.predict_proba(features) - graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, features, - gc_regul, gc_edge_type, dict_debug_imgs=dict_debug_imgs) + graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, + features, + gc_regul, gc_edge_type, + dict_debug_imgs=dict_debug_imgs) segm = graph_labels[slic] + # relabel according classif classes + if hasattr(model_pipeline, 'classes_'): + segm = model_pipeline.classes_[segm] return segm @@ -249,7 +281,7 @@ def compute_color2d_superpixels_features(image, clr_space='rgb', # plt.figure(), plt.imshow(slic) logging.debug('extract slic/superpixels features.') - image = convert_img_color_space(image, clr_space) + image = convert_img_color_from_rgb(image, clr_space) features, _ = seg_fts.compute_selected_features_img2d(image, slic, dict_features) logging.debug('list of features RAW: %s', repr(features.shape)) @@ -289,12 +321,15 @@ def wrapper_compute_color2d_slic_features_labels(img_annot, clr_space, return slic, features, labels -def train_classif_color2d_slic_features(list_images, list_annots, clr_space='rgb', +def train_classif_color2d_slic_features(list_images, list_annots, + clr_space='rgb', sp_size=30, sp_regul=0.2, dict_features=FTS_SET_SIMPLE, - clf_name=CLASSIF_NAME, label_purity=0.9, + clf_name=CLASSIF_NAME, + label_purity=0.9, feature_balance='unique', pca_coef=None, nb_classif_search=1, + nb_hold_out=CROSS_VAL_LEAVE_OUT, nb_jobs=1): """ train classifier on list of annotated images @@ -310,6 +345,7 @@ def train_classif_color2d_slic_features(list_images, list_annots, clr_space='rgb :param str feature_balance: set how to balance datasets :param float pca_coef: select PCA coef or None :param int nb_classif_search: number of tries for hyper-parameters seach + :param int nb_hold_out: cross-val leave out :param int nb_jobs: parallelism :return: """ @@ -319,33 +355,18 @@ def train_classif_color2d_slic_features(list_images, list_annots, clr_space='rgb % (len(list_images), len(list_annots)) list_slic, list_features, list_labels = list(), list(), list() - wrapper_compute = partial(wrapper_compute_color2d_slic_features_labels, - clr_space=clr_space, sp_size=sp_size, - sp_regul=sp_regul, dict_features=dict_features, - label_purity=label_purity) + _wrapper_compute = partial(wrapper_compute_color2d_slic_features_labels, + clr_space=clr_space, sp_size=sp_size, + sp_regul=sp_regul, dict_features=dict_features, + label_purity=label_purity) list_imgs_annot = zip(list_images, list_annots) - iterate = tl_expt.WrapExecuteSequence(wrapper_compute, list_imgs_annot, + iterate = tl_expt.WrapExecuteSequence(_wrapper_compute, list_imgs_annot, nb_jobs=nb_jobs) for slic, fts, lbs in iterate: list_slic.append(slic) list_features.append(fts) list_labels.append(lbs) - # for img, annot in zip(list_images, list_annots): - # assert img.shape[:2] == annot.shape[:2] - # slic, features = compute_color2d_superpixels_features(img, clr_space, - # sp_size, sp_regul, - # dict_features, - # fts_norm=False) - # list_slic.append(slic) - # list_features.append(features) - # - # label_hist = seg_lbs.histogram_regions_labels_norm(slic, annot) - # labels = np.argmax(label_hist, axis=1) - # purity = np.max(label_hist, axis=1) - # labels[purity < label_purity] = -1 - # list_labels.append(labels) - logging.debug('concentrate features...') # concentrate features, labels features, labels, sizes = seg_clf.convert_set_features_labels_2_dataset( @@ -359,83 +380,30 @@ def train_classif_color2d_slic_features(list_images, list_annots, clr_space='rgb # clf_pipeline = seg_clf.create_clf_pipeline(clf_name, pca_coef) # clf_pipeline.fit(np.array(features), np.array(labels, dtype=int)) - if len(sizes) > (CROSS_VAL_LEAVE_OUT * 5): - cv = seg_clf.CrossValidatePSetsOut(sizes, nb_hold_out=CROSS_VAL_LEAVE_OUT) + if len(sizes) > (nb_hold_out * 5): + cv = seg_clf.CrossValidatePSetsOut(sizes, nb_hold_out=nb_hold_out) # for small nuber of training images this does not make sence else: cv = 10 classif, _ = seg_clf.create_classif_train_export(clf_name, features, labels, nb_search_iter=nb_classif_search, - cross_val=cv, nb_jobs=nb_jobs, + cross_val=cv, + nb_jobs=nb_jobs, pca_coef=pca_coef) return classif, list_slic, list_features, list_labels -def segment_color2d_slic_features_classif_graphcut(image, classif, - clr_space='rgb', - sp_size=30, sp_regul=0.2, - gc_regul=1., - dict_features=FTS_SET_SIMPLE, - gc_edge_type='model', - dict_debug_imgs=None): - """ take trained classifier and apply it on new images - - :param ndarray image: input image - :param classif: trained classifier - :param str clr_space: chose the color space - :param int sp_size: initial size of a superpixel(meaning edge lenght) - :param float sp_regul: regularisation in range(0;1) where "0" gives elastic - and "1" nearly square segments - :param {str: [str]} dict_features: list of features to be extracted - :param gc_regul: regularisation for GC - :param str gc_edge_type: select the GC edge type - :param dict_debug_imgs: - :return: - - >>> np.random.seed(0) - >>> seg_fts.USE_CYTHON = False - >>> image = np.random.random((125, 150, 3)) / 2. - >>> image[:, 75:] += 0.5 - >>> annot = np.zeros(image.shape[:2], dtype=int) - >>> annot[:, 75:] = 1 - >>> clf, _, _, _ = train_classif_color2d_slic_features([image], [annot]) - >>> segm = segment_color2d_slic_features_classif_graphcut(image, clf) - >>> segm.shape - (125, 150) - """ - logging.info('SEGMENTATION Superpixels-Features-Classifier-GraphCut') - slic, features = compute_color2d_superpixels_features(image, clr_space, - sp_size, sp_regul, - dict_features, - fts_norm=False) - - proba = classif.predict_proba(features) - - if dict_debug_imgs is not None: - if image.ndim == 2: # duplicate channels to be like RGB - image = np.rollaxis(np.tile(image, (3, 1, 1)), 0, 3) - dict_debug_imgs['image'] = image - dict_debug_imgs['slic'] = slic - dict_debug_imgs['slic_mean'] = sk_color.label2rgb(slic, image, kind='avg') - - graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, features, - gc_regul, gc_edge_type, - dict_debug_imgs=dict_debug_imgs) - segm = graph_labels[slic] - # relabel according classif classes - segm = classif.classes_[segm] - return segm - - -def pipe_gray3d_slic_features_gmm_graphcut(image, nb_classes=4, spacing=(12, 1, 1), - sp_size=15, sp_regul=0.2, gc_regul=0.1, +def pipe_gray3d_slic_features_gmm_graphcut(image, nb_classes=4, + spacing=(12, 1, 1), + sp_size=15, sp_regul=0.2, + gc_regul=0.1, dict_features=FTS_SET_SIMPLE): """ complete pipe-line for segmentation using superpixels, extracting features and graphCut segmentation - :param ndarray img: input RGB image + :param ndarray image: input RGB image :param int sp_size: initial size of a superpixel(meaning edge lenght) :param float sp_regul: regularisation in range(0;1) where "0" gives elastic and "1" nearly square segments @@ -457,7 +425,8 @@ def pipe_gray3d_slic_features_gmm_graphcut(image, nb_classes=4, spacing=(12, 1, # plt.imshow(segments) logging.info('extract segments/superpixels features.') # f = features.computeColourMean(image, segments) - features, _ = seg_fts.compute_selected_features_gray3d(image, slic, dict_features) + features, _ = seg_fts.compute_selected_features_gray3d(image, slic, + dict_features) # merge features together logging.debug('list of features RAW: %s', repr(features.shape)) features[np.isnan(features)] = 0 @@ -466,13 +435,13 @@ def pipe_gray3d_slic_features_gmm_graphcut(image, nb_classes=4, spacing=(12, 1, features, _ = seg_fts.norm_features(features) logging.debug('list of features NORM: %s', repr(features.shape)) - model = seg_gc.estim_class_model_gmm(features, nb_classes) + model = seg_gc.estim_class_model(features, nb_classes) proba = model.predict_proba(features) logging.debug('list of probabilities: %s', repr(proba.shape)) # resultGraph = graphCut.segment_graph_cut_int_vals(segments, prob, gcReg) - graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, features, - gc_regul) + graph_labels = seg_gc.segment_graph_cut_general(slic, proba, image, + features, gc_regul) return graph_labels[slic] diff --git a/imsegm/region_growing.py b/imsegm/region_growing.py index 9abfcedb..f01430b3 100755 --- a/imsegm/region_growing.py +++ b/imsegm/region_growing.py @@ -3,7 +3,7 @@ * general GraphCut segmentation with and without shape model * region growing with shape prior - greedy & GraphCut -Copyright (C) 2016-2017 Jiri Borovec +Copyright (C) 2016-2018 Jiri Borovec """ import logging @@ -146,7 +146,7 @@ def object_segmentation_graphcut_pixels(segm, centres, dict_debug_imgs=None): """ object segmentation using Graph Cut directly on pixel level - :param ndarray slic: superpixel pre-segmentation + :param ndarray centres: :param ndarray segm: input structure segmentation :param [(int, int)] centres: superpixel centres :param [float] labels_fg_prob: set how much particular label belongs to foreground @@ -582,7 +582,7 @@ def compute_shape_prior_table_cdf(point, cum_distribution, centre, :param (int, int) point: single points :param (int, int) centre: center of model - :param [[float]] cum_hist: cumulative histogram + :param [[float]] cum_distribution: cumulative histogram :return float: >>> chist = [[1.0, 1.0, 0.8, 0.7, 0.6, 0.5, 0.3, 0.0, 0.0], @@ -1562,7 +1562,7 @@ def region_growing_shape_slic_graphcut(segm, slic, centres, shape_model, if len(gc_edges) > 0: graph_labels = cut_general_graph(np.array(gc_edges), edge_weights, unary, pairwise, n_iter=999) - labels_gc[gc_vestexes] = graph_labels + labels_gc[gc_vestexes] = graph_labels else: for i in range(len(centres)): diff --git a/imsegm/superpixels.py b/imsegm/superpixels.py index cc231369..06195830 100755 --- a/imsegm/superpixels.py +++ b/imsegm/superpixels.py @@ -6,7 +6,7 @@ SEE: * http://scikit-image.org/docs/dev/auto_examples/plot_segmentations.html -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ @@ -22,7 +22,7 @@ def segment_slic_img2d(img, sp_size=50, rltv_compact=0.1, slico=False): """ segmentation by SLIC superpixels using original SLIC implementation - :param ndarray im: input color image + :param ndarray img: input color image :param int sp_size: superpixel initial size :param float rltv_compact: relative regularisation in range (0, 1) where 0 is for free form and 1 for nearly rectangular superpixels diff --git a/imsegm/tests/test-classification.py b/imsegm/tests/test-classification.py index 0b78ab96..278d44c3 100644 --- a/imsegm/tests/test-classification.py +++ b/imsegm/tests/test-classification.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os diff --git a/imsegm/tests/test-descriptors.py b/imsegm/tests/test-descriptors.py index 32bbc76d..ddfc4ca2 100644 --- a/imsegm/tests/test-descriptors.py +++ b/imsegm/tests/test-descriptors.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os diff --git a/imsegm/tests/test-ellipse_fitting.py b/imsegm/tests/test-ellipse_fitting.py index 698bdc20..6fa4438b 100644 --- a/imsegm/tests/test-ellipse_fitting.py +++ b/imsegm/tests/test-ellipse_fitting.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os @@ -20,7 +20,7 @@ # set some default paths PATH_OUTPUT = tl_data.update_path('output', absolute=True) -PATH_OVARY = os.path.join(tl_data.update_path('images', absolute=True), +PATH_OVARY = os.path.join(tl_data.update_path('data_images', absolute=True), 'drosophila_ovary_slice') PATH_IMAGES = os.path.join(PATH_OVARY, 'image') PATH_SEGM = os.path.join(PATH_OVARY, 'segm') diff --git a/imsegm/tests/test-graph_cut.py b/imsegm/tests/test-graph_cut.py index a5af9e13..689d41d6 100644 --- a/imsegm/tests/test-graph_cut.py +++ b/imsegm/tests/test-graph_cut.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os diff --git a/imsegm/tests/test-labels.py b/imsegm/tests/test-labels.py index 1cdd49e1..58cdbea8 100644 --- a/imsegm/tests/test-labels.py +++ b/imsegm/tests/test-labels.py @@ -2,7 +2,7 @@ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os diff --git a/imsegm/tests/test-pipelines.py b/imsegm/tests/test-pipelines.py index 56d4d234..4417f77e 100644 --- a/imsegm/tests/test-pipelines.py +++ b/imsegm/tests/test-pipelines.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging @@ -79,8 +79,9 @@ def run_segm2d_gmm_gc(img2d, dir_name, types_edge=('model', 'const'), if not os.path.isdir(path_dir): os.mkdir(path_dir) - scaler, pca, model = pipelines.estim_model_classes_group( - [img2d], proba_type='GMM', **dict_params) + model, _ = pipelines.estim_model_classes_group([img2d], + proba_type='GMM', + **dict_params) dict_params.pop('nb_classes', None) dict_params.pop('pca_coef', None) @@ -92,13 +93,13 @@ def run_segm2d_gmm_gc(img2d, dir_name, types_edge=('model', 'const'), # dict_debug_imgs=dict_imgs, **dict_params) seg = pipelines.segment_color2d_slic_features_model_graphcut( - img2d, scaler, pca, model, gc_regul=regul, gc_edge_type=edge, + img2d, model, gc_regul=regul, gc_edge_type=edge, dict_debug_imgs=dict_imgs, **dict_params) show_segm_debugs_2d(dict_imgs, path_dir, 'fig_regul-%.2f_edge-%s_debug.png' % (regul, edge)) show_segm_results_2d(img2d, seg, path_dir, - 'fig_regul-%.2f_edge-%s.png' % (regul, edge)) + 'fig_regul-%.2f_edge-%s.png' % (regul, edge)) dict_imgs = None @@ -198,7 +199,7 @@ def test_segm_supervised(self): classif, _, _, _ = pipelines.train_classif_color2d_slic_features( [img], [annot], sp_size, dict_features=FEATURES_TEXTURE) - _ = pipelines.segment_color2d_slic_features_classif_graphcut( + _ = pipelines.segment_color2d_slic_features_model_graphcut( img, classif, sp_size=sp_size, gc_regul=0., dict_features=FEATURES_TEXTURE, dict_debug_imgs=dict_imgs) show_segm_debugs_2d(dict_imgs, path_dir, name % (1, 0, '_debug')) @@ -206,12 +207,12 @@ def test_segm_supervised(self): for edge in tp_edge: dict_imgs = dict() for regul in list_regul: - seg = pipelines.segment_color2d_slic_features_classif_graphcut( + seg = pipelines.segment_color2d_slic_features_model_graphcut( img, classif, sp_size=sp_size, gc_regul=regul, gc_edge_type=edge, dict_features=FEATURES_TEXTURE) show_segm_results_2d(img, seg, path_dir, name % (1, regul, edge)) - seg = pipelines.segment_color2d_slic_features_classif_graphcut( + seg = pipelines.segment_color2d_slic_features_model_graphcut( img2, classif, sp_size=sp_size, gc_regul=regul, gc_edge_type=edge, dict_features=FEATURES_TEXTURE, dict_debug_imgs=dict_imgs) show_segm_results_2d(img2, seg, path_dir, name % (2, regul, edge)) diff --git a/imsegm/tests/test-region_growing.py b/imsegm/tests/test-region_growing.py index f5af5f5e..0da458cf 100644 --- a/imsegm/tests/test-region_growing.py +++ b/imsegm/tests/test-region_growing.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import logging @@ -22,7 +22,7 @@ import imsegm.superpixels as seg_spx import imsegm.region_growing as seg_rg -PATH_OVARY = os.path.join(tl_data.update_path('images', absolute=True), +PATH_OVARY = os.path.join(tl_data.update_path('data_images', absolute=True), 'drosophila_ovary_slice') PATH_IMAGE = os.path.join(PATH_OVARY, 'image') PATH_SEGM = os.path.join(PATH_OVARY, 'segm') diff --git a/imsegm/tests/test-superpixels.py b/imsegm/tests/test-superpixels.py index b2f0a5fd..a171c7d4 100644 --- a/imsegm/tests/test-superpixels.py +++ b/imsegm/tests/test-superpixels.py @@ -1,7 +1,7 @@ """ Unit testing for particular segmentation module -Copyright (C) 2014-2017 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os diff --git a/imsegm/utils/data_io.py b/imsegm/utils/data_io.py index 69fe50ae..b25875ef 100755 --- a/imsegm/utils/data_io.py +++ b/imsegm/utils/data_io.py @@ -1,7 +1,7 @@ """ Framework for handling input/output -Copyright (C) 2015-2016 Jiri Borovec +Copyright (C) 2015-2018 Jiri Borovec """ import os @@ -236,7 +236,7 @@ def scale_image_intensity(img, im_range=1., quantiles=(2, 98)): in_range=(p_low, p_high), out_range='float') if im_range == 255: - img = (img * im_range).astype(np.uint8) + img = np.array(img * im_range).astype(np.uint8) return img @@ -582,12 +582,12 @@ def load_image_tiff_volume(path_img, im_range=None): :param float im_range: range to scale image values (1. or 255) :return ndarray: - >>> p_img = os.path.join(update_path('images'), 'drosophila_ovary_3D', + >>> p_img = os.path.join(update_path('data_images'), 'drosophila_ovary_3D', ... 'AU10-13_f0011.tif') >>> img = load_image_tiff_volume(p_img) >>> img.shape (30, 323, 512) - >>> p_img = os.path.join(update_path('images'), + >>> p_img = os.path.join(update_path('data_images'), ... 'drosophila_ovary_slice', 'image', 'insitu7545.tif') >>> img = load_image_tiff_volume(p_img) >>> img.shape @@ -633,14 +633,14 @@ def load_tiff_volume_split_double_band(path_img, im_range=None): :param float im_range: range to scale image values (1. or 255) :return ndarray, ndarray: - >>> p_img = os.path.join(update_path('images'), 'drosophila_ovary_3D', + >>> p_img = os.path.join(update_path('data_images'), 'drosophila_ovary_3D', ... 'AU10-13_f0011.tif') >>> img_b1, img_b2 = load_tiff_volume_split_double_band(p_img) >>> img_b1.shape (15, 323, 512) >>> img_b2.shape (15, 323, 512) - >>> p_img = os.path.join(update_path('images'), + >>> p_img = os.path.join(update_path('data_images'), ... 'drosophila_ovary_slice', 'image', 'insitu7545.tif') >>> img_b1, img_b2 = load_tiff_volume_split_double_band(p_img) >>> img_b1.shape @@ -678,7 +678,7 @@ def load_zvi_volume_double_band_split(path_img): :param str path_img: path to the image :return ndarray, ndarray: - >>> p_img = os.path.join(update_path('images'), + >>> p_img = os.path.join(update_path('data_images'), ... 'others', 'sample.zvi') >>> img_b1, img_b2 = load_zvi_volume_double_band_split(p_img) >>> img_b1.shape @@ -700,7 +700,7 @@ def load_img_double_band_split(path_img, im_range=1., quantiles=(2, 98)): :param (int, int) quantiles: scale image values in certain quantile range :return: - >>> p_imgs = os.path.join(update_path('images'), + >>> p_imgs = os.path.join(update_path('data_images'), ... 'drosophila_ovary_slice', 'image') >>> p_img = os.path.join(p_imgs, 'insitu7545.jpg') >>> img_b1, img_b2 = load_img_double_band_split(p_img) @@ -710,7 +710,7 @@ def load_img_double_band_split(path_img, im_range=1., quantiles=(2, 98)): >>> img_b1, img_b2 = load_img_double_band_split(p_img) >>> img_b1.shape (647, 1024) - >>> p_img = os.path.join(update_path('images'), + >>> p_img = os.path.join(update_path('data_images'), ... 'drosophila_ovary_3D', 'AU10-13_f0011.tif') >>> img_b1, img_b2 = load_img_double_band_split(p_img) >>> img_b1.shape @@ -772,7 +772,7 @@ def load_complete_image_folder(path_dir, img_name_pattern='*.png', :param [str] skip: skip some prticular images by name :return: - >>> p_imgs = os.path.join(update_path('images'), + >>> p_imgs = os.path.join(update_path('data_images'), ... 'drosophila_ovary_slice', 'image') >>> l_imgs, l_names = load_complete_image_folder(p_imgs, '*.jpg') >>> len(l_imgs) @@ -881,20 +881,19 @@ def find_files_match_names_across_dirs(list_path_pattern, drop_none=True): :param bool drop_none: drop if there are some none - missing values in rows :return: DF - >>> def mpath(d, n): - ... p = os.path.join(update_path('images'), - ... 'drosophila_ovary_slice', d, n) - ... return p - >>> df = find_files_match_names_across_dirs([mpath('image', '*.jpg'), - ... mpath('segm', '*.png'), - ... mpath('center_levels', '*.csv')]) + >>> def _mp(d, n): + ... return os.path.join(update_path('data_images'), + ... 'drosophila_ovary_slice', d, n) + >>> df = find_files_match_names_across_dirs([_mp('image', '*.jpg'), + ... _mp('segm', '*.png'), + ... _mp('center_levels', '*.csv')]) >>> len(df) > 0 True >>> df.columns.tolist() ['path_1', 'path_2', 'path_3'] - >>> df = find_files_match_names_across_dirs([mpath('image', '*.png'), - ... mpath('segm', '*.jpg'), - ... mpath('center_levels', '*.csv')]) + >>> df = find_files_match_names_across_dirs([_mp('image', '*.png'), + ... _mp('segm', '*.jpg'), + ... _mp('center_levels', '*.csv')]) >>> len(df) 0 """ @@ -904,21 +903,21 @@ def find_files_match_names_across_dirs(list_path_pattern, drop_none=True): assert os.path.exists(os.path.dirname(p)), \ 'missing "%s"' % os.path.dirname(p) - def get_name(path, pattern='*'): + def _get_name(path, pattern='*'): name = os.path.splitext(os.path.basename(path))[0] for s in pattern.split('*'): name = name.replace(s, '') return name - def get_paths_names(path_pattern): + def _get_paths_names(path_pattern): paths_ = glob.glob(path_pattern) if len(paths_) == 0: return [None], [None] - names_ = [get_name(p, os.path.basename(path_pattern)) for p in paths_] + names_ = [_get_name(p, os.path.basename(path_pattern)) for p in paths_] return paths_, names_ logging.info('find match files...') - paths_0, names_0 = get_paths_names(list_path_pattern[0]) + paths_0, names_0 = _get_paths_names(list_path_pattern[0]) list_paths = [paths_0] for path_pattern_n in list_path_pattern[1:]: @@ -927,7 +926,7 @@ def get_paths_names(path_pattern): list_files = glob.glob(path_pattern_n) logging.debug('found %i files in %s', len(list_files), path_pattern_n) for path_n in list_files: - name_n = get_name(path_n, name_pattern) + name_n = _get_name(path_n, name_pattern) if name_n in names_0: idx = names_0.index(name_n) paths_n[idx] = path_n @@ -942,7 +941,7 @@ def get_paths_names(path_pattern): return df_paths -def get_background_color(image): +def get_image2d_boundary_color(image, size=1): """ extract background color as median along image boundaries :param image: @@ -956,20 +955,22 @@ def get_background_color(image): [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) - >>> get_background_color(img) + >>> get_image2d_boundary_color(img) 0 - >>> get_background_color(np.ones((5, 15, 3), dtype=int)) + >>> get_image2d_boundary_color(np.ones((5, 15, 3), dtype=int), size=2) array([1, 1, 1]) - >>> get_background_color(np.ones((5, 15, 3, 1), dtype=int)) + >>> get_image2d_boundary_color(np.ones((5, 15, 3, 1), dtype=int)) array(0) """ + size = int(size) if image.ndim == 2: - bg_pixels = np.hstack([image[0, :], image[:, 0], - image[-1, :], image[:, -1]]) - bg_color = np.argmax(np.bincount(bg_pixels)) + bg_pixels = np.hstack([image[:size, :], image[:, :size].T, + image[-size:, :], image[:, -size:].T]) + bg_color = np.argmax(np.bincount(bg_pixels.ravel())) elif image.ndim == 3: - bg_pixels = np.vstack([image[0, :, ...], image[:, 0, ...], - image[-1, :, ...], image[:, -1, ...]]) + bounds = [image[:size, :, ...], image[:, :size, ...], + image[-size:, :, ...], image[:, -size:, ...]] + bg_pixels = np.vstack([b.reshape(-1, image.shape[-1]) for b in bounds]) bg_color = np.median(bg_pixels, axis=0) else: logging.error('not supported image dim: %s' % repr(image.shape)) @@ -1035,7 +1036,7 @@ def cut_object(img, mask, padding, use_mask=False, bg_color=None): bg_mask = np.argmax(np.bincount(bg_pixels)) if bg_color is None: - bg_color = get_background_color(img) + bg_color = get_image2d_boundary_color(img) rotate = np.rad2deg(prop.orientation) shift = prop.centroid - (np.array(mask.shape) / 2.) diff --git a/imsegm/utils/data_samples.py b/imsegm/utils/data_samples.py index 7eba923f..50ae5e69 100755 --- a/imsegm/utils/data_samples.py +++ b/imsegm/utils/data_samples.py @@ -2,13 +2,13 @@ """ Sandbox with some sample images -Copyright (C) 2015-2016 Jiri Borovec +Copyright (C) 2015-2018 Jiri Borovec """ import os import logging -from PIL import Image +# from PIL import Image import numpy as np import imsegm.utils.data_io as tl_data @@ -18,12 +18,12 @@ SAMPLE_SEG_NB_CLASSES = 3 SAMPLE_SEG_SIZE_3D_SMALL = (10, 5, 6) -PATH_IMAGES = tl_data.update_path('images') +PATH_IMAGES = tl_data.update_path('data_images') IMAGE_LENNA = os.path.join('others', 'lena.png') IMAGE_OBJECTS = os.path.join('synthetic', 'reference.jpg') -IMAGE_3CLS = os.path.join('textures', 'sample_rgb_3cls.jpg') -IMAGE_STAR_1 = os.path.join('see_starfish', 'star_nb1.jpg') -IMAGE_STAR_2 = os.path.join('see_starfish', 'stars_nb2.jpg') +IMAGE_3CLS = os.path.join('synthetic', 'texture_rgb_3cls.jpg') +IMAGE_STAR_1 = os.path.join('others', 'sea_starfish-1.jpg') +IMAGE_STAR_2 = os.path.join('others', 'sea_starfish-2.jpg') IMAGE_HISTOL_CIMA = \ os.path.join('histology_CIMA', '29-041-Izd2-w35-CD31-3-les1.jpg') IMAGE_HISTOL_FLAGSHIP = \ diff --git a/imsegm/utils/drawing.py b/imsegm/utils/drawing.py index a06cb55c..0289aba9 100755 --- a/imsegm/utils/drawing.py +++ b/imsegm/utils/drawing.py @@ -1,7 +1,7 @@ """ Framework for visualisations -Copyright (C) 2016-2017 Jiri Borovec +Copyright (C) 2016-2018 Jiri Borovec """ import os @@ -248,7 +248,7 @@ def figure_image_segm_results(img, seg, subfig_size=9): axarr[1].set_title('original image w. segment overlap') axarr[1].imshow(color.rgb2gray(img), cmap=plt.cm.Greys_r) axarr[1].imshow(seg, alpha=0.2, cmap=plt.cm.jet) - axarr[1].contour(seg, levels=np.unique(seg), linewidth=2, cmap=plt.cm.jet) + axarr[1].contour(seg, levels=np.unique(seg), linewidths=2, cmap=plt.cm.jet) axarr[2].set_title('segmentation of all labels') axarr[2].imshow(seg, cmap=plt.cm.jet) @@ -289,12 +289,12 @@ def figure_overlap_annot_segm_image(annot, segm, img=None, subfig_size=9): axarr[0].set_title('Annotation') axarr[0].imshow(img) axarr[0].imshow(annot, alpha=0.2) - axarr[0].contour(annot, levels=np.unique(annot), linewidth=2) + axarr[0].contour(annot, levels=np.unique(annot), linewidths=2) axarr[1].set_title('Segmentation') axarr[1].imshow(img) axarr[1].imshow(segm, alpha=0.2) - axarr[1].contour(segm, levels=np.unique(segm), linewidth=2) + axarr[1].contour(segm, levels=np.unique(segm), linewidths=2) # visualise the 3th label axarr[2].set_title('difference annot & segment') @@ -307,8 +307,8 @@ def figure_overlap_annot_segm_image(annot, segm, img=None, subfig_size=9): boundaries=np.linspace(-max_val - 0.5, max_val + 0.5, max_val * 2 + 2)) # plt.clim(-max_val - 0.5, max_val - 0.5) - # axarr[2].contour(annot, levels=np.unique(annot), linewidth=1, colors='g') - # axarr[2].contour(segm, levels=np.unique(segm), linewidth=1, colors='b') + # axarr[2].contour(annot, levels=np.unique(annot), linewidths=1, colors='g') + # axarr[2].contour(segm, levels=np.unique(segm), linewidths=1, colors='b') for i in range(len(axarr)): axarr[i].axis('off') @@ -427,13 +427,21 @@ def figure_annot_slic_histogram_labels(dict_label_hist, slic_size=-1, :param int slic_size: :param float slic_regul: :return Figure: + + >>> np.random.seed(0) + >>> dict_label_hist = {'a': np.tile([1, 0, 0, 0, 1], (25, 1)), + ... 'b': np.tile([0, 1, 0, 0, 1], (30, 1))} + >>> figure_annot_slic_histogram_labels(dict_label_hist) # doctest: +ELLIPSIS + """ matrix_hist_all = np.concatenate(tuple(dict_label_hist.values()), axis=0) - nb_labels = matrix_hist_all.shape[1] + lb_sums = np.sum(matrix_hist_all, axis=0) fig = plt.figure(figsize=(10, 5)) ax = fig.gca() - for i in range(nb_labels): + for i, nb in enumerate(lb_sums): + if nb == 0: + continue patches, bin_edges = np.histogram(matrix_hist_all[:, i], bins=50, density=True) bins = [(a + b) / 2. for a, b in zip(bin_edges[:-1], bin_edges[1:])] @@ -592,7 +600,7 @@ def draw_eggs_ellipse(mask_shape, pos_ant, pos_lat, pos_post, def parse_annot_rectangles(rows_slice): """ parse annotation fromDF to lists - :param row_slice: + :param rows_slice: :return: >>> import pandas as pd @@ -732,13 +740,13 @@ def draw_image_segm_points(ax, img, points, labels=None, slic=None, if slic is not None: ax.contour(slic, levels=np.unique(slic), alpha=0.5, colors=clr_slic, - linewidth=0.5) + linewidths=0.5) # fig.gca().imshow(mark_boundaries(img, slic)) if seg_contour is not None and isinstance(seg_contour, np.ndarray): assert img.shape[:2] == seg_contour.shape[:2], \ 'image size %s and segm. %s should match' \ % (repr(img.shape), repr(seg_contour.shape)) - ax.contour(seg_contour, linewidth=3, levels=np.unique(seg_contour)) + ax.contour(seg_contour, linewidths=3, levels=np.unique(seg_contour)) if labels is not None: assert len(points) == len(labels), \ 'number of points (%i) and labels (%i) should match' \ @@ -776,7 +784,7 @@ def figure_image_segm_centres(img, segm, centers=None, segm_show = segm if segm.ndim > 2: segm_show = np.argmax(segm, axis=2) - ax.contour(segm_show, cmap=cmap_contour, linewidth=0.5) + ax.contour(segm_show, cmap=cmap_contour, linewidths=0.5) if isinstance(centers, list): ax.plot(np.array(centers)[:, 1], np.array(centers)[:, 0], 'o', color=COLOR_ORANGE) @@ -878,7 +886,7 @@ def figure_rg2sp_debug_complete(seg, slic, dict_rg2sp_debug, iter_index=-1, :param ndarray seg: :param ndarray slic: :param dict_rg2sp_debug: - :param int iter: + :param int iter_index: :param int max_size: :return Figure: @@ -991,7 +999,7 @@ def make_overlap_images_chess(imgs, chess_field=SIZE_CHESS_FIELD): # copy images to the maximal image for i, im in enumerate(imgs): imgs_w[i][:im.shape[0], :im.shape[1]] = im - img = np.zeros(max_size, dtype=im.dtype) + img = np.zeros(max_size, dtype=imgs[0].dtype) idx_row = 0 for i in range(int(max_size[0] / chess_field)): idx = idx_row diff --git a/imsegm/utils/experiments.py b/imsegm/utils/experiments.py index 50a3f632..ef50e1ef 100755 --- a/imsegm/utils/experiments.py +++ b/imsegm/utils/experiments.py @@ -1,7 +1,7 @@ """ Framework for general experiments -Copyright (C) 2014-2016 Jiri Borovec +Copyright (C) 2014-2018 Jiri Borovec """ import os @@ -81,7 +81,8 @@ def __create_folder(self, time_stamp): """ # create results folder for experiments if not os.path.exists(self.params.get('path_out')): - logging.error('no results folder: %s', repr(self.p.get('path_out'))) + logging.error('no results folder: %s', + repr(self.params.get('path_out'))) self.params['path_exp'] = '' return self.params = create_experiment_folder(self.params, @@ -137,15 +138,15 @@ def create_experiment_folder(params, dir_name, stamp_unique=True, skip_load=True if not os.path.exists(path_expt): os.mkdir(path_expt) path_config = os.path.join(path_expt, CONFIG_JSON) - params.update({'computer': os.uname(), - 'path_exp': path_expt}) if os.path.exists(path_config) and not skip_load: + params_in = params logging.debug('loading saved params from file "%s"', CONFIG_JSON) with open(path_config, 'r') as fp: params = json.load(fp) - params.update({'computer': os.uname(), - 'path_exp': path_expt}) + params.update({k: params_in[k] for k in params_in if 'path' in k}) logging.info('loaded following PARAMETERS: %s', string_dict(params)) + params.update({'computer': os.uname(), + 'path_exp': path_expt}) logging.debug('saving params to file "%s"', CONFIG_JSON) with open(path_config, 'w') as f: json.dump(params, f) @@ -297,7 +298,8 @@ def create_subfolders(path_out, list_folders): class WrapExecuteSequence: """ wrapper for execution paralle of single thread as for... - >>> it = WrapExecuteSequence(lambda x: (x, x ** 2), range(5), 1) + >>> it = WrapExecuteSequence(lambda x: (x, x ** 2), range(5), + ... nb_jobs=1, ordered=True) >>> list(it) [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16)] >>> it = WrapExecuteSequence(sum, [[0, 1]] * 5, 2, desc=None) @@ -308,11 +310,22 @@ class WrapExecuteSequence: [0, 0, 0, 0, 0] """ - def __init__(self, wrap_func, iterate_vals, nb_jobs=NB_THREADS, desc=''): + def __init__(self, wrap_func, iterate_vals, nb_jobs=NB_THREADS, desc='', + ordered=False): + """ the init of this wrapper fro parallelism + + :param wrap_func: funtion which will be exectited in the iterations + :param [] iterate_vals: list or iterator which will ide in terations + :param int nb_jobs: number og jobs running in parallel + :param str desc: decrotion for the bar, + if it is set None, bar is suppresed + :param bool ordered: whether enforce ordering in the parallelism + """ self.wrap_func = wrap_func self.iterate_vals = list(iterate_vals) self.nb_jobs = nb_jobs self.desc = desc + self.ordered = ordered def __iter__(self): if self.desc is not None: @@ -323,7 +336,10 @@ def __iter__(self): if self.nb_jobs > 1: logging.debug('perform sequential in %i threads', self.nb_jobs) pool = mproc.Pool(self.nb_jobs) - for out in pool.imap_unordered(self.wrap_func, self.iterate_vals): + + pooling = pool.imap if self.ordered else pool.imap_unordered + + for out in pooling(self.wrap_func, self.iterate_vals): yield out if tqdm_bar is not None: tqdm_bar.update() @@ -339,7 +355,8 @@ def __len__(self): return len(self.iterate_vals) -# def wrap_execute_parallel(wrap_func, iterate_vals, nb_jobs=NB_THREADS, desc=''): +# def wrap_execute_parallel(wrap_func, iterate_vals, +# nb_jobs=NB_THREADS, desc=''): # """ wrapper for execution paralle of single thread as for... # # :param func wrap_func: diff --git a/imsegm/utils/read_zvi.py b/imsegm/utils/read_zvi.py index 166db15a..78b14412 100755 --- a/imsegm/utils/read_zvi.py +++ b/imsegm/utils/read_zvi.py @@ -14,7 +14,7 @@ >>> import os, sys >>> sys.path += [os.path.abspath(os.path.join('..', '..'))] >>> import imsegm.utils.data_io as tl_io ->>> path_file = os.path.join('images', 'others', 'sample.zvi') +>>> path_file = os.path.join('data_images', 'others', 'sample.zvi') >>> path_file = tl_io.update_path(path_file) >>> n = get_layer_count(path_file) >>> get_dir(path_file) # doctest: +ELLIPSIS diff --git a/notebooks/RG2SP_region-growing.ipynb b/notebooks/RG2Sp_region-growing.ipynb similarity index 99% rename from notebooks/RG2SP_region-growing.ipynb rename to notebooks/RG2Sp_region-growing.ipynb index 3c7cbbd0..6d6654ac 100755 --- a/notebooks/RG2SP_region-growing.ipynb +++ b/notebooks/RG2Sp_region-growing.ipynb @@ -77,8 +77,8 @@ "source": [ "COLORS = 'bgrmyck'\n", "RG2SP_THRESHOLDS = seg_rg.DEFAULT_RG2SP_THRESHOLDS\n", - "PATH_IMAGES = tl_io.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", - "PATH_DATA = tl_io.update_path('data', absolute=True)\n", + "PATH_IMAGES = tl_io.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", + "PATH_DATA = tl_io.update_path('data_images', absolute=True)\n", "PATH_OUT = tl_io.update_path('output', absolute=True)\n", "print ([os.path.basename(p) for p in glob.glob(os.path.join(PATH_IMAGES, '*')) if os.path.isdir(p)])" ] diff --git a/notebooks/RG2Sp_shape-models.ipynb b/notebooks/RG2Sp_shape-models.ipynb index 9d01b142..8b047c32 100755 --- a/notebooks/RG2Sp_shape-models.ipynb +++ b/notebooks/RG2Sp_shape-models.ipynb @@ -80,8 +80,8 @@ ], "source": [ "COLORS = 'bgrmyck'\n", - "PATH_IMAGES = tl_io.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", - "PATH_DATA = tl_io.update_path('data', absolute=True)\n", + "PATH_IMAGES = tl_io.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", + "PATH_DATA = tl_io.update_path('data_images', absolute=True)\n", "PATH_OUT = tl_io.update_path('output', absolute=True)\n", "print ([os.path.basename(p) for p in glob.glob(os.path.join(PATH_IMAGES, '*')) if os.path.isdir(p)])\n", "dir_annot = os.path.join(PATH_IMAGES, 'annot_eggs')\n", diff --git a/notebooks/egg-center_candidates-clustering.ipynb b/notebooks/egg-center_candidates-clustering.ipynb index 87a3f370..c2910d51 100644 --- a/notebooks/egg-center_candidates-clustering.ipynb +++ b/notebooks/egg-center_candidates-clustering.ipynb @@ -78,7 +78,7 @@ "outputs": [], "source": [ "name = 'insitu7545'\n", - "PATH_BASE = tl_io.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", + "PATH_BASE = tl_io.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", "PATH_IMAGES = os.path.join(PATH_BASE, 'image')\n", "PATH_SEGM = os.path.join(PATH_BASE, 'segm')\n", "PATH_ANNOT = os.path.join(PATH_BASE, 'annot_eggs')\n", diff --git a/notebooks/egg-detect_ellipse-fitting.ipynb b/notebooks/egg-detect_ellipse-fitting.ipynb index cd51700e..6049773d 100755 --- a/notebooks/egg-detect_ellipse-fitting.ipynb +++ b/notebooks/egg-detect_ellipse-fitting.ipynb @@ -74,7 +74,7 @@ }, "outputs": [], "source": [ - "PATH_BASE = tl_io.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", + "PATH_BASE = tl_io.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", "PATH_IMAGES = os.path.join(PATH_BASE, 'image')\n", "PATH_SEGM = os.path.join(PATH_BASE, 'segm')\n", "PATH_ANNOT = os.path.join(PATH_BASE, 'annot_eggs')\n", diff --git a/notebooks/egg_segment_graphcut.ipynb b/notebooks/egg_segment_graphcut.ipynb index ede08a3f..ac22a0ed 100755 --- a/notebooks/egg_segment_graphcut.ipynb +++ b/notebooks/egg_segment_graphcut.ipynb @@ -72,7 +72,7 @@ ], "source": [ "COLORS = 'bgrmyck'\n", - "PATH_IMAGES = tl_io.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", + "PATH_IMAGES = tl_io.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", "print ([os.path.basename(p) for p in glob.glob(os.path.join(PATH_IMAGES, '*')) if os.path.isdir(p)])\n", "dir_img = os.path.join(PATH_IMAGES, 'image')\n", "dir_segm = os.path.join(PATH_IMAGES, 'segm')\n", diff --git a/notebooks/segment-2d_slic-fts-classif-gc.ipynb b/notebooks/segment-2d_slic-fts-classif-gc.ipynb index 880cbcb9..1cf523d7 100755 --- a/notebooks/segment-2d_slic-fts-classif-gc.ipynb +++ b/notebooks/segment-2d_slic-fts-classif-gc.ipynb @@ -58,7 +58,7 @@ } ], "source": [ - "path_dir = tl_data.update_path(os.path.join('images', 'drosophila_ovary_slice'))\n", + "path_dir = tl_data.update_path(os.path.join('data_images', 'drosophila_ovary_slice'))\n", "path_images = os.path.join(path_dir, 'image')\n", "print ([os.path.basename(p) for p in glob.glob(os.path.join(path_images, '*.jpg'))])\n", "# loading images\n", @@ -169,7 +169,7 @@ "outputs": [], "source": [ "dict_debug = {}\n", - "seg = segm_pipe.segment_color2d_slic_features_classif_graphcut(img2, classif, clr_space, sp_size, sp_regul, \n", + "seg = segm_pipe.segment_color2d_slic_features_model_graphcut(img2, classif, clr_space, sp_size, sp_regul, \n", " gc_regul=1., dict_features=dict_features, gc_edge_type='model', dict_debug_imgs=dict_debug)" ] }, diff --git a/notebooks/segment-2d_slic-fts-model-gc.ipynb b/notebooks/segment-2d_slic-fts-model-gc.ipynb index 3862a023..2b3f2d6a 100644 --- a/notebooks/segment-2d_slic-fts-model-gc.ipynb +++ b/notebooks/segment-2d_slic-fts-model-gc.ipynb @@ -68,9 +68,9 @@ } ], "source": [ - "path_dir = tl_data.update_path(os.path.join('images', 'see_starfish'))\n", + "path_dir = tl_data.update_path(os.path.join('data_images', 'others'))\n", "print ([os.path.basename(p) for p in glob.glob(os.path.join(path_dir, '*.jpg'))])\n", - "path_img = os.path.join(path_dir, 'stars_nb2.jpg')\n", + "path_img = os.path.join(path_dir, 'sea_starfish-2.jpg')\n", "\n", "img = np.array(Image.open(path_img))\n", "\n", @@ -122,8 +122,8 @@ }, "outputs": [], "source": [ - "scaler, pca, model = segm_pipe.estim_model_classes_group([img], nb_classes, clr_space, sp_size, sp_regul, \n", - " dict_features=dict_features, pca_coef=None, proba_type='GMM')" + "model, _ = segm_pipe.estim_model_classes_group([img], nb_classes, clr_space, sp_size, sp_regul, \n", + " dict_features=dict_features, pca_coef=None, estim_model='GMM')" ] }, { @@ -151,7 +151,7 @@ ], "source": [ "dict_debug = {}\n", - "seg = segm_pipe.segment_color2d_slic_features_model_graphcut(img, scaler, pca, model, clr_space, sp_size, sp_regul, \n", + "seg = segm_pipe.segment_color2d_slic_features_model_graphcut(img, model, clr_space, sp_size, sp_regul, \n", " dict_features=dict_features, gc_regul=5., gc_edge_type='color', dict_debug_imgs=dict_debug)" ] }, diff --git a/notebooks/transform-img-plane_inter-circle.ipynb b/notebooks/transform-img-plane_inter-circle.ipynb index b27a3643..2a2ce549 100755 --- a/notebooks/transform-img-plane_inter-circle.ipynb +++ b/notebooks/transform-img-plane_inter-circle.ipynb @@ -43,7 +43,7 @@ } ], "source": [ - "path_image = os.path.abspath(os.path.join('images', 'other', 'industry.jpg'))\n", + "path_image = os.path.abspath(os.path.join('data_images', 'other', 'industry.jpg'))\n", "img = Image.open(path_image)\n", "img = img.resize((int(img.width * 0.5), int(img.height * 0.5)), Image.ANTIALIAS)\n", "img = np.array(img)\n", diff --git a/setup.cfg b/setup.cfg index 224a7795..0c61b02b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,4 @@ [metadata] -description-file = README.md \ No newline at end of file +description-file = README.md +license-file = LICENSE +requirements-file = requirements.txt \ No newline at end of file diff --git a/setup.py b/setup.py index 1c5b7859..65c42012 100644 --- a/setup.py +++ b/setup.py @@ -47,9 +47,11 @@ def finalize_options(self): def _parse_requirements(file_path): - pip_version = list(map(int, pkg_resources.get_distribution('pip').version.split('.')[:2])) + pip_ver = pkg_resources.get_distribution('pip').version + pip_version = list(map(int, pip_ver.split('.')[:2])) if pip_version >= [6, 0]: - raw = pip.req.parse_requirements(file_path, session=pip.download.PipSession()) + raw = pip.req.parse_requirements(file_path, + session=pip.download.PipSession()) else: raw = pip.req.parse_requirements(file_path) return [str(i.req) for i in raw]