mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-04 22:12:52 +08:00
Merge tag 'v0.9.0' into releases
* tag 'v0.9.0': (1278 commits) Update docversions correctly for 0.9.x. Mark BRIEF and Censure as experimental in release notes. Update gh-pages instructions in RELEASE.txt. Fix markup error in marching cubes docs. Correctly determine version number from module. Update version in docs. Update versions for 0.9.0 release. Update 0.9 release notes with new features. Contrib script now shows PRs and merges. Update contributors script to count by date. Speed up memory views in watershed function Speed up memory views in skeletonize function Speed up memory views in line drawing function Speed up memory views in local_binary_pattern Fix Py3 exception in GTK plugin. Fix Python 3 list handling. Dictionary interface to preserve backward compatibility for RegionProps. Update author order for 0.9 release notes. Add contributors to 0.9. Rewrite contributor lister in Python. ...
This commit is contained in:
+15
-2
@@ -1,7 +1,7 @@
|
||||
# vim ft=yaml
|
||||
# travis-ci.org definition for skimage build
|
||||
#
|
||||
# We pretend to be erlang because we need can't use the python support in
|
||||
# We pretend to be erlang because we can't use the python support in
|
||||
# travis-ci; it uses virtualenvs, they do not have numpy, scipy, matplotlib,
|
||||
# and it is impractical to build them
|
||||
|
||||
@@ -19,11 +19,24 @@ install:
|
||||
- sudo easy_install$PYSUF pip
|
||||
- sudo pip-$PYVER install cython
|
||||
- sudo apt-get install libfreeimage3
|
||||
- if [[ $PYVER == '2.7' ]]; then sudo apt-get install $PYTHON-matplotlib; fi
|
||||
- if [[ $PYVER == '3.2' ]]; then sudo pip-$PYVER install git+git://github.com/matplotlib/matplotlib.git@v1.2.x; fi
|
||||
- sudo pip-$PYVER install flake8
|
||||
- $PYTHON setup.py build
|
||||
- sudo $PYTHON setup.py install
|
||||
script:
|
||||
# Check if setup.py's match bento.info
|
||||
- $PYTHON check_bento_build.py
|
||||
# Change into an innocuous directory and find tests from installation
|
||||
- mkdir $HOME/.matplotlib
|
||||
- "echo 'backend : Agg' > $HOME/.matplotlib/matplotlibrc"
|
||||
- "echo 'backend.qt4 : PyQt4' >> $HOME/.matplotlib/matplotlibrc"
|
||||
- mkdir for_test
|
||||
- cd for_test
|
||||
- nosetests-$PYVER --exe -v --cover-package=skimage skimage
|
||||
|
||||
# Change back to repository root directory and run all doc examples
|
||||
- cd ..
|
||||
- for f in doc/examples/*.py; do $PYTHON "$f"; if [ $? -ne 0 ]; then exit 1; fi done
|
||||
- for f in doc/examples/applications/*.py; do $PYTHON "$f"; if [ $? -ne 0 ]; then exit 1; fi done
|
||||
# Run pep8 and flake tests
|
||||
- flake8 --exit-zero --exclude=test_*,six.py skimage doc/examples viewer_examples
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
Development process
|
||||
-------------------
|
||||
|
||||
Here's the long and short of it:
|
||||
|
||||
1. If you are a first-time contributor:
|
||||
|
||||
* Go to `https://github.com/scikit-image/scikit-image
|
||||
<http://github.com/scikit-image/scikit-image>`_ and click the
|
||||
"fork" button to create your own copy of the project.
|
||||
|
||||
* Clone the project to your local computer::
|
||||
|
||||
git clone git@github.com:your-username/scikit-image.git
|
||||
|
||||
* Add upstream repository::
|
||||
|
||||
git remote add upstream git@github.com:scikit-image/scikit-image.git
|
||||
|
||||
* Now, you have remote repositories named:
|
||||
|
||||
- ``upstream``, which refers to the ``scikit-image`` repository
|
||||
- ``origin``, which refers to your personal fork
|
||||
|
||||
2. Develop your contribution:
|
||||
|
||||
* Pull the latest changes from upstream::
|
||||
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
|
||||
* Create a branch for the feature you want to work on. Since the
|
||||
branch name will appear in the merge message, use a sensible name
|
||||
such as 'transform-speedups'::
|
||||
|
||||
git checkout -b transform-speedups
|
||||
|
||||
* Commit locally as you progress (``git add`` and ``git commit``)
|
||||
|
||||
3. To submit your contribution:
|
||||
|
||||
* Push your changes back to your fork on GitHub::
|
||||
|
||||
git push origin transform-speedups
|
||||
|
||||
* Go to GitHub. The new branch will show up with a Pull Request button -
|
||||
click it.
|
||||
|
||||
* If you want, post on the `mailing list
|
||||
<http://groups.google.com/group/scikit-image>`_ to explain your changes or
|
||||
to ask for review.
|
||||
|
||||
For a more detailed discussion, read these :doc:`detailed documents
|
||||
<gitwash/index>` on how to use Git with ``scikit-image``
|
||||
(`<http://scikit-image.org/docs/dev/gitwash/index.html>`_).
|
||||
|
||||
.. note::
|
||||
|
||||
To reviewers: add a short explanation of what a branch did to the merge
|
||||
message and, if closing a bug, also add "Closes gh-123" where 123 is the
|
||||
bug number.
|
||||
|
||||
|
||||
Divergence between ``upstream master`` and your feature branch
|
||||
..............................................................
|
||||
|
||||
Do *not* ever merge the main branch into yours. If GitHub indicates that the
|
||||
branch of your Pull Request can no longer be merged automatically, rebase
|
||||
onto master::
|
||||
|
||||
git checkout master
|
||||
git pull upstream master
|
||||
git checkout transform-speedups
|
||||
git rebase master
|
||||
|
||||
If any conflicts occur, fix the according files and continue::
|
||||
|
||||
git add conflict-file1 conflict-file2
|
||||
git rebase --continue
|
||||
|
||||
However, you should only rebase your own branches and must generally not
|
||||
rebase any branch which you collaborate on with someone else.
|
||||
|
||||
Finally, you must push your rebased branch::
|
||||
|
||||
git push --force origin transform-speedups
|
||||
|
||||
(If you are curious, here's a further discussion on the
|
||||
`dangers of rebasing <http://tinyurl.com/lll385>`__.
|
||||
Also see this `LWN article <http://tinyurl.com/nqcbkj>`__.)
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
* All code should have tests (see `test coverage`_ below for more details).
|
||||
* All code should be documented, to the same
|
||||
`standard <http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines>`_
|
||||
as NumPy and SciPy.
|
||||
* For new functionality, always add an example to the
|
||||
gallery.
|
||||
* No changes should be committed without review. Ask on the
|
||||
`mailing list <http://groups.google.com/group/scikit-image>`_ if
|
||||
you get no response to your pull request.
|
||||
**Never merge your own pull request.**
|
||||
* Examples in the gallery should have a maximum figure width of 8 inches.
|
||||
|
||||
Stylistic Guidelines
|
||||
--------------------
|
||||
|
||||
* Set up your editor to remove trailing whitespace. Follow `PEP08
|
||||
<www.python.org/dev/peps/pep-0008/>`__. Check code with pyflakes / flake8.
|
||||
|
||||
* Use numpy data types instead of strings (``np.uint8`` instead of
|
||||
``"uint8"``).
|
||||
|
||||
* Use the following import conventions::
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
cimport numpy as cnp # in Cython code
|
||||
|
||||
* When documenting array parameters, use ``image : (M, N) ndarray``
|
||||
and then refer to ``M`` and ``N`` in the docstring, if necessary.
|
||||
|
||||
* Functions should support all input image dtypes. Use utility functions such
|
||||
as ``img_as_float`` to help convert to an appropriate type. The output
|
||||
format can be whatever is most efficient. This allows us to string together
|
||||
several functions into a pipeline, e.g.::
|
||||
|
||||
hough(canny(my_image))
|
||||
|
||||
* Use ``Py_ssize_t`` as data type for all indexing, shape and size variables
|
||||
in C/C++ and Cython code.
|
||||
|
||||
Test coverage
|
||||
-------------
|
||||
|
||||
Tests for a module should ideally cover all code in that module,
|
||||
i.e., statement coverage should be at 100%.
|
||||
|
||||
To measure the test coverage, install
|
||||
`coverage.py <http://nedbatchelder.com/code/coverage/>`__
|
||||
(using ``easy_install coverage``) and then run::
|
||||
|
||||
$ make coverage
|
||||
|
||||
This will print a report with one line for each file in `skimage`,
|
||||
detailing the test coverage::
|
||||
|
||||
Name Stmts Exec Cover Missing
|
||||
------------------------------------------------------------------------------
|
||||
skimage/color/colorconv 77 77 100%
|
||||
skimage/filter/__init__ 1 1 100%
|
||||
...
|
||||
|
||||
Activate Travis-CI for your fork (optional)
|
||||
-------------------------------------------
|
||||
|
||||
Travis-CI checks all unittests in the project to prevent breakage.
|
||||
|
||||
Before sending a pull request, you may want to check that Travis-CI
|
||||
successfully passes all tests. To do so,
|
||||
|
||||
* Go to `Travis-CI <http://travis-ci.org/>`__ and follow the Sign In link at the top
|
||||
|
||||
* Go to your `profile page <https://travis-ci.org/profile>`__ and switch on your
|
||||
scikit-image fork
|
||||
|
||||
It corresponds to steps one and two in
|
||||
`Travis-CI documentation <http://about.travis-ci.org/docs/user/getting-started/>`__
|
||||
(Step three is already done in scikit-image).
|
||||
|
||||
Thus, as soon as you push your code to your fork, it will trigger Travis-CI,
|
||||
and you will receive an email notification when the process is done.
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Please `report bugs on GitHub <https://github.com/scikit-image/scikit-image/issues>`_.
|
||||
+26
-4
@@ -48,7 +48,7 @@
|
||||
Incorporating CellProfiler's Sobel edge detector, build and bug fixes.
|
||||
Radon transform, template matching.
|
||||
|
||||
- Emmanuelle Guillart
|
||||
- Emmanuelle Gouillart
|
||||
Total variation noise filtering, integration of CellProfiler's
|
||||
mathematical morphology tools, random walker segmentation,
|
||||
tutorials, and more.
|
||||
@@ -113,7 +113,8 @@
|
||||
Fixes and tests for Histograms of Oriented Gradients.
|
||||
|
||||
- Joshua Warner
|
||||
Multichannel random walker segmentation.
|
||||
Multichannel random walker segmentation, unified peak finder backend,
|
||||
n-dimensional array padding, marching cubes, bug and doc fixes.
|
||||
|
||||
- Petter Strandmark
|
||||
Perimeter calculation in regionprops.
|
||||
@@ -131,8 +132,29 @@
|
||||
Dense DAISY feature description, circle perimeter drawing.
|
||||
|
||||
- François Boulogne
|
||||
Andres Method for circle perimeter, ellipse perimeter drawing.
|
||||
Circular Hough Transform
|
||||
Drawing: Andres Method for circle perimeter, ellipse perimeter drawing,
|
||||
Bezier curve, anti-aliasing.
|
||||
Circular and elliptical Hough Transforms
|
||||
Various fixes
|
||||
|
||||
- Thouis Jones
|
||||
Vectorized operators for arrays of 16-bit ints.
|
||||
|
||||
- Xavier Moles Lopez
|
||||
Color separation (color deconvolution) for several stainings.
|
||||
|
||||
- Jostein Bø Fløystad
|
||||
Reconstruction circle mode for Radon transform
|
||||
Simultaneous Algebraic Reconstruction Technique for inverse Radon transform
|
||||
|
||||
- Matt Terry
|
||||
Color difference functions
|
||||
|
||||
- Eugene Dvoretsky
|
||||
Yen threshold implementation.
|
||||
|
||||
- Riaan van den Dool
|
||||
skimage.io plugin: GDAL
|
||||
|
||||
- Fedor Morozov
|
||||
Drawing: Wu's anti-aliased circle
|
||||
|
||||
+17
-3
@@ -2,11 +2,15 @@ Build Requirements
|
||||
------------------
|
||||
* `Python >= 2.5 <http://python.org>`__
|
||||
* `Numpy >= 1.6 <http://numpy.scipy.org/>`__
|
||||
* `Cython >= 0.15 <http://www.cython.org/>`__
|
||||
* `Cython >= 0.17 <http://www.cython.org/>`__
|
||||
|
||||
`Matplotlib >= 1.0 <http://matplotlib.sf.net>`__ is needed to generate the
|
||||
examples in the documentation.
|
||||
|
||||
You can use pip to automatically install the base dependencies as follows::
|
||||
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
Runtime requirements
|
||||
--------------------
|
||||
* `SciPy >= 0.10 <http://scipy.org>`__
|
||||
@@ -31,10 +35,20 @@ Optional Requirements
|
||||
You can use this scikit with the basic requirements listed above, but some
|
||||
functionality is only available with the following installed:
|
||||
|
||||
`PyQt4 <http://wiki.python.org/moin/PyQt>`__
|
||||
* `PyQt4 <http://wiki.python.org/moin/PyQt>`__
|
||||
The ``qt`` plugin that provides ``imshow(x, fancy=True)`` and `skivi`.
|
||||
|
||||
`FreeImage <http://freeimage.sf.net>`__
|
||||
* `FreeImage <http://freeimage.sf.net>`__
|
||||
The ``freeimage`` plugin provides support for reading various types of
|
||||
image file formats, including multi-page TIFFs.
|
||||
|
||||
* `PyAMG <http://pyamg.org/>`__
|
||||
The ``pyamg`` module is used for the fast `cg_mg` mode of random
|
||||
walker segmentation.
|
||||
|
||||
Testing requirements
|
||||
--------------------
|
||||
* `Nose <https://nose.readthedocs.org/en/latest/>`__
|
||||
A Python Unit Testing Framework
|
||||
* `Coverage.py <http://nedbatchelder.com/code/coverage/>`__
|
||||
A tool that generates a unit test code coverage report
|
||||
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
Development process
|
||||
-------------------
|
||||
|
||||
Here's the long and short of it:
|
||||
|
||||
* Go to `https://github.com/scikit-image/scikit-image
|
||||
<http://github.com/scikit-image/scikit-image>`_ and follow the
|
||||
instructions on making your own fork.
|
||||
* Create a new branch for the feature you want to work on. Since the
|
||||
branch name will appear in the merge message, use a sensible name
|
||||
such as 'transform-speedups'.
|
||||
* Commit locally as you progress.
|
||||
* Push your changes back to GitHub and create a Pull Request by
|
||||
clicking 'Pull Request' in GitHub.
|
||||
* Optionally, post on the `mailing list <http://groups.google.com/group/scikit-image>`_ to explain your changes.
|
||||
|
||||
Read these :doc:`detailed documents <gitwash/index>` on how to use Git with
|
||||
``scikit-image`` (`<http://scikit-image.org/docs/dev/gitwash/index.html>`_).
|
||||
|
||||
.. note::
|
||||
|
||||
Do *not* merge the main branch into yours. If GitHub indicates that the
|
||||
Pull Request can no longer be merged automatically, rebase onto master.
|
||||
|
||||
(If you are curious, here's a further discussion on
|
||||
the `dangers of rebasing <http://tinyurl.com/lll385>`__. Also
|
||||
see this `LWN article <http://tinyurl.com/nqcbkj>`__.)
|
||||
|
||||
* To reviewers: add a short explanation of what a branch did to the merge
|
||||
message or, if closing a bug, add "Closes gh-XXXX".
|
||||
|
||||
You may also read this summary by Fernando Perez of the IPython
|
||||
project on how they manage to keep review overhead to a minimum:
|
||||
|
||||
http://mail.scipy.org/pipermail/ipython-dev/2010-October/006746.html
|
||||
|
||||
Guidelines
|
||||
----------
|
||||
|
||||
* All code should have tests (see `test coverage`_ below for more details).
|
||||
* All code should be documented, to the same
|
||||
`standard <http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines>`_
|
||||
as NumPy and SciPy. For new functionality, always add an example to the
|
||||
gallery.
|
||||
* Follow the `Python PEPs <http://www.python.org/dev/peps/pep-0008/>`_
|
||||
where possible.
|
||||
* No major changes should be committed without review. Ask on the
|
||||
`mailing list <http://groups.google.com/group/scikit-image>`_ if
|
||||
you get no response to your pull request.
|
||||
* Examples in the gallery should have a maximum figure width of 8 inches.
|
||||
|
||||
Stylistic Guidelines
|
||||
--------------------
|
||||
|
||||
* Use numpy data types instead of strings (``np.uint8`` instead of
|
||||
``"uint8"``).
|
||||
|
||||
* Use the following import conventions::
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
cimport numpy as cnp # in Cython code
|
||||
|
||||
* When documenting array parameters, use ``image : (M, N) ndarray``,
|
||||
``image : (M, N, 3) ndarray`` and then refer to ``M`` and ``N`` in the
|
||||
docstring.
|
||||
|
||||
* Set up your editor to remove trailing whitespace. Follow `PEP08
|
||||
<www.python.org/dev/peps/pep-0008/>`__. Check code with pyflakes / flake8.
|
||||
|
||||
* If a function name, say ``segment(...)``, has the same name as the file in
|
||||
which it is implemented, name that file ``_segment.py`` so that it can still
|
||||
be imported. All Cython files start with an underscore, e.g.
|
||||
``_some_module.pyx``.
|
||||
|
||||
* Functions should support all input image dtypes. Use utility functions such
|
||||
as ``img_as_float`` to help convert to an appropriate type. The output
|
||||
format can be whatever is most efficient. This allows us to string together
|
||||
several functions into a pipeline, e.g.::
|
||||
|
||||
hough(canny(my_image))
|
||||
|
||||
* Use `Py_ssize_t` as data type for all indexing, shape and size variables in
|
||||
C/C++ and Cython code.
|
||||
|
||||
Test coverage
|
||||
-------------
|
||||
|
||||
Tests for a module should ideally cover all code in that module,
|
||||
i.e., statement coverage should be at 100%.
|
||||
|
||||
To measure the test coverage, install
|
||||
`coverage.py <http://nedbatchelder.com/code/coverage/>`__
|
||||
(using ``easy_install coverage``) and then run::
|
||||
|
||||
$ make coverage
|
||||
|
||||
This will print a report with one line for each file in `skimage`,
|
||||
detailing the test coverage::
|
||||
|
||||
Name Stmts Exec Cover Missing
|
||||
------------------------------------------------------------------------------
|
||||
skimage/color/colorconv 77 77 100%
|
||||
skimage/filter/__init__ 1 1 100%
|
||||
...
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
Please `report bugs on GitHub <https://github.com/scikit-image/scikit-image/issues>`_.
|
||||
+8
-4
@@ -1,23 +1,25 @@
|
||||
How to make a new release of ``skimage``
|
||||
========================================
|
||||
|
||||
- Check ``TODO.txt`` for any outstanding tasks.
|
||||
|
||||
- Update release notes.
|
||||
|
||||
- To show a list contributors, run ``doc/release/contributors.sh <commit>``,
|
||||
where ``<commit>`` is the first commit since the previous release.
|
||||
- To show a list of contributors and changes, run
|
||||
``doc/release/contribs.py <tag of prev release>``.
|
||||
|
||||
- Update the version number in ``setup.py`` and ``bento.info`` and commit
|
||||
|
||||
- Update the docs:
|
||||
|
||||
- Edit ``doc/source/themes/agogo/static/docversions.js`` and commit
|
||||
- Edit ``doc/source/_static/docversions.js`` and commit
|
||||
- Build a clean version of the docs. Run ``make`` in the root dir, then
|
||||
``rm -rf build; make html`` in the docs.
|
||||
- Run ``make html`` again to copy the newly generated ``random.js`` into
|
||||
place. Double check ``random.js``, otherwise the skimage.org front
|
||||
page gets broken!
|
||||
- Build using ``make gh-pages``.
|
||||
- Push upstream: ``git push`` in ``doc/gh-pages``.
|
||||
- Push upstream: ``git push origin gh-pages`` in ``doc/gh-pages``.
|
||||
|
||||
- Add the version number as a tag in git::
|
||||
|
||||
@@ -46,6 +48,8 @@ How to make a new release of ``skimage``
|
||||
- Update stable and development version numbers in
|
||||
``_templates/sidebar_versions.html``.
|
||||
- Add release date to ``index.rst`` under "Announcements".
|
||||
- Add previous stable version documentation path to disallowed paths
|
||||
in `robots.txt`
|
||||
- Build using ``make gh-pages``.
|
||||
- Push upstream: ``git push`` in ``gh-pages``.
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
Version 0.10
|
||||
------------
|
||||
* Remove deprecated functions in `skimage.filter.rank.*`
|
||||
* Remove deprecated parameter `epsilon` of `skimage.viewer.LineProfile`
|
||||
* Remove backwards-compatability of `skimage.measure.regionprops`
|
||||
* Remove {`ratio`, `sigma`} deprecation warnings of `skimage.segmentation.slic`
|
||||
* Change default mode of random_walker segmentation to 'cg_mg' > 'cg' > 'bf',
|
||||
depending on which optional dependencies are available.
|
||||
* Remove deprecated `out` parameter of `skimage.morphology.binary_*`
|
||||
* Remove deprecated parameter `depth` in `skimage.segmentation.random_walker`
|
||||
* Remove deprecated logger function in `skimage/__init__.py`
|
||||
* Remove deprecated function `filter.median_filter`
|
||||
* Remove deprecated `skimage.color.is_gray` and `skimage.color.is_rgb`
|
||||
functions
|
||||
|
||||
+21
-28
@@ -1,5 +1,5 @@
|
||||
Name: scikit-image
|
||||
Version: 0.8.1
|
||||
Version: 0.9.0
|
||||
Summary: Image processing routines for SciPy
|
||||
Url: http://scikit-image.org
|
||||
DownloadUrl: http://github.com/scikit-image/scikit-image
|
||||
@@ -28,7 +28,6 @@ Classifiers:
|
||||
Operating System :: Unix,
|
||||
Operating System :: MacOS
|
||||
|
||||
HookFile: bscript
|
||||
UseBackends: Waf
|
||||
|
||||
Library:
|
||||
@@ -52,6 +51,9 @@ Library:
|
||||
Extension: skimage.measure._moments
|
||||
Sources:
|
||||
skimage/measure/_moments.pyx
|
||||
Extension: skimage.measure._marching_cubes_cy
|
||||
Sources:
|
||||
skimage/measure/_marching_cubes_cy.pyx
|
||||
Extension: skimage.graph._mcp
|
||||
Sources:
|
||||
skimage/graph/_mcp.pyx
|
||||
@@ -91,6 +93,12 @@ Library:
|
||||
Extension: skimage.morphology._greyreconstruct
|
||||
Sources:
|
||||
skimage/morphology/_greyreconstruct.pyx
|
||||
Extension: skimage.feature.censure_cy
|
||||
Sources:
|
||||
skimage/feature/censure_cy.pyx
|
||||
Extension: skimage.feature._brief_cy
|
||||
Sources:
|
||||
skimage/feature/_brief_cy.pyx
|
||||
Extension: skimage.feature.corner_cy
|
||||
Sources:
|
||||
skimage/feature/corner_cy.pyx
|
||||
@@ -109,6 +117,9 @@ Library:
|
||||
Extension: skimage.morphology._skeletonize_cy
|
||||
Sources:
|
||||
skimage/morphology/_skeletonize_cy.pyx
|
||||
Extension: skimage.transform._radon_transform
|
||||
Sources:
|
||||
skimage/transform/_radon_transform.pyx
|
||||
Extension: skimage.transform._warps_cy
|
||||
Sources:
|
||||
skimage/transform/_warps_cy.pyx
|
||||
@@ -121,36 +132,18 @@ Library:
|
||||
Extension: skimage._shared.geometry
|
||||
Sources:
|
||||
skimage/_shared/geometry.pyx
|
||||
Extension: skimage.filter.rank._core16
|
||||
Extension: skimage.filter.rank.generic_cy
|
||||
Sources:
|
||||
skimage/filter/rank/_core16.pyx
|
||||
Extension: skimage.filter.rank._crank8
|
||||
skimage/filter/rank/generic_cy.pyx
|
||||
Extension: skimage.filter.rank.percentile_cy
|
||||
Sources:
|
||||
skimage/filter/rank/_crank8.pyx
|
||||
Extension: skimage.filter.rank._crank16
|
||||
skimage/filter/rank/percentile_cy.pyx
|
||||
Extension: skimage.filter.rank.core_cy
|
||||
Sources:
|
||||
skimage/filter/rank/_crank16.pyx
|
||||
Extension: skimage.filter.rank._core8
|
||||
skimage/filter/rank/core_cy.pyx
|
||||
Extension: skimage.filter.rank.bilateral_cy
|
||||
Sources:
|
||||
skimage/filter/rank/_core8.pyx
|
||||
Extension: skimage.filter.rank.rank
|
||||
Sources:
|
||||
skimage/filter/rank/rank.pyx
|
||||
Extension: skimage.filter.rank.bilateral_rank
|
||||
Sources:
|
||||
skimage/filter/rank/bilateral_rank.pyx
|
||||
Extension: skimage.filter.rank._crank16_percentiles
|
||||
Sources:
|
||||
skimage/filter/rank/_crank16_percentiles.pyx
|
||||
Extension: skimage.filter.rank.percentile_rank
|
||||
Sources:
|
||||
skimage/filter/rank/percentile_rank.pyx
|
||||
Extension: skimage.filter.rank._crank8_percentiles
|
||||
Sources:
|
||||
skimage/filter/rank/_crank8_percentiles.pyx
|
||||
Extension: skimage.filter.rank._crank16_bilateral
|
||||
Sources:
|
||||
skimage/filter/rank/_crank16_bilateral.pyx
|
||||
skimage/filter/rank/bilateral_cy.pyx
|
||||
|
||||
Executable: skivi
|
||||
Module: skimage.scripts.skivi
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import os.path as op
|
||||
|
||||
from numpy.distutils.misc_util \
|
||||
import \
|
||||
get_numpy_include_dirs
|
||||
|
||||
from bento.commands import hooks
|
||||
|
||||
@hooks.post_configure
|
||||
def post_configure(context):
|
||||
conf = context.waf_context
|
||||
conf.env.INCLUDES = get_numpy_include_dirs() + [op.join("skimage", "morphology")]
|
||||
@@ -3,6 +3,7 @@ Check that Cython extensions in setup.py files match those in bento.info.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
RE_CYTHON = re.compile("config.add_extension\(\s*['\"]([\S]+)['\"]")
|
||||
@@ -62,12 +63,12 @@ def remove_common_extensions(cy_bento, cy_setup):
|
||||
|
||||
def print_results(cy_bento, cy_setup):
|
||||
def info(text):
|
||||
print
|
||||
print('')
|
||||
print(text)
|
||||
print('-' * len(text))
|
||||
|
||||
if not (cy_bento or cy_setup):
|
||||
print "bento.info and setup.py files match."
|
||||
print("bento.info and setup.py files match.")
|
||||
|
||||
if cy_bento:
|
||||
info("Extensions found in 'bento.info' but not in any 'setup.py:")
|
||||
@@ -80,8 +81,8 @@ def print_results(cy_bento, cy_setup):
|
||||
info("Consider adding the following to the 'bento.info' Library:")
|
||||
for dir_path in cy_setup:
|
||||
module_path = dir_path.replace('/', '.')
|
||||
print BENTO_TEMPLATE.format(module_path=module_path,
|
||||
dir_path=dir_path)
|
||||
print(BENTO_TEMPLATE.format(module_path=module_path,
|
||||
dir_path=dir_path))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -93,3 +94,6 @@ if __name__ == '__main__':
|
||||
|
||||
cy_bento, cy_setup = remove_common_extensions(cy_bento, cy_setup)
|
||||
print_results(cy_bento, cy_setup)
|
||||
|
||||
if cy_setup or cy_bento:
|
||||
sys.exit(1)
|
||||
|
||||
+10
-9
@@ -2,9 +2,10 @@
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
PYTHON ?= python
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
PAPER ?=
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
@@ -36,14 +37,14 @@ clean:
|
||||
-find ./source/auto_examples/* -type f | grep -v blank | xargs rm -f
|
||||
api:
|
||||
@mkdir -p source/api
|
||||
python tools/build_modref_templates.py
|
||||
$(PYTHON) tools/build_modref_templates.py
|
||||
@echo "Build API docs...done."
|
||||
|
||||
random_gallery:
|
||||
@cd source && python random_gallery.py
|
||||
@cd source && $(PYTHON) random_gallery.py
|
||||
|
||||
coveragetable:
|
||||
@cd source && python coverage_generator.py
|
||||
@cd source && $(PYTHON) coverage_generator.py
|
||||
|
||||
html: api coveragetable random_gallery
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DEST)/html
|
||||
@@ -90,7 +91,7 @@ devhelp:
|
||||
@echo "# ln -s build/devhelp $$HOME/.local/share/devhelp/scikitimage"
|
||||
@echo "# devhelp"
|
||||
|
||||
latex:
|
||||
latex: api
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(DEST)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(DEST)/latex."
|
||||
@@ -120,10 +121,10 @@ doctest:
|
||||
"results in build/doctest/output.txt."
|
||||
|
||||
gh-pages:
|
||||
python gh-pages.py
|
||||
$(PYTHON) gh-pages.py
|
||||
|
||||
gitwash:
|
||||
python tools/gitwash/gitwash_dumper.py source scikit-image \
|
||||
$(PYTHON) tools/gitwash/gitwash_dumper.py source scikit-image \
|
||||
--project-url=http://scikit-image.org \
|
||||
--project-ml-url=http://groups.google.com/group/scikit-image \
|
||||
--repo-name=scikit-image \
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Comparing edge-based segmentation and region-based segmentation
|
||||
===============================================================
|
||||
|
||||
In this example, we will see how to segment objects from a background. We use
|
||||
In this example, we will see how to segment objects from a background. We use
|
||||
the ``coins`` image from ``skimage.data``, which shows several coins outlined
|
||||
against a darker background.
|
||||
"""
|
||||
@@ -108,9 +108,8 @@ closed are not filled correctly, as is the case for one unfilled coin above.
|
||||
Region-based segmentation
|
||||
=========================
|
||||
|
||||
We therefore try a region-based method using the
|
||||
watershed transform. First, we find an elevation map using the Sobel gradient of the
|
||||
image.
|
||||
We therefore try a region-based method using the watershed transform. First, we
|
||||
find an elevation map using the Sobel gradient of the image.
|
||||
|
||||
"""
|
||||
|
||||
@@ -142,7 +141,8 @@ plt.title('markers')
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Finally, we use the watershed transform to fill regions of the elevation map starting from the markers determined above:
|
||||
Finally, we use the watershed transform to fill regions of the elevation map
|
||||
starting from the markers determined above:
|
||||
|
||||
"""
|
||||
segmentation = morphology.watershed(elevation_map, markers)
|
||||
@@ -155,13 +155,16 @@ plt.title('segmentation')
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
This last method works even better, and the coins can be segmented and
|
||||
labeled individually.
|
||||
This last method works even better, and the coins can be segmented and labeled
|
||||
individually.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.color import label2rgb
|
||||
|
||||
segmentation = ndimage.binary_fill_holes(segmentation - 1)
|
||||
labeled_coins, _ = ndimage.label(segmentation)
|
||||
image_label_overlay = label2rgb(labeled_coins, image=coins)
|
||||
|
||||
plt.figure(figsize=(6, 3))
|
||||
plt.subplot(121)
|
||||
@@ -169,7 +172,7 @@ plt.imshow(coins, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.contour(segmentation, [0.5], linewidths=1.2, colors='y')
|
||||
plt.axis('off')
|
||||
plt.subplot(122)
|
||||
plt.imshow(labeled_coins, cmap=plt.cm.spectral, interpolation='nearest')
|
||||
plt.imshow(image_label_overlay, interpolation='nearest')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplots_adjust(**margins)
|
||||
|
||||
@@ -7,6 +7,8 @@ In this example, we will see how to use geometric transformations in the context
|
||||
of image processing.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
@@ -31,7 +33,7 @@ First we create a transformation using explicit parameters:
|
||||
|
||||
tform = tf.SimilarityTransform(scale=1, rotation=math.pi / 2,
|
||||
translation=(0, 1))
|
||||
print tform._matrix
|
||||
print(tform._matrix)
|
||||
|
||||
"""
|
||||
Alternatively you can define a transformation by the transformation matrix
|
||||
@@ -49,8 +51,8 @@ systems:
|
||||
"""
|
||||
|
||||
coord = [1, 0]
|
||||
print tform2(coord)
|
||||
print tform2.inverse(tform(coord))
|
||||
print(tform2(coord))
|
||||
print(tform2.inverse(tform(coord)))
|
||||
|
||||
"""
|
||||
Image warping
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
"""
|
||||
=======================
|
||||
Morphological Filtering
|
||||
=======================
|
||||
|
||||
Morphological image processing is a collection of non-linear operations related
|
||||
to the shape or morphology of features in an image, such as boundaries,
|
||||
skeletons, etc. In any given technique, we probe an image with a small shape or
|
||||
template called a structuring element, which defines the region of interest or
|
||||
neighborhood around a pixel.
|
||||
|
||||
In this document we outline the following basic morphological operations:
|
||||
|
||||
1. Erosion
|
||||
2. Dilation
|
||||
3. Opening
|
||||
4. Closing
|
||||
5. White Tophat
|
||||
6. Black Tophat
|
||||
7. Skeletonize
|
||||
8. Convex Hull
|
||||
|
||||
|
||||
To get started, let's load an image using ``io.imread``. Note that morphology
|
||||
functions only work on gray-scale or binary images, so we set ``as_grey=True``.
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from skimage.data import data_dir
|
||||
from skimage.util import img_as_ubyte
|
||||
from skimage import io
|
||||
|
||||
plt.gray()
|
||||
phantom = img_as_ubyte(io.imread(data_dir+'/phantom.png', as_grey=True))
|
||||
plt.imshow(phantom)
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Let's also define a convenience function for plotting comparisons:
|
||||
"""
|
||||
|
||||
def plot_comparison(original, filtered, filter_name):
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4))
|
||||
ax1.imshow(original)
|
||||
ax1.set_title('original')
|
||||
ax1.axis('off')
|
||||
ax2.imshow(filtered)
|
||||
ax2.set_title(filter_name)
|
||||
ax2.axis('off')
|
||||
|
||||
"""
|
||||
Erosion
|
||||
=======
|
||||
|
||||
Morphological ``erosion`` sets a pixel at (i, j) to the *minimum over all
|
||||
pixels in the neighborhood centered at (i, j)*. The structuring element,
|
||||
``selem``, passed to ``erosion`` is a boolean array that describes this
|
||||
neighborhood. Below, we use ``disk`` to create a circular structuring element,
|
||||
which we use for most of the following examples.
|
||||
"""
|
||||
|
||||
from skimage.morphology import erosion, dilation, opening, closing, white_tophat
|
||||
from skimage.morphology import black_tophat, skeletonize, convex_hull_image
|
||||
from skimage.morphology import disk
|
||||
|
||||
selem = disk(6)
|
||||
eroded = erosion(phantom, selem)
|
||||
plot_comparison(phantom, eroded, 'erosion')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Notice how the white boundary of the image disappears or gets eroded as we
|
||||
increase the size of the disk. Also notice the increase in size of the two
|
||||
black ellipses in the center and the disappearance of the 3 light grey
|
||||
patches in the lower part of the image.
|
||||
|
||||
|
||||
Dilation
|
||||
========
|
||||
|
||||
Morphological ``dilation`` sets a pixel at (i, j) to the *maximum over all
|
||||
pixels in the neighborhood centered at (i, j)*. Dilation enlarges bright
|
||||
regions and shrinks dark regions.
|
||||
"""
|
||||
|
||||
dilated = dilation(phantom, selem)
|
||||
plot_comparison(phantom, dilated, 'dilation')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Notice how the white boundary of the image thickens, or gets dilated, as we
|
||||
increase the size of the disk. Also notice the decrease in size of the two
|
||||
black ellipses in the centre, and the thickening of the light grey circle in
|
||||
the center and the 3 patches in the lower part of the image.
|
||||
|
||||
|
||||
Opening
|
||||
=======
|
||||
|
||||
Morphological ``opening`` on an image is defined as an *erosion followed by a
|
||||
dilation*. Opening can remove small bright spots (i.e. "salt") and connect
|
||||
small dark cracks.
|
||||
"""
|
||||
|
||||
opened = opening(phantom, selem)
|
||||
plot_comparison(phantom, opened, 'opening')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Since ``opening`` an image starts with an erosion operation, light regions that
|
||||
are *smaller* than the structuring element are removed. The dilation operation
|
||||
that follows ensures that light regions that are *larger* than the structuring
|
||||
element retain their original size. Notice how the light and dark shapes in the
|
||||
center their original thickness but the 3 lighter patches in the bottom get
|
||||
completely eroded. The size dependence is highlighted by the outer white ring:
|
||||
The parts of the ring thinner than the structuring element were completely
|
||||
erased, while the thicker region at the top retains its original thickness.
|
||||
|
||||
|
||||
Closing
|
||||
=======
|
||||
|
||||
Morphological ``closing`` on an image is defined as a *dilation followed by an
|
||||
erosion*. Closing can remove small dark spots (i.e. "pepper") and connect
|
||||
small bright cracks.
|
||||
|
||||
To illustrate this more clearly, let's add a small crack to the white border:
|
||||
"""
|
||||
|
||||
phantom = img_as_ubyte(io.imread(data_dir+'/phantom.png', as_grey=True))
|
||||
phantom[10:30, 200:210] = 0
|
||||
|
||||
closed = closing(phantom, selem)
|
||||
plot_comparison(phantom, closed, 'closing')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Since ``closing`` an image starts with an dilation operation, dark regions
|
||||
that are *smaller* than the structuring element are removed. The dilation
|
||||
operation that follows ensures that dark regions that are *larger* than the
|
||||
structuring element retain their original size. Notice how the white ellipses
|
||||
at the bottom get connected because of dilation, but other dark region retain
|
||||
their original sizes. Also notice how the crack we added is mostly removed.
|
||||
|
||||
|
||||
White tophat
|
||||
============
|
||||
|
||||
The ``white_tophat`` of an image is defined as the *image minus its
|
||||
morphological opening*. This operation returns the bright spots of the image
|
||||
that are smaller than the structuring element.
|
||||
|
||||
To make things interesting, we'll add bright and dark spots to the image:
|
||||
"""
|
||||
|
||||
phantom = img_as_ubyte(io.imread(data_dir+'/phantom.png', as_grey=True))
|
||||
phantom[340:350, 200:210] = 255
|
||||
phantom[100:110, 200:210] = 0
|
||||
|
||||
w_tophat = white_tophat(phantom, selem)
|
||||
plot_comparison(phantom, w_tophat, 'white tophat')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As you can see, the 10-pixel wide white square is highlighted since it is
|
||||
smaller than the structuring element. Also, the thin, white edges around most
|
||||
of the ellipse are retained because they're smaller than the structuring
|
||||
element, but the thicker region at the top disappears.
|
||||
|
||||
|
||||
Black tophat
|
||||
============
|
||||
|
||||
The ``black_tophat`` of an image is defined as its morphological **closing
|
||||
minus the original image**. This operation returns the *dark spots of the
|
||||
image that are smaller than the structuring element*.
|
||||
"""
|
||||
|
||||
b_tophat = black_tophat(phantom, selem)
|
||||
plot_comparison(phantom, b_tophat, 'black tophat')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As you can see, the 10-pixel wide black square is highlighted since it is
|
||||
smaller than the structuring element.
|
||||
|
||||
|
||||
Duality
|
||||
-------
|
||||
|
||||
As you should have noticed, many of these operations are simply the reverse
|
||||
of another operation. This duality can be summarized as follows:
|
||||
|
||||
1. Erosion <-> Dilation
|
||||
2. Opening <-> Closing
|
||||
3. White tophat <-> Black tophat
|
||||
|
||||
|
||||
Skeletonize
|
||||
===========
|
||||
|
||||
Thinning is used to reduce each connected component in a binary image to a
|
||||
*single-pixel wide skeleton*. It is important to note that this is performed
|
||||
on binary images only.
|
||||
|
||||
"""
|
||||
|
||||
from skimage import img_as_bool
|
||||
horse = ~img_as_bool(io.imread(data_dir+'/horse.png', as_grey=True))
|
||||
|
||||
sk = skeletonize(horse)
|
||||
plot_comparison(horse, sk, 'skeletonize')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As the name suggests, this technique is used to thin the image to 1-pixel wide
|
||||
skeleton by applying thinning successively.
|
||||
|
||||
|
||||
Convex hull
|
||||
===========
|
||||
|
||||
The ``convex_hull_image`` is the *set of pixels included in the smallest
|
||||
convex polygon that surround all white pixels in the input image*. Again note
|
||||
that this is also performed on binary images.
|
||||
|
||||
"""
|
||||
|
||||
hull1 = convex_hull_image(horse)
|
||||
plot_comparison(horse, hull1, 'convex hull')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
As the figure illustrates, ``convex_hull_image`` gives the smallest polygon
|
||||
which covers the white or True completely in the image.
|
||||
|
||||
If we add a small grain to the image, we can see how the convex hull adapts to
|
||||
enclose that grain:
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
horse2 = np.copy(horse)
|
||||
horse2[45:50, 75:80] = 1
|
||||
|
||||
hull2 = convex_hull_image(horse2)
|
||||
plot_comparison(horse2, hull2, 'convex hull')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
|
||||
Additional Resources
|
||||
====================
|
||||
|
||||
1. `MathWorks tutorial on morphological processing
|
||||
<http://www.mathworks.com/help/images/morphology-fundamentals-dilation-and-erosion.html>`_
|
||||
2. `Auckland university's tutorial on Morphological Image Processing
|
||||
<http://www.cs.auckland.ac.nz/courses/compsci773s1c/lectures/ImageProcessing-html/topic4.htm>`_
|
||||
3. http://en.wikipedia.org/wiki/Mathematical_morphology
|
||||
|
||||
"""
|
||||
|
||||
plt.show()
|
||||
@@ -3,11 +3,11 @@
|
||||
Rank filters
|
||||
============
|
||||
|
||||
Rank filters are non-linear filters using the local greylevels ordering to
|
||||
Rank filters are non-linear filters using the local gray-level ordering to
|
||||
compute the filtered value. This ensemble of filters share a common base: the
|
||||
local grey-level histogram extraction computed on the neighborhood of a pixel
|
||||
(defined by a 2D structuring element). If the filtered value is taken as the
|
||||
middle value of the histogram, we get the classical median filter.
|
||||
local gray-level histogram is computed on the neighborhood of a pixel (defined
|
||||
by a 2-D structuring element). If the filtered value is taken as the middle
|
||||
value of the histogram, we get the classical median filter.
|
||||
|
||||
Rank filters can be used for several purposes such as:
|
||||
|
||||
@@ -26,11 +26,9 @@ Rank filters can be used for several purposes such as:
|
||||
Some well known filters are specific cases of rank filters [1]_ e.g.
|
||||
morphological dilation, morphological erosion, median filters.
|
||||
|
||||
The different implementation availables in `skimage` are compared.
|
||||
|
||||
In this example, we will see how to filter a greylevel image using some of the
|
||||
linear and non-linear filters availables in skimage. We use the `camera`
|
||||
image from `skimage.data`.
|
||||
In this example, we will see how to filter a gray-level image using some of the
|
||||
linear and non-linear filters available in skimage. We use the `camera` image
|
||||
from `skimage.data` for all comparisons.
|
||||
|
||||
.. [1] Pierre Soille, On morphological operators based on rank filters, Pattern
|
||||
Recognition 35 (2002) 527-535.
|
||||
@@ -40,18 +38,19 @@ image from `skimage.data`.
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import img_as_ubyte
|
||||
from skimage import data
|
||||
|
||||
ima = data.camera()
|
||||
hist = np.histogram(ima, bins=np.arange(0, 256))
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
hist = np.histogram(noisy_image, bins=np.arange(0, 256))
|
||||
|
||||
plt.figure(figsize=(8, 3))
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.imshow(noisy_image, interpolation='nearest')
|
||||
plt.axis('off')
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.plot(hist[1][:-1], hist[0], lw=2)
|
||||
plt.title('histogram of grey values')
|
||||
plt.title('Histogram of grey values')
|
||||
|
||||
"""
|
||||
|
||||
@@ -65,50 +64,56 @@ randomly set to 0. The **median** filter is applied to remove the noise.
|
||||
|
||||
.. note::
|
||||
|
||||
there are different implementations of median filter :
|
||||
There are different implementations of median filter:
|
||||
`skimage.filter.median_filter` and `skimage.filter.rank.median`
|
||||
|
||||
"""
|
||||
|
||||
noise = np.random.random(ima.shape)
|
||||
nima = data.camera()
|
||||
nima[noise > 0.99] = 255
|
||||
nima[noise < 0.01] = 0
|
||||
|
||||
from skimage.filter.rank import median
|
||||
from skimage.morphology import disk
|
||||
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
noise = np.random.random(noisy_image.shape)
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
noisy_image[noise > 0.99] = 255
|
||||
noisy_image[noise < 0.01] = 0
|
||||
|
||||
fig = plt.figure(figsize=(10, 7))
|
||||
|
||||
lo = median(nima, disk(1))
|
||||
hi = median(nima, disk(5))
|
||||
ext = median(nima, disk(20))
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(nima, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('noised image')
|
||||
plt.imshow(noisy_image, vmin=0, vmax=255)
|
||||
plt.title('Noisy image')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(lo, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('median $r=1$')
|
||||
plt.imshow(median(noisy_image, disk(1)), vmin=0, vmax=255)
|
||||
plt.title('Median $r=1$')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(hi, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('median $r=5$')
|
||||
plt.imshow(median(noisy_image, disk(5)), vmin=0, vmax=255)
|
||||
plt.title('Median $r=5$')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(ext, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('median $r=20$')
|
||||
plt.imshow(median(noisy_image, disk(20)), vmin=0, vmax=255)
|
||||
plt.title('Median $r=20$')
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
The added noise is efficiently removed, as the image defaults are small (1 pixel
|
||||
wide), a small filter radius is sufficient. As the radius is increasing, objects
|
||||
with a bigger size are filtered as well, such as the camera tripod. The median
|
||||
filter is commonly used for noise removal because borders are preserved.
|
||||
The added noise is efficiently removed, as the image defaults are small (1
|
||||
pixel wide), a small filter radius is sufficient. As the radius is increasing,
|
||||
objects with bigger sizes are filtered as well, such as the camera tripod. The
|
||||
median filter is often used for noise removal because borders are preserved and
|
||||
e.g. salt and pepper noise typically does not distort the gray-level.
|
||||
|
||||
Image smoothing
|
||||
================
|
||||
|
||||
The example hereunder shows how a local **mean** smoothes the camera man image.
|
||||
The example hereunder shows how a local **mean** filter smooths the camera man
|
||||
image.
|
||||
|
||||
"""
|
||||
|
||||
@@ -116,13 +121,17 @@ from skimage.filter.rank import mean
|
||||
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
|
||||
loc_mean = mean(nima, disk(10))
|
||||
loc_mean = mean(noisy_image, disk(10))
|
||||
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, vmin=0, vmax=255)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.imshow(loc_mean, cmap=plt.cm.gray, vmin=0, vmax=255)
|
||||
plt.xlabel('local mean $r=10$')
|
||||
plt.imshow(loc_mean, vmin=0, vmax=255)
|
||||
plt.title('Local mean $r=10$')
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -130,35 +139,41 @@ plt.xlabel('local mean $r=10$')
|
||||
|
||||
One may be interested in smoothing an image while preserving important borders
|
||||
(median filters already achieved this), here we use the **bilateral** filter
|
||||
that restricts the local neighborhood to pixel having a greylevel similar to
|
||||
that restricts the local neighborhood to pixel having a gray-level similar to
|
||||
the central one.
|
||||
|
||||
.. note::
|
||||
|
||||
a different implementation is available for color images in
|
||||
A different implementation is available for color images in
|
||||
`skimage.filter.denoise_bilateral`.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import bilateral_mean
|
||||
|
||||
ima = data.camera()
|
||||
selem = disk(10)
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
bilat = bilateral_mean(ima.astype(np.uint16), disk(20), s0=10, s1=10)
|
||||
bilat = bilateral_mean(noisy_image.astype(np.uint16), disk(20), s0=10, s1=10)
|
||||
|
||||
# display results
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, cmap=plt.cm.gray)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(bilat, cmap=plt.cm.gray)
|
||||
plt.xlabel('bilateral mean')
|
||||
plt.title('Bilateral mean')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(ima[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.imshow(noisy_image[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(bilat[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -175,7 +190,7 @@ We compare here how the global histogram equalization is applied locally.
|
||||
|
||||
The equalized image [2]_ has a roughly linear cumulative distribution function
|
||||
for each pixel neighborhood. The local version [3]_ of the histogram
|
||||
equalization emphasizes every local greylevel variations.
|
||||
equalization emphasizes every local gray-level variations.
|
||||
|
||||
.. [2] http://en.wikipedia.org/wiki/Histogram_equalization
|
||||
.. [3] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
|
||||
@@ -185,101 +200,112 @@ equalization emphasizes every local greylevel variations.
|
||||
from skimage import exposure
|
||||
from skimage.filter import rank
|
||||
|
||||
ima = data.camera()
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
# equalize globally and locally
|
||||
glob = exposure.equalize(ima) * 255
|
||||
loc = rank.equalize(ima, disk(20))
|
||||
glob = exposure.equalize(noisy_image) * 255
|
||||
loc = rank.equalize(noisy_image, disk(20))
|
||||
|
||||
# extract histogram for each image
|
||||
hist = np.histogram(ima, bins=np.arange(0, 256))
|
||||
hist = np.histogram(noisy_image, bins=np.arange(0, 256))
|
||||
glob_hist = np.histogram(glob, bins=np.arange(0, 256))
|
||||
loc_hist = np.histogram(loc, bins=np.arange(0, 256))
|
||||
|
||||
plt.figure(figsize=(10, 10))
|
||||
|
||||
plt.subplot(321)
|
||||
plt.imshow(ima, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.imshow(noisy_image, interpolation='nearest')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(322)
|
||||
plt.plot(hist[1][:-1], hist[0], lw=2)
|
||||
plt.title('histogram of grey values')
|
||||
plt.title('Histogram of gray values')
|
||||
|
||||
plt.subplot(323)
|
||||
plt.imshow(glob, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.imshow(glob, interpolation='nearest')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(324)
|
||||
plt.plot(glob_hist[1][:-1], glob_hist[0], lw=2)
|
||||
plt.title('histogram of grey values')
|
||||
plt.title('Histogram of gray values')
|
||||
|
||||
plt.subplot(325)
|
||||
plt.imshow(loc, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.imshow(loc, interpolation='nearest')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(326)
|
||||
plt.plot(loc_hist[1][:-1], loc_hist[0], lw=2)
|
||||
plt.title('histogram of grey values')
|
||||
plt.title('Histogram of gray values')
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
another way to maximize the number of greylevels used for an image is to apply
|
||||
a local autoleveling, i.e. here a pixel greylevel is proportionally remapped
|
||||
between local minimum and local maximum.
|
||||
Another way to maximize the number of gray-levels used for an image is to apply
|
||||
a local auto-leveling, i.e. the gray-value of a pixel is proportionally
|
||||
remapped between local minimum and local maximum.
|
||||
|
||||
The following example shows how local autolevel enhances the camara man picture.
|
||||
The following example shows how local auto-level enhances the camara man
|
||||
picture.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import autolevel
|
||||
|
||||
ima = data.camera()
|
||||
selem = disk(10)
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
auto = autolevel(ima.astype(np.uint16), disk(20))
|
||||
auto = autolevel(noisy_image.astype(np.uint16), disk(20))
|
||||
|
||||
# display results
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, cmap=plt.cm.gray)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.imshow(auto, cmap=plt.cm.gray)
|
||||
plt.xlabel('local autolevel')
|
||||
plt.title('Local autolevel')
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
This filter is very sensitive to local outlayers, see the little white spot in
|
||||
the sky left part. This is due to a local maximum which is very high comparing
|
||||
to the rest of the neighborhood. One can moderate this using the percentile
|
||||
version of the autolevel filter which uses given percentiles (one inferior,
|
||||
one superior) in place of local minimum and maximum. The example below
|
||||
illustrates how the percentile parameters influence the local autolevel result.
|
||||
This filter is very sensitive to local outliers, see the little white spot in
|
||||
the left part of the sky. This is due to a local maximum which is very high
|
||||
comparing to the rest of the neighborhood. One can moderate this using the
|
||||
percentile version of the auto-level filter which uses given percentiles (one
|
||||
inferior, one superior) in place of local minimum and maximum. The example
|
||||
below illustrates how the percentile parameters influence the local auto-level
|
||||
result.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import percentile_autolevel
|
||||
from skimage.filter.rank import autolevel_percentile
|
||||
|
||||
image = data.camera()
|
||||
|
||||
selem = disk(20)
|
||||
loc_autolevel = autolevel(image, selem=selem)
|
||||
loc_perc_autolevel0 = percentile_autolevel(image, selem=selem, p0=.00, p1=1.0)
|
||||
loc_perc_autolevel1 = percentile_autolevel(image, selem=selem, p0=.01, p1=.99)
|
||||
loc_perc_autolevel2 = percentile_autolevel(image, selem=selem, p0=.05, p1=.95)
|
||||
loc_perc_autolevel3 = percentile_autolevel(image, selem=selem, p0=.1, p1=.9)
|
||||
loc_perc_autolevel0 = autolevel_percentile(image, selem=selem, p0=.00, p1=1.0)
|
||||
loc_perc_autolevel1 = autolevel_percentile(image, selem=selem, p0=.01, p1=.99)
|
||||
loc_perc_autolevel2 = autolevel_percentile(image, selem=selem, p0=.05, p1=.95)
|
||||
loc_perc_autolevel3 = autolevel_percentile(image, selem=selem, p0=.1, p1=.9)
|
||||
|
||||
fig, axes = plt.subplots(nrows=3, figsize=(7, 8))
|
||||
ax0, ax1, ax2 = axes
|
||||
plt.gray()
|
||||
|
||||
ax0.imshow(np.hstack((image, loc_autolevel)))
|
||||
ax0.set_title('original / autolevel')
|
||||
ax0.set_title('Original / auto-level')
|
||||
|
||||
ax1.imshow(
|
||||
np.hstack((loc_perc_autolevel0, loc_perc_autolevel1)), vmin=0, vmax=255)
|
||||
ax1.set_title('percentile autolevel 0%,1%')
|
||||
ax1.set_title('Percentile auto-level 0%,1%')
|
||||
ax2.imshow(
|
||||
np.hstack((loc_perc_autolevel2, loc_perc_autolevel3)), vmin=0, vmax=255)
|
||||
ax2.set_title('percentile autolevel 5% and 10%')
|
||||
ax2.set_title('Percentile auto-level 5% and 10%')
|
||||
|
||||
for ax in axes:
|
||||
ax.axis('off')
|
||||
@@ -289,29 +315,35 @@ for ax in axes:
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
The morphological contrast enhancement filter replaces the central pixel by the
|
||||
local maximum if the original pixel value is closest to local maximum, otherwise
|
||||
by the minimum local.
|
||||
local maximum if the original pixel value is closest to local maximum,
|
||||
otherwise by the minimum local.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import morph_contr_enh
|
||||
from skimage.filter.rank import enhance_contrast
|
||||
|
||||
ima = data.camera()
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
enh = morph_contr_enh(ima, disk(5))
|
||||
enh = enhance_contrast(noisy_image, disk(5))
|
||||
|
||||
# display results
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, cmap=plt.cm.gray)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(enh, cmap=plt.cm.gray)
|
||||
plt.xlabel('local morphlogical contrast enhancement')
|
||||
plt.title('Local morphological contrast enhancement')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(ima[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.imshow(noisy_image[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(enh[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -322,24 +354,30 @@ percentile *p0* and *p1* instead of the local minimum and maximum.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import percentile_morph_contr_enh
|
||||
from skimage.filter.rank import enhance_contrast_percentile
|
||||
|
||||
ima = data.camera()
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
penh = percentile_morph_contr_enh(ima, disk(5), p0=.1, p1=.9)
|
||||
penh = enhance_contrast_percentile(noisy_image, disk(5), p0=.1, p1=.9)
|
||||
|
||||
# display results
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, cmap=plt.cm.gray)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(penh, cmap=plt.cm.gray)
|
||||
plt.xlabel('local percentile morphlogical\n contrast enhancement')
|
||||
plt.title('Local percentile morphological\n contrast enhancement')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(ima[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.imshow(noisy_image[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(penh[200:350, 350:450], cmap=plt.cm.gray)
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -348,20 +386,20 @@ plt.imshow(penh[200:350, 350:450], cmap=plt.cm.gray)
|
||||
Image threshold
|
||||
===============
|
||||
|
||||
The Otsu's threshold [1]_ method can be applied locally using the local
|
||||
greylevel distribution. In the example below, for each pixel, an "optimal"
|
||||
threshold is determined by maximizing the variance between two classes of pixels
|
||||
of the local neighborhood defined by a structuring element.
|
||||
The Otsu threshold [1]_ method can be applied locally using the local gray-
|
||||
level distribution. In the example below, for each pixel, an "optimal"
|
||||
threshold is determined by maximizing the variance between two classes of
|
||||
pixels of the local neighborhood defined by a structuring element.
|
||||
|
||||
The example compares the local threshold with the global threshold
|
||||
`skimage.filter.threshold_otsu`.
|
||||
|
||||
.. note::
|
||||
|
||||
Local thresholding is much slower than global one. There exists a function
|
||||
for global Otsu thresholding: `skimage.filter.threshold_otsu`.
|
||||
Local is much slower than global thresholding. A function for global Otsu
|
||||
thresholding can be found in : `skimage.filter.threshold_otsu`.
|
||||
|
||||
.. [1] http://en.wikipedia.org/wiki/Otsu's_method
|
||||
.. [4] http://en.wikipedia.org/wiki/Otsu's_method
|
||||
|
||||
"""
|
||||
|
||||
@@ -382,27 +420,35 @@ t_glob_otsu = threshold_otsu(p8)
|
||||
glob_otsu = p8 >= t_glob_otsu
|
||||
|
||||
plt.figure()
|
||||
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(p8, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.title('Original')
|
||||
plt.colorbar()
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(t_loc_otsu, cmap=plt.cm.gray)
|
||||
plt.xlabel('local Otsu ($radius=%d$)' % radius)
|
||||
plt.title('Local Otsu ($r=%d$)' % radius)
|
||||
plt.colorbar()
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(p8 >= t_loc_otsu, cmap=plt.cm.gray)
|
||||
plt.xlabel('original>=local Otsu' % t_glob_otsu)
|
||||
plt.title('Original >= local Otsu' % t_glob_otsu)
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(glob_otsu, cmap=plt.cm.gray)
|
||||
plt.xlabel('global Otsu ($t=%d$)' % t_glob_otsu)
|
||||
plt.title('Global Otsu ($t=%d$)' % t_glob_otsu)
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
The following example shows how local Otsu's threshold handles a global level
|
||||
shift applied to a synthetic image .
|
||||
The following example shows how local Otsu thresholding handles a global level
|
||||
shift applied to a synthetic image.
|
||||
|
||||
"""
|
||||
|
||||
@@ -413,13 +459,18 @@ m = (np.tile(x, (n, 1)) * np.linspace(0.1, 1, n) * 128 + 128).astype(np.uint8)
|
||||
|
||||
radius = 10
|
||||
t = rank.otsu(m, disk(radius))
|
||||
|
||||
plt.figure()
|
||||
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.imshow(m)
|
||||
plt.xlabel('original')
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.imshow(m >= t, interpolation='nearest')
|
||||
plt.xlabel('local Otsu ($radius=%d$)' % radius)
|
||||
plt.title('Local Otsu ($r=%d$)' % radius)
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -428,7 +479,7 @@ plt.xlabel('local Otsu ($radius=%d$)' % radius)
|
||||
Image morphology
|
||||
================
|
||||
|
||||
Local maximum and local minimum are the base operators for greylevel
|
||||
Local maximum and local minimum are the base operators for gray-level
|
||||
morphology.
|
||||
|
||||
.. note::
|
||||
@@ -436,33 +487,41 @@ morphology.
|
||||
`skimage.dilate` and `skimage.erode` are equivalent filters (see below for
|
||||
comparison).
|
||||
|
||||
Here is an example of the classical morphological greylevel filters: opening,
|
||||
Here is an example of the classical morphological gray-level filters: opening,
|
||||
closing and morphological gradient.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.filter.rank import maximum, minimum, gradient
|
||||
|
||||
ima = data.camera()
|
||||
noisy_image = img_as_ubyte(data.camera())
|
||||
|
||||
closing = maximum(minimum(ima, disk(5)), disk(5))
|
||||
opening = minimum(maximum(ima, disk(5)), disk(5))
|
||||
grad = gradient(ima, disk(5))
|
||||
closing = maximum(minimum(noisy_image, disk(5)), disk(5))
|
||||
opening = minimum(maximum(noisy_image, disk(5)), disk(5))
|
||||
grad = gradient(noisy_image, disk(5))
|
||||
|
||||
# display results
|
||||
fig = plt.figure(figsize=[10, 7])
|
||||
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(ima, cmap=plt.cm.gray)
|
||||
plt.xlabel('original')
|
||||
plt.imshow(noisy_image, cmap=plt.cm.gray)
|
||||
plt.title('Original')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(closing, cmap=plt.cm.gray)
|
||||
plt.xlabel('greylevel closing')
|
||||
plt.title('Gray-level closing')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(opening, cmap=plt.cm.gray)
|
||||
plt.xlabel('greylevel opening')
|
||||
plt.title('Gray-level opening')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(grad, cmap=plt.cm.gray)
|
||||
plt.xlabel('morphological gradient')
|
||||
plt.title('Morphological gradient')
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
@@ -471,13 +530,14 @@ plt.xlabel('morphological gradient')
|
||||
Feature extraction
|
||||
===================
|
||||
|
||||
Local histogram can be exploited to compute local entropy, which is related to
|
||||
Local histograms can be exploited to compute local entropy, which is related to
|
||||
the local image complexity. Entropy is computed using base 2 logarithm i.e. the
|
||||
filter returns the minimum number of bits needed to encode local greylevel
|
||||
filter returns the minimum number of bits needed to encode local gray-level
|
||||
distribution.
|
||||
|
||||
`skimage.rank.entropy` returns local entropy on a given structuring element.
|
||||
The following example shows this filter applied on 8- and 16- bit images.
|
||||
`skimage.rank.entropy` returns the local entropy on a given structuring
|
||||
element. The following example shows applies this filter on 8- and 16-bit
|
||||
images.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -492,47 +552,36 @@ from skimage.morphology import disk
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# defining a 8- and a 16-bit test images
|
||||
a8 = data.camera()
|
||||
a16 = data.camera().astype(np.uint16) * 4
|
||||
image = data.camera()
|
||||
|
||||
ent8 = entropy(a8, disk(5)) # pixel value contain 10x the local entropy
|
||||
ent16 = entropy(a16, disk(5)) # pixel value contain 1000x the local entropy
|
||||
plt.figure(figsize=(10, 4))
|
||||
|
||||
# display results
|
||||
plt.figure(figsize=(10, 10))
|
||||
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.imshow(a8, cmap=plt.cm.gray)
|
||||
plt.xlabel('8-bit image')
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
plt.title('Image')
|
||||
plt.colorbar()
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.imshow(ent8, cmap=plt.cm.jet)
|
||||
plt.xlabel('entropy*10')
|
||||
plt.colorbar()
|
||||
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(a16, cmap=plt.cm.gray)
|
||||
plt.xlabel('16-bit image')
|
||||
plt.colorbar()
|
||||
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(ent16, cmap=plt.cm.jet)
|
||||
plt.xlabel('entropy*1000')
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.imshow(entropy(image, disk(5)), cmap=plt.cm.jet)
|
||||
plt.title('Entropy')
|
||||
plt.colorbar()
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Implementation
|
||||
================
|
||||
==============
|
||||
|
||||
The central part of the `skimage.rank` filters is build on a sliding window that
|
||||
update local greylevel histogram. This approach limits the algorithm complexity
|
||||
to O(n) where n is the number of image pixels. The complexity is also limited
|
||||
with respect to the structuring element size.
|
||||
The central part of the `skimage.rank` filters is build on a sliding window
|
||||
that updates the local gray-level histogram. This approach limits the algorithm
|
||||
complexity to O(n) where n is the number of image pixels. The complexity is
|
||||
also limited with respect to the structuring element size.
|
||||
|
||||
In the following we compare the performance of different implementations
|
||||
available in `skimage`.
|
||||
|
||||
"""
|
||||
|
||||
@@ -583,10 +632,10 @@ def ndi_med(image, n):
|
||||
|
||||
Comparison between
|
||||
|
||||
* `rank.maximum`
|
||||
* `cmorph.dilate`
|
||||
* `filter.rank.maximum`
|
||||
* `morphology.dilate`
|
||||
|
||||
on increasing structuring element size
|
||||
on increasing structuring element size:
|
||||
|
||||
"""
|
||||
|
||||
@@ -603,18 +652,18 @@ for r in e_range:
|
||||
rec = np.asarray(rec)
|
||||
|
||||
plt.figure()
|
||||
plt.title('increasing element size')
|
||||
plt.ylabel('time (ms)')
|
||||
plt.xlabel('element radius')
|
||||
plt.title('Performance with respect to element size')
|
||||
plt.ylabel('Time (ms)')
|
||||
plt.title('Element radius')
|
||||
plt.plot(e_range, rec)
|
||||
plt.legend(['crank.maximum', 'cmorph.dilate'])
|
||||
plt.legend(['filter.rank.maximum', 'morphology.dilate'])
|
||||
|
||||
"""
|
||||
|
||||
and increasing image size
|
||||
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
and increasing image size:
|
||||
|
||||
"""
|
||||
|
||||
r = 9
|
||||
@@ -623,7 +672,7 @@ elem = disk(r + 1)
|
||||
rec = []
|
||||
s_range = range(100, 1000, 100)
|
||||
for s in s_range:
|
||||
a = (np.random.random((s, s)) * 256).astype('uint8')
|
||||
a = (np.random.random((s, s)) * 256).astype(np.uint8)
|
||||
(rc, ms_rc) = cr_max(a, elem)
|
||||
(rcm, ms_rcm) = cm_dil(a, elem)
|
||||
rec.append((ms_rc, ms_rcm))
|
||||
@@ -631,11 +680,11 @@ for s in s_range:
|
||||
rec = np.asarray(rec)
|
||||
|
||||
plt.figure()
|
||||
plt.title('increasing image size')
|
||||
plt.ylabel('time (ms)')
|
||||
plt.xlabel('image size')
|
||||
plt.title('Performance with respect to image size')
|
||||
plt.ylabel('Time (ms)')
|
||||
plt.title('Image size')
|
||||
plt.plot(s_range, rec)
|
||||
plt.legend(['crank.maximum', 'cmorph.dilate'])
|
||||
plt.legend(['filter.rank.maximum', 'morphology.dilate'])
|
||||
|
||||
|
||||
"""
|
||||
@@ -644,11 +693,11 @@ plt.legend(['crank.maximum', 'cmorph.dilate'])
|
||||
|
||||
Comparison between:
|
||||
|
||||
* `rank.median`
|
||||
* `ctmf.median_filter`
|
||||
* `ndimage.percentile`
|
||||
* `filter.rank.median`
|
||||
* `filter.median_filter`
|
||||
* `scipy.ndimage.percentile`
|
||||
|
||||
on increasing structuring element size
|
||||
on increasing structuring element size:
|
||||
|
||||
"""
|
||||
|
||||
@@ -666,27 +715,29 @@ for r in e_range:
|
||||
rec = np.asarray(rec)
|
||||
|
||||
plt.figure()
|
||||
plt.title('increasing element size')
|
||||
plt.title('Performance with respect to element size')
|
||||
plt.plot(e_range, rec)
|
||||
plt.legend(['rank.median', 'ctmf.median_filter', 'ndimage.percentile'])
|
||||
plt.ylabel('time (ms)')
|
||||
plt.xlabel('element radius')
|
||||
plt.legend(['filter.rank.median', 'filter.median_filter',
|
||||
'scipy.ndimage.percentile'])
|
||||
plt.ylabel('Time (ms)')
|
||||
plt.title('Element radius')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
comparison of outcome of the three methods
|
||||
Comparison of outcome of the three methods:
|
||||
|
||||
"""
|
||||
|
||||
plt.figure()
|
||||
plt.imshow(np.hstack((rc, rctmf, rndi)))
|
||||
plt.xlabel('rank.median vs ctmf.median_filter vs ndimage.percentile')
|
||||
plt.title('filter.rank.median vs filtermedian_filter vs scipy.ndimage.percentile')
|
||||
plt.axis('off')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
and increasing image size
|
||||
and increasing image size:
|
||||
|
||||
"""
|
||||
|
||||
@@ -696,7 +747,7 @@ elem = disk(r + 1)
|
||||
rec = []
|
||||
s_range = [100, 200, 500, 1000]
|
||||
for s in s_range:
|
||||
a = (np.random.random((s, s)) * 256).astype('uint8')
|
||||
a = (np.random.random((s, s)) * 256).astype(np.uint8)
|
||||
(rc, ms_rc) = cr_med(a, elem)
|
||||
rctmf, ms_rctmf = ctmf_med(a, r)
|
||||
rndi, ms_ndi = ndi_med(a, r)
|
||||
@@ -705,11 +756,12 @@ for s in s_range:
|
||||
rec = np.asarray(rec)
|
||||
|
||||
plt.figure()
|
||||
plt.title('increasing image size')
|
||||
plt.title('Performance with respect to image size')
|
||||
plt.plot(s_range, rec)
|
||||
plt.legend(['rank.median', 'ctmf.median_filter', 'ndimage.percentile'])
|
||||
plt.ylabel('time (ms)')
|
||||
plt.xlabel('image size')
|
||||
plt.legend(['filter.rank.median', 'filter.median_filter',
|
||||
'scipy.ndimage.percentile'])
|
||||
plt.ylabel('Time (ms)')
|
||||
plt.title('Image size')
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
"""
|
||||
==============================
|
||||
Bilateral mean
|
||||
==============================
|
||||
This example compares
|
||||
|
||||
* local mean
|
||||
* percentile mean
|
||||
* bilateral mean
|
||||
|
||||
build on the local histogram distribution
|
||||
local mean uses all pixels belonging to the structuring element to compute average gray level,
|
||||
percentile mean uses only values between percentiles p0 and p1 (here 10% and 90%),
|
||||
whereas bilateral mean uses only pixels of the structuring element having a gray level situated inside
|
||||
g-s0 and g+s1 (here g-500 and g+500).
|
||||
The filters are applied on a 16 bit image (actual bitdepth is 12bit).
|
||||
|
||||
Percentile and usual mean give here similar results, these filters smooth the complete image (background and details).
|
||||
Bilateral mean exhibits a high filtering rate for continuous area (i.e. background) while image higher frequencies
|
||||
remains untouched.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.morphology import disk
|
||||
import skimage.filter.rank as rank
|
||||
|
||||
a16 = (data.coins()).astype('uint16') * 16
|
||||
selem = disk(20)
|
||||
|
||||
f1 = rank.percentile_mean(a16, selem=selem, p0=.1, p1=.9)
|
||||
f2 = rank.bilateral_mean(a16, selem=selem, s0=500, s1=500)
|
||||
f3 = rank.mean(a16, selem=selem)
|
||||
|
||||
# display results
|
||||
fig, axes = plt.subplots(nrows=3, figsize=(15, 10))
|
||||
ax0, ax1, ax2 = axes
|
||||
|
||||
ax0.imshow(np.hstack((a16, f1)))
|
||||
ax0.set_title('percentile mean')
|
||||
ax1.imshow(np.hstack((a16, f2)))
|
||||
ax1.set_title('bilateral mean')
|
||||
ax2.imshow(np.hstack((a16, f3)))
|
||||
ax2.set_title('local mean')
|
||||
plt.show()
|
||||
@@ -13,12 +13,15 @@ thresholding on the gradient magnitude.
|
||||
The Canny has three adjustable parameters: the width of the Gaussian (the
|
||||
noisier the image, the greater the width), and the low and high threshold for
|
||||
the hysteresis thresholding.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy import ndimage
|
||||
|
||||
from skimage import filter
|
||||
|
||||
|
||||
# Generate noisy image of a square
|
||||
im = np.zeros((128, 128))
|
||||
im[32:-32, 32:-32] = 1
|
||||
@@ -52,6 +55,5 @@ plt.title('Canny filter, $\sigma=3$', fontsize=20)
|
||||
plt.subplots_adjust(wspace=0.02, hspace=0.02, top=0.9,
|
||||
bottom=0.02, left=0.02, right=0.98)
|
||||
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
========================================
|
||||
Circular and Elliptical Hough Transforms
|
||||
========================================
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect
|
||||
straight lines <http://en.wikipedia.org/wiki/Hough_transform>`__
|
||||
but it can also be used to detect circles or ellipses.
|
||||
The algorithm assumes that the edge is detected and it is robust against
|
||||
noise or missing points.
|
||||
|
||||
Circle detection
|
||||
================
|
||||
|
||||
In the following example, the Hough transform is used to detect
|
||||
coin positions and match their edges. We provide a range of
|
||||
plausible radii. For each radius, two circles are extracted and
|
||||
we finally keep the five most prominent candidates.
|
||||
The result shows that coin positions are well-detected.
|
||||
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Given a black circle on a white background, we first guess its
|
||||
radius (or a range of radii) to construct a new circle.
|
||||
This circle is applied on each black pixel of the original picture
|
||||
and the coordinates of this circle are voting in an accumulator.
|
||||
From this geometrical construction, the original circle center
|
||||
position receives the highest score.
|
||||
|
||||
Note that the accumulator size is built to be larger than the
|
||||
original picture in order to detect centers outside the frame.
|
||||
Its size is extended by two times the larger radius.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, filter, color
|
||||
from skimage.transform import hough_circle
|
||||
from skimage.feature import peak_local_max
|
||||
from skimage.draw import circle_perimeter
|
||||
from skimage.util import img_as_ubyte
|
||||
|
||||
|
||||
# Load picture and detect edges
|
||||
image = img_as_ubyte(data.coins()[0:95, 70:370])
|
||||
edges = filter.canny(image, sigma=3, low_threshold=10, high_threshold=50)
|
||||
|
||||
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
|
||||
|
||||
# Detect two radii
|
||||
hough_radii = np.arange(15, 30, 2)
|
||||
hough_res = hough_circle(edges, hough_radii)
|
||||
|
||||
centers = []
|
||||
accums = []
|
||||
radii = []
|
||||
|
||||
for radius, h in zip(hough_radii, hough_res):
|
||||
# For each radius, extract two circles
|
||||
peaks = peak_local_max(h, num_peaks=2)
|
||||
centers.extend(peaks)
|
||||
accums.extend(h[peaks[:, 0], peaks[:, 1]])
|
||||
radii.extend([radius, radius])
|
||||
|
||||
# Draw the most prominent 5 circles
|
||||
image = color.gray2rgb(image)
|
||||
for idx in np.argsort(accums)[::-1][:5]:
|
||||
center_x, center_y = centers[idx]
|
||||
radius = radii[idx]
|
||||
cx, cy = circle_perimeter(center_y, center_x, radius)
|
||||
image[cy, cx] = (220, 20, 20)
|
||||
|
||||
ax.imshow(image, cmap=plt.cm.gray)
|
||||
|
||||
|
||||
"""
|
||||
Ellipse detection
|
||||
=================
|
||||
|
||||
In this second example, the aim is to detect the edge of a coffee cup.
|
||||
Basically, this is a projection of a circle, i.e. an ellipse.
|
||||
The problem to solve is much more difficult because five parameters have to be
|
||||
determined, instead of three for circles.
|
||||
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
The algorithm takes two different points belonging to the ellipse. It assumes
|
||||
that it is the main axis. A loop on all the other points determines how much
|
||||
an ellipse passes to them. A good match corresponds to high accumulator values.
|
||||
|
||||
A full description of the algorithm can be found in reference [1]_.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Xie, Yonghong, and Qiang Ji. "A new efficient ellipse detection
|
||||
method." Pattern Recognition, 2002. Proceedings. 16th International
|
||||
Conference on. Vol. 2. IEEE, 2002
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, filter, color
|
||||
from skimage.transform import hough_ellipse
|
||||
from skimage.draw import ellipse_perimeter
|
||||
|
||||
# Load picture, convert to grayscale and detect edges
|
||||
image_rgb = data.coffee()[0:220, 160:420]
|
||||
image_gray = color.rgb2gray(image_rgb)
|
||||
edges = filter.canny(image_gray, sigma=2.0,
|
||||
low_threshold=0.55, high_threshold=0.8)
|
||||
|
||||
# Perform a Hough Transform
|
||||
# The accuracy corresponds to the bin size of a major axis.
|
||||
# The value is chosen in order to get a single high accumulator.
|
||||
# The threshold eliminates low accumulators
|
||||
result = hough_ellipse(edges, accuracy=20, threshold=250,
|
||||
min_size=100, max_size=120)
|
||||
result.sort(order='accumulator')
|
||||
|
||||
# Estimated parameters for the ellipse
|
||||
best = result[-1]
|
||||
yc = int(best[1])
|
||||
xc = int(best[2])
|
||||
a = int(best[3])
|
||||
b = int(best[4])
|
||||
orientation = best[5]
|
||||
|
||||
# Draw the ellipse on the original image
|
||||
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
|
||||
image_rgb[cy, cx] = (0, 0, 255)
|
||||
# Draw the edge (white) and the resulting ellipse (red)
|
||||
edges = color.gray2rgb(edges)
|
||||
edges[cy, cx] = (250, 0, 0)
|
||||
|
||||
fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(10, 6))
|
||||
|
||||
ax1.set_title('Original picture')
|
||||
ax1.imshow(image_rgb)
|
||||
|
||||
ax2.set_title('Edge (white) and result (red)')
|
||||
ax2.imshow(edges)
|
||||
|
||||
plt.show()
|
||||
@@ -1,72 +0,0 @@
|
||||
"""
|
||||
========================
|
||||
Circular Hough Transform
|
||||
========================
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect
|
||||
straight lines <http://en.wikipedia.org/wiki/Hough_transform>`__
|
||||
but it can also be used to detect circles.
|
||||
|
||||
In the following example, the Hough transform is used to detect
|
||||
coin positions and match their edges. We provide a range of
|
||||
plausible radii. For each radius, two circles are extracted and
|
||||
we finally keep the five most prominent candidates.
|
||||
The result shows that coin positions are well-detected.
|
||||
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Given a black circle on a white background, we first guess its
|
||||
radius (or a range of radii) to construct a new circle.
|
||||
This circle is applied on each black pixel of the original picture
|
||||
and the coordinates of this circle are voting in an accumulator.
|
||||
From this geometrical construction, the original circle center
|
||||
position receives the highest score.
|
||||
|
||||
Note that the accumulator size is built to be larger than the
|
||||
original picture in order to detect centers outside the frame.
|
||||
Its size is extended by two times the larger radius.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, filter, color
|
||||
from skimage.transform import hough_circle
|
||||
from skimage.feature import peak_local_max
|
||||
from skimage.draw import circle_perimeter
|
||||
|
||||
# Load picture and detect edges
|
||||
image = data.coins()[0:95, 70:370]
|
||||
edges = filter.canny(image, sigma=3, low_threshold=10, high_threshold=50)
|
||||
|
||||
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
|
||||
|
||||
# Detect two radii
|
||||
hough_radii = np.arange(15, 30, 2)
|
||||
hough_res = hough_circle(edges, hough_radii)
|
||||
|
||||
centers = []
|
||||
accums = []
|
||||
radii = []
|
||||
|
||||
for radius, h in zip(hough_radii, hough_res):
|
||||
# For each radius, extract two circles
|
||||
peaks = peak_local_max(h, num_peaks=2)
|
||||
centers.extend(peaks - hough_radii.max())
|
||||
accums.extend(h[peaks[:, 0], peaks[:, 1]])
|
||||
radii.extend([radius, radius])
|
||||
|
||||
# Draw the most prominent 5 circles
|
||||
image = color.gray2rgb(image)
|
||||
for idx in np.argsort(accums)[::-1][:5]:
|
||||
center_x, center_y = centers[idx]
|
||||
radius = radii[idx]
|
||||
cx, cy = circle_perimeter(center_y, center_x, radius)
|
||||
image[cy, cx] = (220, 20, 20)
|
||||
|
||||
ax.imshow(image, cmap=plt.cm.gray)
|
||||
plt.show()
|
||||
@@ -15,13 +15,12 @@ Cubes: A High Resolution 3D Surface Construction Algorithm. Computer Graphics
|
||||
(SIGGRAPH 87 Proceedings) 21(4) July 1987, p. 163-170).
|
||||
|
||||
"""
|
||||
|
||||
from skimage import data
|
||||
from skimage import measure
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import measure
|
||||
|
||||
|
||||
# Construct some test data
|
||||
x, y = np.ogrid[-np.pi:np.pi:100j, -np.pi:np.pi:100j]
|
||||
r = np.sin(np.exp((np.sin(x)**3 + np.cos(y)**2)))
|
||||
@@ -39,4 +38,3 @@ plt.axis('image')
|
||||
plt.xticks([])
|
||||
plt.yticks([])
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ A good overview of the algorithm is given on `Steve Eddin's blog
|
||||
<http://blogs.mathworks.com/steve/2011/10/04/binary-image-convex-hull-algorithm-notes/>`__.
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.morphology import convex_hull_image
|
||||
|
||||
|
||||
image = np.array(
|
||||
[[0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||
@@ -27,9 +27,24 @@ image = np.array(
|
||||
[0, 1, 0, 0, 0, 0, 0, 1, 0],
|
||||
[0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=float)
|
||||
|
||||
chull = convex_hull_image(image)
|
||||
image[chull] += 1.7
|
||||
image -= -1.7
|
||||
original_image = np.copy(image)
|
||||
|
||||
chull = convex_hull_image(image)
|
||||
image[chull] += 1
|
||||
# image is now:
|
||||
#[[ 0. 0. 0. 0. 0. 0. 0. 0. 0.]
|
||||
# [ 0. 0. 0. 0. 2. 0. 0. 0. 0.]
|
||||
# [ 0. 0. 0. 2. 1. 2. 0. 0. 0.]
|
||||
# [ 0. 0. 2. 1. 1. 1. 2. 0. 0.]
|
||||
# [ 0. 2. 1. 1. 1. 1. 1. 2. 0.]
|
||||
# [ 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
|
||||
|
||||
|
||||
fig = plt.subplots(figsize=(10, 6))
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.title('Original picture')
|
||||
plt.imshow(original_image, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.title('Transformed picture')
|
||||
plt.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
|
||||
plt.show()
|
||||
|
||||
@@ -10,7 +10,6 @@ position of corners.
|
||||
.. [2] http://en.wikipedia.org/wiki/Interest_point_detection
|
||||
|
||||
"""
|
||||
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
@@ -18,6 +17,7 @@ from skimage.feature import corner_harris, corner_subpix, corner_peaks
|
||||
from skimage.transform import warp, AffineTransform
|
||||
from skimage.draw import ellipse
|
||||
|
||||
|
||||
tform = AffineTransform(scale=(1.3, 1.1), rotation=1, shear=0.7,
|
||||
translation=(210, 50))
|
||||
image = warp(data.checkerboard(), tform.inverse, output_shape=(350, 350))
|
||||
|
||||
@@ -11,7 +11,6 @@ representations.
|
||||
In this example a limited number of DAISY descriptors are extracted at a large
|
||||
scale for illustrative purposes.
|
||||
"""
|
||||
|
||||
from skimage.feature import daisy
|
||||
from skimage import data
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -25,13 +25,13 @@ A bilateral filter is an edge-preserving and noise reducing filter. It averages
|
||||
pixels based on their spatial closeness and radiometric similarity.
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, color, img_as_float
|
||||
from skimage import data, img_as_float
|
||||
from skimage.filter import denoise_tv_chambolle, denoise_bilateral
|
||||
|
||||
|
||||
lena = img_as_float(data.lena())
|
||||
lena = lena[220:300, 220:320]
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"""
|
||||
==============
|
||||
Edge operators
|
||||
==============
|
||||
|
||||
Edge operators are used in image processing within edge detection algorithms.
|
||||
They are discrete differentiation operators, computing an approximation of the
|
||||
gradient of the image intensity function.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.data import camera
|
||||
from skimage.filter import roberts, sobel
|
||||
|
||||
|
||||
image = camera()
|
||||
edge_roberts = roberts(image)
|
||||
edge_sobel = sobel(image)
|
||||
|
||||
fig, (ax0, ax1) = plt.subplots(ncols=2)
|
||||
|
||||
ax0.imshow(edge_roberts, cmap=plt.cm.gray)
|
||||
ax0.set_title('Roberts Edge Detection')
|
||||
ax0.axis('off')
|
||||
|
||||
ax1.imshow(edge_sobel, cmap=plt.cm.gray)
|
||||
ax1.set_title('Sobel Edge Detection')
|
||||
ax1.axis('off')
|
||||
|
||||
plt.show()
|
||||
@@ -1,44 +1,32 @@
|
||||
"""
|
||||
===================
|
||||
=======
|
||||
Entropy
|
||||
===================
|
||||
=======
|
||||
|
||||
Image entropy is a quantity which is used to describe the amount of information
|
||||
coded in an image.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.filter.rank import entropy
|
||||
from skimage.morphology import disk
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from skimage.util import img_as_ubyte
|
||||
|
||||
# defining a 8- and a 16-bit test images
|
||||
a8 = data.camera()
|
||||
a16 = data.camera().astype(np.uint16)*4
|
||||
|
||||
ent8 = entropy(a8,disk(5)) # pixel value contain 10x the local entropy
|
||||
ent16 = entropy(a16,disk(5)) # pixel value contain 1000x the local entropy
|
||||
image = img_as_ubyte(data.camera())
|
||||
|
||||
# display results
|
||||
plt.figure(figsize=(10, 10))
|
||||
fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(10, 4))
|
||||
|
||||
plt.subplot(2,2,1)
|
||||
plt.imshow(a8, cmap=plt.cm.gray)
|
||||
plt.xlabel('8-bit image')
|
||||
plt.colorbar()
|
||||
img0 = ax0.imshow(image, cmap=plt.cm.gray)
|
||||
ax0.set_title('Image')
|
||||
ax0.axis('off')
|
||||
plt.colorbar(img0, ax=ax0)
|
||||
|
||||
plt.subplot(2,2,2)
|
||||
plt.imshow(ent8, cmap=plt.cm.jet)
|
||||
plt.xlabel('entropy*10')
|
||||
plt.colorbar()
|
||||
img1 = ax1.imshow(entropy(image, disk(5)), cmap=plt.cm.jet)
|
||||
ax1.set_title('Entropy')
|
||||
ax1.axis('off')
|
||||
plt.colorbar(img1, ax=ax1)
|
||||
|
||||
plt.subplot(2,2,3)
|
||||
plt.imshow(a16, cmap=plt.cm.gray)
|
||||
plt.xlabel('16-bit image')
|
||||
plt.colorbar()
|
||||
|
||||
plt.subplot(2,2,4)
|
||||
plt.imshow(ent16, cmap=plt.cm.jet)
|
||||
plt.xlabel('entropy*1000')
|
||||
plt.colorbar()
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -17,13 +17,12 @@ that fall within the 2nd and 98th percentiles [2]_.
|
||||
.. [2] http://homepages.inf.ed.ac.uk/rbf/HIPR2/stretch.htm
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from skimage import data, img_as_float
|
||||
from skimage import exposure
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
|
||||
def plot_img_and_hist(img, axes, bins=256):
|
||||
"""Plot an image along with its histogram and cumulative histogram.
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
=============================================
|
||||
Gabor filter banks for texture classification
|
||||
=============================================
|
||||
|
||||
In this example, we will see how to classify textures based on Gabor filter
|
||||
banks. Frequency and orientation representations of the Gabor filter are similar
|
||||
to those of the human visual system.
|
||||
|
||||
The images are filtered using the real parts of various different Gabor filter
|
||||
kernels. The mean and variance of the filtered images are then used as features
|
||||
for classification, which is based on the least squared error for simplicity.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from scipy import ndimage as nd
|
||||
|
||||
from skimage import data
|
||||
from skimage.util import img_as_float
|
||||
from skimage.filter import gabor_kernel
|
||||
|
||||
|
||||
matplotlib.rcParams['font.size'] = 9
|
||||
|
||||
|
||||
def compute_feats(image, kernels):
|
||||
feats = np.zeros((len(kernels), 2), dtype=np.double)
|
||||
for k, kernel in enumerate(kernels):
|
||||
filtered = nd.convolve(image, kernel, mode='wrap')
|
||||
feats[k, 0] = filtered.mean()
|
||||
feats[k, 1] = filtered.var()
|
||||
return feats
|
||||
|
||||
|
||||
def match(feats, ref_feats):
|
||||
min_error = np.inf
|
||||
min_i = None
|
||||
for i in range(ref_feats.shape[0]):
|
||||
error = np.sum((feats - ref_feats[i, :])**2)
|
||||
if error < min_error:
|
||||
min_error = error
|
||||
min_i = i
|
||||
return min_i
|
||||
|
||||
|
||||
# prepare filter bank kernels
|
||||
kernels = []
|
||||
for theta in range(4):
|
||||
theta = theta / 4. * np.pi
|
||||
for sigma in (1, 3):
|
||||
for frequency in (0.05, 0.25):
|
||||
kernel = np.real(gabor_kernel(frequency, theta=theta,
|
||||
sigma_x=sigma, sigma_y=sigma))
|
||||
kernels.append(kernel)
|
||||
|
||||
|
||||
shrink = (slice(0, None, 3), slice(0, None, 3))
|
||||
brick = img_as_float(data.load('brick.png'))[shrink]
|
||||
grass = img_as_float(data.load('grass.png'))[shrink]
|
||||
wall = img_as_float(data.load('rough-wall.png'))[shrink]
|
||||
image_names = ('brick', 'grass', 'wall')
|
||||
images = (brick, grass, wall)
|
||||
|
||||
# prepare reference features
|
||||
ref_feats = np.zeros((3, len(kernels), 2), dtype=np.double)
|
||||
ref_feats[0, :, :] = compute_feats(brick, kernels)
|
||||
ref_feats[1, :, :] = compute_feats(grass, kernels)
|
||||
ref_feats[2, :, :] = compute_feats(wall, kernels)
|
||||
|
||||
print('Rotated images matched against references using Gabor filter banks:')
|
||||
|
||||
print('original: brick, rotated: 30deg, match result: ', end='')
|
||||
feats = compute_feats(nd.rotate(brick, angle=190, reshape=False), kernels)
|
||||
print(image_names[match(feats, ref_feats)])
|
||||
|
||||
print('original: brick, rotated: 70deg, match result: ', end='')
|
||||
feats = compute_feats(nd.rotate(brick, angle=70, reshape=False), kernels)
|
||||
print(image_names[match(feats, ref_feats)])
|
||||
|
||||
print('original: grass, rotated: 145deg, match result: ', end='')
|
||||
feats = compute_feats(nd.rotate(grass, angle=145, reshape=False), kernels)
|
||||
print(image_names[match(feats, ref_feats)])
|
||||
|
||||
|
||||
def power(image, kernel):
|
||||
# Normalize images for better comparison.
|
||||
image = (image - image.mean()) / image.std()
|
||||
return np.sqrt(nd.convolve(image, np.real(kernel), mode='wrap')**2 +
|
||||
nd.convolve(image, np.imag(kernel), mode='wrap')**2)
|
||||
|
||||
# Plot a selection of the filter bank kernels and their responses.
|
||||
results = []
|
||||
kernel_params = []
|
||||
for theta in (0, 1):
|
||||
theta = theta / 4. * np.pi
|
||||
for frequency in (0.1, 0.4):
|
||||
kernel = gabor_kernel(frequency, theta=theta)
|
||||
params = 'theta=%d,\nfrequency=%.2f' % (theta * 180 / np.pi, frequency)
|
||||
kernel_params.append(params)
|
||||
# Save kernel and the power image for each image
|
||||
results.append((kernel, [power(img, kernel) for img in images]))
|
||||
|
||||
fig, axes = plt.subplots(nrows=5, ncols=4, figsize=(9, 6))
|
||||
plt.gray()
|
||||
|
||||
fig.suptitle('Image responses for Gabor filter kernels', fontsize=15)
|
||||
|
||||
axes[0][0].axis('off')
|
||||
|
||||
# Plot original images
|
||||
for label, img, ax in zip(image_names, images, axes[0][1:]):
|
||||
ax.imshow(img)
|
||||
ax.set_title(label)
|
||||
ax.axis('off')
|
||||
|
||||
for label, (kernel, powers), ax_row in zip(kernel_params, results, axes[1:]):
|
||||
# Plot Gabor kernel
|
||||
ax = ax_row[0]
|
||||
ax.imshow(np.real(kernel), interpolation='nearest')
|
||||
ax.set_ylabel(label)
|
||||
ax.set_xticks([])
|
||||
ax.set_yticks([])
|
||||
|
||||
# Plot Gabor responses with the contrast normalized for each filter
|
||||
vmin = np.min(powers)
|
||||
vmax = np.max(powers)
|
||||
for patch, ax in zip(powers, ax_row[1:]):
|
||||
ax.imshow(patch, vmin=vmin, vmax=vmax)
|
||||
ax.axis('off')
|
||||
|
||||
plt.show()
|
||||
@@ -3,8 +3,6 @@
|
||||
Gabors / Primary Visual Cortex "Simple Cells" from Lena
|
||||
=======================================================
|
||||
|
||||
(under construction)
|
||||
|
||||
How to build a (bio-plausible) "sparse" dictionary (or 'codebook', or
|
||||
'filterbank') for e.g. image classification without any fancy math and
|
||||
with just standard python scientific libraries?
|
||||
@@ -37,7 +35,6 @@ is not rocket science.
|
||||
Interaction, and Functional Architecture in the Cat's Visual Cortex,
|
||||
J. Physiol. 160 pp. 106-154 1962
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy.cluster.vq import kmeans2
|
||||
from scipy import ndimage as ndi
|
||||
|
||||
@@ -19,10 +19,11 @@ this example) would be to train a classifier, such as logistic
|
||||
regression, to label image patches from new images.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.feature import greycomatrix, greycoprops
|
||||
from skimage import data
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
PATCH_SIZE = 21
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
r'''
|
||||
"""
|
||||
===============================
|
||||
Histogram of Oriented Gradients
|
||||
===============================
|
||||
@@ -77,12 +77,13 @@ References
|
||||
.. [2] David G. Lowe, "Distinctive image features from scale-invariant
|
||||
keypoints," International Journal of Computer Vision, 60, 2 (2004),
|
||||
pp. 91-110.
|
||||
'''
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.feature import hog
|
||||
from skimage import data, color, exposure
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
image = color.rgb2gray(data.lena())
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ image represent the maximum and minimum possible values of the reconstructed
|
||||
image.
|
||||
|
||||
We start with an image containing both peaks and holes:
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
r'''
|
||||
===============
|
||||
Hough transform
|
||||
===============
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect
|
||||
straight lines <http://en.wikipedia.org/wiki/Hough_transform>`__.
|
||||
|
||||
In the following example, we construct an image with a line
|
||||
intersection. We then use the Hough transform to explore a parameter
|
||||
space for straight lines that may run through the image.
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Usually, lines are parameterised as :math:`y = mx + c`, with a
|
||||
gradient :math:`m` and y-intercept `c`. However, this would mean that
|
||||
:math:`m` goes to infinity for vertical lines. Instead, we therefore
|
||||
construct a segment perpendicular to the line, leading to the origin.
|
||||
The line is represented by the length of that segment, :math:`r`, and
|
||||
the angle it makes with the x-axis, :math:`\theta`.
|
||||
|
||||
The Hough transform constructs a histogram array representing the
|
||||
parameter space (i.e., an :math:`M \times N` matrix, for :math:`M`
|
||||
different values of the radius and :math:`N` different values of
|
||||
:math:`\theta`). For each parameter combination, :math:`r` and
|
||||
:math:`\theta`, we then find the number of non-zero pixels in the
|
||||
input image that would fall close to the corresponding line, and
|
||||
increment the array at position :math:`(r, \theta)` appropriately.
|
||||
|
||||
We can think of each non-zero pixel "voting" for potential line
|
||||
candidates. The local maxima in the resulting histogram indicates the
|
||||
parameters of the most probably lines. In our example, the maxima
|
||||
occur at 45 and 135 degrees, corresponding to the normal vector
|
||||
angles of each line.
|
||||
|
||||
Another approach is the Progressive Probabilistic Hough Transform
|
||||
[1]_. It is based on the assumption that using a random subset of
|
||||
voting points give a good approximation to the actual result, and that
|
||||
lines can be extracted during the voting process by walking along
|
||||
connected components. This returns the beginning and end of each
|
||||
line segment, which is useful.
|
||||
|
||||
The function `probabilistic_hough` has three parameters: a general
|
||||
threshold that is applied to the Hough accumulator, a minimum line
|
||||
length and the line gap that influences line merging. In the example
|
||||
below, we find lines longer than 10 with a gap less than 3 pixels.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
.. [1] C. Galamhos, J. Matas and J. Kittler,"Progressive probabilistic
|
||||
Hough transform for line detection", in IEEE Computer Society
|
||||
Conference on Computer Vision and Pattern Recognition, 1999.
|
||||
|
||||
.. [2] Duda, R. O. and P. E. Hart, "Use of the Hough Transformation to
|
||||
Detect Lines and Curves in Pictures," Comm. ACM, Vol. 15,
|
||||
pp. 11-15 (January, 1972)
|
||||
|
||||
'''
|
||||
|
||||
from skimage.transform import hough, hough_peaks, probabilistic_hough
|
||||
from skimage.filter import canny
|
||||
from skimage import data
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Construct test image
|
||||
|
||||
image = np.zeros((100, 100))
|
||||
|
||||
|
||||
# Classic straight-line Hough transform
|
||||
|
||||
idx = np.arange(25, 75)
|
||||
image[idx[::-1], idx] = 255
|
||||
image[idx, idx] = 255
|
||||
|
||||
h, theta, d = hough(image)
|
||||
|
||||
plt.figure(figsize=(8, 4))
|
||||
|
||||
plt.subplot(131)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
plt.title('Input image')
|
||||
|
||||
plt.subplot(132)
|
||||
plt.imshow(np.log(1 + h),
|
||||
extent=[np.rad2deg(theta[-1]), np.rad2deg(theta[0]),
|
||||
d[-1], d[0]],
|
||||
cmap=plt.cm.gray, aspect=1/1.5)
|
||||
plt.title('Hough transform')
|
||||
plt.xlabel('Angles (degrees)')
|
||||
plt.ylabel('Distance (pixels)')
|
||||
|
||||
plt.subplot(133)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
rows, cols = image.shape
|
||||
for _, angle, dist in zip(*hough_peaks(h, theta, d)):
|
||||
y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
|
||||
y1 = (dist - cols * np.cos(angle)) / np.sin(angle)
|
||||
plt.plot((0, cols), (y0, y1), '-r')
|
||||
plt.axis((0, cols, rows, 0))
|
||||
plt.title('Detected lines')
|
||||
|
||||
# Line finding, using the Probabilistic Hough Transform
|
||||
|
||||
image = data.camera()
|
||||
edges = canny(image, 2, 1, 25)
|
||||
lines = probabilistic_hough(edges, threshold=10, line_length=5, line_gap=3)
|
||||
|
||||
plt.figure(figsize=(8, 3))
|
||||
|
||||
plt.subplot(131)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
plt.title('Input image')
|
||||
|
||||
plt.subplot(132)
|
||||
plt.imshow(edges, cmap=plt.cm.gray)
|
||||
plt.title('Canny edges')
|
||||
|
||||
plt.subplot(133)
|
||||
plt.imshow(edges * 0)
|
||||
|
||||
for line in lines:
|
||||
p0, p1 = line
|
||||
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))
|
||||
|
||||
plt.title('Probabilistic Hough')
|
||||
plt.axis('image')
|
||||
plt.show()
|
||||
@@ -0,0 +1,72 @@
|
||||
"""
|
||||
==============================================
|
||||
Immunohistochemical staining colors separation
|
||||
==============================================
|
||||
|
||||
In this example we separate the immunohistochemical (IHC) staining from the
|
||||
hematoxylin counterstaining. The separation is achieved with the method
|
||||
described in [1]_, known as "color deconvolution".
|
||||
|
||||
The IHC staining expression of the FHL2 protein is here revealed with
|
||||
Diaminobenzidine (DAB) which gives a brown color.
|
||||
|
||||
|
||||
.. [1] A. C. Ruifrok and D. A. Johnston, "Quantification of histochemical
|
||||
staining by color deconvolution.," Analytical and quantitative
|
||||
cytology and histology / the International Academy of Cytology [and]
|
||||
American Society of Cytology, vol. 23, no. 4, pp. 291-9, Aug. 2001.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.color import rgb2hed
|
||||
|
||||
|
||||
ihc_rgb = data.immunohistochemistry()
|
||||
ihc_hed = rgb2hed(ihc_rgb)
|
||||
|
||||
fig, axes = plt.subplots(2, 2, figsize=(7, 6))
|
||||
ax0, ax1, ax2, ax3 = axes.ravel()
|
||||
|
||||
ax0.imshow(ihc_rgb)
|
||||
ax0.set_title("Original image")
|
||||
|
||||
ax1.imshow(ihc_hed[:, :, 0], cmap=plt.cm.gray)
|
||||
ax1.set_title("Hematoxylin")
|
||||
|
||||
ax2.imshow(ihc_hed[:, :, 1], cmap=plt.cm.gray)
|
||||
ax2.set_title("Eosin")
|
||||
|
||||
ax3.imshow(ihc_hed[:, :, 2], cmap=plt.cm.gray)
|
||||
ax3.set_title("DAB")
|
||||
|
||||
for ax in axes.ravel():
|
||||
ax.axis('off')
|
||||
|
||||
fig.subplots_adjust(hspace=0.3)
|
||||
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Now we can easily manipulate the hematoxylin and DAB "channels":
|
||||
"""
|
||||
import numpy as np
|
||||
|
||||
from skimage.exposure import rescale_intensity
|
||||
|
||||
# Rescale hematoxylin and DAB signals and give them a fluorescence look
|
||||
h = rescale_intensity(ihc_hed[:, :, 0], out_range=(0, 1))
|
||||
d = rescale_intensity(ihc_hed[:, :, 2], out_range=(0, 1))
|
||||
zdh = np.dstack((np.zeros_like(h), d, h))
|
||||
|
||||
plt.figure()
|
||||
plt.imshow(zdh)
|
||||
plt.title("Stain separated image (rescaled)")
|
||||
plt.axis('off')
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
"""
|
||||
@@ -8,59 +8,52 @@ segmentations. The `skimage.segmentation.join_segmentations` function
|
||||
computes the join of two segmentations, in which a pixel is placed in
|
||||
the same segment if and only if it is in the same segment in _both_
|
||||
segmentations.
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy import ndimage as nd
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib as mpl
|
||||
|
||||
from skimage.filter import sobel
|
||||
from skimage.segmentation import slic, join_segmentations
|
||||
from skimage.morphology import watershed
|
||||
from skimage.color import label2rgb
|
||||
from skimage import data, img_as_float
|
||||
|
||||
from skimage import data
|
||||
|
||||
coins = data.coins()
|
||||
coins = img_as_float(data.coins())
|
||||
|
||||
# make segmentation using edge-detection and watershed
|
||||
edges = sobel(coins)
|
||||
markers = np.zeros_like(coins)
|
||||
foreground, background = 1, 2
|
||||
markers[coins < 30] = background
|
||||
markers[coins > 150] = foreground
|
||||
markers[coins < 30.0 / 255] = background
|
||||
markers[coins > 150.0 / 255] = foreground
|
||||
|
||||
ws = watershed(edges, markers)
|
||||
seg1 = nd.label(ws == foreground)[0]
|
||||
|
||||
# make segmentation using SLIC superpixels
|
||||
|
||||
# make the RGB equivalent of `coins`
|
||||
coins_colour = np.tile(coins[..., np.newaxis], (1, 1, 3))
|
||||
seg2 = slic(coins_colour, n_segments=30, max_iter=160, sigma=1, ratio=9,
|
||||
convert2lab=False)
|
||||
seg2 = slic(coins, n_segments=117, max_iter=160, sigma=1, compactness=0.75,
|
||||
multichannel=False)
|
||||
|
||||
# combine the two
|
||||
segj = join_segmentations(seg1, seg2)
|
||||
|
||||
### Display the result ###
|
||||
|
||||
# make a random colormap for a set number of values
|
||||
def random_cmap(im):
|
||||
np.random.seed(9)
|
||||
cmap_array = np.concatenate(
|
||||
(np.zeros((1, 3)), np.random.rand(np.ceil(im.max()), 3)))
|
||||
return mpl.colors.ListedColormap(cmap_array)
|
||||
|
||||
# show the segmentations
|
||||
fig, axes = plt.subplots(ncols=4, figsize=(9, 2.5))
|
||||
axes[0].imshow(coins, cmap=plt.cm.gray, interpolation='nearest')
|
||||
axes[0].set_title('Image')
|
||||
axes[1].imshow(seg1, cmap=random_cmap(seg1), interpolation='nearest')
|
||||
|
||||
color1 = label2rgb(seg1, image=coins, bg_label=0)
|
||||
axes[1].imshow(color1, interpolation='nearest')
|
||||
axes[1].set_title('Sobel+Watershed')
|
||||
axes[2].imshow(seg2, cmap=random_cmap(seg2), interpolation='nearest')
|
||||
|
||||
color2 = label2rgb(seg2, image=coins, image_alpha=0.5)
|
||||
axes[2].imshow(color2, interpolation='nearest')
|
||||
axes[2].set_title('SLIC superpixels')
|
||||
axes[3].imshow(segj, cmap=random_cmap(segj), interpolation='nearest')
|
||||
|
||||
color3 = label2rgb(segj, image=coins, image_alpha=0.5)
|
||||
axes[3].imshow(color3, interpolation='nearest')
|
||||
axes[3].set_title('Join')
|
||||
|
||||
for ax in axes:
|
||||
|
||||
@@ -12,7 +12,6 @@ steps are applied:
|
||||
4. Measure image regions to filter small objects
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.patches as mpatches
|
||||
@@ -22,6 +21,7 @@ from skimage.filter import threshold_otsu
|
||||
from skimage.segmentation import clear_border
|
||||
from skimage.morphology import label, closing, square
|
||||
from skimage.measure import regionprops
|
||||
from skimage.color import label2rgb
|
||||
|
||||
|
||||
image = data.coins()[50:-50, 50:-50]
|
||||
@@ -38,9 +38,10 @@ clear_border(cleared)
|
||||
label_image = label(cleared)
|
||||
borders = np.logical_xor(bw, cleared)
|
||||
label_image[borders] = -1
|
||||
image_label_overlay = label2rgb(label_image, image=image)
|
||||
|
||||
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6))
|
||||
ax.imshow(label_image, cmap='jet')
|
||||
ax.imshow(image_label_overlay)
|
||||
|
||||
for region in regionprops(label_image, ['Area', 'BoundingBox']):
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
r"""
|
||||
=============================
|
||||
Straight line Hough transform
|
||||
=============================
|
||||
|
||||
The Hough transform in its simplest form is a `method to detect straight lines
|
||||
<http://en.wikipedia.org/wiki/Hough_transform>`__.
|
||||
|
||||
In the following example, we construct an image with a line intersection. We
|
||||
then use the Hough transform to explore a parameter space for straight lines
|
||||
that may run through the image.
|
||||
|
||||
Algorithm overview
|
||||
------------------
|
||||
|
||||
Usually, lines are parameterised as :math:`y = mx + c`, with a gradient
|
||||
:math:`m` and y-intercept `c`. However, this would mean that :math:`m` goes to
|
||||
infinity for vertical lines. Instead, we therefore construct a segment
|
||||
perpendicular to the line, leading to the origin. The line is represented by the
|
||||
length of that segment, :math:`r`, and the angle it makes with the x-axis,
|
||||
:math:`\theta`.
|
||||
|
||||
The Hough transform constructs a histogram array representing the parameter
|
||||
space (i.e., an :math:`M \times N` matrix, for :math:`M` different values of the
|
||||
radius and :math:`N` different values of :math:`\theta`). For each parameter
|
||||
combination, :math:`r` and :math:`\theta`, we then find the number of non-zero
|
||||
pixels in the input image that would fall close to the corresponding line, and
|
||||
increment the array at position :math:`(r, \theta)` appropriately.
|
||||
|
||||
We can think of each non-zero pixel "voting" for potential line candidates. The
|
||||
local maxima in the resulting histogram indicates the parameters of the most
|
||||
probably lines. In our example, the maxima occur at 45 and 135 degrees,
|
||||
corresponding to the normal vector angles of each line.
|
||||
|
||||
Another approach is the Progressive Probabilistic Hough Transform [1]_. It is
|
||||
based on the assumption that using a random subset of voting points give a good
|
||||
approximation to the actual result, and that lines can be extracted during the
|
||||
voting process by walking along connected components. This returns the beginning
|
||||
and end of each line segment, which is useful.
|
||||
|
||||
The function `probabilistic_hough` has three parameters: a general threshold
|
||||
that is applied to the Hough accumulator, a minimum line length and the line gap
|
||||
that influences line merging. In the example below, we find lines longer than 10
|
||||
with a gap less than 3 pixels.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
.. [1] C. Galamhos, J. Matas and J. Kittler,"Progressive probabilistic
|
||||
Hough transform for line detection", in IEEE Computer Society
|
||||
Conference on Computer Vision and Pattern Recognition, 1999.
|
||||
|
||||
.. [2] Duda, R. O. and P. E. Hart, "Use of the Hough Transformation to
|
||||
Detect Lines and Curves in Pictures," Comm. ACM, Vol. 15,
|
||||
pp. 11-15 (January, 1972)
|
||||
|
||||
"""
|
||||
|
||||
from skimage.transform import (hough_line, hough_line_peaks,
|
||||
probabilistic_hough_line)
|
||||
from skimage.filter import canny
|
||||
from skimage import data
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Construct test image
|
||||
|
||||
image = np.zeros((100, 100))
|
||||
|
||||
|
||||
# Classic straight-line Hough transform
|
||||
|
||||
idx = np.arange(25, 75)
|
||||
image[idx[::-1], idx] = 255
|
||||
image[idx, idx] = 255
|
||||
|
||||
h, theta, d = hough_line(image)
|
||||
|
||||
plt.figure(figsize=(8, 4))
|
||||
|
||||
plt.subplot(131)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
plt.title('Input image')
|
||||
|
||||
plt.subplot(132)
|
||||
plt.imshow(np.log(1 + h),
|
||||
extent=[np.rad2deg(theta[-1]), np.rad2deg(theta[0]),
|
||||
d[-1], d[0]],
|
||||
cmap=plt.cm.gray, aspect=1/1.5)
|
||||
plt.title('Hough transform')
|
||||
plt.xlabel('Angles (degrees)')
|
||||
plt.ylabel('Distance (pixels)')
|
||||
|
||||
plt.subplot(133)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
rows, cols = image.shape
|
||||
for _, angle, dist in zip(*hough_line_peaks(h, theta, d)):
|
||||
y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
|
||||
y1 = (dist - cols * np.cos(angle)) / np.sin(angle)
|
||||
plt.plot((0, cols), (y0, y1), '-r')
|
||||
plt.axis((0, cols, rows, 0))
|
||||
plt.title('Detected lines')
|
||||
|
||||
# Line finding, using the Probabilistic Hough Transform
|
||||
|
||||
image = data.camera()
|
||||
edges = canny(image, 2, 1, 25)
|
||||
lines = probabilistic_hough_line(edges, threshold=10, line_length=5, line_gap=3)
|
||||
|
||||
plt.figure(figsize=(8, 3))
|
||||
|
||||
plt.subplot(131)
|
||||
plt.imshow(image, cmap=plt.cm.gray)
|
||||
plt.title('Input image')
|
||||
|
||||
plt.subplot(132)
|
||||
plt.imshow(edges, cmap=plt.cm.gray)
|
||||
plt.title('Canny edges')
|
||||
|
||||
plt.subplot(133)
|
||||
plt.imshow(edges * 0)
|
||||
|
||||
for line in lines:
|
||||
p0, p1 = line
|
||||
plt.plot((p0[0], p1[0]), (p0[1], p1[1]))
|
||||
|
||||
plt.title('Probabilistic Hough')
|
||||
plt.axis('image')
|
||||
plt.show()
|
||||
@@ -4,24 +4,159 @@ Local Binary Pattern for texture classification
|
||||
===============================================
|
||||
|
||||
In this example, we will see how to classify textures based on LBP (Local
|
||||
Binary Pattern). The histogram of the LBP result is a good measure to classify
|
||||
textures. For simplicity the histogram distributions are then tested against
|
||||
each other using the Kullback-Leibler-Divergence.
|
||||
Binary Pattern). LBP looks at points surrounding a central point and tests
|
||||
whether the surrounding points are greater than or less than the central point
|
||||
(i.e. gives a binary result).
|
||||
|
||||
Before trying out LBP on an image, it helps to look at a schematic of LBPs.
|
||||
The below code is just used to plot the schematic.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
METHOD = 'uniform'
|
||||
plt.rcParams['font.size'] = 9
|
||||
|
||||
|
||||
def plot_circle(ax, center, radius, color):
|
||||
circle = plt.Circle(center, radius, facecolor=color, edgecolor='0.5')
|
||||
ax.add_patch(circle)
|
||||
|
||||
|
||||
def plot_lbp_model(ax, binary_values):
|
||||
"""Draw the schematic for a local binary pattern."""
|
||||
# Geometry spec
|
||||
theta = np.deg2rad(45)
|
||||
R = 1
|
||||
r = 0.15
|
||||
w = 1.5
|
||||
gray = '0.5'
|
||||
|
||||
# Draw the central pixel.
|
||||
plot_circle(ax, (0, 0), radius=r, color=gray)
|
||||
# Draw the surrounding pixels.
|
||||
for i, facecolor in enumerate(binary_values):
|
||||
x = R * np.cos(i * theta)
|
||||
y = R * np.sin(i * theta)
|
||||
plot_circle(ax, (x, y), radius=r, color=str(facecolor))
|
||||
|
||||
# Draw the pixel grid.
|
||||
for x in np.linspace(-w, w, 4):
|
||||
ax.axvline(x, color=gray)
|
||||
ax.axhline(x, color=gray)
|
||||
|
||||
# Tweak the layout.
|
||||
ax.axis('image')
|
||||
ax.axis('off')
|
||||
size = w + 0.2
|
||||
ax.set_xlim(-size, size)
|
||||
ax.set_ylim(-size, size)
|
||||
|
||||
|
||||
fig, axes = plt.subplots(ncols=5, figsize=(7, 2))
|
||||
|
||||
titles = ['flat', 'flat', 'edge', 'corner', 'non-uniform']
|
||||
|
||||
binary_patterns = [np.zeros(8),
|
||||
np.ones(8),
|
||||
np.hstack([np.ones(4), np.zeros(4)]),
|
||||
np.hstack([np.zeros(3), np.ones(5)]),
|
||||
[1, 0, 0, 1, 1, 1, 0, 0]]
|
||||
|
||||
for ax, values, name in zip(axes, binary_patterns, titles):
|
||||
plot_lbp_model(ax, values)
|
||||
ax.set_title(name)
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
The figure above shows example results with black (or white) representing
|
||||
pixels that are less (or more) intense than the central pixel. When surrounding
|
||||
pixels are all black or all white, then that image region is flat (i.e.
|
||||
featureless). Groups of continuous black or white pixels are considered
|
||||
"uniform" patterns that can be interpreted as corners or edges. If pixels
|
||||
switch back-and-forth between black and white pixels, the pattern is considered
|
||||
"non-uniform".
|
||||
|
||||
When using LBP to detect texture, you measure a collection of LBPs over an
|
||||
image patch and look at the distribution of these LBPs. Lets apply LBP to
|
||||
a brick texture.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import scipy.ndimage as nd
|
||||
import skimage.feature as ft
|
||||
from skimage.transform import rotate
|
||||
from skimage.feature import local_binary_pattern
|
||||
from skimage import data
|
||||
|
||||
from skimage.color import label2rgb
|
||||
|
||||
# settings for LBP
|
||||
METHOD = 'uniform'
|
||||
P = 16
|
||||
R = 2
|
||||
matplotlib.rcParams['font.size'] = 9
|
||||
radius = 3
|
||||
n_points = 8 * radius
|
||||
|
||||
|
||||
def overlay_labels(image, lbp, labels):
|
||||
mask = np.logical_or.reduce([lbp == each for each in labels])
|
||||
return label2rgb(mask, image=image, bg_label=0, alpha=0.5)
|
||||
|
||||
|
||||
def highlight_bars(bars, indexes):
|
||||
for i in indexes:
|
||||
bars[i].set_facecolor('r')
|
||||
|
||||
|
||||
image = data.load('brick.png')
|
||||
lbp = local_binary_pattern(image, n_points, radius, METHOD)
|
||||
|
||||
def hist(ax, lbp):
|
||||
n_bins = lbp.max() + 1
|
||||
return ax.hist(lbp.ravel(), normed=True, bins=n_bins, range=(0, n_bins),
|
||||
facecolor='0.5')
|
||||
|
||||
# plot histograms of LBP of textures
|
||||
fig, (ax_img, ax_hist) = plt.subplots(nrows=2, ncols=3, figsize=(9, 6))
|
||||
plt.gray()
|
||||
|
||||
titles = ('edge', 'flat', 'corner')
|
||||
w = width = radius - 1
|
||||
edge_labels = range(n_points // 2 - w, n_points // 2 + w + 1)
|
||||
flat_labels = list(range(0, w + 1)) + list(range(n_points - w, n_points + 2))
|
||||
i_14 = n_points // 4 # 1/4th of the histogram
|
||||
i_34 = 3 * (n_points // 4) # 3/4th of the histogram
|
||||
corner_labels = (list(range(i_14 - w, i_14 + w + 1)) +
|
||||
list(range(i_34 - w, i_34 + w + 1)))
|
||||
|
||||
label_sets = (edge_labels, flat_labels, corner_labels)
|
||||
|
||||
for ax, labels in zip(ax_img, label_sets):
|
||||
ax.imshow(overlay_labels(image, lbp, labels))
|
||||
|
||||
for ax, labels, name in zip(ax_hist, label_sets, titles):
|
||||
counts, _, bars = hist(ax, lbp)
|
||||
highlight_bars(bars, labels)
|
||||
ax.set_ylim(ymax=np.max(counts[:-1]))
|
||||
ax.set_xlim(xmax=n_points + 2)
|
||||
ax.set_title(name)
|
||||
|
||||
ax_hist[0].set_ylabel('Percentage')
|
||||
for ax in ax_img:
|
||||
ax.axis('off')
|
||||
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
The above plot highlights flat, edge-like, and corner-like regions of the
|
||||
image.
|
||||
|
||||
The histogram of the LBP result is a good measure to classify textures. Here,
|
||||
we test the histogram distributions against each other using the
|
||||
Kullback-Leibler-Divergence.
|
||||
"""
|
||||
|
||||
# settings for LBP
|
||||
radius = 2
|
||||
n_points = 8 * radius
|
||||
|
||||
|
||||
def kullback_leibler_divergence(p, q):
|
||||
@@ -34,11 +169,12 @@ def kullback_leibler_divergence(p, q):
|
||||
def match(refs, img):
|
||||
best_score = 10
|
||||
best_name = None
|
||||
lbp = ft.local_binary_pattern(img, P, R, METHOD)
|
||||
hist, _ = np.histogram(lbp, normed=True, bins=P + 2, range=(0, P + 2))
|
||||
lbp = local_binary_pattern(img, n_points, radius, METHOD)
|
||||
n_bins = lbp.max() + 1
|
||||
hist, _ = np.histogram(lbp, normed=True, bins=n_bins, range=(0, n_bins))
|
||||
for name, ref in refs.items():
|
||||
ref_hist, _ = np.histogram(ref, normed=True, bins=P + 2,
|
||||
range=(0, P + 2))
|
||||
ref_hist, _ = np.histogram(ref, normed=True, bins=n_bins,
|
||||
range=(0, n_bins))
|
||||
score = kullback_leibler_divergence(hist, ref_hist)
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
@@ -51,19 +187,19 @@ grass = data.load('grass.png')
|
||||
wall = data.load('rough-wall.png')
|
||||
|
||||
refs = {
|
||||
'brick': ft.local_binary_pattern(brick, P, R, METHOD),
|
||||
'grass': ft.local_binary_pattern(grass, P, R, METHOD),
|
||||
'wall': ft.local_binary_pattern(wall, P, R, METHOD)
|
||||
'brick': local_binary_pattern(brick, n_points, radius, METHOD),
|
||||
'grass': local_binary_pattern(grass, n_points, radius, METHOD),
|
||||
'wall': local_binary_pattern(wall, n_points, radius, METHOD)
|
||||
}
|
||||
|
||||
# classify rotated textures
|
||||
print 'Rotated images matched against references using LBP:'
|
||||
print 'original: brick, rotated: 30deg, match result:',
|
||||
print match(refs, nd.rotate(brick, angle=30, reshape=False))
|
||||
print 'original: brick, rotated: 70deg, match result:',
|
||||
print match(refs, nd.rotate(brick, angle=70, reshape=False))
|
||||
print 'original: grass, rotated: 145deg, match result:',
|
||||
print match(refs, nd.rotate(grass, angle=145, reshape=False))
|
||||
print('Rotated images matched against references using LBP:')
|
||||
print('original: brick, rotated: 30deg, match result: ',
|
||||
match(refs, rotate(brick, angle=30, resize=False)))
|
||||
print('original: brick, rotated: 70deg, match result: ',
|
||||
match(refs, rotate(brick, angle=70, resize=False)))
|
||||
print('original: grass, rotated: 145deg, match result: ',
|
||||
match(refs, rotate(grass, angle=145, resize=False)))
|
||||
|
||||
# plot histograms of LBP of textures
|
||||
fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(nrows=2, ncols=3,
|
||||
@@ -72,16 +208,20 @@ plt.gray()
|
||||
|
||||
ax1.imshow(brick)
|
||||
ax1.axis('off')
|
||||
ax4.hist(refs['brick'].ravel(), normed=True, bins=P + 2, range=(0, P + 2))
|
||||
hist(ax4, refs['brick'])
|
||||
ax4.set_ylabel('Percentage')
|
||||
|
||||
ax2.imshow(grass)
|
||||
ax2.axis('off')
|
||||
ax5.hist(refs['grass'].ravel(), normed=True, bins=P + 2, range=(0, P + 2))
|
||||
hist(ax5, refs['grass'])
|
||||
ax5.set_xlabel('Uniform LBP values')
|
||||
|
||||
ax3.imshow(wall)
|
||||
ax3.axis('off')
|
||||
ax6.hist(refs['wall'].ravel(), normed=True, bins=P + 2, range=(0, P + 2))
|
||||
hist(ax6, refs['wall'])
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
"""
|
||||
|
||||
plt.show()
|
||||
|
||||
@@ -1,27 +1,32 @@
|
||||
"""
|
||||
===============================
|
||||
============================
|
||||
Local Histogram Equalization
|
||||
===============================
|
||||
============================
|
||||
|
||||
This examples enhances an image with low contrast, using a method called
|
||||
*local histogram equalization*, which "spreads out the most frequent intensity
|
||||
values" in an image .
|
||||
The equalized image [1]_ has a roughly linear cumulative distribution function for each pixel neighborhood.
|
||||
The local version [2]_ of the histogram equalization emphasized every local graylevel variations.
|
||||
This examples enhances an image with low contrast, using a method called *local
|
||||
histogram equalization*, which spreads out the most frequent intensity values in
|
||||
an image.
|
||||
|
||||
The equalized image [1]_ has a roughly linear cumulative distribution function
|
||||
for each pixel neighborhood.
|
||||
|
||||
The local version [2]_ of the histogram equalization emphasized every local
|
||||
graylevel variations.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://en.wikipedia.org/wiki/Histogram_equalization
|
||||
.. [2] http://en.wikipedia.org/wiki/Adaptive_histogram_equalization
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.util.dtype import dtype_range
|
||||
from skimage.util import img_as_ubyte
|
||||
from skimage import exposure
|
||||
from skimage.morphology import disk
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
import numpy as np
|
||||
from skimage.filter import rank
|
||||
|
||||
|
||||
@@ -52,7 +57,7 @@ def plot_img_and_hist(img, axes, bins=256):
|
||||
|
||||
|
||||
# Load an example image
|
||||
img = data.moon()
|
||||
img = img_as_ubyte(data.moon())
|
||||
|
||||
# Contrast stretching
|
||||
p2 = np.percentile(img, 2)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
"""
|
||||
=====================
|
||||
====================
|
||||
Local Otsu Threshold
|
||||
=====================
|
||||
This example shows how Otsu's threshold [1]_ method can be applied locally.
|
||||
For each pixel, an "optimal" threshold is determined by maximizing the variance between two classes of pixels
|
||||
of the local neighborhood defined by a structuring element.
|
||||
====================
|
||||
|
||||
This example shows how Otsu's threshold [1]_ method can be applied locally. For
|
||||
each pixel, an "optimal" threshold is determined by maximizing the variance
|
||||
between two classes of pixels of the local neighborhood defined by a structuring
|
||||
element.
|
||||
|
||||
The example compares the local threshold with the global threshold.
|
||||
|
||||
.. note: local threshold is much slower than global one.
|
||||
.. note: local is much slower than global thresholding
|
||||
|
||||
.. [1] http://en.wikipedia.org/wiki/Otsu's_method
|
||||
|
||||
@@ -16,12 +18,12 @@ The example compares the local threshold with the global threshold.
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.morphology.selem import disk
|
||||
import skimage.filter.rank as rank
|
||||
from skimage.filter import threshold_otsu
|
||||
from skimage.morphology import disk
|
||||
from skimage.filter import threshold_otsu, rank
|
||||
from skimage.util import img_as_ubyte
|
||||
|
||||
|
||||
p8 = data.page()
|
||||
p8 = img_as_ubyte(data.page())
|
||||
|
||||
radius = 10
|
||||
selem = disk(radius)
|
||||
@@ -42,8 +44,8 @@ plt.xlabel('local Otsu ($radius=%d$)' % radius)
|
||||
plt.colorbar()
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.imshow(p8 >= loc_otsu, cmap=plt.cm.gray)
|
||||
plt.xlabel('original>=local Otsu' % t_glob_otsu)
|
||||
plt.xlabel('original >= local Otsu' % t_glob_otsu)
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.imshow(glob_otsu, cmap=plt.cm.gray)
|
||||
plt.xlabel('global Otsu ($t=%d$)' % t_glob_otsu)
|
||||
plt.xlabel('global Otsu ($t = %d$)' % t_glob_otsu)
|
||||
plt.show()
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
==============
|
||||
Marching Cubes
|
||||
==============
|
||||
|
||||
Marching cubes is an algorithm to extract a 2D surface mesh from a 3D volume.
|
||||
This can be conceptualized as a 3D generalization of isolines on topographical
|
||||
or weather maps. It works by iterating across the volume, looking for regions
|
||||
which cross the level of interest. If such regions are found, triangulations
|
||||
are generated and added to an output mesh. The final result is a set of
|
||||
vertices and a set of triangular faces.
|
||||
|
||||
The algorithm requires a data volume and an isosurface value. For example, in
|
||||
CT imaging Hounsfield units of +700 to +3000 represent bone. So, one potential
|
||||
input would be a reconstructed CT set of data and the value +700, to extract
|
||||
a mesh for regions of bone or bone-like density.
|
||||
|
||||
This implementation also works correctly on anisotropic datasets, where the
|
||||
voxel spacing is not equal for every spatial dimension, through use of the
|
||||
`spacing` kwarg.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
|
||||
|
||||
from skimage import measure
|
||||
from skimage.draw import ellipsoid
|
||||
|
||||
# Generate a level set about zero of two identical ellipsoids in 3D
|
||||
ellip_base = ellipsoid(6, 10, 16, levelset=True)
|
||||
ellip_double = np.concatenate((ellip_base[:-1, ...],
|
||||
ellip_base[2:, ...]), axis=0)
|
||||
|
||||
# Use marching cubes to obtain the surface mesh of these ellipsoids
|
||||
verts, faces = measure.marching_cubes(ellip_double, 0)
|
||||
|
||||
# Display resulting triangular mesh using Matplotlib. This can also be done
|
||||
# with mayavi (see skimage.measure.marching_cubes docstring).
|
||||
fig = plt.figure(figsize=(10, 12))
|
||||
ax = fig.add_subplot(111, projection='3d')
|
||||
|
||||
# Fancy indexing: `verts[faces]` to generate a collection of triangles
|
||||
mesh = Poly3DCollection(verts[faces])
|
||||
ax.add_collection3d(mesh)
|
||||
|
||||
ax.set_xlabel("x-axis: a = 6 per ellipsoid")
|
||||
ax.set_ylabel("y-axis: b = 10")
|
||||
ax.set_zlabel("z-axis: c = 16")
|
||||
|
||||
ax.set_xlim(0, 24) # a = 6 (times two for 2nd ellipsoid)
|
||||
ax.set_ylim(0, 20) # b = 10
|
||||
ax.set_zlim(0, 32) # c = 16
|
||||
|
||||
plt.show()
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
================================
|
||||
===============================
|
||||
Markers for watershed transform
|
||||
================================
|
||||
===============================
|
||||
|
||||
The watershed is a classical algorithm used for **segmentation**, that
|
||||
is, for separating different objects in an image.
|
||||
@@ -16,13 +16,14 @@ See Wikipedia_ for more details on the algorithm.
|
||||
|
||||
from scipy import ndimage
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.morphology import watershed, disk
|
||||
from skimage import data
|
||||
|
||||
# original data
|
||||
from skimage.filter import rank
|
||||
from skimage.util import img_as_ubyte
|
||||
|
||||
image = data.camera()
|
||||
|
||||
image = img_as_ubyte(data.camera())
|
||||
|
||||
# denoise image
|
||||
denoised = rank.median(image, disk(2))
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
============================
|
||||
Robust matching using RANSAC
|
||||
============================
|
||||
|
||||
In this simplified example we first generate two synthetic images as if they
|
||||
were taken from different view points.
|
||||
|
||||
In the next step we find interest points in both images and find
|
||||
correspondences based on a weighted sum of squared differences of a small
|
||||
neighborhood around them. Note, that this measure is only robust towards
|
||||
linear radiometric and not geometric distortions and is thus only usable with
|
||||
slight view point changes.
|
||||
|
||||
After finding the correspondences we end up having a set of source and
|
||||
destination coordinates which can be used to estimate the geometric
|
||||
transformation between both images. However, many of the correspondences are
|
||||
faulty and simply estimating the parameter set with all coordinates is not
|
||||
sufficient. Therefore, the RANSAC algorithm is used on top of the normal model
|
||||
to robustly estimate the parameter set by detecting outliers.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.util import img_as_float
|
||||
from skimage.feature import corner_harris, corner_subpix, corner_peaks
|
||||
from skimage.transform import warp, AffineTransform
|
||||
from skimage.exposure import rescale_intensity
|
||||
from skimage.color import rgb2gray
|
||||
from skimage.measure import ransac
|
||||
|
||||
|
||||
# generate synthetic checkerboard image and add gradient for the later matching
|
||||
checkerboard = img_as_float(data.checkerboard())
|
||||
img_orig = np.zeros(list(checkerboard.shape) + [3])
|
||||
img_orig[..., 0] = checkerboard
|
||||
gradient_r, gradient_c = np.mgrid[0:img_orig.shape[0],
|
||||
0:img_orig.shape[1]] / float(img_orig.shape[0])
|
||||
img_orig[..., 1] = gradient_r
|
||||
img_orig[..., 2] = gradient_c
|
||||
img_orig = rescale_intensity(img_orig)
|
||||
img_orig_gray = rgb2gray(img_orig)
|
||||
|
||||
# warp synthetic image
|
||||
tform = AffineTransform(scale=(0.9, 0.9), rotation=0.2, translation=(20, -10))
|
||||
img_warped = warp(img_orig, tform.inverse, output_shape=(200, 200))
|
||||
img_warped_gray = rgb2gray(img_warped)
|
||||
|
||||
# extract corners using Harris' corner measure
|
||||
coords_orig = corner_peaks(corner_harris(img_orig_gray), threshold_rel=0.001,
|
||||
min_distance=5)
|
||||
coords_warped = corner_peaks(corner_harris(img_warped_gray),
|
||||
threshold_rel=0.001, min_distance=5)
|
||||
|
||||
# determine sub-pixel corner position
|
||||
coords_orig_subpix = corner_subpix(img_orig_gray, coords_orig, window_size=9)
|
||||
coords_warped_subpix = corner_subpix(img_warped_gray, coords_warped,
|
||||
window_size=9)
|
||||
|
||||
|
||||
def gaussian_weights(window_ext, sigma=1):
|
||||
y, x = np.mgrid[-window_ext:window_ext+1, -window_ext:window_ext+1]
|
||||
g = np.zeros(y.shape, dtype=np.double)
|
||||
g[:] = np.exp(-0.5 * (x**2 / sigma**2 + y**2 / sigma**2))
|
||||
g /= 2 * np.pi * sigma * sigma
|
||||
return g
|
||||
|
||||
|
||||
def match_corner(coord, window_ext=5):
|
||||
r, c = np.round(coord)
|
||||
window_orig = img_orig[r-window_ext:r+window_ext+1,
|
||||
c-window_ext:c+window_ext+1, :]
|
||||
|
||||
# weight pixels depending on distance to center pixel
|
||||
weights = gaussian_weights(window_ext, 3)
|
||||
weights = np.dstack((weights, weights, weights))
|
||||
|
||||
# compute sum of squared differences to all corners in warped image
|
||||
SSDs = []
|
||||
for cr, cc in coords_warped:
|
||||
window_warped = img_warped[cr-window_ext:cr+window_ext+1,
|
||||
cc-window_ext:cc+window_ext+1, :]
|
||||
SSD = np.sum(weights * (window_orig - window_warped)**2)
|
||||
SSDs.append(SSD)
|
||||
|
||||
# use corner with minimum SSD as correspondence
|
||||
min_idx = np.argmin(SSDs)
|
||||
return coords_warped_subpix[min_idx]
|
||||
|
||||
|
||||
# find correspondences using simple weighted sum of squared differences
|
||||
src = []
|
||||
dst = []
|
||||
for coord in coords_orig_subpix:
|
||||
src.append(coord)
|
||||
dst.append(match_corner(coord))
|
||||
src = np.array(src)
|
||||
dst = np.array(dst)
|
||||
|
||||
|
||||
# estimate affine transform model using all coordinates
|
||||
model = AffineTransform()
|
||||
model.estimate(src, dst)
|
||||
|
||||
# robustly estimate affine transform model with RANSAC
|
||||
model_robust, inliers = ransac((src, dst), AffineTransform, min_samples=3,
|
||||
residual_threshold=2, max_trials=100)
|
||||
outliers = inliers == False
|
||||
|
||||
|
||||
# compare "true" and estimated transform parameters
|
||||
print(tform.scale, tform.translation, tform.rotation)
|
||||
print(model.scale, model.translation, model.rotation)
|
||||
print(model_robust.scale, model_robust.translation, model_robust.rotation)
|
||||
|
||||
|
||||
# visualize correspondences
|
||||
img_combined = np.concatenate((img_orig_gray, img_warped_gray), axis=1)
|
||||
|
||||
fig, ax = plt.subplots(nrows=2, ncols=1)
|
||||
plt.gray()
|
||||
|
||||
ax[0].imshow(img_combined, interpolation='nearest')
|
||||
ax[0].axis('off')
|
||||
ax[0].axis((0, 400, 200, 0))
|
||||
ax[0].set_title('Correct correspondences')
|
||||
ax[1].imshow(img_combined, interpolation='nearest')
|
||||
ax[1].axis('off')
|
||||
ax[1].axis((0, 400, 200, 0))
|
||||
ax[1].set_title('Faulty correspondences')
|
||||
|
||||
|
||||
for ax_idx, (m, color) in enumerate(((inliers, 'g'), (outliers, 'r'))):
|
||||
ax[ax_idx].plot((src[m, 1], dst[m, 1] + 200), (src[m, 0], dst[m, 0]), '-',
|
||||
color=color)
|
||||
ax[ax_idx].plot(src[m, 1], src[m, 0], '.', markersize=10, color=color)
|
||||
ax[ax_idx].plot(dst[m, 1] + 200, dst[m, 0], '.', markersize=10,
|
||||
color=color)
|
||||
|
||||
plt.show()
|
||||
@@ -3,7 +3,7 @@
|
||||
Medial axis skeletonization
|
||||
===========================
|
||||
|
||||
The medial axis of an object is the set of all points having more than one
|
||||
The medial axis of an object is the set of all points having more than one
|
||||
closest point on the object's boundary. It is often called the **topological
|
||||
skeleton**, because it is a 1-pixel wide skeleton of the object, with the same
|
||||
connectivity as the original object.
|
||||
@@ -15,11 +15,11 @@ argument ``return_distance=True``), it is possible to compute the distance to
|
||||
the background for all points of the medial axis with this function. This gives
|
||||
an estimate of the local width of the objects.
|
||||
|
||||
For a skeleton with fewer branches, there exists another skeletonization
|
||||
For a skeleton with fewer branches, there exists another skeletonization
|
||||
algorithm in ``skimage``: ``skimage.morphology.skeletonize``, that computes
|
||||
a skeleton by iterative morphological thinnings.
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from skimage.morphology import medial_axis
|
||||
@@ -33,7 +33,7 @@ def microstructure(l=256):
|
||||
Parameters
|
||||
----------
|
||||
|
||||
l: int, optional
|
||||
l: int, optional
|
||||
linear size of the returned image
|
||||
|
||||
"""
|
||||
@@ -64,7 +64,5 @@ plt.imshow(dist_on_skel, cmap=plt.cm.spectral, interpolation='nearest')
|
||||
plt.contour(data, [0.5], colors='w')
|
||||
plt.axis('off')
|
||||
|
||||
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0,
|
||||
right=1)
|
||||
plt.subplots_adjust(hspace=0.01, wspace=0.01, top=1, bottom=0, left=0, right=1)
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ the intra-class variance.
|
||||
.. [1] http://en.wikipedia.org/wiki/Otsu's_method
|
||||
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.data import camera
|
||||
@@ -42,4 +41,3 @@ plt.title('Thresholded')
|
||||
plt.axis('off')
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
===============================================================================
|
||||
====================
|
||||
Finding local maxima
|
||||
===============================================================================
|
||||
====================
|
||||
|
||||
The ``peak_local_max`` function returns the coordinates of local peaks (maxima)
|
||||
in an image. A maximum filter is used for finding local maxima. This operation
|
||||
@@ -47,4 +47,3 @@ plt.subplots_adjust(wspace=0.02, hspace=0.02, top=0.9,
|
||||
bottom=0.02, left=0.02, right=0.98)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ Piecewise Affine Transformation
|
||||
===============================
|
||||
|
||||
This example shows how to use the Piecewise Affine Transformation.
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from skimage.transform import PiecewiseAffineTransform, warp
|
||||
|
||||
@@ -5,10 +5,13 @@ Approximate and subdivide polygons
|
||||
|
||||
This example shows how to approximate (Douglas-Peucker algorithm) and subdivide
|
||||
(B-Splines) polygonal chains.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.draw import ellipse
|
||||
from skimage.measure import find_contours, approximate_polygon, \
|
||||
subdivide_polygon
|
||||
@@ -45,7 +48,7 @@ for _ in range(5):
|
||||
# approximate subdivided polygon with Douglas-Peucker algorithm
|
||||
appr_hand = approximate_polygon(new_hand, tolerance=0.02)
|
||||
|
||||
print "Number of coordinates:", len(hand), len(new_hand), len(appr_hand)
|
||||
print("Number of coordinates:", len(hand), len(new_hand), len(appr_hand))
|
||||
|
||||
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(9, 4))
|
||||
|
||||
@@ -70,7 +73,7 @@ for contour in find_contours(img, 0):
|
||||
ax2.plot(coords[:, 1], coords[:, 0], '-r', linewidth=2)
|
||||
coords2 = approximate_polygon(contour, tolerance=39.5)
|
||||
ax2.plot(coords2[:, 1], coords2[:, 0], '-g', linewidth=2)
|
||||
print "Number of coordinates:", len(contour), len(coords), len(coords2)
|
||||
print("Number of coordinates:", len(contour), len(coords), len(coords2))
|
||||
|
||||
ax2.axis((0, 800, 0, 800))
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ implement algorithms for denoising, texture discrimination, and scale- invariant
|
||||
detection.
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
@@ -3,55 +3,197 @@
|
||||
Radon transform
|
||||
===============
|
||||
|
||||
The radon transform is a technique widely used in tomography to
|
||||
reconstruct an object from different projections. A projection is, for
|
||||
example, the scattering data obtained as the output of a tomographic
|
||||
scan.
|
||||
In computed tomography, the tomography reconstruction problem is to obtain
|
||||
a tomographic slice image from a set of projections [1]_. A projection is formed
|
||||
by drawing a set of parallel rays through the 2D object of interest, assigning
|
||||
the integral of the object's contrast along each ray to a single pixel in the
|
||||
projection. A single projection of a 2D object is one dimensional. To
|
||||
enable computed tomography reconstruction of the object, several projections
|
||||
must be acquired, each of them corresponding to a different angle between the
|
||||
rays with respect to the object. A collection of projections at several angles
|
||||
is called a sinogram, which is a linear transform of the original image.
|
||||
|
||||
For more information see:
|
||||
The inverse Radon transform is used in computed tomography to reconstruct
|
||||
a 2D image from the measured projections (the sinogram). A practical, exact
|
||||
implementation of the inverse Radon transform does not exist, but there are
|
||||
several good approximate algorithms available.
|
||||
|
||||
- http://en.wikipedia.org/wiki/Radon_transform
|
||||
- http://www.clear.rice.edu/elec431/projects96/DSP/bpanalysis.html
|
||||
As the inverse Radon transform reconstructs the object from a set of
|
||||
projections, the (forward) Radon transform can be used to simulate a
|
||||
tomography experiment.
|
||||
|
||||
This script performs the radon transform, and reconstructs the
|
||||
input image based on the resulting sinogram.
|
||||
This script performs the Radon transform to simulate a tomography experiment
|
||||
and reconstructs the input image based on the resulting sinogram formed by
|
||||
the simulation. Two methods for performing the inverse Radon transform
|
||||
and reconstructing the original image are compared: The Filtered Back
|
||||
Projection (FBP) and the Simultaneous Algebraic Reconstruction
|
||||
Technique (SART).
|
||||
|
||||
.. seealso::
|
||||
|
||||
- AC Kak, M Slaney, "Principles of Computerized Tomographic Imaging",
|
||||
http://www.slaney.org/pct/pct-toc.html
|
||||
- http://en.wikipedia.org/wiki/Radon_transform
|
||||
|
||||
The forward transform
|
||||
=====================
|
||||
|
||||
As our original image, we will use the Shepp-Logan phantom. When calculating
|
||||
the Radon transform, we need to decide how many projection angles we wish
|
||||
to use. As a rule of thumb, the number of projections should be about the
|
||||
same as the number of pixels there are across the object (to see why this
|
||||
is so, consider how many unknown pixel values must be determined in the
|
||||
reconstruction process and compare this to the number of measurements
|
||||
provided by the projections), and we follow that rule here. Below is the
|
||||
original image and its Radon transform, often known as its _sinogram_:
|
||||
"""
|
||||
|
||||
from __future__ import print_function, division
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.io import imread
|
||||
from skimage import data_dir
|
||||
from skimage.transform import radon, iradon
|
||||
from scipy.ndimage import zoom
|
||||
from skimage.transform import radon, rescale
|
||||
|
||||
image = imread(data_dir + "/phantom.png", as_grey=True)
|
||||
image = zoom(image, 0.4)
|
||||
image = rescale(image, scale=0.4)
|
||||
|
||||
plt.figure(figsize=(8, 4.5))
|
||||
|
||||
plt.subplot(121)
|
||||
plt.title("Original")
|
||||
plt.imshow(image, cmap=plt.cm.Greys_r)
|
||||
|
||||
theta = np.linspace(0., 180., max(image.shape), endpoint=True)
|
||||
sinogram = radon(image, theta=theta, circle=True)
|
||||
plt.subplot(122)
|
||||
plt.title("Radon transform\n(Sinogram)")
|
||||
plt.xlabel("Projection angle (deg)")
|
||||
plt.ylabel("Projection position (pixels)")
|
||||
plt.imshow(sinogram, cmap=plt.cm.Greys_r,
|
||||
extent=(0, 180, 0, sinogram.shape[0]), aspect='auto')
|
||||
|
||||
plt.subplots_adjust(hspace=0.4, wspace=0.5)
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Reconstruction with the Filtered Back Projection (FBP)
|
||||
======================================================
|
||||
|
||||
The mathematical foundation of the filtered back projection is the Fourier
|
||||
slice theorem [2]_. It uses Fourier transform of the projection and
|
||||
interpolation in Fourier space to obtain the 2D Fourier transform of the image,
|
||||
which is then inverted to form the reconstructed image. The filtered back
|
||||
projection is among the fastest methods of performing the inverse Radon
|
||||
transform. The only tunable parameter for the FBP is the filter, which is
|
||||
applied to the Fourier transformed projections. It may be used to suppress
|
||||
high frequency noise in the reconstruction. ``skimage`` provides a few
|
||||
different options for the filter.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.transform import iradon
|
||||
|
||||
reconstruction_fbp = iradon(sinogram, theta=theta, circle=True)
|
||||
error = reconstruction_fbp - image
|
||||
print('FBP rms reconstruction error: %.3g' % np.sqrt(np.mean(error**2)))
|
||||
|
||||
imkwargs = dict(vmin=-0.2, vmax=0.2)
|
||||
plt.figure(figsize=(8, 4.5))
|
||||
plt.subplot(121)
|
||||
plt.title("Reconstruction\nFiltered back projection")
|
||||
plt.imshow(reconstruction_fbp, cmap=plt.cm.Greys_r)
|
||||
plt.subplot(122)
|
||||
plt.title("Reconstruction error\nFiltered back projection")
|
||||
plt.imshow(reconstruction_fbp - image, cmap=plt.cm.Greys_r, **imkwargs)
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
Reconstruction with the Simultaneous Algebraic Reconstruction Technique
|
||||
=======================================================================
|
||||
|
||||
Algebraic reconstruction techniques for tomography are based on a
|
||||
straightforward idea: for a pixelated image the value of a single ray in a
|
||||
particular projection is simply a sum of all the pixels the ray passes through
|
||||
on its way through the object. This is a way of expressing the forward Radon
|
||||
transform. The inverse Radon transform can then be formulated as a (large) set
|
||||
of linear equations. As each ray passes through a small fraction of the pixels
|
||||
in the image, this set of equations is sparse, allowing iterative solvers for
|
||||
sparse linear systems to tackle the system of equations. One iterative method
|
||||
has been particularly popular, namely Kaczmarz' method [3]_, which has the
|
||||
property that the solution will approach a least-squares solution of the
|
||||
equation set.
|
||||
|
||||
The combination of the formulation of the reconstruction problem as a set
|
||||
of linear equations and an iterative solver makes algebraic techniques
|
||||
relatively flexible, hence some forms of prior knowledge can be incorporated
|
||||
with relative ease.
|
||||
|
||||
``skimage`` provides one of the more popular variations of the algebraic
|
||||
reconstruction techniques: the Simultaneous Algebraic Reconstruction Technique
|
||||
(SART) [1]_ [4]_. It uses Kaczmarz' method [3]_ as the iterative solver. A good
|
||||
reconstruction is normally obtained in a single iteration, making the method
|
||||
computationally effective. Running one or more extra iterations will normally
|
||||
improve the reconstruction of sharp, high frequency features and reduce the
|
||||
mean squared error at the expense of increased high frequency noise (the user
|
||||
will need to decide on what number of iterations is best suited to the problem
|
||||
at hand. The implementation in ``skimage`` allows prior information of the
|
||||
form of a lower and upper threshold on the reconstructed values to be supplied
|
||||
to the reconstruction.
|
||||
|
||||
"""
|
||||
|
||||
from skimage.transform import iradon_sart
|
||||
|
||||
reconstruction_sart = iradon_sart(sinogram, theta=theta)
|
||||
error = reconstruction_sart - image
|
||||
print('SART (1 iteration) rms reconstruction error: %.3g'
|
||||
% np.sqrt(np.mean(error**2)))
|
||||
|
||||
plt.figure(figsize=(8, 8.5))
|
||||
|
||||
plt.subplot(221)
|
||||
plt.title("Original");
|
||||
plt.imshow(image, cmap=plt.cm.Greys_r)
|
||||
|
||||
plt.title("Reconstruction\nSART")
|
||||
plt.imshow(reconstruction_sart, cmap=plt.cm.Greys_r)
|
||||
plt.subplot(222)
|
||||
projections = radon(image, theta=[0, 45, 90])
|
||||
plt.plot(projections);
|
||||
plt.title("Projections at\n0, 45 and 90 degrees")
|
||||
plt.xlabel("Projection axis");
|
||||
plt.ylabel("Intensity");
|
||||
plt.title("Reconstruction error\nSART")
|
||||
plt.imshow(reconstruction_sart - image, cmap=plt.cm.Greys_r, **imkwargs)
|
||||
|
||||
# Run a second iteration of SART by supplying the reconstruction
|
||||
# from the first iteration as an initial estimate
|
||||
reconstruction_sart2 = iradon_sart(sinogram, theta=theta,
|
||||
image=reconstruction_sart)
|
||||
error = reconstruction_sart2 - image
|
||||
print('SART (2 iterations) rms reconstruction error: %.3g'
|
||||
% np.sqrt(np.mean(error**2)))
|
||||
|
||||
projections = radon(image)
|
||||
plt.subplot(223)
|
||||
plt.title("Radon transform\n(Sinogram)");
|
||||
plt.xlabel("Projection axis");
|
||||
plt.ylabel("Intensity");
|
||||
plt.imshow(projections)
|
||||
|
||||
reconstruction = iradon(projections)
|
||||
plt.title("Reconstruction\nSART, 2 iterations")
|
||||
plt.imshow(reconstruction_sart2, cmap=plt.cm.Greys_r)
|
||||
plt.subplot(224)
|
||||
plt.title("Reconstruction\nfrom sinogram")
|
||||
plt.imshow(reconstruction, cmap=plt.cm.Greys_r)
|
||||
|
||||
plt.subplots_adjust(hspace=0.4, wspace=0.5)
|
||||
plt.title("Reconstruction error\nSART, 2 iterations")
|
||||
plt.imshow(reconstruction_sart2 - image, cmap=plt.cm.Greys_r, **imkwargs)
|
||||
plt.show()
|
||||
|
||||
"""
|
||||
.. image:: PLOT2RST.current_figure
|
||||
|
||||
|
||||
.. [1] AC Kak, M Slaney, "Principles of Computerized Tomographic Imaging",
|
||||
IEEE Press 1988. http://www.slaney.org/pct/pct-toc.html
|
||||
.. [2] Wikipedia, Radon transform,
|
||||
http://en.wikipedia.org/wiki/Radon_transform#Relationship_with_the_Fourier_transform
|
||||
.. [3] S Kaczmarz, "Angenaeherte Aufloesung von Systemen linearer
|
||||
Gleichungen", Bulletin International de l'Academie Polonaise des
|
||||
Sciences et des Lettres 35 pp 355--357 (1937)
|
||||
.. [4] AH Andersen, AC Kak, "Simultaneous algebraic reconstruction technique
|
||||
(SART): a superior implementation of the ART algorithm", Ultrasonic
|
||||
Imaging 6 pp 81--94 (1984)
|
||||
|
||||
"""
|
||||
|
||||
@@ -18,13 +18,14 @@ values, and use the random walker for the segmentation.
|
||||
|
||||
.. [1] *Random walks for image segmentation*, Leo Grady, IEEE Trans. Pattern
|
||||
Anal. Mach. Intell. 2006 Nov; 28(11):1768-83
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
from skimage.segmentation import random_walker
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.segmentation import random_walker
|
||||
|
||||
|
||||
def microstructure(l=256):
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
============
|
||||
Mean filters
|
||||
============
|
||||
|
||||
This example compares the following mean filters of the rank filter package:
|
||||
|
||||
* **local mean**: all pixels belonging to the structuring element to compute
|
||||
average gray level.
|
||||
* **percentile mean**: only use values between percentiles p0 and p1
|
||||
(here 10% and 90%).
|
||||
* **bilateral mean**: only use pixels of the structuring element having a gray
|
||||
level situated inside g-s0 and g+s1 (here g-500 and g+500)
|
||||
|
||||
Percentile and usual mean give here similar results, these filters smooth the
|
||||
complete image (background and details). Bilateral mean exhibits a high
|
||||
filtering rate for continuous area (i.e. background) while higher image
|
||||
frequencies remain untouched.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.morphology import disk
|
||||
from skimage.filter import rank
|
||||
|
||||
|
||||
image = (data.coins()).astype(np.uint16) * 16
|
||||
selem = disk(20)
|
||||
|
||||
percentile_result = rank.mean_percentile(image, selem=selem, p0=.1, p1=.9)
|
||||
bilateral_result = rank.mean_bilateral(image, selem=selem, s0=500, s1=500)
|
||||
normal_result = rank.mean(image, selem=selem)
|
||||
|
||||
|
||||
fig, axes = plt.subplots(nrows=3, figsize=(8, 10))
|
||||
ax0, ax1, ax2 = axes
|
||||
|
||||
ax0.imshow(np.hstack((image, percentile_result)))
|
||||
ax0.set_title('Percentile mean')
|
||||
ax0.axis('off')
|
||||
|
||||
ax1.imshow(np.hstack((image, bilateral_result)))
|
||||
ax1.set_title('Bilateral mean')
|
||||
ax1.axis('off')
|
||||
|
||||
ax2.imshow(np.hstack((image, normal_result)))
|
||||
ax2.set_title('Local mean')
|
||||
ax2.axis('off')
|
||||
|
||||
plt.show()
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
=========================================
|
||||
Robust line model estimation using RANSAC
|
||||
=========================================
|
||||
|
||||
In this example we see how to robustly fit a line model to faulty data using
|
||||
the RANSAC algorithm.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
from skimage.measure import LineModel, ransac
|
||||
|
||||
|
||||
np.random.seed(seed=1)
|
||||
|
||||
# generate coordinates of line
|
||||
x = np.arange(-200, 200)
|
||||
y = 0.2 * x + 20
|
||||
data = np.column_stack([x, y])
|
||||
|
||||
# add faulty data
|
||||
faulty = np.array(30 * [(180., -100)])
|
||||
faulty += 5 * np.random.normal(size=faulty.shape)
|
||||
data[:faulty.shape[0]] = faulty
|
||||
|
||||
# add gaussian noise to coordinates
|
||||
noise = np.random.normal(size=data.shape)
|
||||
data += 0.5 * noise
|
||||
data[::2] += 5 * noise[::2]
|
||||
data[::4] += 20 * noise[::4]
|
||||
|
||||
# fit line using all data
|
||||
model = LineModel()
|
||||
model.estimate(data)
|
||||
|
||||
# robustly fit line only using inlier data with RANSAC algorithm
|
||||
model_robust, inliers = ransac(data, LineModel, min_samples=2,
|
||||
residual_threshold=1, max_trials=1000)
|
||||
outliers = inliers == False
|
||||
|
||||
# generate coordinates of estimated models
|
||||
line_x = np.arange(-250, 250)
|
||||
line_y = model.predict_y(line_x)
|
||||
line_y_robust = model_robust.predict_y(line_x)
|
||||
|
||||
plt.plot(data[inliers, 0], data[inliers, 1], '.b', alpha=0.6,
|
||||
label='Inlier data')
|
||||
plt.plot(data[outliers, 0], data[outliers, 1], '.r', alpha=0.6,
|
||||
label='Outlier data')
|
||||
plt.plot(line_x, line_y, '-k', label='Line model from all data')
|
||||
plt.plot(line_x, line_y_robust, '-b', label='Robust line model')
|
||||
plt.legend(loc='lower left')
|
||||
plt.show()
|
||||
@@ -11,14 +11,15 @@ First we try reconstruction by dilation starting at the edges of the image. We
|
||||
initialize a seed image to the minimum intensity of the image, and set its
|
||||
border to be the pixel values in the original image. These maximal pixels will
|
||||
get dilated in order to reconstruct the background image.
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy.ndimage import gaussian_filter
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage import img_as_float
|
||||
from skimage.morphology import reconstruction
|
||||
from scipy.ndimage import gaussian_filter
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Convert to float: Important for subtraction later which won't work with uint8
|
||||
image = img_as_float(data.coins())
|
||||
|
||||
@@ -4,8 +4,8 @@ Measure region properties
|
||||
=========================
|
||||
|
||||
This example shows how to measure properties of labelled image regions.
|
||||
"""
|
||||
|
||||
"""
|
||||
import math
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
@@ -13,48 +13,34 @@ import numpy as np
|
||||
from skimage.draw import ellipse
|
||||
from skimage.morphology import label
|
||||
from skimage.measure import regionprops
|
||||
from scipy.ndimage import geometric_transform
|
||||
from skimage.transform import rotate
|
||||
|
||||
|
||||
ANGLE = 0.2
|
||||
|
||||
def rotate(xy):
|
||||
x, y = xy
|
||||
out_x = math.cos(ANGLE) * x - math.sin(ANGLE) * y
|
||||
out_y = math.sin(ANGLE) * x + math.cos(ANGLE) * y
|
||||
return (out_x, out_y)
|
||||
|
||||
image = np.zeros((600, 600), 'int')
|
||||
image = np.zeros((600, 600))
|
||||
|
||||
rr, cc = ellipse(300, 350, 100, 220)
|
||||
image[rr,cc] = 1
|
||||
|
||||
image = geometric_transform(image, rotate)
|
||||
image = rotate(image, angle=15, order=0)
|
||||
|
||||
label_img = label(image)
|
||||
props = regionprops(label_img, [
|
||||
'BoundingBox',
|
||||
'Centroid',
|
||||
'Orientation',
|
||||
'MajorAxisLength',
|
||||
'MinorAxisLength'
|
||||
])
|
||||
regions = regionprops(label_img)
|
||||
|
||||
plt.imshow(image)
|
||||
|
||||
for prop in props:
|
||||
x0 = prop['Centroid'][1]
|
||||
y0 = prop['Centroid'][0]
|
||||
x1 = x0 + math.cos(prop['Orientation']) * 0.5 * prop['MajorAxisLength']
|
||||
y1 = y0 - math.sin(prop['Orientation']) * 0.5 * prop['MajorAxisLength']
|
||||
x2 = x0 - math.sin(prop['Orientation']) * 0.5 * prop['MinorAxisLength']
|
||||
y2 = y0 - math.cos(prop['Orientation']) * 0.5 * prop['MinorAxisLength']
|
||||
for props in regions:
|
||||
y0, x0 = props.centroid
|
||||
orientation = props.orientation
|
||||
x1 = x0 + math.cos(orientation) * 0.5 * props.major_axis_length
|
||||
y1 = y0 - math.sin(orientation) * 0.5 * props.major_axis_length
|
||||
x2 = x0 - math.sin(orientation) * 0.5 * props.minor_axis_length
|
||||
y2 = y0 - math.cos(orientation) * 0.5 * props.minor_axis_length
|
||||
|
||||
plt.plot((x0, x1), (y0, y1), '-r', linewidth=2.5)
|
||||
plt.plot((x0, x2), (y0, y2), '-r', linewidth=2.5)
|
||||
plt.plot(x0, y0, '.g', markersize=15)
|
||||
|
||||
minr, minc, maxr, maxc = prop['BoundingBox']
|
||||
minr, minc, maxr, maxc = props.bbox
|
||||
bx = (minc, maxc, maxc, minc, minc)
|
||||
by = (minr, minr, maxr, maxr, minr)
|
||||
plt.plot(bx, by, '-b', linewidth=2.5)
|
||||
|
||||
@@ -58,6 +58,7 @@ of Quickshift, while ``n_segments`` chooses the number of centers for kmeans.
|
||||
Pascal Fua, and Sabine Suesstrunk, SLIC Superpixels Compared to
|
||||
State-of-the-art Superpixel Methods, TPAMI, May 2012.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
+64
-16
@@ -1,27 +1,34 @@
|
||||
"""
|
||||
===========
|
||||
Fill shapes
|
||||
===========
|
||||
======
|
||||
Shapes
|
||||
======
|
||||
|
||||
This example shows how to fill several different shapes:
|
||||
This example shows how to draw several different shapes:
|
||||
* line
|
||||
* Bezier curve
|
||||
* polygon
|
||||
* circle
|
||||
* ellipse
|
||||
|
||||
"""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.draw import line, polygon, circle, circle_perimeter, ellipse
|
||||
import numpy as np
|
||||
from skimage.draw import (line, polygon, circle,
|
||||
circle_perimeter,
|
||||
ellipse, ellipse_perimeter,
|
||||
bezier_curve)
|
||||
|
||||
|
||||
img = np.zeros((500, 500, 3), 'uint8')
|
||||
fig, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(10, 6))
|
||||
|
||||
|
||||
img = np.zeros((500, 500, 3), dtype=np.double)
|
||||
|
||||
# draw line
|
||||
rr, cc = line(120, 123, 20, 400)
|
||||
img[rr,cc,0] = 255
|
||||
img[rr, cc, 0] = 255
|
||||
|
||||
# fill polygon
|
||||
poly = np.array((
|
||||
@@ -31,20 +38,61 @@ poly = np.array((
|
||||
(220, 590),
|
||||
(300, 300),
|
||||
))
|
||||
rr, cc = polygon(poly[:,0], poly[:,1], img.shape)
|
||||
img[rr,cc,1] = 255
|
||||
rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape)
|
||||
img[rr, cc, 1] = 1
|
||||
|
||||
# fill circle
|
||||
rr, cc = circle(200, 200, 100, img.shape)
|
||||
img[rr,cc,:] = (255, 255, 0)
|
||||
img[rr, cc, :] = (1, 1, 0)
|
||||
|
||||
# fill ellipse
|
||||
rr, cc = ellipse(300, 300, 100, 200, img.shape)
|
||||
img[rr,cc,2] = 255
|
||||
img[rr, cc, 2] = 1
|
||||
|
||||
# circle
|
||||
rr, cc = circle_perimeter(120, 400, 50)
|
||||
img[rr, cc, :] = (255, 0, 255)
|
||||
rr, cc = circle_perimeter(120, 400, 15)
|
||||
img[rr, cc, :] = (1, 0, 0)
|
||||
|
||||
# Bezier curve
|
||||
rr, cc = bezier_curve(70, 100, 10, 10, 150, 100, 1)
|
||||
img[rr, cc, :] = (1, 0, 0)
|
||||
|
||||
# ellipses
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 4.)
|
||||
img[rr, cc, :] = (1, 0, 1)
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=-math.pi / 4.)
|
||||
img[rr, cc, :] = (0, 0, 1)
|
||||
rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 2.)
|
||||
img[rr, cc, :] = (1, 1, 1)
|
||||
|
||||
ax1.imshow(img)
|
||||
ax1.set_title('No anti-aliasing')
|
||||
ax1.axis('off')
|
||||
|
||||
"""
|
||||
|
||||
Anti-aliased drawing for:
|
||||
* line
|
||||
* circle
|
||||
|
||||
"""
|
||||
|
||||
from skimage.draw import line_aa, circle_perimeter_aa
|
||||
|
||||
|
||||
img = np.zeros((100, 100), dtype=np.double)
|
||||
|
||||
# anti-aliased line
|
||||
rr, cc, val = line_aa(12, 12, 20, 50)
|
||||
img[rr, cc] = val
|
||||
|
||||
# anti-aliased circle
|
||||
rr, cc, val = circle_perimeter_aa(60, 40, 30)
|
||||
img[rr, cc] = val
|
||||
|
||||
|
||||
ax2.imshow(img, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax2.set_title('Anti-aliasing')
|
||||
ax2.axis('off')
|
||||
|
||||
plt.imshow(img)
|
||||
plt.show()
|
||||
|
||||
@@ -29,10 +29,12 @@ image[-100:-10, 10:-10] = 1
|
||||
image[10:-10, -100:-10] = 1
|
||||
|
||||
# foreground object 2
|
||||
rs, cs = draw.bresenham(250, 150, 10, 280)
|
||||
for i in range(10): image[rs+i, cs] = 1
|
||||
rs, cs = draw.bresenham(10, 150, 250, 280)
|
||||
for i in range(20): image[rs+i, cs] = 1
|
||||
rs, cs = draw.line(250, 150, 10, 280)
|
||||
for i in range(10):
|
||||
image[rs + i, cs] = 1
|
||||
rs, cs = draw.line(10, 150, 250, 280)
|
||||
for i in range(20):
|
||||
image[rs + i, cs] = 1
|
||||
|
||||
# foreground object 3
|
||||
ir, ic = np.indices(image.shape)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
===========================
|
||||
Structural similarity index
|
||||
===========================
|
||||
@@ -20,12 +20,13 @@ but with very different mean structural similarity indices.
|
||||
Transactions on Image Processing, vol. 13, no. 4, pp. 600-612,
|
||||
Apr. 2004.
|
||||
|
||||
'''
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data, color, io, exposure, img_as_float
|
||||
from skimage import data, img_as_float
|
||||
from skimage.measure import structural_similarity as ssim
|
||||
|
||||
import numpy as np
|
||||
|
||||
img = img_as_float(data.camera())
|
||||
rows, cols = img.shape
|
||||
@@ -33,13 +34,13 @@ rows, cols = img.shape
|
||||
noise = np.ones_like(img) * 0.2 * (img.max() - img.min())
|
||||
noise[np.random.random(size=noise.shape) > 0.5] *= -1
|
||||
|
||||
|
||||
def mse(x, y):
|
||||
return np.linalg.norm(x - y)
|
||||
|
||||
img_noise = img + noise
|
||||
img_const = img + abs(noise)
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
f, (ax0, ax1, ax2) = plt.subplots(1, 3)
|
||||
|
||||
mse_none = mse(img, img)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
r"""
|
||||
"""
|
||||
=====
|
||||
Swirl
|
||||
=====
|
||||
@@ -8,7 +8,7 @@ effect. This example describes the implementation of this transform in
|
||||
``skimage``, as well as the underlying warp mechanism.
|
||||
|
||||
Image warping
|
||||
`````````````
|
||||
-------------
|
||||
When applying a geometric transformation on an image, we typically make use of
|
||||
a reverse mapping, i.e., for each pixel in the output image, we compute its
|
||||
corresponding position in the input. The reason is that, if we were to do it
|
||||
@@ -19,7 +19,7 @@ image, and even if that position is non-integer, we may use interpolation to
|
||||
compute the corresponding image value.
|
||||
|
||||
Performing a reverse mapping
|
||||
````````````````````````````
|
||||
----------------------------
|
||||
To perform a geometric warp in ``skimage``, you simply need to provide the
|
||||
reverse mapping to the ``skimage.transform.warp`` function. E.g., consider the
|
||||
case where we would like to shift an image 50 pixels to the left. The reverse
|
||||
@@ -35,16 +35,16 @@ The corresponding call to warp is::
|
||||
warp(image, shift_left)
|
||||
|
||||
The swirl transformation
|
||||
````````````````````````
|
||||
------------------------
|
||||
Consider the coordinate :math:`(x, y)` in the output image. The reverse
|
||||
mapping for the swirl transformation first computes, relative to a center
|
||||
:math:`(x_0, y_0)`, its polar coordinates,
|
||||
|
||||
.. math::
|
||||
|
||||
\theta = \arctan(y/x)
|
||||
\\theta = \\arctan(y/x)
|
||||
|
||||
\rho = \sqrt{(x - x_0)^2 + (y - y_0)^2},
|
||||
\\rho = \sqrt{(x - x_0)^2 + (y - y_0)^2},
|
||||
|
||||
and then transforms them according to
|
||||
|
||||
@@ -56,19 +56,20 @@ and then transforms them according to
|
||||
|
||||
s = \mathtt{strength}
|
||||
|
||||
\theta' = \phi + s \, e^{-\rho / r + \theta}
|
||||
\\theta' = \phi + s \, e^{-\\rho / r + \\theta}
|
||||
|
||||
where ``strength`` is a parameter for the amount of swirl, ``radius`` indicates
|
||||
the swirl extent in pixels, and ``rotation`` adds a rotation angle. The
|
||||
transformation of ``radius`` into :math:`r` is to ensure that the
|
||||
transformation decays to :math:`\approx 1/1000^{\mathsf{th}}` within the
|
||||
transformation decays to :math:`\\approx 1/1000^{\mathsf{th}}` within the
|
||||
specified radius.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.transform import swirl
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
image = data.checkerboard()
|
||||
swirled = swirl(image, rotation=0, strength=10, radius=120, order=2)
|
||||
|
||||
@@ -17,13 +17,15 @@ the template.
|
||||
|
||||
.. [1] J. P. Lewis, "Fast Normalized Cross-Correlation", Industrial Light and
|
||||
Magic.
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage import data
|
||||
from skimage.feature import match_template
|
||||
|
||||
|
||||
image = data.coins()
|
||||
coin = image[170:220, 75:130]
|
||||
|
||||
@@ -53,4 +55,3 @@ ax3.autoscale(False)
|
||||
ax3.plot(x, y, 'o', markeredgecolor='r', markerfacecolor='none', markersize=10)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ blocks. Then, on each block, we either pool the mean, the max or the
|
||||
median value of that block. The results are displayed altogether, along
|
||||
with a spline interpolation of order 3 rescaling of the original `lena`
|
||||
image.
|
||||
"""
|
||||
|
||||
"""
|
||||
import numpy as np
|
||||
from scipy import ndimage as ndi
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
@@ -24,11 +24,13 @@ See Wikipedia_ for more details on the algorithm.
|
||||
.. _Wikipedia: http://en.wikipedia.org/wiki/Watershed_(image_processing)
|
||||
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from skimage.morphology import watershed, is_local_maximum
|
||||
from scipy import ndimage
|
||||
|
||||
from skimage.morphology import watershed
|
||||
from skimage.feature import peak_local_max
|
||||
|
||||
|
||||
# Generate an initial image with two overlapping circles
|
||||
x, y = np.indices((80, 80))
|
||||
@@ -40,9 +42,9 @@ image = np.logical_or(mask_circle1, mask_circle2)
|
||||
|
||||
# Now we want to separate the two objects in image
|
||||
# Generate the markers as local maxima of the distance to the background
|
||||
from scipy import ndimage
|
||||
distance = ndimage.distance_transform_edt(image)
|
||||
local_maxi = is_local_maximum(distance, image, np.ones((3, 3)))
|
||||
local_maxi = peak_local_max(distance, indices=False, footprint=np.ones((3, 3)),
|
||||
labels=image)
|
||||
markers = ndimage.label(local_maxi)[0]
|
||||
labels = watershed(-distance, markers, mask=image)
|
||||
|
||||
@@ -50,8 +52,11 @@ fig, axes = plt.subplots(ncols=3, figsize=(8, 2.7))
|
||||
ax0, ax1, ax2 = axes
|
||||
|
||||
ax0.imshow(image, cmap=plt.cm.gray, interpolation='nearest')
|
||||
ax0.set_title('Overlapping objects')
|
||||
ax1.imshow(-distance, cmap=plt.cm.jet, interpolation='nearest')
|
||||
ax1.set_title('Distances')
|
||||
ax2.imshow(labels, cmap=plt.cm.spectral, interpolation='nearest')
|
||||
ax2.set_title('Separated objects')
|
||||
|
||||
for ax in axes:
|
||||
ax.axis('off')
|
||||
|
||||
@@ -9,6 +9,7 @@ import pydoc
|
||||
from StringIO import StringIO
|
||||
from warnings import warn
|
||||
|
||||
|
||||
class Reader(object):
|
||||
"""A line-based string reader.
|
||||
|
||||
@@ -369,7 +370,7 @@ class NumpyDocString(object):
|
||||
idx = self['index']
|
||||
out = []
|
||||
out += ['.. index:: %s' % idx.get('default','')]
|
||||
for section, references in idx.iteritems():
|
||||
for section, references in idx.items():
|
||||
if section == 'default':
|
||||
continue
|
||||
out += [' :%s: %s' % (section, ', '.join(references))]
|
||||
|
||||
@@ -2,6 +2,7 @@ import re, inspect, textwrap, pydoc
|
||||
import sphinx
|
||||
from docscrape import NumpyDocString, FunctionDoc, ClassDoc
|
||||
|
||||
|
||||
class SphinxDocString(NumpyDocString):
|
||||
def __init__(self, docstring, config={}):
|
||||
self.use_plots = config.get('use_plots', False)
|
||||
@@ -127,7 +128,7 @@ class SphinxDocString(NumpyDocString):
|
||||
return out
|
||||
|
||||
out += ['.. index:: %s' % idx.get('default','')]
|
||||
for section, references in idx.iteritems():
|
||||
for section, references in idx.items():
|
||||
if section == 'default':
|
||||
continue
|
||||
elif section == 'refguide':
|
||||
|
||||
+12
-12
@@ -187,7 +187,7 @@ def generate_example_galleries(app):
|
||||
def generate_examples_and_gallery(example_dir, rst_dir, cfg):
|
||||
"""Generate rst from examples and create gallery to showcase examples."""
|
||||
if not example_dir.exists:
|
||||
print "No example directory found at", example_dir
|
||||
print("No example directory found at", example_dir)
|
||||
return
|
||||
rst_dir.makedirs()
|
||||
|
||||
@@ -225,12 +225,12 @@ def write_gallery(gallery_index, src_dir, rst_dir, cfg, depth=0):
|
||||
index_name = cfg.plot2rst_index_name + cfg.source_suffix
|
||||
gallery_template = src_dir.pjoin(index_name)
|
||||
if not os.path.exists(gallery_template):
|
||||
print src_dir
|
||||
print 80*'_'
|
||||
print ('Example directory %s does not have a %s file'
|
||||
print(src_dir)
|
||||
print(80*'_')
|
||||
print('Example directory %s does not have a %s file'
|
||||
% (src_dir, index_name))
|
||||
print 'Skipping this directory'
|
||||
print 80*'_'
|
||||
print('Skipping this directory')
|
||||
print(80*'_')
|
||||
return
|
||||
|
||||
gallery_description = file(gallery_template).read()
|
||||
@@ -252,11 +252,11 @@ def write_gallery(gallery_index, src_dir, rst_dir, cfg, depth=0):
|
||||
try:
|
||||
write_example(src_name, src_dir, rst_dir, cfg)
|
||||
except Exception:
|
||||
print "Exception raised while running:"
|
||||
print "%s in %s" % (src_name, src_dir)
|
||||
print '~' * 60
|
||||
print("Exception raised while running:")
|
||||
print("%s in %s" % (src_name, src_dir))
|
||||
print('~' * 60)
|
||||
traceback.print_exc()
|
||||
print '~' * 60
|
||||
print('~' * 60)
|
||||
continue
|
||||
|
||||
link_name = sub_dir.pjoin(src_name)
|
||||
@@ -354,8 +354,8 @@ def write_example(src_name, src_dir, rst_dir, cfg):
|
||||
|
||||
if not thumb_path.exists:
|
||||
if cfg.plot2rst_default_thumb is None:
|
||||
print "WARNING: No plots found and default thumbnail not defined."
|
||||
print "Specify 'plot2rst_default_thumb' in Sphinx config file."
|
||||
print("WARNING: No plots found and default thumbnail not defined.")
|
||||
print("Specify 'plot2rst_default_thumb' in Sphinx config file.")
|
||||
else:
|
||||
shutil.copy(cfg.plot2rst_default_thumb, thumb_path)
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ except ImportError:
|
||||
def format_template(template, **kw):
|
||||
return jinja.from_string(template, **kw)
|
||||
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.cbook as cbook
|
||||
matplotlib.use('Agg')
|
||||
@@ -234,7 +235,7 @@ def mark_plot_labels(app, document):
|
||||
the "htmlonly" (or "latexonly") node to the actual figure node
|
||||
itself.
|
||||
"""
|
||||
for name, explicit in document.nametypes.iteritems():
|
||||
for name, explicit in document.nametypes.items():
|
||||
if not explicit:
|
||||
continue
|
||||
labelid = document.nameids[name]
|
||||
|
||||
+9
-5
@@ -48,7 +48,7 @@ def sh2(cmd):
|
||||
out = p.communicate()[0]
|
||||
retcode = p.returncode
|
||||
if retcode:
|
||||
print out.rstrip()
|
||||
print(out.rstrip())
|
||||
raise CalledProcessError(retcode, cmd)
|
||||
else:
|
||||
return out.rstrip()
|
||||
@@ -85,6 +85,10 @@ if __name__ == '__main__':
|
||||
for l in setup_lines:
|
||||
if l.startswith('VERSION'):
|
||||
tag = l.split("'")[1]
|
||||
|
||||
# Rename to, e.g., 0.9.x
|
||||
tag = '.'.join(tag.split('.')[:-1] + ['x'])
|
||||
|
||||
break
|
||||
|
||||
if "dev" in tag:
|
||||
@@ -123,12 +127,12 @@ if __name__ == '__main__':
|
||||
sh('git add %s' % tag)
|
||||
sh2('git commit -m"Updated doc release: %s"' % tag)
|
||||
|
||||
print 'Most recent commit:'
|
||||
print('Most recent commit:')
|
||||
sys.stdout.flush()
|
||||
sh('git --no-pager log --oneline HEAD~1..')
|
||||
finally:
|
||||
cd(startdir)
|
||||
|
||||
print
|
||||
print 'Now verify the build in: %r' % dest
|
||||
print "If everything looks good, run 'git push' inside doc/gh-pages."
|
||||
print('')
|
||||
print('Now verify the build in: %r' % dest)
|
||||
print("If everything looks good, run 'git push' inside doc/gh-pages.")
|
||||
|
||||
+76
-137
@@ -1,80 +1,57 @@
|
||||
"""
|
||||
Script to draw skimage logo using Scipy logo as stencil. The easiest
|
||||
starting point is the `plot_colorized_logo`; the "if-main" demonstrates its use.
|
||||
starting point is the `plot_colorized_logo`.
|
||||
|
||||
Original snake image from pixabay [1]_
|
||||
|
||||
.. [1] http://pixabay.com/en/snake-green-toxic-close-yellow-3237/
|
||||
"""
|
||||
import numpy as np
|
||||
import sys
|
||||
if len(sys.argv) != 2 or sys.argv[1] != '--no-plot':
|
||||
print("Run with '--no-plot' flag to generate logo silently.")
|
||||
else:
|
||||
import matplotlib as mpl
|
||||
mpl.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import scipy.misc
|
||||
|
||||
import numpy as np
|
||||
|
||||
import skimage.io as sio
|
||||
import skimage.filter as imfilt
|
||||
from skimage import img_as_float
|
||||
from skimage.color import gray2rgb, rgb2gray
|
||||
from skimage.exposure import rescale_intensity
|
||||
from skimage.filter import sobel
|
||||
|
||||
import scipy_logo
|
||||
|
||||
|
||||
# Utility functions
|
||||
# =================
|
||||
|
||||
def get_edges(img):
|
||||
edge = np.empty(img.shape)
|
||||
if len(img.shape) == 3:
|
||||
for i in range(3):
|
||||
edge[:, :, i] = imfilt.sobel(img[:, :, i])
|
||||
else:
|
||||
edge = imfilt.sobel(img)
|
||||
edge = rescale_intensity(edge)
|
||||
return edge
|
||||
def colorize(image, color, whiten=False):
|
||||
"""Return colorized image from gray scale image.
|
||||
|
||||
def rescale_intensity(img):
|
||||
i_range = float(img.max() - img.min())
|
||||
img = (img - img.min()) / i_range * 255
|
||||
return np.uint8(img)
|
||||
|
||||
def colorize(img, color, whiten=False):
|
||||
"""Return colorized image from gray scale image
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img : N x M array
|
||||
grayscale image
|
||||
color : length-3 sequence of floats
|
||||
RGB color spec. Float values should be between 0 and 1.
|
||||
whiten : bool
|
||||
If True, a color value less than 1 increases the image intensity.
|
||||
The colorized image has values from ranging between black at the lowest
|
||||
intensity to `color` at the highest. If `whiten=True`, then the color
|
||||
ranges from `color` to white.
|
||||
"""
|
||||
color = np.asarray(color)[np.newaxis, np.newaxis, :]
|
||||
img = img[:, :, np.newaxis]
|
||||
image = image[:, :, np.newaxis]
|
||||
if whiten:
|
||||
# truncate and stretch intensity range to enhance contrast
|
||||
img = np.clip(img, 80, 255)
|
||||
img = rescale_intensity(img)
|
||||
return np.uint8(color * (255 - img) + img)
|
||||
image = rescale_intensity(image, in_range=(0.3, 1))
|
||||
return color * (1 - image) + image
|
||||
else:
|
||||
return np.uint8(img * color)
|
||||
return image * color
|
||||
|
||||
|
||||
def prepare_axes(ax):
|
||||
plt.sca(ax)
|
||||
ax.xaxis.set_visible(False)
|
||||
ax.yaxis.set_visible(False)
|
||||
for spine in ax.spines.itervalues():
|
||||
for spine in ax.spines.values():
|
||||
spine.set_visible(False)
|
||||
|
||||
|
||||
_rgb_stack = np.ones((1, 1, 3), dtype=bool)
|
||||
def gray2rgb(arr):
|
||||
"""Return RGB image from a grayscale image.
|
||||
|
||||
Expand h x w image to h x w x 3 image where color channels are simply copies
|
||||
of the grayscale image.
|
||||
"""
|
||||
return arr[:, :, np.newaxis] * _rgb_stack
|
||||
|
||||
|
||||
# Logo generating classes
|
||||
# =======================
|
||||
|
||||
@@ -82,21 +59,17 @@ class LogoBase(object):
|
||||
|
||||
def __init__(self):
|
||||
self.logo = scipy_logo.ScipyLogo(radius=self.radius)
|
||||
self.mask_1 = self.logo.get_mask(self.img.shape, 'upper left')
|
||||
self.mask_2 = self.logo.get_mask(self.img.shape, 'lower right')
|
||||
self.edges = get_edges(self.img)
|
||||
self.mask_1 = self.logo.get_mask(self.image.shape, 'upper left')
|
||||
self.mask_2 = self.logo.get_mask(self.image.shape, 'lower right')
|
||||
|
||||
edges = np.array([sobel(img) for img in self.image.T]).T
|
||||
# truncate and stretch intensity range to enhance contrast
|
||||
self.edges = np.clip(self.edges, 0, 100)
|
||||
self.edges = rescale_intensity(self.edges)
|
||||
self.edges = rescale_intensity(edges, in_range=(0, 0.4))
|
||||
|
||||
|
||||
def _crop_image(self, img):
|
||||
def _crop_image(self, image):
|
||||
w = 2 * self.radius
|
||||
x, y = self.origin
|
||||
return img[y:y+w, x:x+w]
|
||||
|
||||
def get_canvas(self):
|
||||
return 255 * np.ones(self.img.shape, dtype=np.uint8)
|
||||
return image[y:y + w, x:x + w]
|
||||
|
||||
def plot_curve(self, **kwargs):
|
||||
self.logo.plot_snake_curve(**kwargs)
|
||||
@@ -104,15 +77,13 @@ class LogoBase(object):
|
||||
|
||||
class SnakeLogo(LogoBase):
|
||||
|
||||
def __init__(self):
|
||||
self.radius = 250
|
||||
self.origin = (420, 0)
|
||||
img = sio.imread('data/snake_pixabay.jpg')
|
||||
img = self._crop_image(img)
|
||||
radius = 250
|
||||
origin = (420, 0)
|
||||
|
||||
img = img.astype(float) * 1.1
|
||||
img[img > 255] = 255
|
||||
self.img = img.astype(np.uint8)
|
||||
def __init__(self):
|
||||
image = sio.imread('data/snake_pixabay.jpg')
|
||||
image = self._crop_image(image)
|
||||
self.image = img_as_float(image)
|
||||
|
||||
LogoBase.__init__(self)
|
||||
|
||||
@@ -120,107 +91,75 @@ class SnakeLogo(LogoBase):
|
||||
snake_color = SnakeLogo()
|
||||
snake = SnakeLogo()
|
||||
# turn RGB image into gray image
|
||||
snake.img = np.mean(snake.img, axis=2)
|
||||
snake.edges = np.mean(snake.edges, axis=2)
|
||||
snake.image = rgb2gray(snake.image)
|
||||
snake.edges = rgb2gray(snake.edges)
|
||||
|
||||
|
||||
# Demo plotting functions
|
||||
# =======================
|
||||
|
||||
def plot_colorized_logo(logo, color, edges='light', switch=False, whiten=False):
|
||||
"""Convenience function to plot artificially colored logo.
|
||||
def plot_colorized_logo(logo, color, edges='light', whiten=False):
|
||||
"""Convenience function to plot artificially-colored logo.
|
||||
|
||||
The upper-left half of the logo is an edge filtered image, while the
|
||||
lower-right half is unfiltered.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
logo : subclass of LogoBase
|
||||
color : length-3 sequence of floats
|
||||
logo : LogoBase instance
|
||||
color : length-3 sequence of floats or 2 length-3 sequences
|
||||
RGB color spec. Float values should be between 0 and 1.
|
||||
edges : {'light'|'dark'}
|
||||
Specifies whether Sobel edges are drawn light or dark
|
||||
switch : bool
|
||||
If False, the image is drawn on the southeast half of the Scipy curve
|
||||
and the edge image is drawn on northwest half.
|
||||
whiten : bool
|
||||
whiten : bool or 2 bools
|
||||
If True, a color value less than 1 increases the image intensity.
|
||||
"""
|
||||
if not hasattr(color[0], '__iter__'):
|
||||
color = [color] * 2
|
||||
color = [color] * 2 # use same color for upper-left & lower-right
|
||||
if not hasattr(whiten, '__iter__'):
|
||||
whiten = [whiten] * 2
|
||||
img = gray2rgb(logo.get_canvas())
|
||||
whiten = [whiten] * 2 # use same setting for upper-left & lower-right
|
||||
|
||||
image = gray2rgb(np.ones_like(logo.image))
|
||||
mask_img = gray2rgb(logo.mask_2)
|
||||
mask_edge = gray2rgb(logo.mask_1)
|
||||
if switch:
|
||||
mask_img, mask_edge = mask_edge, mask_img
|
||||
|
||||
# Compose image with colorized image and edge-image.
|
||||
if edges == 'dark':
|
||||
lg_edge = colorize(255 - logo.edges, color[0], whiten=whiten[0])
|
||||
logo_edge = colorize(1 - logo.edges, color[0], whiten=whiten[0])
|
||||
else:
|
||||
lg_edge = colorize(logo.edges, color[0], whiten=whiten[0])
|
||||
lg_img = colorize(logo.img, color[1], whiten=whiten[1])
|
||||
img[mask_img] = lg_img[mask_img]
|
||||
img[mask_edge] = lg_edge[mask_edge]
|
||||
logo.plot_curve(lw=5, color='w')
|
||||
plt.imshow(img)
|
||||
logo_edge = colorize(logo.edges, color[0], whiten=whiten[0])
|
||||
logo_img = colorize(logo.image, color[1], whiten=whiten[1])
|
||||
image[mask_img] = logo_img[mask_img]
|
||||
image[mask_edge] = logo_edge[mask_edge]
|
||||
|
||||
|
||||
def red_light_edges(logo, **kwargs):
|
||||
plot_colorized_logo(logo, (1, 0, 0), edges='light', **kwargs)
|
||||
|
||||
|
||||
def red_dark_edges(logo, **kwargs):
|
||||
plot_colorized_logo(logo, (1, 0, 0), edges='dark', **kwargs)
|
||||
|
||||
def blue_light_edges(logo, **kwargs):
|
||||
plot_colorized_logo(logo, (0.35, 0.55, 0.85), edges='light', **kwargs)
|
||||
|
||||
|
||||
def blue_dark_edges(logo, **kwargs):
|
||||
plot_colorized_logo(logo, (0.35, 0.55, 0.85), edges='dark', **kwargs)
|
||||
|
||||
|
||||
def green_orange_light_edges(logo, **kwargs):
|
||||
colors = ((0.6, 0.8, 0.3), (1, 0.5, 0.1))
|
||||
plot_colorized_logo(logo, colors, edges='light', **kwargs)
|
||||
|
||||
def green_orange_dark_edges(logo, **kwargs):
|
||||
colors = ((0.6, 0.8, 0.3), (1, 0.5, 0.1))
|
||||
plot_colorized_logo(logo, colors, edges='dark', **kwargs)
|
||||
logo.plot_curve(lw=5, color='w') # plot snake curve on current axes
|
||||
plt.imshow(image)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
import sys
|
||||
plot = False
|
||||
if len(sys.argv) < 2 or sys.argv[1] != '--no-plot':
|
||||
plot = True
|
||||
|
||||
print "Run with '--no-plot' flag to generate logo silently."
|
||||
# Colors to use for the logo:
|
||||
red = (1, 0, 0)
|
||||
blue = (0.35, 0.55, 0.85)
|
||||
green_orange = ((0.6, 0.8, 0.3), (1, 0.5, 0.1))
|
||||
|
||||
def plot_all():
|
||||
plotters = (red_light_edges, red_dark_edges,
|
||||
blue_light_edges, blue_dark_edges,
|
||||
green_orange_light_edges, green_orange_dark_edges)
|
||||
|
||||
f, axes_array = plt.subplots(nrows=2, ncols=len(plotters))
|
||||
for plot, ax_col in zip(plotters, axes_array.T):
|
||||
prepare_axes(ax_col[0])
|
||||
plot(snake)
|
||||
prepare_axes(ax_col[1])
|
||||
plot(snake, whiten=True)
|
||||
color_list = [red, blue, green_orange]
|
||||
edge_list = ['light', 'dark']
|
||||
f, axes = plt.subplots(nrows=len(edge_list), ncols=len(color_list))
|
||||
for axes_row, edges in zip(axes, edge_list):
|
||||
for ax, color in zip(axes_row, color_list):
|
||||
prepare_axes(ax)
|
||||
plot_colorized_logo(snake, color, edges=edges)
|
||||
plt.tight_layout()
|
||||
|
||||
def plot_snake():
|
||||
|
||||
def plot_official_logo():
|
||||
f, ax = plt.subplots()
|
||||
prepare_axes(ax)
|
||||
green_orange_dark_edges(snake, whiten=(False, True))
|
||||
plot_colorized_logo(snake, green_orange, edges='dark',
|
||||
whiten=(False, True))
|
||||
plt.savefig('green_orange_snake.png', bbox_inches='tight')
|
||||
|
||||
if plot:
|
||||
plot_all()
|
||||
|
||||
plot_snake()
|
||||
|
||||
if plot:
|
||||
plt.show()
|
||||
plot_all()
|
||||
plot_official_logo()
|
||||
|
||||
plt.show()
|
||||
|
||||
+6
-10
@@ -3,10 +3,11 @@ Code used to trace Scipy logo.
|
||||
"""
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import skimage.io as imgio
|
||||
from scipy.misc import lena
|
||||
import matplotlib.nxutils as nx
|
||||
|
||||
from skimage import io
|
||||
from skimage import data
|
||||
|
||||
|
||||
class SymmetricAnchorPoint(object):
|
||||
"""Anchor point in a parametric curve with symmetric handles
|
||||
@@ -185,7 +186,7 @@ class ScipyLogo(object):
|
||||
|
||||
def plot_image(self, **kwargs):
|
||||
ax = kwargs.pop('ax', plt.gca())
|
||||
img = imgio.imread('data/scipy.png')
|
||||
img = io.imread('data/scipy.png')
|
||||
ax.imshow(img, **kwargs)
|
||||
|
||||
def get_mask(self, shape, region):
|
||||
@@ -236,9 +237,7 @@ def plot_snake_overlay():
|
||||
logo = ScipyLogo((670, 250), 250)
|
||||
logo.plot_snake_curve()
|
||||
logo.plot_circle()
|
||||
img = imgio.imread('data/snake_pixabay.jpg')
|
||||
#mask = logo.get_mask(img.shape, 'upper left')
|
||||
#img[mask] = 255
|
||||
img = io.imread('data/snake_pixabay.jpg')
|
||||
plt.imshow(img)
|
||||
|
||||
|
||||
@@ -247,9 +246,7 @@ def plot_lena_overlay():
|
||||
logo = ScipyLogo((300, 300), 180)
|
||||
logo.plot_snake_curve()
|
||||
logo.plot_circle()
|
||||
img = lena()
|
||||
#mask = logo.get_mask(img.shape, 'upper left')
|
||||
#img[mask] = 255
|
||||
img = data.lena()
|
||||
plt.imshow(img)
|
||||
|
||||
|
||||
@@ -259,4 +256,3 @@ if __name__ == '__main__':
|
||||
plot_lena_overlay()
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
@@ -27,6 +27,14 @@ if "%1" == "help" (
|
||||
goto end
|
||||
)
|
||||
|
||||
for %%x in (html htmlhelp latex qthelp) do (
|
||||
if "%1" == "%%x" (
|
||||
md source\api 2>NUL
|
||||
python tools/build_modref_templates.py
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (build\*) do rmdir /q /s %%i
|
||||
del /q /s build\*
|
||||
@@ -34,6 +42,7 @@ if "%1" == "clean" (
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
cd source && python random_gallery.py && python coverage_generator.py && cd ..
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% build/html
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in build/html.
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
import subprocess
|
||||
import sys
|
||||
import string
|
||||
import shlex
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print "Usage: ./contributors.py tag-of-previous-release"
|
||||
sys.exit(-1)
|
||||
|
||||
tag = sys.argv[1]
|
||||
|
||||
def call(cmd):
|
||||
return subprocess.check_output(shlex.split(cmd)).split('\n')
|
||||
|
||||
tag_date = call("git show --format='%%ci' %s" % tag)[0]
|
||||
print "Release %s was on %s" % (tag, tag_date)
|
||||
|
||||
merges = call("git log --since='%s' --merges --format='>>>%%B' --reverse" % tag_date)
|
||||
merges = [m for m in merges if m.strip()]
|
||||
merges = '\n'.join(merges).split('>>>')
|
||||
merges = [m.split('\n')[:2] for m in merges]
|
||||
merges = [m for m in merges if len(m) == 2 and m[1].strip()]
|
||||
|
||||
print "\nIt contained the following %d merges:" % len(merges)
|
||||
print
|
||||
for (merge, message) in merges:
|
||||
if merge.startswith('Merge pull request #'):
|
||||
PR = ' (%s)' % merge.split()[3]
|
||||
else:
|
||||
PR = ''
|
||||
|
||||
print '- ' + message + PR
|
||||
|
||||
|
||||
print "\nMade by the following committers [alphabetical by last name]:\n"
|
||||
|
||||
authors = call("git log --since='%s' --format=%%aN" % tag_date)
|
||||
authors = [a.strip() for a in authors if a.strip()]
|
||||
|
||||
def key(author):
|
||||
author = [v for v in author.split() if v[0] in string.letters]
|
||||
return author[-1]
|
||||
|
||||
authors = sorted(set(authors), key=key)
|
||||
|
||||
for a in authors:
|
||||
print '-', a
|
||||
@@ -1,2 +0,0 @@
|
||||
git log $1..HEAD --format='- %aN' | sed 's/@/\-at\-/' | sed 's/<>//' | sort -u
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
Announcement: scikits-image 0.9.0
|
||||
=================================
|
||||
|
||||
We're happy to announce the release of scikit-image v0.9.0!
|
||||
|
||||
scikit-image is an image processing toolbox for SciPy that includes algorithms
|
||||
for segmentation, geometric transformations, color space manipulation,
|
||||
analysis, filtering, morphology, feature detection, and more.
|
||||
|
||||
For more information, examples, and documentation, please visit our website:
|
||||
|
||||
http://scikit-image.org
|
||||
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
||||
`scikit-image` now runs without translation under both Python 2 and 3.
|
||||
|
||||
In addition to several bug fixes, speed improvements and examples, the 204 pull
|
||||
requests merged for this release include the following new features (PR number
|
||||
in brackets):
|
||||
|
||||
Segmentation:
|
||||
|
||||
- 3D support in SLIC segmentation (#546)
|
||||
- SLIC voxel spacing (#719)
|
||||
- Generalized anisotropic spacing support for random_walker (#775)
|
||||
- Yen threshold method (#686)
|
||||
|
||||
Transforms and filters:
|
||||
|
||||
- SART algorithm for tomography reconstruction (#584)
|
||||
- Gabor filters (#371)
|
||||
- Hough transform for ellipses (#597)
|
||||
- Fast resampling of nD arrays (#511)
|
||||
- Rotation axis center for Radon transforms with inverses. (#654)
|
||||
- Reconstruction circle in inverse Radon transform (#567)
|
||||
- Pixelwise image adjustment curves and methods (#505)
|
||||
|
||||
Feature detection:
|
||||
|
||||
- [experimental API] BRIEF feature descriptor (#591)
|
||||
- [experimental API] Censure (STAR) Feature Detector (#668)
|
||||
- Octagon structural element (#669)
|
||||
- Add non rotation invariant uniform LBPs (#704)
|
||||
|
||||
Color and noise:
|
||||
|
||||
- Add deltaE color comparison and lab2lch conversion (#665)
|
||||
- Isotropic denoising (#653)
|
||||
- Generator to add various types of random noise to images (#625)
|
||||
- Color deconvolution for immunohistochemical images (#441)
|
||||
- Color label visualization (#485)
|
||||
|
||||
Drawing and visualization:
|
||||
|
||||
- Wu's anti-aliased circle, line, bezier curve (#709)
|
||||
- Linked image viewers and docked plugins (#575)
|
||||
- Rotated ellipse + bezier curve drawing (#510)
|
||||
- PySide & PyQt4 compatibility in skimage-viewer (#551)
|
||||
|
||||
Other:
|
||||
|
||||
- Python 3 support without 2to3. (#620)
|
||||
- 3D Marching Cubes (#469)
|
||||
- Line, Circle, Ellipse total least squares fitting and RANSAC algorithm (#440)
|
||||
- N-dimensional array padding (#577)
|
||||
- Add a wrapper around `scipy.ndimage.gaussian_filter` with useful default behaviors. (#712)
|
||||
- Predefined structuring elements for 3D morphology (#484)
|
||||
|
||||
|
||||
API changes
|
||||
-----------
|
||||
|
||||
The following backward-incompatible API changes were made between 0.8 and 0.9:
|
||||
|
||||
- No longer wrap ``imread`` output in an ``Image`` class
|
||||
- Change default value of `sigma` parameter in ``skimage.segmentation.slic``
|
||||
to 0
|
||||
- ``hough_circle`` now returns a stack of arrays that are the same size as the
|
||||
input image. Set the ``full_output`` flag to True for the old behavior.
|
||||
- The following functions were deprecated over two releases:
|
||||
`skimage.filter.denoise_tv_chambolle`,
|
||||
`skimage.morphology.is_local_maximum`, `skimage.transform.hough`,
|
||||
`skimage.transform.probabilistic_hough`,`skimage.transform.hough_peaks`.
|
||||
Their functionality still exists, but under different names.
|
||||
|
||||
|
||||
Contributors to this release
|
||||
----------------------------
|
||||
|
||||
This release was made possible by the collaborative efforts of many
|
||||
contributors, both new and old. They are listed in alphabetical order by
|
||||
surname:
|
||||
|
||||
- Ankit Agrawal
|
||||
- K.-Michael Aye
|
||||
- Chris Beaumont
|
||||
- François Boulogne
|
||||
- Luis Pedro Coelho
|
||||
- Marianne Corvellec
|
||||
- Olivier Debeir
|
||||
- Ferdinand Deger
|
||||
- Kemal Eren
|
||||
- Jostein Bø Fløystad
|
||||
- Christoph Gohlke
|
||||
- Emmanuelle Gouillart
|
||||
- Christian Horea
|
||||
- Thouis (Ray) Jones
|
||||
- Almar Klein
|
||||
- Xavier Moles Lopez
|
||||
- Alexis Mignon
|
||||
- Juan Nunez-Iglesias
|
||||
- Zachary Pincus
|
||||
- Nicolas Pinto
|
||||
- Davin Potts
|
||||
- Malcolm Reynolds
|
||||
- Umesh Sharma
|
||||
- Johannes Schönberger
|
||||
- Chintak Sheth
|
||||
- Kirill Shklovsky
|
||||
- Steven Silvester
|
||||
- Matt Terry
|
||||
- Riaan van den Dool
|
||||
- Stéfan van der Walt
|
||||
- Josh Warner
|
||||
- Adam Wisniewski
|
||||
- Yang Zetian
|
||||
- Tony S Yu
|
||||
@@ -1,17 +1,21 @@
|
||||
function insert_version_links() {
|
||||
var labels = ['dev', '0.8.0', '0.7.0', '0.6', '0.5', '0.4', '0.3'];
|
||||
var versions = ['dev', '0.9.x', '0.8.0', '0.7.0', '0.6', '0.5', '0.4', '0.3'];
|
||||
|
||||
for (i = 0; i < labels.length; i++){
|
||||
function insert_version_links() {
|
||||
for (i = 0; i < versions.length; i++){
|
||||
open_list = '<li>'
|
||||
if (typeof(DOCUMENTATION_OPTIONS) !== 'undefined') {
|
||||
if ((DOCUMENTATION_OPTIONS['VERSION'] == labels[i]) ||
|
||||
if ((DOCUMENTATION_OPTIONS['VERSION'] == versions[i]) ||
|
||||
(DOCUMENTATION_OPTIONS['VERSION'].match(/dev$/) && (i == 0))) {
|
||||
open_list = '<li id="current">'
|
||||
}
|
||||
}
|
||||
document.write(open_list);
|
||||
document.write('<a href="URL">skimage VERSION</a> </li>\n'
|
||||
.replace('VERSION', labels[i])
|
||||
.replace('URL', 'http://scikit-image.org/docs/' + labels[i]));
|
||||
.replace('VERSION', versions[i])
|
||||
.replace('URL', 'http://scikit-image.org/docs/' + versions[i]));
|
||||
}
|
||||
}
|
||||
|
||||
function stable_version() {
|
||||
return versions[1];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
Version 0.9
|
||||
-----------
|
||||
- No longer wrap ``imread`` output in an ``Image`` class
|
||||
- Change default value of `sigma` parameter in ``skimage.segmentation.slic``
|
||||
to 0
|
||||
- ``hough_circle`` now returns a stack of arrays that are the same size as the
|
||||
input image. Set the ``full_output`` flag to True for the old behavior.
|
||||
- The following functions were deprecated over two releases:
|
||||
`skimage.filter.denoise_tv_chambolle`,
|
||||
`skimage.morphology.is_local_maximum`, `skimage.transform.hough`,
|
||||
`skimage.transform.probabilistic_hough`,`skimage.transform.hough_peaks`.
|
||||
Their functionality still exists, but under different names.
|
||||
|
||||
Version 0.4
|
||||
-----------
|
||||
- Switch mask and radius arguments for ``median_filter``
|
||||
|
||||
Version 0.3
|
||||
-----------
|
||||
- Remove ``as_grey``, ``dtype`` keyword from ImageCollection
|
||||
- Remove ``dtype`` from imread
|
||||
- Generalise ImageCollection to accept a load_func
|
||||
|
||||
Version 0.4
|
||||
-----------
|
||||
- Switch mask and radius arguments for median_filter
|
||||
|
||||
+45
-9
@@ -26,9 +26,26 @@ sys.path.append(os.path.join(curpath, '..', 'ext'))
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath', 'numpydoc',
|
||||
'sphinx.ext.autosummary', 'plot_directive', 'plot2rst',
|
||||
'sphinx.ext.autosummary', 'plot2rst',
|
||||
'sphinx.ext.intersphinx']
|
||||
|
||||
# Determine if the matplotlib has a recent enough version of the
|
||||
# plot_directive, otherwise use the local fork.
|
||||
try:
|
||||
from matplotlib.sphinxext import plot_directive
|
||||
except ImportError:
|
||||
use_matplotlib_plot_directive = False
|
||||
else:
|
||||
try:
|
||||
use_matplotlib_plot_directive = (plot_directive.__version__ >= 2)
|
||||
except AttributeError:
|
||||
use_matplotlib_plot_directive = False
|
||||
|
||||
if use_matplotlib_plot_directive:
|
||||
extensions.append('matplotlib.sphinxext.plot_directive')
|
||||
else:
|
||||
extensions.append('plot_directive')
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
@@ -42,8 +59,8 @@ source_suffix = '.txt'
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'skimage'
|
||||
copyright = u'2011, the scikit-image team'
|
||||
project = 'skimage'
|
||||
copyright = '2013, the scikit-image team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -185,13 +202,13 @@ htmlhelp_basename = 'scikitimagedoc'
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('contents', 'scikitimage.tex', u'The Image Scikit Documentation',
|
||||
u'SciPy Developers', 'manual'),
|
||||
('contents', 'scikit-image.tex', u'The scikit-image Documentation',
|
||||
u'scikit-image development team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
@@ -203,13 +220,32 @@ latex_documents = [
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
latex_preamble = r'''
|
||||
\usepackage{enumitem}
|
||||
\setlistdepth{100}
|
||||
|
||||
\usepackage{amsmath}
|
||||
\DeclareUnicodeCharacter{00A0}{\nobreakspace}
|
||||
|
||||
% In the parameters section, place a newline after the Parameters header
|
||||
\usepackage{expdlist}
|
||||
\let\latexdescription=\description
|
||||
\def\description{\latexdescription{}{} \breaklabel}
|
||||
|
||||
% Make Examples/etc section headers smaller and more compact
|
||||
\makeatletter
|
||||
\titleformat{\paragraph}{\normalsize\py@HeaderFamily}%
|
||||
{\py@TitleColor}{0em}{\py@TitleColor}{\py@NormalColor}
|
||||
\titlespacing*{\paragraph}{0pt}{1ex}{0pt}
|
||||
\makeatother
|
||||
|
||||
'''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
latex_use_modindex = False
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Numpy extensions
|
||||
@@ -243,7 +279,7 @@ matplotlib.rcParams.update({
|
||||
|
||||
"""
|
||||
plot_include_source = True
|
||||
plot_formats = [('png', 100)]
|
||||
plot_formats = [('png', 100), ('pdf', 100)]
|
||||
|
||||
plot2rst_index_name = 'README'
|
||||
plot2rst_rcparams = {'image.cmap' : 'gray',
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
.. include:: ../../TASKS.txt
|
||||
.. include:: ../../DEVELOPMENT.txt
|
||||
.. include:: ../../CONTRIBUTING.txt
|
||||
|
||||
@@ -49,13 +49,13 @@ def calculate_coverage(reader):
|
||||
partial_items,
|
||||
total_items - (partial_items + done_items + na_items),
|
||||
na_items)
|
||||
|
||||
|
||||
return list(i / total_items for i in counts)
|
||||
|
||||
def read_table_titles(reader):
|
||||
r"""Create a dictionary with keys as section names and values as a list of
|
||||
table names
|
||||
|
||||
|
||||
return (dict)
|
||||
"""
|
||||
section_titles = []
|
||||
@@ -69,39 +69,39 @@ def read_table_titles(reader):
|
||||
# Extract names of the tables
|
||||
for name in row[1:]:
|
||||
if len(name) > 0:
|
||||
names.append(name)
|
||||
names.append(name)
|
||||
else:
|
||||
break
|
||||
section_titles.append(row[0])
|
||||
table_names[row[0]] = names
|
||||
except csv.Error, e:
|
||||
sys.exit('line %d: %s' % (reader.line_num, e))
|
||||
|
||||
|
||||
return section_titles,table_names
|
||||
|
||||
def table_seperator(stream,lengths,character="-"):
|
||||
r"""Write out table row seperator
|
||||
|
||||
|
||||
:Input:
|
||||
- *stream* (io/stream) Stream where output is put
|
||||
- *lengths* (list) A list of the lengths of the columns
|
||||
- *character* (string) Character to be filled between +, defaults to "-".
|
||||
|
||||
|
||||
"""
|
||||
stream.write("+")
|
||||
stream.write('+'.join([character*(length+2) for length in lengths]))
|
||||
stream.write("+")
|
||||
|
||||
|
||||
def table_row(stream,data,lengths,num_columns=None):
|
||||
r"""Write out table row data
|
||||
|
||||
|
||||
:Input:
|
||||
- *stream* (io/stream) Stream where output is put
|
||||
- *data* (list) List of strings containing data
|
||||
- *lengths* (list) A list of the lengths of the columns
|
||||
- *num_columns* (string) Number of columns, defaults to the length of the
|
||||
- *num_columns* (string) Number of columns, defaults to the length of the
|
||||
data array
|
||||
|
||||
|
||||
"""
|
||||
if num_columns is None:
|
||||
num_columns = len(data)
|
||||
@@ -115,11 +115,11 @@ def table_row(stream,data,lengths,num_columns=None):
|
||||
else:
|
||||
entry = MISSING_STRING
|
||||
stream.write(" " + entry + " "*(lengths[i] - len(entry)) + " |")
|
||||
|
||||
|
||||
def generate_table(reader,stream,table_name=None,
|
||||
column_titles=["Functionality","Matlab","Scipy","Scipy"]):
|
||||
r"""Generate a reST grid table based on the CSV data in reader
|
||||
|
||||
|
||||
Reads CSV data from *reader* until an empty line is found and generates a
|
||||
reST table based on the data into *stream*. A table name can be given for
|
||||
a section and table label. All rows are read in and checked for maximum
|
||||
@@ -127,13 +127,13 @@ def generate_table(reader,stream,table_name=None,
|
||||
widths so that the table can be constructed. If a row contains less than
|
||||
the maximum number of columns a string is inserted that defaults to the
|
||||
string *MISSING_STRING* which is a global parameter.
|
||||
|
||||
|
||||
:Input:
|
||||
- reader (csv.reader) The CSV reader to read in from
|
||||
- stream (iostream) Output target
|
||||
- table_name (string) Optional name of table, defaults to *None*
|
||||
- column_titles (list) List of column titles
|
||||
|
||||
|
||||
"""
|
||||
# Find number of columns and column widths, base number of columns is
|
||||
# determined by the headers
|
||||
@@ -141,7 +141,6 @@ def generate_table(reader,stream,table_name=None,
|
||||
data = [column_titles]
|
||||
try:
|
||||
for row in reader:
|
||||
# print row
|
||||
if len(row[0]) == 0:
|
||||
break
|
||||
data.append([entry.expandtabs() for entry in row])
|
||||
@@ -153,7 +152,7 @@ def generate_table(reader,stream,table_name=None,
|
||||
for row in data:
|
||||
for i in xrange(len(row)):
|
||||
column_lengths[i] = max(column_lengths[i],len(row[i]))
|
||||
|
||||
|
||||
# Output table header
|
||||
stream.write(table_name + "\n")
|
||||
if table_name is not None:
|
||||
@@ -167,7 +166,7 @@ def generate_table(reader,stream,table_name=None,
|
||||
stream.write("\n")
|
||||
table_seperator(stream,column_lengths,character="=")
|
||||
stream.write("\n")
|
||||
|
||||
|
||||
# Output table data
|
||||
for row in data[1:]:
|
||||
table_row(stream,row,column_lengths,num_columns)
|
||||
@@ -175,28 +174,28 @@ def generate_table(reader,stream,table_name=None,
|
||||
table_seperator(stream,column_lengths,character='-')
|
||||
stream.write("\n")
|
||||
stream.write("\n\n")
|
||||
|
||||
|
||||
def generate_page(csv_path,stream,page_title="Coverage Tables"):
|
||||
r"""Generate coverage table page
|
||||
|
||||
|
||||
Generates all reST for all tables contained in the CSV file at *csv_path*
|
||||
and output it to *stream*.
|
||||
|
||||
|
||||
:Input:
|
||||
- *csv_path* (path) Path to CSV file
|
||||
- *stream* (iostream) Output stream
|
||||
- *page_title* (string) Optional page title, defaults to
|
||||
- *page_title* (string) Optional page title, defaults to
|
||||
``Coverage Tables``.
|
||||
"""
|
||||
# Open reader
|
||||
csv_file = open(csv_path,'U')
|
||||
|
||||
|
||||
# Sniffer does not seem to work all the time even when an Excel
|
||||
# spread sheet is being used
|
||||
# dialect = csv.Sniffer().sniff(csv_file.read(1024))
|
||||
# csv_file.seek(0)
|
||||
# reader = csv.reader(csv_file, dialect)
|
||||
|
||||
|
||||
reader = csv.reader(csv_file)
|
||||
item_counts = calculate_coverage(reader)
|
||||
csv_file.seek(0)
|
||||
@@ -254,21 +253,21 @@ if __name__ == "__main__":
|
||||
output_path = './coverage_table.txt'
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1][:5].lower() == "help":
|
||||
print "Coverage Table Generator: coverage_generator.py"
|
||||
print " Usage: coverage_generator.py [csv] [output]"
|
||||
print " csv - Path to csv file, defaults to ./coverage.csv"
|
||||
print " output - Ouput path, defaults to ./coverage_table.txt"
|
||||
print ""
|
||||
print("Coverage Table Generator: coverage_generator.py")
|
||||
print(" Usage: coverage_generator.py [csv] [output]")
|
||||
print(" csv - Path to csv file, defaults to ./coverage.csv")
|
||||
print(" output - Ouput path, defaults to ./coverage_table.txt")
|
||||
print('')
|
||||
sys.exit(0)
|
||||
if len(sys.argv) == 2:
|
||||
csv_path = os.path.abspath(sys.argv[1])
|
||||
if len(sys.argv) == 3:
|
||||
output_path = os.path.abspath(sys.argv[2])
|
||||
|
||||
|
||||
output = open(output_path,'w')
|
||||
generate_page(csv_path,output)
|
||||
output.close()
|
||||
|
||||
|
||||
print("Generated %s from %s." % (output_path,csv_path))
|
||||
|
||||
|
||||
|
||||
+37
-15
@@ -6,8 +6,15 @@ Pre-built installation
|
||||
are kindly provided by Christoph Gohlke.
|
||||
|
||||
The latest stable release is also included as part of the `Enthought Python
|
||||
Distribution (EPD) <http://enthought.com/products/epd.php>`__ and `Python(x,y)
|
||||
<http://code.google.com/p/pythonxy/wiki/Welcome>`__.
|
||||
Distribution (EPD) <http://enthought.com/products/epd.php>`__, `Python(x,y)
|
||||
<http://code.google.com/p/pythonxy/wiki/Welcome>`__ and
|
||||
`Anaconda <https://store.continuum.io/cshop/anaconda/>`__.
|
||||
|
||||
On Debian and Ubuntu, a Debian package ``python-skimage`` can be found in
|
||||
`the Neurodebian repository <http://neuro.debian.net>`__. Follow `the
|
||||
instructions <http://neuro.debian.net/#how-to-use-this-repository>`__ to
|
||||
add Neurodebian to your system package manager, then look for
|
||||
``python-skimage`` in the package manager.
|
||||
|
||||
On systems that support setuptools, the package can be installed from the
|
||||
`Python packaging index <http://pypi.python.org/pypi/scikit-image>`__ using
|
||||
@@ -28,36 +35,51 @@ Installation from source
|
||||
|
||||
Obtain the source from the git-repository at
|
||||
`http://github.com/scikit-image/scikit-image
|
||||
<http://github.com/scikit-image/scikit-image>`_.
|
||||
|
||||
by running
|
||||
|
||||
::
|
||||
<http://github.com/scikit-image/scikit-image>`_ by running::
|
||||
|
||||
git clone http://github.com/scikit-image/scikit-image.git
|
||||
|
||||
in a terminal (You will need to have git installed on your machine).
|
||||
in a terminal (you will need to have git installed on your machine).
|
||||
|
||||
If you do not have git installed, you can also download a zipball from
|
||||
`https://github.com/scikit-image/scikit-image/zipball/master
|
||||
<https://github.com/scikit-image/scikit-image/zipball/master>`_.
|
||||
|
||||
The SciKit can be installed globally using
|
||||
|
||||
::
|
||||
The SciKit can be installed globally using::
|
||||
|
||||
python setup.py install
|
||||
|
||||
or locally using
|
||||
|
||||
::
|
||||
or locally using::
|
||||
|
||||
python setup.py install --prefix=${HOME}
|
||||
|
||||
If you prefer, you can use it without installing, by simply adding
|
||||
this path to your PYTHONPATH variable and compiling extensions
|
||||
this path to your ``PYTHONPATH`` variable and compiling extensions
|
||||
in-place::
|
||||
|
||||
python setup.py build_ext -i
|
||||
|
||||
Building with bento
|
||||
-------------------
|
||||
|
||||
``scikit-image`` can also be built using `bento
|
||||
<http://cournape.github.io/Bento/>`__. Bento depends on `WAF
|
||||
<https://code.google.com/p/waf/>`__ for compilation.
|
||||
|
||||
Follow the `Bento installation instructions
|
||||
<http://cournape.github.io/Bento/html/install.html>`__ and `download the WAF
|
||||
source <http://code.google.com/p/waf/downloads/list>`__.
|
||||
|
||||
Tell Bento where to find WAF by setting the ``WAFDIR`` environment variable::
|
||||
|
||||
export WAFDIR=<path/to/waf>
|
||||
|
||||
From the ``scikit-image`` source directory::
|
||||
|
||||
bentomaker configure
|
||||
bentomaker build -j # (add -i for in-place build)
|
||||
bentomaker install # (when not builing in-place)
|
||||
|
||||
Depending on file permissions, the install commands may need to be run as sudo.
|
||||
|
||||
.. include:: ../../DEPENDS.txt
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from skimage.transform import hough
|
||||
from skimage.transform import hough_line
|
||||
from skimage.draw import line
|
||||
|
||||
img = np.zeros((100, 150), dtype=bool)
|
||||
img[30, :] = 1
|
||||
img[:, 65] = 1
|
||||
img[35:45, 35:50] = 1
|
||||
for i in range(90):
|
||||
img[i, i] = 1
|
||||
rr, cc = line(60, 130, 80, 10)
|
||||
img[rr, cc] = 1
|
||||
img += np.random.random(img.shape) > 0.95
|
||||
|
||||
out, angles, d = hough(img)
|
||||
out, angles, d = hough_line(img)
|
||||
|
||||
plt.subplot(1, 2, 1)
|
||||
|
||||
@@ -20,8 +21,8 @@ plt.title('Input image')
|
||||
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.imshow(out, cmap=plt.cm.bone,
|
||||
extent=(d[0], d[-1],
|
||||
np.rad2deg(angles[0]), np.rad2deg(angles[-1])))
|
||||
extent=(np.rad2deg(angles[-1]), np.rad2deg(angles[0]),
|
||||
d[-1], d[0]))
|
||||
plt.title('Hough transform')
|
||||
plt.xlabel('Angle (degree)')
|
||||
plt.ylabel('Distance (pixel)')
|
||||
|
||||
@@ -41,6 +41,12 @@ h6 {
|
||||
font-size: 13px;
|
||||
line-height: 15px;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 0;
|
||||
}
|
||||
dt {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.logo {
|
||||
float: left;
|
||||
@@ -73,6 +79,10 @@ h6 {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
#current {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.headerlink {
|
||||
margin-left: 10px;
|
||||
color: #ddd;
|
||||
@@ -222,3 +232,8 @@ p.admonition-title {
|
||||
width: 200px;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* misc */
|
||||
div.math {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -34,9 +34,9 @@ violates these assumptions about the dtype range::
|
||||
|
||||
>>> from skimage import img_as_float
|
||||
>>> image = np.arange(0, 50, 10, dtype=np.uint8)
|
||||
>>> print image.astype(np.float) # These float values are out of range.
|
||||
>>> print(image.astype(np.float)) # These float values are out of range.
|
||||
[ 0. 10. 20. 30. 40.]
|
||||
>>> print img_as_float(image)
|
||||
>>> print(img_as_float(image))
|
||||
[ 0. 0.03921569 0.07843137 0.11764706 0.15686275]
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ Contributing examples to the gallery can be done on github (see
|
||||
Search field
|
||||
------------
|
||||
|
||||
The ``quick search`` field located in the sidebar of the html
|
||||
The ``quick search`` field located in the navigation bar of the html
|
||||
documentation can be used to search for specific keywords (segmentation,
|
||||
rescaling, denoising, etc.).
|
||||
|
||||
|
||||
+3
-3
@@ -176,7 +176,7 @@ class ApiDocWriter(object):
|
||||
''' Parse module defined in *uri* '''
|
||||
filename = self._uri2path(uri)
|
||||
if filename is None:
|
||||
print filename, 'erk'
|
||||
print(filename, 'erk')
|
||||
# nothing that we could handle here.
|
||||
return ([],[])
|
||||
f = open(filename, 'rt')
|
||||
@@ -260,7 +260,7 @@ class ApiDocWriter(object):
|
||||
# get the names of all classes and functions
|
||||
functions, classes = self._parse_module_with_import(uri)
|
||||
if not len(functions) and not len(classes) and DEBUG:
|
||||
print 'WARNING: Empty -', uri # dbg
|
||||
print('WARNING: Empty -', uri) # dbg
|
||||
return ''
|
||||
|
||||
# Make a shorter version of the uri that omits the package name for
|
||||
@@ -449,7 +449,7 @@ class ApiDocWriter(object):
|
||||
relpath = (outdir + os.path.sep).replace(relative_to + os.path.sep, '')
|
||||
else:
|
||||
relpath = outdir
|
||||
print "outdir: ", relpath
|
||||
print("outdir: ", relpath)
|
||||
idx = open(path,'wt')
|
||||
w = idx.write
|
||||
w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
|
||||
|
||||
@@ -13,7 +13,7 @@ from distutils.version import LooseVersion as V
|
||||
#*****************************************************************************
|
||||
|
||||
def abort(error):
|
||||
print '*WARNING* API documentation not generated: %s'%error
|
||||
print('*WARNING* API documentation not generated: %s' % error)
|
||||
exit()
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -35,7 +35,7 @@ if __name__ == '__main__':
|
||||
# are not (re)generated. This avoids automatic generation of documentation
|
||||
# for older or newer versions if such versions are installed on the system.
|
||||
|
||||
installed_version = V(module.version.version)
|
||||
installed_version = V(module.__version__)
|
||||
|
||||
setup_lines = open('../setup.py').readlines()
|
||||
version = 'vUndefined'
|
||||
@@ -54,4 +54,4 @@ if __name__ == '__main__':
|
||||
]
|
||||
docwriter.write_api_docs(outdir)
|
||||
docwriter.write_index(outdir, 'api', relative_to='source/api')
|
||||
print '%d files written' % len(docwriter.written_modules)
|
||||
print('%d files written' % len(docwriter.written_modules))
|
||||
|
||||
+62
-42
@@ -1,14 +1,15 @@
|
||||
import urllib
|
||||
import json
|
||||
import copy
|
||||
import urllib
|
||||
import dateutil.parser
|
||||
from collections import OrderedDict
|
||||
from datetime import datetime, timedelta
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.ticker import FuncFormatter
|
||||
from matplotlib.transforms import blended_transform_factory
|
||||
|
||||
import dateutil.parser
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
cache = '_pr_cache.txt'
|
||||
|
||||
@@ -22,16 +23,16 @@ cache = '_pr_cache.txt'
|
||||
releases = OrderedDict([
|
||||
#('0.1', u'2009-10-07 13:52:19 +0200'),
|
||||
#('0.2', u'2009-11-12 14:48:45 +0200'),
|
||||
('0.3', u'2011-10-10 03:28:47 -0700'),
|
||||
#('0.3', u'2011-10-10 03:28:47 -0700'),
|
||||
('0.4', u'2011-12-03 14:31:32 -0800'),
|
||||
('0.5', u'2012-02-26 21:00:51 -0800'),
|
||||
('0.6', u'2012-06-24 21:37:05 -0700')])
|
||||
('0.6', u'2012-06-24 21:37:05 -0700'),
|
||||
('0.7', u'2012-09-29 18:08:49 -0700'),
|
||||
('0.8', u'2013-03-04 20:46:09 +0100')])
|
||||
|
||||
|
||||
month_duration = 24
|
||||
|
||||
for r in releases:
|
||||
releases[r] = dateutil.parser.parse(releases[r])
|
||||
|
||||
def fetch_PRs(user='scikit-image', repo='scikit-image', state='open'):
|
||||
params = {'state': state,
|
||||
@@ -46,12 +47,12 @@ def fetch_PRs(user='scikit-image', repo='scikit-image', state='open'):
|
||||
'repo': repo,
|
||||
'params': urllib.urlencode(params)}
|
||||
|
||||
fetch_status = 'Fetching page %(page)d (state=%(state)s)' % params + \
|
||||
' from %(user)s/%(repo)s...' % config
|
||||
print fetch_status
|
||||
fetch_status = ('Fetching page %(page)d (state=%(state)s)' % params +
|
||||
' from %(user)s/%(repo)s...' % config)
|
||||
print(fetch_status)
|
||||
|
||||
f = urllib.urlopen(
|
||||
'https://api.github.com/repos/%(user)s/%(repo)s/pulls?%(params)s' \
|
||||
'https://api.github.com/repos/%(user)s/%(repo)s/pulls?%(params)s'
|
||||
% config
|
||||
)
|
||||
|
||||
@@ -61,15 +62,40 @@ def fetch_PRs(user='scikit-image', repo='scikit-image', state='open'):
|
||||
|
||||
if 'message' in page_data and page_data['message'] == "Not Found":
|
||||
page_data = []
|
||||
print 'Warning: Repo not found (%(user)s/%(repo)s)' % config
|
||||
print('Warning: Repo not found (%(user)s/%(repo)s)' % config)
|
||||
else:
|
||||
data.extend(page_data)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def seconds_from_epoch(dates):
|
||||
seconds = [(dt - epoch).total_seconds() for dt in dates]
|
||||
return seconds
|
||||
|
||||
|
||||
def get_month_bins(dates):
|
||||
now = datetime.now(tz=dates[0].tzinfo)
|
||||
this_month = datetime(year=now.year, month=now.month, day=1,
|
||||
tzinfo=dates[0].tzinfo)
|
||||
|
||||
bins = [this_month - relativedelta(months=i)
|
||||
for i in reversed(range(-1, month_duration))]
|
||||
return seconds_from_epoch(bins)
|
||||
|
||||
|
||||
def date_formatter(value, _):
|
||||
dt = epoch + timedelta(seconds=value)
|
||||
return dt.strftime('%Y/%m')
|
||||
|
||||
|
||||
for r in releases:
|
||||
releases[r] = dateutil.parser.parse(releases[r])
|
||||
|
||||
|
||||
try:
|
||||
PRs = json.loads(open(cache, 'r').read())
|
||||
print 'Loaded PRs from cache...'
|
||||
print('Loaded PRs from cache...')
|
||||
|
||||
except IOError:
|
||||
PRs = fetch_PRs(user='stefanv', repo='scikits.image', state='closed')
|
||||
@@ -81,53 +107,47 @@ except IOError:
|
||||
cf.flush()
|
||||
|
||||
nrs = [pr['number'] for pr in PRs]
|
||||
print 'Processing %d pull requests...' % len(nrs)
|
||||
print('Processing %d pull requests...' % len(nrs))
|
||||
|
||||
dates = [dateutil.parser.parse(pr['created_at']) for pr in PRs]
|
||||
|
||||
epoch = datetime(2009, 1, 1, tzinfo=dates[0].tzinfo)
|
||||
|
||||
def seconds_from_epoch(dates):
|
||||
seconds = [(dt - epoch).total_seconds() for dt in dates]
|
||||
return seconds
|
||||
|
||||
dates_f = seconds_from_epoch(dates)
|
||||
bins = get_month_bins(dates)
|
||||
|
||||
def date_formatter(value, _):
|
||||
dt = epoch + timedelta(seconds=value)
|
||||
return dt.strftime('%Y/%m')
|
||||
fig, ax = plt.subplots(figsize=(7, 5))
|
||||
|
||||
plt.figure(figsize=(7, 5))
|
||||
n, bins, _ = ax.hist(dates_f, bins=bins, color='blue', alpha=0.6)
|
||||
|
||||
now = datetime.now(tz=dates[0].tzinfo)
|
||||
this_month = datetime(year=now.year, month=now.month, day=1,
|
||||
tzinfo=dates[0].tzinfo)
|
||||
|
||||
bins = [this_month - relativedelta(months=i) \
|
||||
for i in reversed(range(-1, month_duration))]
|
||||
bins = seconds_from_epoch(bins)
|
||||
plt.hist(dates_f, bins=bins)
|
||||
|
||||
ax = plt.gca()
|
||||
ax.xaxis.set_major_formatter(FuncFormatter(date_formatter))
|
||||
ax.set_xticks(bins[:-1])
|
||||
ax.set_xticks(bins[2:-1:3]) # Date label every 3 months.
|
||||
|
||||
labels = ax.get_xticklabels()
|
||||
for l in labels:
|
||||
l.set_rotation(40)
|
||||
l.set_size(10)
|
||||
|
||||
mixed_transform = blended_transform_factory(ax.transData, ax.transAxes)
|
||||
|
||||
for version, date in releases.items():
|
||||
date = seconds_from_epoch([date])[0]
|
||||
plt.axvline(date, color='r', label=version)
|
||||
ax.axvline(date, color='black', linestyle=':', label=version)
|
||||
ax.text(date, 1, version, color='r', va='bottom', ha='center',
|
||||
transform=mixed_transform)
|
||||
|
||||
plt.title('Pull request activity').set_y(1.05)
|
||||
plt.xlabel('Date')
|
||||
plt.ylabel('PRs created')
|
||||
plt.legend(loc=2, title='Release')
|
||||
plt.subplots_adjust(top=0.875, bottom=0.225)
|
||||
ax.set_title('Pull request activity').set_y(1.05)
|
||||
ax.set_xlabel('Date')
|
||||
ax.set_ylabel('PRs per month', color='blue')
|
||||
fig.subplots_adjust(top=0.875, bottom=0.225)
|
||||
|
||||
plt.savefig('PRs.png')
|
||||
cumulative = np.cumsum(n)
|
||||
cumulative += len(dates) - cumulative[-1]
|
||||
|
||||
ax2 = ax.twinx()
|
||||
ax2.plot(bins[1:], cumulative, color='black', linewidth=2)
|
||||
ax2.set_ylabel('Total PRs', color='black')
|
||||
|
||||
fig.savefig('PRs.png')
|
||||
|
||||
plt.show()
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
cython>=0.17
|
||||
matplotlib>=1.0
|
||||
numpy>=1.6
|
||||
@@ -17,11 +17,11 @@ MAINTAINER_EMAIL = 'stefan@sun.ac.za'
|
||||
URL = 'http://scikit-image.org'
|
||||
LICENSE = 'Modified BSD'
|
||||
DOWNLOAD_URL = 'http://github.com/scikit-image/scikit-image'
|
||||
VERSION = '0.8.1'
|
||||
VERSION = '0.9.0'
|
||||
PYTHON_VERSION = (2, 5)
|
||||
DEPENDENCIES = {
|
||||
'numpy': (1, 6),
|
||||
'Cython': (0, 15),
|
||||
'Cython': (0, 17),
|
||||
}
|
||||
|
||||
|
||||
@@ -30,10 +30,7 @@ import sys
|
||||
import re
|
||||
import setuptools
|
||||
from numpy.distutils.core import setup
|
||||
try:
|
||||
from distutils.command.build_py import build_py_2to3 as build_py
|
||||
except ImportError:
|
||||
from distutils.command.build_py import build_py
|
||||
from distutils.command.build_py import build_py
|
||||
|
||||
|
||||
def configuration(parent_package='', top_path=None):
|
||||
|
||||
+53
-30
@@ -38,8 +38,6 @@ util
|
||||
|
||||
Utility Functions
|
||||
-----------------
|
||||
get_log
|
||||
Returns the ``skimage`` log. Use this to print debug output.
|
||||
img_as_float
|
||||
Convert an image to floating point format, with values in [0, 1].
|
||||
img_as_uint
|
||||
@@ -54,6 +52,7 @@ img_as_ubyte
|
||||
import os.path as _osp
|
||||
import imp as _imp
|
||||
import functools as _functools
|
||||
from skimage._shared.utils import deprecated as _deprecated
|
||||
|
||||
pkg_dir = _osp.abspath(_osp.dirname(__file__))
|
||||
data_dir = _osp.join(pkg_dir, 'data')
|
||||
@@ -62,6 +61,7 @@ try:
|
||||
from .version import version as __version__
|
||||
except ImportError:
|
||||
__version__ = "unbuilt-dev"
|
||||
del version
|
||||
|
||||
|
||||
try:
|
||||
@@ -88,6 +88,56 @@ test_verbose = _functools.partial(test, verbose=True)
|
||||
test_verbose.__doc__ = test.__doc__
|
||||
|
||||
|
||||
class _Log(Warning):
|
||||
pass
|
||||
|
||||
|
||||
class _FakeLog(object):
|
||||
def __init__(self, name):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the log.
|
||||
repeat : bool
|
||||
Whether to print repeating messages more than once (False by
|
||||
default).
|
||||
"""
|
||||
self._name = name
|
||||
|
||||
import warnings
|
||||
warnings.simplefilter("always", _Log)
|
||||
|
||||
self._warnings = warnings
|
||||
|
||||
def _warn(self, msg, wtype):
|
||||
self._warnings.warn('%s: %s' % (wtype, msg), _Log)
|
||||
|
||||
def debug(self, msg):
|
||||
self._warn(msg, 'DEBUG')
|
||||
|
||||
def info(self, msg):
|
||||
self._warn(msg, 'INFO')
|
||||
|
||||
def warning(self, msg):
|
||||
self._warn(msg, 'WARNING')
|
||||
|
||||
warn = warning
|
||||
|
||||
def error(self, msg):
|
||||
self._warn(msg, 'ERROR')
|
||||
|
||||
def critical(self, msg):
|
||||
self._warn(msg, 'CRITICAL')
|
||||
|
||||
def addHandler(*args):
|
||||
pass
|
||||
|
||||
def setLevel(*args):
|
||||
pass
|
||||
|
||||
|
||||
@_deprecated()
|
||||
def get_log(name=None):
|
||||
"""Return a console logger.
|
||||
|
||||
@@ -105,39 +155,12 @@ def get_log(name=None):
|
||||
http://docs.python.org/library/logging.html
|
||||
|
||||
"""
|
||||
import logging
|
||||
|
||||
if name is None:
|
||||
name = 'skimage'
|
||||
else:
|
||||
name = 'skimage.' + name
|
||||
|
||||
log = logging.getLogger(name)
|
||||
return log
|
||||
return _FakeLog(name)
|
||||
|
||||
|
||||
def _setup_log():
|
||||
"""Configure root logger.
|
||||
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
|
||||
formatter = logging.Formatter(
|
||||
'%(name)s: %(levelname)s: %(message)s'
|
||||
)
|
||||
|
||||
try:
|
||||
handler = logging.StreamHandler(stream=sys.stdout)
|
||||
except TypeError:
|
||||
handler = logging.StreamHandler(strm=sys.stdout)
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
log = get_log()
|
||||
log.addHandler(handler)
|
||||
log.setLevel(logging.WARNING)
|
||||
log.propagate = False
|
||||
|
||||
_setup_log()
|
||||
|
||||
from .util.dtype import *
|
||||
|
||||
+2
-1
@@ -8,7 +8,8 @@ import subprocess
|
||||
try:
|
||||
WindowsError
|
||||
except NameError:
|
||||
WindowsError = None
|
||||
class WindowsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def cython(pyx_files, working_path=''):
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
# Copyright (c) 2010-2013 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.3.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
class _LazyDescr(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result)
|
||||
# This is a bit ugly, but it avoids running this again.
|
||||
delattr(tp, self.name)
|
||||
return result
|
||||
|
||||
|
||||
class MovedModule(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old, new=None):
|
||||
super(MovedModule, self).__init__(name)
|
||||
if PY3:
|
||||
if new is None:
|
||||
new = name
|
||||
self.mod = new
|
||||
else:
|
||||
self.mod = old
|
||||
|
||||
def _resolve(self):
|
||||
return _import_module(self.mod)
|
||||
|
||||
|
||||
class MovedAttribute(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||
super(MovedAttribute, self).__init__(name)
|
||||
if PY3:
|
||||
if new_mod is None:
|
||||
new_mod = name
|
||||
self.mod = new_mod
|
||||
if new_attr is None:
|
||||
if old_attr is None:
|
||||
new_attr = name
|
||||
else:
|
||||
new_attr = old_attr
|
||||
self.attr = new_attr
|
||||
else:
|
||||
self.mod = old_mod
|
||||
if old_attr is None:
|
||||
old_attr = name
|
||||
self.attr = old_attr
|
||||
|
||||
def _resolve(self):
|
||||
module = _import_module(self.mod)
|
||||
return getattr(module, self.attr)
|
||||
|
||||
|
||||
|
||||
class _MovedItems(types.ModuleType):
|
||||
"""Lazy loading of moved objects"""
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||
MovedModule("http_client", "httplib", "http.client"),
|
||||
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||
MovedModule("cPickle", "cPickle", "pickle"),
|
||||
MovedModule("queue", "Queue"),
|
||||
MovedModule("reprlib", "repr"),
|
||||
MovedModule("socketserver", "SocketServer"),
|
||||
MovedModule("tkinter", "Tkinter"),
|
||||
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||
"tkinter.colorchooser"),
|
||||
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||
"tkinter.commondialog"),
|
||||
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
del attr
|
||||
|
||||
moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves")
|
||||
|
||||
|
||||
def add_move(move):
|
||||
"""Add an item to six.moves."""
|
||||
setattr(_MovedItems, move.name, move)
|
||||
|
||||
|
||||
def remove_move(name):
|
||||
"""Remove item from six.moves."""
|
||||
try:
|
||||
delattr(_MovedItems, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
del moves.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError("no such move, %r" % (name,))
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_closure = "__closure__"
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
_func_globals = "__globals__"
|
||||
|
||||
_iterkeys = "keys"
|
||||
_itervalues = "values"
|
||||
_iteritems = "items"
|
||||
_iterlists = "lists"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_closure = "func_closure"
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
_func_globals = "func_globals"
|
||||
|
||||
_iterkeys = "iterkeys"
|
||||
_itervalues = "itervalues"
|
||||
_iteritems = "iteritems"
|
||||
_iterlists = "iterlists"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
try:
|
||||
callable = callable
|
||||
except NameError:
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
create_bound_method = types.MethodType
|
||||
|
||||
Iterator = object
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
def create_bound_method(func, obj):
|
||||
return types.MethodType(func, obj, obj.__class__)
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_closure = operator.attrgetter(_func_closure)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
get_function_globals = operator.attrgetter(_func_globals)
|
||||
|
||||
|
||||
def iterkeys(d, **kw):
|
||||
"""Return an iterator over the keys of a dictionary."""
|
||||
return iter(getattr(d, _iterkeys)(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
"""Return an iterator over the values of a dictionary."""
|
||||
return iter(getattr(d, _itervalues)(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
"""Return an iterator over the (key, value) pairs of a dictionary."""
|
||||
return iter(getattr(d, _iteritems)(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
"""Return an iterator over the (key, [values]) pairs of a dictionary."""
|
||||
return iter(getattr(d, _iterlists)(**kw))
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
def u(s):
|
||||
return s
|
||||
unichr = chr
|
||||
if sys.version_info[1] <= 1:
|
||||
def int2byte(i):
|
||||
return bytes((i,))
|
||||
else:
|
||||
# This is about 2x faster than the implementation above on 3.2+
|
||||
int2byte = operator.methodcaller("to_bytes", 1, "big")
|
||||
byte2int = operator.itemgetter(0)
|
||||
indexbytes = operator.getitem
|
||||
iterbytes = iter
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
def u(s):
|
||||
return unicode(s, "unicode_escape")
|
||||
unichr = unichr
|
||||
int2byte = chr
|
||||
def byte2int(bs):
|
||||
return ord(bs[0])
|
||||
def indexbytes(buf, i):
|
||||
return ord(buf[i])
|
||||
def iterbytes(buf):
|
||||
return (ord(byte) for byte in buf)
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
if PY3:
|
||||
import builtins
|
||||
exec_ = getattr(builtins, "exec")
|
||||
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(builtins, "print")
|
||||
del builtins
|
||||
|
||||
else:
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
raise tp, value, tb
|
||||
""")
|
||||
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
return meta("NewBase", bases, {})
|
||||
@@ -1,5 +1,5 @@
|
||||
cimport numpy as cnp
|
||||
|
||||
|
||||
cdef float integrate(cnp.ndarray[float, ndim=2, mode="c"] sat,
|
||||
Py_ssize_t r0, Py_ssize_t c0, Py_ssize_t r1, Py_ssize_t c1)
|
||||
cdef float integrate(float[:, ::1] sat, Py_ssize_t r0, Py_ssize_t c0,
|
||||
Py_ssize_t r1, Py_ssize_t c1)
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
cimport numpy as cnp
|
||||
|
||||
|
||||
cdef float integrate(cnp.ndarray[float, ndim=2, mode="c"] sat,
|
||||
Py_ssize_t r0, Py_ssize_t c0, Py_ssize_t r1, Py_ssize_t c1):
|
||||
cdef float integrate(float[:, ::1] sat, Py_ssize_t r0, Py_ssize_t c0,
|
||||
Py_ssize_t r1, Py_ssize_t c1):
|
||||
"""
|
||||
Using a summed area table / integral image, calculate the sum
|
||||
over a given window.
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import warnings
|
||||
import functools
|
||||
import sys
|
||||
|
||||
from . import six
|
||||
|
||||
|
||||
__all__ = ['deprecated']
|
||||
__all__ = ['deprecated', 'get_bound_method_class']
|
||||
|
||||
|
||||
class skimage_deprecation(Warning):
|
||||
"""Create our own deprecation class, since Python >= 2.7
|
||||
silences deprecations by default.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class deprecated(object):
|
||||
@@ -25,19 +36,38 @@ class deprecated(object):
|
||||
|
||||
def __call__(self, func):
|
||||
|
||||
msg = "Call to deprecated function `%s`." % func.__name__
|
||||
alt_msg = ''
|
||||
if self.alt_func is not None:
|
||||
msg = msg + " Use `%s` instead." % self.alt_func
|
||||
alt_msg = ' Use ``%s`` instead.' % self.alt_func
|
||||
|
||||
msg = 'Call to deprecated function ``%s``.' % func.__name__
|
||||
msg += alt_msg
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapped(*args, **kwargs):
|
||||
if self.behavior == 'warn':
|
||||
func_code = six.get_function_code(func)
|
||||
warnings.simplefilter('always', skimage_deprecation)
|
||||
warnings.warn_explicit(msg,
|
||||
category=DeprecationWarning,
|
||||
filename=func.func_code.co_filename,
|
||||
lineno=func.func_code.co_firstlineno + 1)
|
||||
category=skimage_deprecation,
|
||||
filename=func_code.co_filename,
|
||||
lineno=func_code.co_firstlineno + 1)
|
||||
elif self.behavior == 'raise':
|
||||
raise DeprecationWarning(msg)
|
||||
raise skimage_deprecation(msg)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# modify doc string to display deprecation warning
|
||||
doc = '**Deprecated function**.' + alt_msg
|
||||
if wrapped.__doc__ is None:
|
||||
wrapped.__doc__ = doc
|
||||
else:
|
||||
wrapped.__doc__ = doc + '\n\n ' + wrapped.__doc__
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def get_bound_method_class(m):
|
||||
"""Return the class for a bound method.
|
||||
|
||||
"""
|
||||
return m.im_class if sys.version < '3' else m.__self__.__class__
|
||||
|
||||
+107
-1
@@ -1 +1,107 @@
|
||||
from .colorconv import *
|
||||
from .colorconv import (convert_colorspace,
|
||||
guess_spatial_dimensions,
|
||||
rgb2hsv,
|
||||
hsv2rgb,
|
||||
rgb2xyz,
|
||||
xyz2rgb,
|
||||
rgb2rgbcie,
|
||||
rgbcie2rgb,
|
||||
rgb2grey,
|
||||
rgb2gray,
|
||||
gray2rgb,
|
||||
xyz2lab,
|
||||
lab2xyz,
|
||||
lab2rgb,
|
||||
rgb2lab,
|
||||
rgb2hed,
|
||||
hed2rgb,
|
||||
lab2lch,
|
||||
lch2lab,
|
||||
separate_stains,
|
||||
combine_stains,
|
||||
rgb_from_hed,
|
||||
hed_from_rgb,
|
||||
rgb_from_hdx,
|
||||
hdx_from_rgb,
|
||||
rgb_from_fgx,
|
||||
fgx_from_rgb,
|
||||
rgb_from_bex,
|
||||
bex_from_rgb,
|
||||
rgb_from_rbd,
|
||||
rbd_from_rgb,
|
||||
rgb_from_gdx,
|
||||
gdx_from_rgb,
|
||||
rgb_from_hax,
|
||||
hax_from_rgb,
|
||||
rgb_from_bro,
|
||||
bro_from_rgb,
|
||||
rgb_from_bpx,
|
||||
bpx_from_rgb,
|
||||
rgb_from_ahx,
|
||||
ahx_from_rgb,
|
||||
rgb_from_hpx,
|
||||
hpx_from_rgb,
|
||||
is_rgb,
|
||||
is_gray)
|
||||
|
||||
from .colorlabel import color_dict, label2rgb
|
||||
|
||||
from .delta_e import (deltaE_cie76,
|
||||
deltaE_ciede94,
|
||||
deltaE_ciede2000,
|
||||
deltaE_cmc,
|
||||
)
|
||||
|
||||
|
||||
__all__ = ['convert_colorspace',
|
||||
'guess_spatial_dimensions',
|
||||
'rgb2hsv',
|
||||
'hsv2rgb',
|
||||
'rgb2xyz',
|
||||
'xyz2rgb',
|
||||
'rgb2rgbcie',
|
||||
'rgbcie2rgb',
|
||||
'rgb2grey',
|
||||
'rgb2gray',
|
||||
'gray2rgb',
|
||||
'xyz2lab',
|
||||
'lab2xyz',
|
||||
'lab2rgb',
|
||||
'rgb2lab',
|
||||
'rgb2hed',
|
||||
'hed2rgb',
|
||||
'lab2lch',
|
||||
'lch2lab',
|
||||
'separate_stains',
|
||||
'combine_stains',
|
||||
'rgb_from_hed',
|
||||
'hed_from_rgb',
|
||||
'rgb_from_hdx',
|
||||
'hdx_from_rgb',
|
||||
'rgb_from_fgx',
|
||||
'fgx_from_rgb',
|
||||
'rgb_from_bex',
|
||||
'bex_from_rgb',
|
||||
'rgb_from_rbd',
|
||||
'rbd_from_rgb',
|
||||
'rgb_from_gdx',
|
||||
'gdx_from_rgb',
|
||||
'rgb_from_hax',
|
||||
'hax_from_rgb',
|
||||
'rgb_from_bro',
|
||||
'bro_from_rgb',
|
||||
'rgb_from_bpx',
|
||||
'bpx_from_rgb',
|
||||
'rgb_from_ahx',
|
||||
'ahx_from_rgb',
|
||||
'rgb_from_hpx',
|
||||
'hpx_from_rgb',
|
||||
'is_rgb',
|
||||
'is_gray',
|
||||
'color_dict',
|
||||
'label2rgb',
|
||||
'deltaE_cie76',
|
||||
'deltaE_ciede94',
|
||||
'deltaE_ciede2000',
|
||||
'deltaE_cmc',
|
||||
]
|
||||
|
||||
+443
-29
@@ -26,10 +26,17 @@ Supported color spaces
|
||||
Derived from the RGB CIE color space. Chosen such that
|
||||
``x == y == z == 1/3`` at the whitepoint, and all color matching
|
||||
functions are greater than zero everywhere.
|
||||
* LAB CIE : Lightness, a, b
|
||||
Colorspace derived from XYZ CIE that is intended to be more
|
||||
perceptually uniform
|
||||
* LCH CIE : Lightness, Chroma, Hue
|
||||
Defined in terms of LAB CIE. C and H are the polar representation of
|
||||
a and b. The polar angle C is defined to be on (0, 2*pi)
|
||||
|
||||
:author: Nicolas Pinto (rgb2hsv)
|
||||
:author: Ralf Gommers (hsv2rgb)
|
||||
:author: Travis Oliphant (XYZ and RGB CIE functions)
|
||||
:author: Matt Terry (lab2lch)
|
||||
|
||||
:license: modified BSD
|
||||
|
||||
@@ -43,18 +50,44 @@ References
|
||||
|
||||
from __future__ import division
|
||||
|
||||
__all__ = ['convert_colorspace', 'rgb2hsv', 'hsv2rgb', 'rgb2xyz', 'xyz2rgb',
|
||||
'rgb2rgbcie', 'rgbcie2rgb', 'rgb2grey', 'rgb2gray', 'gray2rgb',
|
||||
'xyz2lab', 'lab2xyz', 'lab2rgb', 'rgb2lab', 'is_rgb', 'is_gray'
|
||||
]
|
||||
|
||||
__docformat__ = "restructuredtext en"
|
||||
|
||||
import numpy as np
|
||||
from scipy import linalg
|
||||
from ..util import dtype
|
||||
from skimage._shared.utils import deprecated
|
||||
|
||||
|
||||
def guess_spatial_dimensions(image):
|
||||
"""Make an educated guess about whether an image has a channels dimension.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
The input image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
spatial_dims : int or None
|
||||
The number of spatial dimensions of `image`. If ambiguous, the value
|
||||
is `None`.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If the image array has less than two or more than four dimensions.
|
||||
"""
|
||||
if image.ndim == 2:
|
||||
return 2
|
||||
if image.ndim == 3 and image.shape[-1] != 3:
|
||||
return 3
|
||||
if image.ndim == 3 and image.shape[-1] == 3:
|
||||
return None
|
||||
if image.ndim == 4 and image.shape[-1] == 3:
|
||||
return 3
|
||||
else:
|
||||
raise ValueError("Expected 2D, 3D, or 4D array, got %iD." % image.ndim)
|
||||
|
||||
|
||||
@deprecated()
|
||||
def is_rgb(image):
|
||||
"""Test whether the image is RGB or RGBA.
|
||||
|
||||
@@ -67,6 +100,7 @@ def is_rgb(image):
|
||||
return (image.ndim == 3 and image.shape[2] in (3, 4))
|
||||
|
||||
|
||||
@deprecated()
|
||||
def is_gray(image):
|
||||
"""Test whether the image is gray (i.e. has only one color band).
|
||||
|
||||
@@ -76,7 +110,7 @@ def is_gray(image):
|
||||
Input image.
|
||||
|
||||
"""
|
||||
return image.ndim == 2
|
||||
return image.ndim in (2, 3) and not is_rgb(image)
|
||||
|
||||
|
||||
def convert_colorspace(arr, fromspace, tospace):
|
||||
@@ -133,8 +167,9 @@ def _prepare_colorarray(arr):
|
||||
"""
|
||||
arr = np.asanyarray(arr)
|
||||
|
||||
if arr.ndim != 3 or arr.shape[2] != 3:
|
||||
msg = "the input array must be have a shape == (.,.,3))"
|
||||
if arr.ndim not in [3, 4] or arr.shape[-1] != 3:
|
||||
msg = ("the input array must be have a shape == (.., ..,[ ..,] 3)), " +
|
||||
"got (" + (", ".join(map(str, arr.shape))) + ")")
|
||||
raise ValueError(msg)
|
||||
|
||||
return dtype.img_as_float(arr)
|
||||
@@ -312,6 +347,90 @@ gray_from_rgb = np.array([[0.2125, 0.7154, 0.0721],
|
||||
# CIE LAB constants for Observer= 2A, Illuminant= D65
|
||||
lab_ref_white = np.array([0.95047, 1., 1.08883])
|
||||
|
||||
|
||||
# Haematoxylin-Eosin-DAB colorspace
|
||||
# From original Ruifrok's paper: A. C. Ruifrok and D. A. Johnston,
|
||||
# "Quantification of histochemical staining by color deconvolution.,"
|
||||
# Analytical and quantitative cytology and histology / the International
|
||||
# Academy of Cytology [and] American Society of Cytology, vol. 23, no. 4,
|
||||
# pp. 291-9, Aug. 2001.
|
||||
rgb_from_hed = np.array([[0.65, 0.70, 0.29],
|
||||
[0.07, 0.99, 0.11],
|
||||
[0.27, 0.57, 0.78]])
|
||||
hed_from_rgb = linalg.inv(rgb_from_hed)
|
||||
|
||||
# Following matrices are adapted form the Java code written by G.Landini.
|
||||
# The original code is available at:
|
||||
# http://www.dentistry.bham.ac.uk/landinig/software/cdeconv/cdeconv.html
|
||||
|
||||
# Hematoxylin + DAB
|
||||
rgb_from_hdx = np.array([[0.650, 0.704, 0.286],
|
||||
[0.268, 0.570, 0.776],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_hdx[2, :] = np.cross(rgb_from_hdx[0, :], rgb_from_hdx[1, :])
|
||||
hdx_from_rgb = linalg.inv(rgb_from_hdx)
|
||||
|
||||
# Feulgen + Light Green
|
||||
rgb_from_fgx = np.array([[0.46420921, 0.83008335, 0.30827187],
|
||||
[0.94705542, 0.25373821, 0.19650764],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_fgx[2, :] = np.cross(rgb_from_fgx[0, :], rgb_from_fgx[1, :])
|
||||
fgx_from_rgb = linalg.inv(rgb_from_fgx)
|
||||
|
||||
# Giemsa: Methyl Blue + Eosin
|
||||
rgb_from_bex = np.array([[0.834750233, 0.513556283, 0.196330403],
|
||||
[0.092789, 0.954111, 0.283111],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_bex[2, :] = np.cross(rgb_from_bex[0, :], rgb_from_bex[1, :])
|
||||
bex_from_rgb = linalg.inv(rgb_from_bex)
|
||||
|
||||
# FastRed + FastBlue + DAB
|
||||
rgb_from_rbd = np.array([[0.21393921, 0.85112669, 0.47794022],
|
||||
[0.74890292, 0.60624161, 0.26731082],
|
||||
[0.268, 0.570, 0.776]])
|
||||
rbd_from_rgb = linalg.inv(rgb_from_rbd)
|
||||
|
||||
# Methyl Green + DAB
|
||||
rgb_from_gdx = np.array([[0.98003, 0.144316, 0.133146],
|
||||
[0.268, 0.570, 0.776],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_gdx[2, :] = np.cross(rgb_from_gdx[0, :], rgb_from_gdx[1, :])
|
||||
gdx_from_rgb = linalg.inv(rgb_from_gdx)
|
||||
|
||||
# Hematoxylin + AEC
|
||||
rgb_from_hax = np.array([[0.650, 0.704, 0.286],
|
||||
[0.2743, 0.6796, 0.6803],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_hax[2, :] = np.cross(rgb_from_hax[0, :], rgb_from_hax[1, :])
|
||||
hax_from_rgb = linalg.inv(rgb_from_hax)
|
||||
|
||||
# Blue matrix Anilline Blue + Red matrix Azocarmine + Orange matrix Orange-G
|
||||
rgb_from_bro = np.array([[0.853033, 0.508733, 0.112656],
|
||||
[0.09289875, 0.8662008, 0.49098468],
|
||||
[0.10732849, 0.36765403, 0.9237484]])
|
||||
bro_from_rgb = linalg.inv(rgb_from_bro)
|
||||
|
||||
# Methyl Blue + Ponceau Fuchsin
|
||||
rgb_from_bpx = np.array([[0.7995107, 0.5913521, 0.10528667],
|
||||
[0.09997159, 0.73738605, 0.6680326],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_bpx[2, :] = np.cross(rgb_from_bpx[0, :], rgb_from_bpx[1, :])
|
||||
bpx_from_rgb = linalg.inv(rgb_from_bpx)
|
||||
|
||||
# Alcian Blue + Hematoxylin
|
||||
rgb_from_ahx = np.array([[0.874622, 0.457711, 0.158256],
|
||||
[0.552556, 0.7544, 0.353744],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_ahx[2, :] = np.cross(rgb_from_ahx[0, :], rgb_from_ahx[1, :])
|
||||
ahx_from_rgb = linalg.inv(rgb_from_ahx)
|
||||
|
||||
# Hematoxylin + PAS
|
||||
rgb_from_hpx = np.array([[0.644211, 0.716556, 0.266844],
|
||||
[0.175411, 0.972178, 0.154589],
|
||||
[0.0, 0.0, 0.0]])
|
||||
rgb_from_hpx[2, :] = np.cross(rgb_from_hpx[0, :], rgb_from_hpx[1, :])
|
||||
hpx_from_rgb = linalg.inv(rgb_from_hpx)
|
||||
|
||||
#-------------------------------------------------------------
|
||||
# The conversion functions that make use of the matrices above
|
||||
#-------------------------------------------------------------
|
||||
@@ -333,12 +452,12 @@ def _convert(matrix, arr):
|
||||
The converted array.
|
||||
"""
|
||||
arr = _prepare_colorarray(arr)
|
||||
arr = np.swapaxes(arr, 0, 2)
|
||||
arr = np.swapaxes(arr, 0, -1)
|
||||
oldshape = arr.shape
|
||||
arr = np.reshape(arr, (3, -1))
|
||||
out = np.dot(matrix, arr)
|
||||
out.shape = oldshape
|
||||
out = np.swapaxes(out, 2, 0)
|
||||
out = np.swapaxes(out, -1, 0)
|
||||
|
||||
return np.ascontiguousarray(out)
|
||||
|
||||
@@ -393,17 +512,19 @@ def rgb2xyz(rgb):
|
||||
Parameters
|
||||
----------
|
||||
rgb : array_like
|
||||
The image in RGB format, in a 3-D array of shape (.., .., 3).
|
||||
The image in RGB format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in XYZ format, in a 3-D array of shape (.., .., 3).
|
||||
The image in XYZ format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `rgb` is not a 3-D array of shape (.., .., 3).
|
||||
If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3).
|
||||
|
||||
Notes
|
||||
-----
|
||||
@@ -548,23 +669,24 @@ def gray2rgb(image):
|
||||
Parameters
|
||||
----------
|
||||
image : array_like
|
||||
Input image of shape ``(M, N)``.
|
||||
Input image of shape ``(M, N [, P])``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
rgb : ndarray
|
||||
RGB image of shape ``(M, N, 3)``.
|
||||
RGB image of shape ``(M, N, [, P], 3)``.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If the input is not 2-dimensional.
|
||||
If the input is not a 2- or 3-dimensional image.
|
||||
|
||||
"""
|
||||
if is_rgb(image):
|
||||
if np.squeeze(image).ndim == 3 and image.shape[2] in (3, 4):
|
||||
return image
|
||||
elif is_gray(image):
|
||||
return np.dstack((image, image, image))
|
||||
elif image.ndim != 1 and np.squeeze(image).ndim in (1, 2, 3):
|
||||
image = image[..., np.newaxis]
|
||||
return np.concatenate(3 * (image,), axis=-1)
|
||||
else:
|
||||
raise ValueError("Input image expected to be RGB, RGBA or gray.")
|
||||
|
||||
@@ -575,17 +697,19 @@ def xyz2lab(xyz):
|
||||
Parameters
|
||||
----------
|
||||
xyz : array_like
|
||||
The image in XYZ format, in a 3-D array of shape (.., .., 3).
|
||||
The image in XYZ format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in CIE-LAB format, in a 3-D array of shape (.., .., 3).
|
||||
The image in CIE-LAB format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `xyz` is not a 3-D array of shape (.., .., 3).
|
||||
If `xyz` is not a 3-D array of shape (.., ..,[ ..,] 3).
|
||||
|
||||
Notes
|
||||
-----
|
||||
@@ -615,14 +739,14 @@ def xyz2lab(xyz):
|
||||
arr[mask] = np.power(arr[mask], 1. / 3.)
|
||||
arr[~mask] = 7.787 * arr[~mask] + 16. / 116.
|
||||
|
||||
x, y, z = arr[:, :, 0], arr[:, :, 1], arr[:, :, 2]
|
||||
x, y, z = arr[..., 0], arr[..., 1], arr[..., 2]
|
||||
|
||||
# Vector scaling
|
||||
L = (116. * y) - 16.
|
||||
a = 500.0 * (x - y)
|
||||
b = 200.0 * (y - z)
|
||||
|
||||
return np.dstack([L, a, b])
|
||||
return np.concatenate([x[..., np.newaxis] for x in [L, a, b]], axis=-1)
|
||||
|
||||
|
||||
def lab2xyz(lab):
|
||||
@@ -679,17 +803,19 @@ def rgb2lab(rgb):
|
||||
Parameters
|
||||
----------
|
||||
rgb : array_like
|
||||
The image in RGB format, in a 3-D array of shape (.., .., 3).
|
||||
The image in RGB format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in Lab format, in a 3-D array of shape (.., .., 3).
|
||||
The image in Lab format, in a 3- or 4-D array of shape
|
||||
(.., ..,[ ..,] 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `rgb` is not a 3-D array of shape (.., .., 3).
|
||||
If `rgb` is not a 3- or 4-D array of shape (.., ..,[ ..,] 3).
|
||||
|
||||
Notes
|
||||
-----
|
||||
@@ -721,3 +847,291 @@ def lab2rgb(lab):
|
||||
This function uses lab2xyz and xyz2rgb.
|
||||
"""
|
||||
return xyz2rgb(lab2xyz(lab))
|
||||
|
||||
|
||||
def rgb2hed(rgb):
|
||||
"""RGB to Haematoxylin-Eosin-DAB (HED) color space conversion.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rgb : array_like
|
||||
The image in RGB format, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in HED format, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `rgb` is not a 3-D array of shape (.., .., 3).
|
||||
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] A. C. Ruifrok and D. A. Johnston, "Quantification of histochemical
|
||||
staining by color deconvolution.," Analytical and quantitative
|
||||
cytology and histology / the International Academy of Cytology [and]
|
||||
American Society of Cytology, vol. 23, no. 4, pp. 291-9, Aug. 2001.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import rgb2hed
|
||||
>>> ihc = data.immunohistochemistry()
|
||||
>>> ihc_hed = rgb2hed(ihc)
|
||||
"""
|
||||
return separate_stains(rgb, hed_from_rgb)
|
||||
|
||||
|
||||
def hed2rgb(hed):
|
||||
"""Haematoxylin-Eosin-DAB (HED) to RGB color space conversion.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
hed : array_like
|
||||
The image in the HED color space, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in RGB, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `hed` is not a 3-D array of shape (.., .., 3).
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] A. C. Ruifrok and D. A. Johnston, "Quantification of histochemical
|
||||
staining by color deconvolution.," Analytical and quantitative
|
||||
cytology and histology / the International Academy of Cytology [and]
|
||||
American Society of Cytology, vol. 23, no. 4, pp. 291-9, Aug. 2001.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import rgb2hed, hed2rgb
|
||||
>>> ihc = data.immunohistochemistry()
|
||||
>>> ihc_hed = rgb2hed(ihc)
|
||||
>>> ihc_rgb = hed2rgb(ihc_hed)
|
||||
"""
|
||||
return combine_stains(hed, rgb_from_hed)
|
||||
|
||||
|
||||
def separate_stains(rgb, conv_matrix):
|
||||
"""RGB to stain color space conversion.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rgb : array_like
|
||||
The image in RGB format, in a 3-D array of shape (.., .., 3).
|
||||
conv_matrix: ndarray
|
||||
The stain separation matrix as described by G. Landini [1]_.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in stain color space, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `rgb` is not a 3-D array of shape (.., .., 3).
|
||||
|
||||
Notes
|
||||
-----
|
||||
Stain separation matrices available in the ``color`` module and their
|
||||
respective colorspace:
|
||||
|
||||
* ``hed_from_rgb``: Hematoxylin + Eosin + DAB
|
||||
* ``hdx_from_rgb``: Hematoxylin + DAB
|
||||
* ``fgx_from_rgb``: Feulgen + Light Green
|
||||
* ``bex_from_rgb``: Giemsa stain : Methyl Blue + Eosin
|
||||
* ``rbd_from_rgb``: FastRed + FastBlue + DAB
|
||||
* ``gdx_from_rgb``: Methyl Green + DAB
|
||||
* ``hax_from_rgb``: Hematoxylin + AEC
|
||||
* ``bro_from_rgb``: Blue matrix Anilline Blue + Red matrix Azocarmine\
|
||||
+ Orange matrix Orange-G
|
||||
* ``bpx_from_rgb``: Methyl Blue + Ponceau Fuchsin
|
||||
* ``ahx_from_rgb``: Alcian Blue + Hematoxylin
|
||||
* ``hpx_from_rgb``: Hematoxylin + PAS
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.dentistry.bham.ac.uk/landinig/software/cdeconv/cdeconv.html
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import separate_stains, hdx_from_rgb
|
||||
>>> ihc = data.immunohistochemistry()
|
||||
>>> ihc_hdx = separate_stains(ihc, hdx_from_rgb)
|
||||
"""
|
||||
rgb = dtype.img_as_float(rgb) + 2
|
||||
stains = np.dot(np.reshape(-np.log(rgb), (-1, 3)), conv_matrix)
|
||||
return np.reshape(stains, rgb.shape)
|
||||
|
||||
|
||||
def combine_stains(stains, conv_matrix):
|
||||
"""Stain to RGB color space conversion.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
stains : array_like
|
||||
The image in stain color space, in a 3-D array of shape (.., .., 3).
|
||||
conv_matrix: ndarray
|
||||
The stain separation matrix as described by G. Landini [1]_.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in RGB format, in a 3-D array of shape (.., .., 3).
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `stains` is not a 3-D array of shape (.., .., 3).
|
||||
|
||||
Notes
|
||||
-----
|
||||
Stain combination matrices available in the ``color`` module and their
|
||||
respective colorspace:
|
||||
|
||||
* ``rgb_from_hed``: Hematoxylin + Eosin + DAB
|
||||
* ``rgb_from_hdx``: Hematoxylin + DAB
|
||||
* ``rgb_from_fgx``: Feulgen + Light Green
|
||||
* ``rgb_from_bex``: Giemsa stain : Methyl Blue + Eosin
|
||||
* ``rgb_from_rbd``: FastRed + FastBlue + DAB
|
||||
* ``rgb_from_gdx``: Methyl Green + DAB
|
||||
* ``rgb_from_hax``: Hematoxylin + AEC
|
||||
* ``rgb_from_bro``: Blue matrix Anilline Blue + Red matrix Azocarmine\
|
||||
+ Orange matrix Orange-G
|
||||
* ``rgb_from_bpx``: Methyl Blue + Ponceau Fuchsin
|
||||
* ``rgb_from_ahx``: Alcian Blue + Hematoxylin
|
||||
* ``rgb_from_hpx``: Hematoxylin + PAS
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.dentistry.bham.ac.uk/landinig/software/cdeconv/cdeconv.html
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import (separate_stains, combine_stains,
|
||||
... hdx_from_rgb, rgb_from_hdx)
|
||||
>>> ihc = data.immunohistochemistry()
|
||||
>>> ihc_hdx = separate_stains(ihc, hdx_from_rgb)
|
||||
>>> ihc_rgb = combine_stains(ihc_hdx, rgb_from_hdx)
|
||||
"""
|
||||
from ..exposure import rescale_intensity
|
||||
|
||||
stains = dtype.img_as_float(stains)
|
||||
logrgb2 = np.dot(-np.reshape(stains, (-1, 3)), conv_matrix)
|
||||
rgb2 = np.exp(logrgb2)
|
||||
return rescale_intensity(np.reshape(rgb2 - 2, stains.shape), in_range=(-1, 1))
|
||||
|
||||
|
||||
def lab2lch(lab):
|
||||
"""CIE-LAB to CIE-LCH color space conversion.
|
||||
|
||||
LCH is the cylindrical representation of the LAB (Cartesian) colorspace
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lab : array_like
|
||||
The N-D image in CIE-LAB format. The last (`N+1`th) dimension must have
|
||||
at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` color
|
||||
channels. Subsequent elements are copied.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in LCH format, in a N-D array with same shape as input `lab`.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `lch` does not have at least 3 color channels (i.e. l, a, b).
|
||||
|
||||
Notes
|
||||
-----
|
||||
The Hue is expressed as an angle between (0, 2*pi)
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import rgb2lab, lab2lch
|
||||
>>> lena = data.lena()
|
||||
>>> lena_lab = rgb2lab(lena)
|
||||
>>> lena_lch = lab2lch(lena_lab)
|
||||
"""
|
||||
lch = _prepare_lab_array(lab)
|
||||
|
||||
a, b = lch[..., 1], lch[..., 2]
|
||||
lch[..., 1], lch[..., 2] = _cart2polar_2pi(a, b)
|
||||
return lch
|
||||
|
||||
|
||||
def _cart2polar_2pi(x, y):
|
||||
"""convert cartesian coordiantes to polar (uses non-standard theta range!)
|
||||
|
||||
NON-STANDARD RANGE! Maps to (0, 2*pi) rather than usual (-pi, +pi)
|
||||
"""
|
||||
r, t = np.hypot(x, y), np.arctan2(y, x)
|
||||
t += np.where(t < 0., 2 * np.pi, 0)
|
||||
return r, t
|
||||
|
||||
|
||||
def lch2lab(lch):
|
||||
"""CIE-LCH to CIE-LAB color space conversion.
|
||||
|
||||
LCH is the cylindrical representation of the LAB (Cartesian) colorspace
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lch : array_like
|
||||
The N-D image in CIE-LCH format. The last (`N+1`th) dimension must have
|
||||
at least 3 elements, corresponding to the ``L``, ``a``, and ``b`` color
|
||||
channels. Subsequent elements are copied.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
The image in LAB format, with same shape as input `lch`.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If `lch` does not have at least 3 color channels (i.e. l, c, h).
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data
|
||||
>>> from skimage.color import rgb2lab, lch2lab
|
||||
>>> lena = data.lena()
|
||||
>>> lena_lab = rgb2lab(lena)
|
||||
>>> lena_lch = lab2lch(lena_lab)
|
||||
>>> lena_lab2 = lch2lab(lena_lch)
|
||||
"""
|
||||
lch = _prepare_lab_array(lch)
|
||||
|
||||
c, h = lch[..., 1], lch[..., 2]
|
||||
lch[..., 1], lch[..., 2] = c * np.cos(h), c * np.sin(h)
|
||||
return lch
|
||||
|
||||
|
||||
def _prepare_lab_array(arr):
|
||||
"""Ensure input for lab2lch, lch2lab are well-posed.
|
||||
|
||||
Arrays must be in floating point and have at least 3 elements in
|
||||
last dimension. Return a new array.
|
||||
"""
|
||||
arr = np.asarray(arr)
|
||||
shape = arr.shape
|
||||
if shape[-1] < 3:
|
||||
raise ValueError('Input array has less than 3 color channels')
|
||||
return dtype.img_as_float(arr, force_copy=True)
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import warnings
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
|
||||
from skimage import img_as_float
|
||||
from skimage._shared import six
|
||||
from skimage._shared.six.moves import zip
|
||||
from .colorconv import rgb2gray, gray2rgb
|
||||
from . import rgb_colors
|
||||
|
||||
|
||||
__all__ = ['color_dict', 'label2rgb', 'DEFAULT_COLORS']
|
||||
|
||||
|
||||
DEFAULT_COLORS = ('red', 'blue', 'yellow', 'magenta', 'green',
|
||||
'indigo', 'darkorange', 'cyan', 'pink', 'yellowgreen')
|
||||
|
||||
|
||||
color_dict = rgb_colors.__dict__
|
||||
|
||||
|
||||
def _rgb_vector(color):
|
||||
"""Return RGB color as (1, 3) array.
|
||||
|
||||
This RGB array gets multiplied by masked regions of an RGB image, which are
|
||||
partially flattened by masking (i.e. dimensions 2D + RGB -> 1D + RGB).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
color : str or array
|
||||
Color name in `color_dict` or RGB float values between [0, 1].
|
||||
"""
|
||||
if isinstance(color, six.string_types):
|
||||
color = color_dict[color]
|
||||
# Slice to handle RGBA colors.
|
||||
return np.array(color[:3])
|
||||
|
||||
|
||||
def _match_label_with_color(label, colors, bg_label, bg_color):
|
||||
"""Return `unique_labels` and `color_cycle` for label array and color list.
|
||||
|
||||
Colors are cycled for normal labels, but the background color should only
|
||||
be used for the background.
|
||||
"""
|
||||
# Temporarily set background color; it will be removed later.
|
||||
if bg_color is None:
|
||||
bg_color = (0, 0, 0)
|
||||
bg_color = _rgb_vector([bg_color])
|
||||
|
||||
unique_labels = list(set(label.flat))
|
||||
# Ensure that the background label is in front to match call to `chain`.
|
||||
if bg_label in unique_labels:
|
||||
unique_labels.remove(bg_label)
|
||||
unique_labels.insert(0, bg_label)
|
||||
|
||||
# Modify labels and color cycle so background color is used only once.
|
||||
color_cycle = itertools.cycle(colors)
|
||||
color_cycle = itertools.chain(bg_color, color_cycle)
|
||||
|
||||
return unique_labels, color_cycle
|
||||
|
||||
|
||||
def label2rgb(label, image=None, colors=None, alpha=0.3,
|
||||
bg_label=-1, bg_color=None, image_alpha=1):
|
||||
"""Return an RGB image where color-coded labels are painted over the image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
label : array
|
||||
Integer array of labels with the same shape as `image`.
|
||||
image : array
|
||||
Image used as underlay for labels. If the input is an RGB image, it's
|
||||
converted to grayscale before coloring.
|
||||
colors : list
|
||||
List of colors. If the number of labels exceeds the number of colors,
|
||||
then the colors are cycled.
|
||||
alpha : float [0, 1]
|
||||
Opacity of colorized labels. Ignored if image is `None`.
|
||||
bg_label : int
|
||||
Label that's treated as the background.
|
||||
bg_color : str or array
|
||||
Background color. Must be a name in `color_dict` or RGB float values
|
||||
between [0, 1].
|
||||
image_alpha : float [0, 1]
|
||||
Opacity of the image.
|
||||
"""
|
||||
if colors is None:
|
||||
colors = DEFAULT_COLORS
|
||||
colors = [_rgb_vector(c) for c in colors]
|
||||
|
||||
if image is None:
|
||||
image = np.zeros(label.shape + (3,), dtype=np.float64)
|
||||
# Opacity doesn't make sense if no image exists.
|
||||
alpha = 1
|
||||
else:
|
||||
if not image.shape[:2] == label.shape:
|
||||
raise ValueError("`image` and `label` must be the same shape")
|
||||
|
||||
if image.min() < 0:
|
||||
warnings.warn("Negative intensities in `image` are not supported")
|
||||
|
||||
image = img_as_float(rgb2gray(image))
|
||||
image = gray2rgb(image) * image_alpha + (1 - image_alpha)
|
||||
|
||||
# Ensure that all labels are non-negative so we can index into
|
||||
# `label_to_color` correctly.
|
||||
offset = min(label.min(), bg_label)
|
||||
if offset != 0:
|
||||
label = label - offset # Make sure you don't modify the input array.
|
||||
bg_label -= offset
|
||||
|
||||
new_type = np.min_scalar_type(label.max())
|
||||
if new_type == np.bool:
|
||||
new_type = np.uint8
|
||||
label = label.astype(new_type)
|
||||
|
||||
unique_labels, color_cycle = _match_label_with_color(label, colors,
|
||||
bg_label, bg_color)
|
||||
|
||||
if len(unique_labels) == 0:
|
||||
return image
|
||||
|
||||
dense_labels = range(max(unique_labels) + 1)
|
||||
label_to_color = np.array([c for i, c in zip(dense_labels, color_cycle)])
|
||||
|
||||
result = label_to_color[label] * alpha + image * (1 - alpha)
|
||||
|
||||
# Remove background label if its color was not specified.
|
||||
remove_background = bg_label in unique_labels and bg_color is None
|
||||
if remove_background:
|
||||
result[label == bg_label] = image[label == bg_label]
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,339 @@
|
||||
"""
|
||||
Functions for calculating the "distance" between colors.
|
||||
|
||||
Implicit in these definitions of "distance" is the notion of "Just Noticeable
|
||||
Distance" (JND). This represents the distance between colors where a human can
|
||||
perceive different colors. Humans are more sensitive to certain colors than
|
||||
others, which different deltaE metrics correct for with varying degrees of
|
||||
sophistication.
|
||||
|
||||
The literature often mentions 1 as the minimum distance for visual
|
||||
differentiation, but more recent studies (Mahy 1994) peg JND at 2.3
|
||||
|
||||
The delta-E notation comes from the German word for "Sensation" (Empfindung).
|
||||
|
||||
Reference
|
||||
---------
|
||||
http://en.wikipedia.org/wiki/Color_difference
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
import numpy as np
|
||||
|
||||
from skimage.color.colorconv import lab2lch, _cart2polar_2pi
|
||||
|
||||
|
||||
def deltaE_cie76(lab1, lab2):
|
||||
"""Euclidean distance between two points in Lab color space
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lab1 : array_like
|
||||
reference color (Lab colorspace)
|
||||
lab2 : array_like
|
||||
comparison color (Lab colorspace)
|
||||
|
||||
Returns
|
||||
-------
|
||||
dE : array_like
|
||||
distance between colors `lab1` and `lab2`
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://en.wikipedia.org/wiki/Color_difference
|
||||
.. [2] A. R. Robertson, "The CIE 1976 color-difference formulae,"
|
||||
Color Res. Appl. 2, 7-11 (1977).
|
||||
"""
|
||||
lab1 = np.asarray(lab1)
|
||||
lab2 = np.asarray(lab2)
|
||||
L1, a1, b1 = np.rollaxis(lab1, -1)[:3]
|
||||
L2, a2, b2 = np.rollaxis(lab2, -1)[:3]
|
||||
return np.sqrt((L2 - L1) ** 2 + (a2 - a1) ** 2 + (b2 - b1) ** 2)
|
||||
|
||||
|
||||
def deltaE_ciede94(lab1, lab2, kH=1, kC=1, kL=1, k1=0.045, k2=0.015):
|
||||
"""Color difference according to CIEDE 94 standard
|
||||
|
||||
Accommodates perceptual non-uniformities through the use of application
|
||||
specific scale factors (`kH`, `kC`, `kL`, `k1`, and `k2`).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lab1 : array_like
|
||||
reference color (Lab colorspace)
|
||||
lab2 : array_like
|
||||
comparison color (Lab colorspace)
|
||||
kH : float, optional
|
||||
Hue scale
|
||||
kC : float, optional
|
||||
Chroma scale
|
||||
kL : float, optional
|
||||
Lightness scale
|
||||
k1 : float, optional
|
||||
first scale parameter
|
||||
k2 : float, optional
|
||||
second scale parameter
|
||||
|
||||
Returns
|
||||
-------
|
||||
dE : array_like
|
||||
color difference between `lab1` and `lab2`
|
||||
|
||||
Notes
|
||||
-----
|
||||
deltaE_ciede94 is not symmetric with respect to lab1 and lab2. CIEDE94
|
||||
defines the scales for the lightness, hue, and chroma in terms of the first
|
||||
color. Consequently, the first color should be regarded as the "reference"
|
||||
color.
|
||||
|
||||
`kL`, `k1`, `k2` depend on the application and default to the values
|
||||
suggested for graphic arts
|
||||
|
||||
========== ============== ==========
|
||||
Parameter Graphic Arts Textiles
|
||||
========== ============== ==========
|
||||
`kL` 1.000 2.000
|
||||
`k1` 0.045 0.048
|
||||
`k2` 0.015 0.014
|
||||
========== ============== ==========
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://en.wikipedia.org/wiki/Color_difference
|
||||
.. [2] http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
|
||||
"""
|
||||
L1, C1 = np.rollaxis(lab2lch(lab1), -1)[:2]
|
||||
L2, C2 = np.rollaxis(lab2lch(lab2), -1)[:2]
|
||||
|
||||
dL = L1 - L2
|
||||
dC = C1 - C2
|
||||
dH2 = get_dH2(lab1, lab2)
|
||||
|
||||
SL = 1
|
||||
SC = 1 + k1 * C1
|
||||
SH = 1 + k2 * C1
|
||||
|
||||
dE2 = (dL / (kL * SL)) ** 2
|
||||
dE2 += (dC / (kC * SC)) ** 2
|
||||
dE2 += dH2 / (kH * SH) ** 2
|
||||
return np.sqrt(dE2)
|
||||
|
||||
|
||||
def deltaE_ciede2000(lab1, lab2, kL=1, kC=1, kH=1):
|
||||
"""Color difference as given by the CIEDE 2000 standard.
|
||||
|
||||
CIEDE 2000 is a major revision of CIDE94. The perceptual calibration is
|
||||
largely based on experience with automotive paint on smooth surfaces.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lab1 : array_like
|
||||
reference color (Lab colorspace)
|
||||
lab2 : array_like
|
||||
comparison color (Lab colorspace)
|
||||
kL : float (range), optional
|
||||
lightness scale factor, 1 for "acceptably close"; 2 for "imperceptible"
|
||||
see deltaE_cmc
|
||||
kC : float (range), optional
|
||||
chroma scale factor, usually 1
|
||||
kH : float (range), optional
|
||||
hue scale factor, usually 1
|
||||
|
||||
Returns
|
||||
-------
|
||||
deltaE : array_like
|
||||
The distance between `lab1` and `lab2`
|
||||
|
||||
Notes
|
||||
-----
|
||||
CIEDE 2000 assumes parametric weighting factors for the lightness, chroma,
|
||||
and hue (`kL`, `kC`, `kH` respectively). These default to 1.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://en.wikipedia.org/wiki/Color_difference
|
||||
.. [2] http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf
|
||||
(doi:10.1364/AO.33.008069)
|
||||
.. [3] M. Melgosa, J. Quesada, and E. Hita, "Uniformity of some recent
|
||||
color metrics tested with an accurate color-difference tolerance
|
||||
dataset," Appl. Opt. 33, 8069-8077 (1994).
|
||||
"""
|
||||
lab1 = np.asarray(lab1)
|
||||
lab2 = np.asarray(lab2)
|
||||
unroll = False
|
||||
if lab1.ndim == 1 and lab2.ndim == 1:
|
||||
unroll = True
|
||||
if lab1.ndim == 1:
|
||||
lab1 = lab1[None, :]
|
||||
if lab2.ndim == 1:
|
||||
lab2 = lab2[None, :]
|
||||
L1, a1, b1 = np.rollaxis(lab1, -1)[:3]
|
||||
L2, a2, b2 = np.rollaxis(lab2, -1)[:3]
|
||||
|
||||
# distort `a` based on average chroma
|
||||
# then convert to lch coordines from distorted `a`
|
||||
# all subsequence calculations are in the new coordiantes
|
||||
# (often denoted "prime" in the literature)
|
||||
Cbar = 0.5 * (np.hypot(a1, b1) + np.hypot(a2, b2))
|
||||
c7 = Cbar ** 7
|
||||
G = 0.5 * (1 - np.sqrt(c7 / (c7 + 25 ** 7)))
|
||||
scale = 1 + G
|
||||
C1, h1 = _cart2polar_2pi(a1 * scale, b1)
|
||||
C2, h2 = _cart2polar_2pi(a2 * scale, b2)
|
||||
# recall that c, h are polar coordiantes. c==r, h==theta
|
||||
|
||||
# cide2000 has four terms to delta_e:
|
||||
# 1) Luminance term
|
||||
# 2) Hue term
|
||||
# 3) Chroma term
|
||||
# 4) hue Rotation term
|
||||
|
||||
# lightness term
|
||||
Lbar = 0.5 * (L1 + L2)
|
||||
tmp = (Lbar - 50) ** 2
|
||||
SL = 1 + 0.015 * tmp / np.sqrt(20 + tmp)
|
||||
L_term = (L2 - L1) / (kL * SL)
|
||||
|
||||
# chroma term
|
||||
Cbar = 0.5 * (C1 + C2) # new coordiantes
|
||||
SC = 1 + 0.045 * Cbar
|
||||
C_term = (C2 - C1) / (kC * SC)
|
||||
|
||||
# hue term
|
||||
h_diff = h2 - h1
|
||||
h_sum = h1 + h2
|
||||
CC = C1 * C2
|
||||
|
||||
dH = h_diff.copy()
|
||||
dH[h_diff > np.pi] -= 2 * np.pi
|
||||
dH[h_diff < -np.pi] += 2 * np.pi
|
||||
dH[CC == 0.] = 0. # if r == 0, dtheta == 0
|
||||
dH_term = 2 * np.sqrt(CC) * np.sin(dH / 2)
|
||||
|
||||
Hbar = h_sum.copy()
|
||||
mask = np.logical_and(CC != 0., np.abs(h_diff) > np.pi)
|
||||
Hbar[mask * (h_sum < 2 * np.pi)] += 2 * np.pi
|
||||
Hbar[mask * (h_sum >= 2 * np.pi)] -= 2 * np.pi
|
||||
Hbar[CC == 0.] *= 2
|
||||
Hbar *= 0.5
|
||||
|
||||
T = (1 -
|
||||
0.17 * np.cos(Hbar - np.deg2rad(30)) +
|
||||
0.24 * np.cos(2 * Hbar) +
|
||||
0.32 * np.cos(3 * Hbar + np.deg2rad(6)) -
|
||||
0.20 * np.cos(4 * Hbar - np.deg2rad(63))
|
||||
)
|
||||
SH = 1 + 0.015 * Cbar * T
|
||||
|
||||
H_term = dH_term / (kH * SH)
|
||||
|
||||
# hue rotation
|
||||
c7 = Cbar ** 7
|
||||
Rc = 2 * np.sqrt(c7 / (c7 + 25 ** 7))
|
||||
dtheta = np.deg2rad(30) * np.exp(-((np.rad2deg(Hbar) - 275) / 25) ** 2)
|
||||
R_term = -np.sin(2 * dtheta) * Rc * C_term * H_term
|
||||
|
||||
# put it all together
|
||||
dE2 = L_term ** 2
|
||||
dE2 += C_term ** 2
|
||||
dE2 += H_term ** 2
|
||||
dE2 += R_term
|
||||
ans = np.sqrt(dE2)
|
||||
if unroll:
|
||||
ans = ans[0]
|
||||
return ans
|
||||
|
||||
|
||||
def deltaE_cmc(lab1, lab2, kL=1, kC=1):
|
||||
"""Color difference from the CMC l:c standard.
|
||||
|
||||
This color difference was developed by the Colour Measurement Committee
|
||||
(CMC) of the Society of Dyers and Colourists (United Kingdom). It is
|
||||
intended for use in the textile industry.
|
||||
|
||||
The scale factors `kL`, `kC` set the weight given to differences in
|
||||
lightness and chroma relative to differences in hue. The usual values are
|
||||
``kL=2``, ``kC=1`` for "acceptability" and ``kL=1``, ``kC=1`` for
|
||||
"imperceptibility". Colors with ``dE > 1`` are "different" for the given
|
||||
scale factors.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lab1 : array_like
|
||||
reference color (Lab colorspace)
|
||||
lab2 : array_like
|
||||
comparison color (Lab colorspace)
|
||||
|
||||
Returns
|
||||
-------
|
||||
dE : array_like
|
||||
distance between colors `lab1` and `lab2`
|
||||
|
||||
Notes
|
||||
-----
|
||||
deltaE_cmc the defines the scales for the lightness, hue, and chroma
|
||||
in terms of the first color. Consequently
|
||||
``deltaE_cmc(lab1, lab2) != deltaE_cmc(lab2, lab1)``
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://en.wikipedia.org/wiki/Color_difference
|
||||
.. [2] http://www.brucelindbloom.com/index.html?Eqn_DeltaE_CIE94.html
|
||||
.. [3] F. J. J. Clarke, R. McDonald, and B. Rigg, "Modification to the
|
||||
JPC79 colour-difference formula," J. Soc. Dyers Colour. 100, 128-132
|
||||
(1984).
|
||||
"""
|
||||
L1, C1, h1 = np.rollaxis(lab2lch(lab1), -1)[:3]
|
||||
L2, C2, h2 = np.rollaxis(lab2lch(lab2), -1)[:3]
|
||||
|
||||
dC = C1 - C2
|
||||
dL = L1 - L2
|
||||
dH2 = get_dH2(lab1, lab2)
|
||||
|
||||
T = np.where(np.logical_and(np.rad2deg(h1) >= 164, np.rad2deg(h1) <= 345),
|
||||
0.56 + 0.2 * np.abs(np.cos(h1 + np.deg2rad(168))),
|
||||
0.36 + 0.4 * np.abs(np.cos(h1 + np.deg2rad(35)))
|
||||
)
|
||||
c1_4 = C1 ** 4
|
||||
F = np.sqrt(c1_4 / (c1_4 + 1900))
|
||||
|
||||
SL = np.where(L1 < 16, 0.511, 0.040975 * L1 / (1. + 0.01765 * L1))
|
||||
SC = 0.638 + 0.0638 * C1 / (1. + 0.0131 * C1)
|
||||
SH = SC * (F * T + 1 - F)
|
||||
|
||||
dE2 = (dL / (kL * SL)) ** 2
|
||||
dE2 += (dC / (kC * SC)) ** 2
|
||||
dE2 += dH2 / (SH ** 2)
|
||||
return np.sqrt(dE2)
|
||||
|
||||
|
||||
def get_dH2(lab1, lab2):
|
||||
"""squared hue difference term occurring in deltaE_cmc and deltaE_ciede94
|
||||
|
||||
Despite its name, "dH" is not a simple difference of hue values. We avoid
|
||||
working directly with the hue value, since differencing angles is
|
||||
troublesome. The hue term is usually written as:
|
||||
c1 = sqrt(a1**2 + b1**2)
|
||||
c2 = sqrt(a2**2 + b2**2)
|
||||
term = (a1-a2)**2 + (b1-b2)**2 - (c1-c2)**2
|
||||
dH = sqrt(term)
|
||||
|
||||
However, this has poor roundoff properties when a or b is dominant.
|
||||
Instead, ab is a vector with elements a and b. The same dH term can be
|
||||
re-written as:
|
||||
|ab1-ab2|**2 - (|ab1| - |ab2|)**2
|
||||
and then simplified to:
|
||||
2*|ab1|*|ab2| - 2*dot(ab1, ab2)
|
||||
"""
|
||||
lab1 = np.asarray(lab1)
|
||||
lab2 = np.asarray(lab2)
|
||||
a1, b1 = np.rollaxis(lab1, -1)[1:3]
|
||||
a2, b2 = np.rollaxis(lab2, -1)[1:3]
|
||||
|
||||
# magnitude of (a, b) is the chroma
|
||||
C1 = np.hypot(a1, b1)
|
||||
C2 = np.hypot(a2, b2)
|
||||
|
||||
term = (C1 * C2) - (a1 * a2 + b1 * b2)
|
||||
return 2*term
|
||||
@@ -0,0 +1,146 @@
|
||||
aliceblue = (0.941, 0.973, 1)
|
||||
antiquewhite = (0.98, 0.922, 0.843)
|
||||
aqua = (0, 1, 1)
|
||||
aquamarine = (0.498, 1, 0.831)
|
||||
azure = (0.941, 1, 1)
|
||||
beige = (0.961, 0.961, 0.863)
|
||||
bisque = (1, 0.894, 0.769)
|
||||
black = (0, 0, 0)
|
||||
blanchedalmond = (1, 0.922, 0.804)
|
||||
blue = (0, 0, 1)
|
||||
blueviolet = (0.541, 0.169, 0.886)
|
||||
brown = (0.647, 0.165, 0.165)
|
||||
burlywood = (0.871, 0.722, 0.529)
|
||||
cadetblue = (0.373, 0.62, 0.627)
|
||||
chartreuse = (0.498, 1, 0)
|
||||
chocolate = (0.824, 0.412, 0.118)
|
||||
coral = (1, 0.498, 0.314)
|
||||
cornflowerblue = (0.392, 0.584, 0.929)
|
||||
cornsilk = (1, 0.973, 0.863)
|
||||
crimson = (0.863, 0.0784, 0.235)
|
||||
cyan = (0, 1, 1)
|
||||
darkblue = (0, 0, 0.545)
|
||||
darkcyan = (0, 0.545, 0.545)
|
||||
darkgoldenrod = (0.722, 0.525, 0.0431)
|
||||
darkgray = (0.663, 0.663, 0.663)
|
||||
darkgreen = (0, 0.392, 0)
|
||||
darkgrey = (0.663, 0.663, 0.663)
|
||||
darkkhaki = (0.741, 0.718, 0.42)
|
||||
darkmagenta = (0.545, 0, 0.545)
|
||||
darkolivegreen = (0.333, 0.42, 0.184)
|
||||
darkorange = (1, 0.549, 0)
|
||||
darkorchid = (0.6, 0.196, 0.8)
|
||||
darkred = (0.545, 0, 0)
|
||||
darksalmon = (0.914, 0.588, 0.478)
|
||||
darkseagreen = (0.561, 0.737, 0.561)
|
||||
darkslateblue = (0.282, 0.239, 0.545)
|
||||
darkslategray = (0.184, 0.31, 0.31)
|
||||
darkslategrey = (0.184, 0.31, 0.31)
|
||||
darkturquoise = (0, 0.808, 0.82)
|
||||
darkviolet = (0.58, 0, 0.827)
|
||||
deeppink = (1, 0.0784, 0.576)
|
||||
deepskyblue = (0, 0.749, 1)
|
||||
dimgray = (0.412, 0.412, 0.412)
|
||||
dimgrey = (0.412, 0.412, 0.412)
|
||||
dodgerblue = (0.118, 0.565, 1)
|
||||
firebrick = (0.698, 0.133, 0.133)
|
||||
floralwhite = (1, 0.98, 0.941)
|
||||
forestgreen = (0.133, 0.545, 0.133)
|
||||
fuchsia = (1, 0, 1)
|
||||
gainsboro = (0.863, 0.863, 0.863)
|
||||
ghostwhite = (0.973, 0.973, 1)
|
||||
gold = (1, 0.843, 0)
|
||||
goldenrod = (0.855, 0.647, 0.125)
|
||||
gray = (0.502, 0.502, 0.502)
|
||||
green = (0, 0.502, 0)
|
||||
greenyellow = (0.678, 1, 0.184)
|
||||
grey = (0.502, 0.502, 0.502)
|
||||
honeydew = (0.941, 1, 0.941)
|
||||
hotpink = (1, 0.412, 0.706)
|
||||
indianred = (0.804, 0.361, 0.361)
|
||||
indigo = (0.294, 0, 0.51)
|
||||
ivory = (1, 1, 0.941)
|
||||
khaki = (0.941, 0.902, 0.549)
|
||||
lavender = (0.902, 0.902, 0.98)
|
||||
lavenderblush = (1, 0.941, 0.961)
|
||||
lawngreen = (0.486, 0.988, 0)
|
||||
lemonchiffon = (1, 0.98, 0.804)
|
||||
lightblue = (0.678, 0.847, 0.902)
|
||||
lightcoral = (0.941, 0.502, 0.502)
|
||||
lightcyan = (0.878, 1, 1)
|
||||
lightgoldenrodyellow = (0.98, 0.98, 0.824)
|
||||
lightgray = (0.827, 0.827, 0.827)
|
||||
lightgreen = (0.565, 0.933, 0.565)
|
||||
lightgrey = (0.827, 0.827, 0.827)
|
||||
lightpink = (1, 0.714, 0.757)
|
||||
lightsalmon = (1, 0.627, 0.478)
|
||||
lightseagreen = (0.125, 0.698, 0.667)
|
||||
lightskyblue = (0.529, 0.808, 0.98)
|
||||
lightslategray = (0.467, 0.533, 0.6)
|
||||
lightslategrey = (0.467, 0.533, 0.6)
|
||||
lightsteelblue = (0.69, 0.769, 0.871)
|
||||
lightyellow = (1, 1, 0.878)
|
||||
lime = (0, 1, 0)
|
||||
limegreen = (0.196, 0.804, 0.196)
|
||||
linen = (0.98, 0.941, 0.902)
|
||||
magenta = (1, 0, 1)
|
||||
maroon = (0.502, 0, 0)
|
||||
mediumaquamarine = (0.4, 0.804, 0.667)
|
||||
mediumblue = (0, 0, 0.804)
|
||||
mediumorchid = (0.729, 0.333, 0.827)
|
||||
mediumpurple = (0.576, 0.439, 0.859)
|
||||
mediumseagreen = (0.235, 0.702, 0.443)
|
||||
mediumslateblue = (0.482, 0.408, 0.933)
|
||||
mediumspringgreen = (0, 0.98, 0.604)
|
||||
mediumturquoise = (0.282, 0.82, 0.8)
|
||||
mediumvioletred = (0.78, 0.0824, 0.522)
|
||||
midnightblue = (0.098, 0.098, 0.439)
|
||||
mintcream = (0.961, 1, 0.98)
|
||||
mistyrose = (1, 0.894, 0.882)
|
||||
moccasin = (1, 0.894, 0.71)
|
||||
navajowhite = (1, 0.871, 0.678)
|
||||
navy = (0, 0, 0.502)
|
||||
oldlace = (0.992, 0.961, 0.902)
|
||||
olive = (0.502, 0.502, 0)
|
||||
olivedrab = (0.42, 0.557, 0.137)
|
||||
orange = (1, 0.647, 0)
|
||||
orangered = (1, 0.271, 0)
|
||||
orchid = (0.855, 0.439, 0.839)
|
||||
palegoldenrod = (0.933, 0.91, 0.667)
|
||||
palegreen = (0.596, 0.984, 0.596)
|
||||
palevioletred = (0.686, 0.933, 0.933)
|
||||
papayawhip = (1, 0.937, 0.835)
|
||||
peachpuff = (1, 0.855, 0.725)
|
||||
peru = (0.804, 0.522, 0.247)
|
||||
pink = (1, 0.753, 0.796)
|
||||
plum = (0.867, 0.627, 0.867)
|
||||
powderblue = (0.69, 0.878, 0.902)
|
||||
purple = (0.502, 0, 0.502)
|
||||
red = (1, 0, 0)
|
||||
rosybrown = (0.737, 0.561, 0.561)
|
||||
royalblue = (0.255, 0.412, 0.882)
|
||||
saddlebrown = (0.545, 0.271, 0.0745)
|
||||
salmon = (0.98, 0.502, 0.447)
|
||||
sandybrown = (0.98, 0.643, 0.376)
|
||||
seagreen = (0.18, 0.545, 0.341)
|
||||
seashell = (1, 0.961, 0.933)
|
||||
sienna = (0.627, 0.322, 0.176)
|
||||
silver = (0.753, 0.753, 0.753)
|
||||
skyblue = (0.529, 0.808, 0.922)
|
||||
slateblue = (0.416, 0.353, 0.804)
|
||||
slategray = (0.439, 0.502, 0.565)
|
||||
slategrey = (0.439, 0.502, 0.565)
|
||||
snow = (1, 0.98, 0.98)
|
||||
springgreen = (0, 1, 0.498)
|
||||
steelblue = (0.275, 0.51, 0.706)
|
||||
tan = (0.824, 0.706, 0.549)
|
||||
teal = (0, 0.502, 0.502)
|
||||
thistle = (0.847, 0.749, 0.847)
|
||||
tomato = (1, 0.388, 0.278)
|
||||
turquoise = (0.251, 0.878, 0.816)
|
||||
violet = (0.933, 0.51, 0.933)
|
||||
wheat = (0.961, 0.871, 0.702)
|
||||
white = (1, 1, 1)
|
||||
whitesmoke = (0.961, 0.961, 0.961)
|
||||
yellow = (1, 1, 0)
|
||||
yellowgreen = (0.604, 0.804, 0.196)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user