Skip to content
This repository has been archived by the owner on Feb 11, 2023. It is now read-only.

Commit

Permalink
drop GCO dependency
Browse files Browse the repository at this point in the history
* absolute imports
* split py2 py3 reqs
* CI: min-max build
* fix skimage region prop.
* general flake8 config
* CI: nose -> pytest
* fix numpy loads
* fix docs links
  • Loading branch information
Borda committed Jan 24, 2020
1 parent db6904b commit 0f339ff
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 85 deletions.
46 changes: 31 additions & 15 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,64 @@

# See doc/travis_notes.txt for some guidelines

dist: xenial # Ubuntu 16.04

env:
global:
- CODACY_PROJECT_TOKEN=ea6533a96dfb4b6a9b6b45fc134fabdf
- DISPLAY=""

language: python

sudo: false

python:
- 2.7
# - 3.4 # will be deprecated for pandas
- 3.5
- 3.6
- 3.7
matrix:
include:
- dist: xenial # Ubuntu 16.04
python: 2.7
- dist: xenial # Ubuntu 16.04
python: 3.5
- dist: xenial # Ubuntu 16.04
python: 3.6
env: MIN_REQUIREMENTS=1
#- dist: xenial # Ubuntu 16.04
# python: 3.7
# env: MIN_REQUIREMENTS=1
- dist: bionic # Ubuntu 18.04
python: 3.6
- dist: bionic # Ubuntu 18.04
python: 3.7

# See http://docs.travis-ci.com/user/caching/#pip-cache
cache: pip

before_install:
- pip install --upgrade pip
# rewrite all minimal requirements as strict
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then
cp requirements-py27.txt requirements-ci.txt ;
else if [[ "${MIN_REQUIREMENTS}" == "1" ]]; then
python -c "req = open('requirements.txt').read().replace('>', '=') ; open('requirements-ci.txt', 'w').write(req)" ;
else
cp requirements.txt requirements-ci.txt ;
fi
fi

install:
- mkdir libs
- pip install -r requirements.txt
- pip install -r requirements-ci.txt -U
- pip install -r ./tests/requirements.txt
- pip list

before_script:
- rm requirements-ci.txt
- mkdir output && mkdir results
- gcc --version ; python --version ; pip --version ; pwd ; ls -l
- check-manifest
- python setup.py check -m -s
- python setup.py check --metadata --strict

script:
- python setup.py build_ext --inplace
# - pytest imsegm -v --doctest-modules
- nosetests imsegm tests -v --exe --with-doctest --with-xunit --with-coverage --cover-package=imsegm
- flake8 . --ignore=E402,E731 --max-line-length=100
- python setup.py install
- coverage run -m py.test imsegm tests --doctest-modules --flake8
# - nosetests imsegm tests -v --exe --with-doctest --with-xunit --with-coverage --cover-package=imsegm
- python setup.py install --dry-run

after_success:
- codecov # public repository on Travis CI
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ include LICENSE

# Include the Requirements
include requirements.txt
include requirements-py27.txt

# Include package
recursive-include imsegm *.py *.pyx
Expand Down
4 changes: 2 additions & 2 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ references:
unset DISPLAY
mkdir output && mkdir results && mkdir test-reports
coverage run --source imsegm -m py.test imsegm tests -v --doctest-modules --junitxml=test-reports/pytest_junit.xml
python setup.py check -m -s
flake8 . --ignore=E402,E731 --max-line-length=100
python setup.py check --metadata --strict
flake8 .
build_project: &build_project
run:
Expand Down
6 changes: 5 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ def find_source():
obj = getattr(obj, part)
fname = inspect.getsourcefile(obj)
# https://github.com/rtfd/readthedocs.org/issues/5735
if any([s in fname for s in ('readthedocs', 'checkouts')]):
if any([s in fname for s in ('readthedocs', 'rtfd', 'checkouts')]):
path_top = os.path.abspath(os.path.join('..', '..', '..'))
fname = os.path.relpath(fname, start=path_top)
else:
Expand All @@ -313,6 +313,10 @@ def find_source():
# import subprocess
# tag = subprocess.Popen(['git', 'rev-parse', 'HEAD'], stdout=subprocess.PIPE,
# universal_newlines=True).communicate()[0][:-1]
branch = filename.split('/')[0]
# do mapping from latest tags to master
branch = {'latest': 'master', 'stable': 'master'}.get(branch, branch)
filename = '/'.join([branch] + filename.split('/')[1:])
return "https://github.com/%s/%s/blob/%s" \
% (github_user, github_repo, filename)

Expand Down
4 changes: 2 additions & 2 deletions experiments_ovary_centres/run_center_candidate_training.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def load_image_segm_center(idx_row, path_out=None, dict_relabel=None):

seg_ext = os.path.splitext(os.path.basename(row_path['path_segm']))[-1]
if seg_ext == '.npz':
with np.load(row_path['path_segm']) as npzfile:
with np.load(row_path['path_segm'], allow_pickle=True) as npzfile:
segm = npzfile[npzfile.files[0]]
if dict_relabel is not None:
segm = seg_lbs.merge_probab_labeling_2d(segm, dict_relabel)
Expand Down Expand Up @@ -620,7 +620,7 @@ def load_dump_data(path_dump_data):
logging.info('loading dumped data "%s"', path_dump_data)
# with open(os.path.join(path_out, NAME_DUMP_TRAIN_DATA), 'r') as f:
# dict_data = pickle.load(f)
npz_file = np.load(path_dump_data, encoding='bytes')
npz_file = np.load(path_dump_data, encoding='bytes', allow_pickle=True)
dict_imgs = dict(npz_file['dict_images'].tolist())
dict_segms = dict(npz_file['dict_segms'].tolist())
dict_slics = dict(npz_file['dict_slics'].tolist())
Expand Down
4 changes: 2 additions & 2 deletions experiments_ovary_detect/run_ovary_egg-segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ def segment_rg2sp_greedy(slic, seg, centers, labels_fg_prob, path_model,
dict_thresholds=RG2SP_THRESHOLDS, debug_export=''):
""" wrapper for region growing method with some debug exporting """
if os.path.splitext(path_model)[-1] == '.npz':
shape_model = np.load(path_model)
shape_model = np.load(path_model, allow_pickle=True)
else:
shape_model = pickle.load(open(path_model, 'rb'))
dict_debug = dict() if os.path.isdir(debug_export) else None
Expand Down Expand Up @@ -550,7 +550,7 @@ def segment_rg2sp_graphcut(slic, seg, centers, labels_fg_prob, path_model,
dict_thresholds=RG2SP_THRESHOLDS, debug_export=''):
""" wrapper for region growing method with some debug exporting """
if os.path.splitext(path_model)[-1] == '.npz':
shape_model = np.load(path_model)
shape_model = np.load(path_model, allow_pickle=True)
else:
shape_model = pickle.load(open(path_model, 'rb'))
dict_debug = dict() if os.path.isdir(debug_export) else None
Expand Down
2 changes: 1 addition & 1 deletion experiments_segmentation/run_segm_slic_classif_graphcut.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def load_dump_data(path_dump_data):
logging.info('loading dumped data "%s"', path_dump_data)
# with open(os.path.join(path_out, NAME_DUMP_TRAIN_DATA), 'r') as f:
# dict_data = pickle.load(f)
npz_file = np.load(path_dump_data)
npz_file = np.load(path_dump_data, allow_pickle=True)
dict_imgs = dict(npz_file['dict_images'].tolist())
dict_annot = dict(npz_file['dict_annot'].tolist())
dict_slics = dict(npz_file['dict_slics'].tolist())
Expand Down
2 changes: 1 addition & 1 deletion imsegm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import traceback
traceback.print_exc()

__version__ = '0.1.7'
__version__ = '0.1.8'
__author__ = 'Jiri Borovec'
__author_email__ = 'jiri.borovec@fel.cvut.cz'
__license__ = 'BSD 3-clause'
Expand Down
2 changes: 1 addition & 1 deletion imsegm/annotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from scipy import interpolate

# sys.path += [os.path.abspath('.'), os.path.abspath('..')] # Add path to root
from .utilities.data_io import io_imread
from imsegm.utilities.data_io import io_imread

#: names of annotated columns
COLUMNS_POSITION = ('ant_x', 'ant_y', 'post_x', 'post_y', 'lat_x', 'lat_y')
Expand Down
4 changes: 2 additions & 2 deletions imsegm/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
except Exception:
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV

from .labeling import relabel_max_overlap_unique
from .utilities.experiments import WrapExecuteSequence, nb_workers
from imsegm.labeling import relabel_max_overlap_unique
from imsegm.utilities.experiments import WrapExecuteSequence, nb_workers

# NAME_FILE_RESULTS = 'results.csv'
#: name template forexporting trained classifier (adding classifier name and version)
Expand Down
6 changes: 3 additions & 3 deletions imsegm/ellipse_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@

from skimage.measure import fit as sk_fit
# from skimage.measure.fit import EllipseModel # fix in future skimage>0.13.0
from .utilities.drawing import ellipse
from .descriptors import (
from imsegm.utilities.drawing import ellipse
from imsegm.descriptors import (
reduce_close_points, compute_ray_features_segm_2d, reconstruct_ray_features_2d)
from .superpixels import (
from imsegm.superpixels import (
segment_slic_img2d, superpixel_centers, make_graph_segm_connect_grid2d_conn4)

# INIT_MASK_BORDER = 50.
Expand Down
12 changes: 8 additions & 4 deletions imsegm/graph_cuts.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@
import logging

import numpy as np
from gco import cut_general_graph
try:
from gco import cut_general_graph
except Exception:
print('WARNING: Missing Grah-Cut (GCO) library,'
' please install it from https://github.com/Borda/pyGCO.')
from skimage import filters
from sklearn import metrics, preprocessing
from sklearn import pipeline, cluster, mixture, decomposition

from .utilities.drawing import (
from imsegm.utilities.drawing import (
draw_graphcut_unary_cost_segments, draw_graphcut_weighted_edges, draw_color_labeling)
from .superpixels import (
from imsegm.superpixels import (
make_graph_segm_connect_grid2d_conn4, make_graph_segm_connect_grid3d_conn6, superpixel_centers)
from .descriptors import compute_selected_features_img2d
from imsegm.descriptors import compute_selected_features_img2d

#: define munber of iteration in Grap-Cut optimization
DEFAULT_GC_ITERATIONS = 25
Expand Down
2 changes: 1 addition & 1 deletion imsegm/labeling.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from scipy import ndimage
import skimage.segmentation as sk_segm

from .utilities.data_io import get_image2d_boundary_color
from imsegm.utilities.data_io import get_image2d_boundary_color


def neighbour_connect4(seg, label, pos):
Expand Down
12 changes: 6 additions & 6 deletions imsegm/pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
import skimage.color as sk_color
# from sklearn import mixture

from .utilities.experiments import WrapExecuteSequence, nb_workers
from .graph_cuts import segment_graph_cut_general, estim_class_model
from .superpixels import segment_slic_img2d, segment_slic_img3d_gray
from .descriptors import (
from imsegm.utilities.experiments import WrapExecuteSequence, nb_workers
from imsegm.graph_cuts import segment_graph_cut_general, estim_class_model
from imsegm.superpixels import segment_slic_img2d, segment_slic_img3d_gray
from imsegm.descriptors import (
FEATURES_SET_COLOR, norm_features, compute_selected_features_img2d,
compute_selected_features_gray3d)
from .labeling import histogram_regions_labels_norm
from .classification import (
from imsegm.labeling import histogram_regions_labels_norm
from imsegm.classification import (
DEFAULT_CLASSIF_NAME, DEFAULT_CLUSTERING, convert_set_features_labels_2_dataset,
CrossValidateGroups, create_classif_search_train_export)

Expand Down
27 changes: 16 additions & 11 deletions imsegm/region_growing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@
from scipy import stats, ndimage, interpolate
from sklearn import cluster, mixture
from skimage import morphology
from gco import cut_general_graph, cut_grid_graph

from .graph_cuts import MAX_PAIRWISE_COST, get_vertexes_edges, compute_spatial_dist
from .labeling import histogram_regions_labels_norm
from .descriptors import (
try:
from gco import cut_general_graph, cut_grid_graph
except Exception:
print('WARNING: Missing Grah-Cut (GCO) library,'
' please install it from https://github.com/Borda/pyGCO.')

from imsegm.graph_cuts import MAX_PAIRWISE_COST, get_vertexes_edges, compute_spatial_dist
from imsegm.labeling import histogram_regions_labels_norm
from imsegm.descriptors import (
compute_ray_features_segm_2d, interpolate_ray_dist, shift_ray_features)
from .superpixels import (
from imsegm.superpixels import (
superpixel_centers, get_neighboring_segments, make_graph_segm_connect_grid2d_conn4)

#: all infinty values in Grah-Cut terms replace by this value
Expand Down Expand Up @@ -364,10 +368,11 @@ def transform_rays_model_cdf_mixture(list_rays, coef_components=1):
>>> list_rays = [[9, 4, 9], [4, 9, 7], [9, 7, 11], [10, 8, 10],
... [9, 11, 8], [4, 8, 5], [8, 10, 6], [9, 7, 11]]
>>> mm, cdist = transform_rays_model_cdf_mixture(list_rays)
>>> np.round(cdist, 1).tolist() # doctest: +NORMALIZE_WHITESPACE
[[1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.8, 0.6, 0.2, 0.0],
[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.5, 0.2, 0.0],
[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.7, 0.5, 0.2, 0.0]]
>>> # the rounding variate a bit according GMM estimated model
>>> np.round(np.array(cdist) * 4) / 4. # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
array([[ 1. , 1. , 1. , 1. , 1. , 1. , 0.75, 0.75, 0.5 , 0.25, 0. ],
[ 1. , 1. , 1. , 1. , 1. , 1. , 1. , 0.75, 0.5 , 0.25, 0. ],
[ 1. , 1. , 1. , 1. , 1. , 1. , ..., 0.75, 0.5 , 0.25, 0. ]])
"""
rays = np.array(list_rays)
ms = cluster.MeanShift()
Expand Down Expand Up @@ -481,7 +486,7 @@ def transform_rays_model_cdf_spectral(list_rays, nb_components=5):
>>> np.round(cdist, 1).tolist() # doctest: +NORMALIZE_WHITESPACE
[[1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.6, 0.5, 0.2, 0.0],
[1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.9, 0.7, 0.5, 0.2, 0.0],
[1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.7, 0.5, 0.3, 0.0]]
[1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.7, 0.5, 0.3, 0.0]]
"""
rays = np.array(list_rays)
sc = cluster.SpectralClustering(nb_components)
Expand Down
41 changes: 25 additions & 16 deletions imsegm/utilities/data_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
Copyright (C) 2015-2018 Jiri Borovec <jiri.borovec@fel.cvut.cz>
"""
from __future__ import absolute_import

import os
import re
Expand All @@ -20,7 +19,7 @@
from skimage import exposure, io, color, measure
import nibabel

from . import read_zvi
from imsegm.utilities.read_zvi import load_image as load_zvi

#: position columns
COLUMNS_COORDS = ['X', 'Y']
Expand Down Expand Up @@ -733,7 +732,7 @@ def load_zvi_volume_double_band_split(path_img):
(2, 488, 648)
"""
assert os.path.isfile(path_img), 'missing: %s' % path_img
img = read_zvi.load_image(path_img)
img = load_zvi(path_img)
nb_half = img.shape[0] / 2
img_b1 = img[:int(nb_half)]
img_b2 = img[int(nb_half):]
Expand Down Expand Up @@ -1058,14 +1057,21 @@ def add_padding(img_size, padding, min_row, min_col, max_row, max_col):
return min_row, min_col, max_row, max_col


def cut_object(img, mask, padding, use_mask=False, bg_color=None):
""" cut an object fro image according binary object segmentation
# prepare a simple mask with one horizontal segment
prop = measure.regionprops(np.array([[0] * 20, [1] * 20, [0] * 20], dtype=int))[0]
#: according to skimage version the major axis are swapped
PROP_ROTATION_OFFSET = prop.orientation

:param ndarray img:
:param ndarray mask:

def cut_object(img, mask, padding, use_mask=False, bg_color=None, allow_rotate=True):
""" cut an object from image according binary object segmentation
:param ndarray img: inout image
:param ndarray mask: segmentation
:param int padding: set padding around segmented object
:param use_mask: fill BG values also outside the mask
:param bool use_mask: fill BG values also outside the mask
:param bg_color: set as default values outside bounding box
:param bool allow_rotate: allowing rotate object to get minimal bbox
:return:
>>> img = np.ones((10, 20), dtype=int)
Expand All @@ -1079,7 +1085,7 @@ def cut_object(img, mask, padding, use_mask=False, bg_color=None):
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])
>>> cut_object(img, mask, 2, use_mask=True)
>>> cut_object(img, mask, 2, use_mask=True, allow_rotate=False)
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1],
Expand All @@ -1096,15 +1102,18 @@ def cut_object(img, mask, padding, use_mask=False, bg_color=None):
if not bg_color:
bg_color = get_image2d_boundary_color(img)

rotate = np.rad2deg(prop.orientation)
shift = prop.centroid - (np.array(mask.shape) / 2.)
shift = np.append(shift, np.zeros(img.ndim - mask.ndim))
if allow_rotate:
rotate = np.rad2deg(prop.orientation - PROP_ROTATION_OFFSET)
shift = prop.centroid - (np.array(mask.shape) / 2.)
shift = np.append(shift, np.zeros(img.ndim - mask.ndim))

mask = ndimage.interpolation.shift(mask, -shift[:mask.ndim], order=0)
mask = ndimage.rotate(mask, -rotate, order=0, mode='constant', cval=np.nan)

mask = ndimage.interpolation.shift(mask, -shift[:mask.ndim], order=0)
mask = ndimage.rotate(mask, -rotate, order=0, mode='constant', cval=np.nan)
img = ndimage.interpolation.shift(img, -shift[:img.ndim], order=0)
img = ndimage.rotate(img, -rotate, order=0, mode='constant', cval=np.nan)

img_cut = ndimage.interpolation.shift(img, -shift[:img.ndim], order=0)
img_cut = ndimage.rotate(img_cut, -rotate, order=0, mode='constant', cval=np.nan)
img_cut = img.copy()
img_cut[np.isnan(mask), ...] = bg_color
mask[np.isnan(mask)] = bg_mask

Expand Down

0 comments on commit 0f339ff

Please sign in to comment.