Skip to content

Commit

Permalink
Merge pull request #18 from LaurentRDC/develop
Browse files Browse the repository at this point in the history
Preparation for 0.4.7 release
  • Loading branch information
LaurentRDC committed Jul 21, 2017
2 parents b48108d + cdc2d1b commit 2d07b16
Show file tree
Hide file tree
Showing 23 changed files with 1,142 additions and 1,244 deletions.
6 changes: 1 addition & 5 deletions .appveyor/install-miniconda.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@

$MINICONDA_URL = "https://repo.continuum.io/miniconda/"

$env:ASTROPY_LTS_VERSION = "1.0"
$env:LATEST_ASTROPY_STABLE = "1.3"
$env:LATEST_NUMPY_STABLE = "1.12"

# We pin the version for conda as it's not the most stable package from
# release to release. Add note here if version is pinned due to a bug upstream.
if (! $env:CONDA_VERSION) {
Expand Down Expand Up @@ -110,7 +106,7 @@ if (! $env:CONDA_CHANNEL_PRIORITY) {
# key may not yet exists
conda config --set channel_priority $CONDA_CHANNEL_PRIORITY

# Create a conda environment using the astropy bonus packages
# Create a conda environment
conda create -q -n test python=$env:PYTHON_VERSION
activate test

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ __pycache__/
# Visual studio cache
*.vs/

# Protein DataBank cache folder
# Protein DataBank files and cache folder
*.ent
pdb_cache/

# Jupyter notebooks
Expand Down
8 changes: 3 additions & 5 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ environment:
# to the matrix section.
TEST_CMD: "python -m unittest discover --verbose"

CONDA_DEPENDENCIES: "numpy scipy cython scikit-image"
PIP_DEPENDENCIES: "pywavelets spglib pycifrw"

matrix:

- PYTHON_VERSION: "3.6"
CONDA_DEPENDENCIES: "numpy scipy cython scikit-image"
PIP_DEPENDENCIES: "pywavelets spglib pycifrw"

- PYTHON_VERSION: "3.5"
CONDA_DEPENDENCIES: "numpy scipy cython scikit-image"
PIP_DEPENDENCIES: "pywavelets spglib pycifrw"

matrix:
fast_finish: true
Expand Down
Binary file added docs/source/tutorials/graphite.tif
Binary file not shown.
149 changes: 110 additions & 39 deletions docs/source/tutorials/image.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Contents

* :ref:`streaming`
* :ref:`alignment`
* :ref:`symmetry`
* :ref:`powder`

.. _streaming:
Expand Down Expand Up @@ -211,6 +212,58 @@ Let's look at the difference:
plt.tight_layout()
plt.show()

.. _symmetry:

Image processing involving symmetry
===================================

Rotational symmetry
-------------------
Diffraction patterns exhibit rotational symmetry based on the crystal structure. We can
take advantage of such symmetry to correct images in case of artifacts or defects. A useful
routine is :func:`nfold`, which averages portions of a diffraction pattern with itself based on
rotational symmetry.

.. plot::

import matplotlib.pyplot as plt
from skimage.io import imread
from skued.image import nfold
import numpy as np

center = (1010, 1111)

mask = np.zeros((2048, 2048), dtype = np.bool)
mask[1100::, 442:480] = True # Artifact line
mask[0:1260, 900:1140] = True # beamblock

image = imread('graphite.tif')
av = nfold(image, mod = 6, center = center, mask = mask)

fig , (ax1, ax2, ax3) = plt.subplots(1,3, figsize = (9,3))
ax1.imshow(image, vmin = 0, vmax = 150)
ax2.imshow(mask, vmin = 0, vmax = 1)
ax3.imshow(av, vmin = 0, vmax = 150)

for ax in (ax1, ax2, ax3):
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)

ax1.set_title('Graphite')
ax2.set_title('Mask')
ax3.set_title('Averaged')

plt.tight_layout()
plt.show()

To use :func:`nfold`, all you need to know is the center of the diffraction pattern::

from skued.image import nfold
from skimage.io import imread

im = imread('graphite.tif')
av = nfold(im, mod = 6, center = center) # mask is optional

.. _powder:

Image analysis on polycrystalline diffraction patterns
Expand All @@ -223,17 +276,22 @@ the center of those concentric rings is important. Let's load a test image:

.. plot::

from skimage.io import imread
import matplotlib.pyplot as plt
path = 'Cr_1.tif'
from skimage.io import imread
import matplotlib.pyplot as plt
path = 'Cr_1.tif'

im = imread(path, plugin = 'tifffile')
mask = np.zeros_like(im, dtype = np.bool)
mask[0:1250, 950:1250] = True
im = imread(path, plugin = 'tifffile')
mask = np.zeros_like(im, dtype = np.bool)
mask[0:1250, 950:1250] = True

im[mask] = 0
plt.imshow(im, vmin = 0, vmax = 200)
plt.show()
im[mask] = 0
fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(im, vmin = 0, vmax = 200)

ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
plt.show()

This is a noisy diffraction pattern of polycrystalline vanadium dioxide.
Finding the center of such a symmetry pattern can be done with the
Expand All @@ -249,25 +307,32 @@ Finding the center of such a symmetry pattern can be done with the
rr = np.sqrt((ii - ic)**2 + (jj - jc)**2)
im[rr < 100] = 0

plt.imshow(im, vmax = 1200)
plt.imshow(im, vmax = 200)
plt.show()

.. plot::

from skimage.io import imread
import numpy as np
import matplotlib.pyplot as plt
path = 'Cr_1.tif'
im = imread(path, plugin = 'tifffile')
from skued.image import powder_center
mask = np.zeros_like(im, dtype = np.bool)
mask[0:1250, 950:1250] = True
ic, jc = powder_center(im, mask = mask)
ii, jj = np.meshgrid(np.arange(im.shape[0]), np.arange(im.shape[1]),indexing = 'ij')
rr = np.sqrt((ii - ic)**2 + (jj - jc)**2)
im[rr < 100] = 1e6
plt.imshow(im, vmin = 0, vmax = 200)
plt.show()
from skimage.io import imread
import numpy as np
import matplotlib.pyplot as plt
path = 'Cr_1.tif'
im = imread(path, plugin = 'tifffile')
from skued.image import powder_center
mask = np.zeros_like(im, dtype = np.bool)
mask[0:1250, 950:1250] = True
ic, jc = powder_center(im, mask = mask)
ii, jj = np.meshgrid(np.arange(im.shape[0]), np.arange(im.shape[1]),indexing = 'ij')
rr = np.sqrt((ii - ic)**2 + (jj - jc)**2)
im[rr < 100] = 1e6

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(im, vmin = 0, vmax = 200)

ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)

plt.show()

Angular average
---------------
Expand Down Expand Up @@ -295,21 +360,27 @@ First, we create a test image::

.. plot::

import numpy as np
import matplotlib.pyplot as plt
from skued import gaussian
image = np.zeros( (256, 256) )
xc, yc = image.shape[0]/2, image.shape[1]/2 # center
extent = np.arange(0, image.shape[0])
xx, yy = np.meshgrid(extent, extent)
rr = np.sqrt((xx - xc)**2 + (yy-yc)**2)
image += gaussian([xx, yy], center = [xc, yc], fwhm = 200)
image[np.logical_and(rr < 40, rr > 38)] = 1
image[np.logical_and(rr < 100, rr > 98)] = 0.5
image /= image.max() # Normalize max to 1
image += np.random.random(size = image.shape)
plt.imshow(image)
plt.show()
import numpy as np
import matplotlib.pyplot as plt
from skued import gaussian
image = np.zeros( (256, 256) )
xc, yc = image.shape[0]/2, image.shape[1]/2 # center
extent = np.arange(0, image.shape[0])
xx, yy = np.meshgrid(extent, extent)
rr = np.sqrt((xx - xc)**2 + (yy-yc)**2)
image += gaussian([xx, yy], center = [xc, yc], fwhm = 200)
image[np.logical_and(rr < 40, rr > 38)] = 1
image[np.logical_and(rr < 100, rr > 98)] = 0.5
image /= image.max() # Normalize max to 1
image += np.random.random(size = image.shape)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(image)

ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
plt.show()


... and we can easily compute an angular average::
Expand Down
19 changes: 13 additions & 6 deletions docs/source/tutorials/structure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,15 @@ To create an atom, simply provide its element and coordinates::

copper = Atom(element = 'Cu', coords = [0,0,0])

:class:`Atom` objects are hashable; this means that they can be stored in a :func:`set`. Therefore,
a list of atoms can be reduced into unique atoms using a :func:`set`.
Since we are most concerned with atoms in crystals, the coordinates here are assumed to be fractional.
The real-space position with respect to a :class:`Crystal` or :class:`Lattice` can be accessed using the
:meth:`xyz` method::

from skued.structure import graphite
carbon = list(graphite)[-1]
fractional = carbon.coords
real = carbon.xyz(lattice = graphite)

One important feature of the :class:`Atom` class is the possibility to compute the electrostatic
potential across meshes::
Expand Down Expand Up @@ -66,11 +73,11 @@ After plot formatting:

The :class:`Crystal` Class
==========================
Diffraction experiments relying on the redundancy of crystals to get good experimental signals;
Diffraction experiments rely on the redundancy of crystals to get good experimental signals;
hence, handling crystal models is the main feature of the :mod:`skued.structure` subpackage.

Constructing a :class:`Crystal` object from a file or database
--------------------------------------------------------------
Constructing a :class:`Crystal` object
--------------------------------------
Creating a :class:`Crystal` object can be done most easily from a Crystal Information File (CIF, .cif)::
from skued.structure import Crystal
Expand Down Expand Up @@ -132,7 +139,7 @@ The :class:`Crystal` object provides some interfaces for easy structure manipula
print(atm.element, atm.coords)

Note that iterating over the :attr:`crystal.atoms` attribute may or may not be equivalent to
:data:`iter(crystal)`, due to the way crystals are defined.
:data:`iter(crystal)`, due to the way symmetry operators are defined.

:class:`Crystal` objects also provide interoperability with :mod:`spglib`::

Expand Down
5 changes: 5 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import re
from setuptools import setup, find_packages
from unittest import TestLoader
#from Cython.Build import cythonize

# To upload to pypi.org:
Expand Down Expand Up @@ -33,6 +34,9 @@
if BASE_PACKAGE not in packages:
packages.append(BASE_PACKAGE)

def skued_test_suite():
return TestLoader().discover('.')

if __name__ == '__main__':
setup(
name = 'scikit-ued',
Expand All @@ -52,6 +56,7 @@
data_files = [('skued\\baseline\\data', wavelets)],
include_package_data = True,
zip_safe = False,
test_suite = 'setup.skued_test_suite',
classifiers = ['Environment :: Console',
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering',
Expand Down
1 change: 1 addition & 0 deletions skued/image/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def ialign(images, reference = None, mask = None, fill_value = 0.0):

yield from map(partial(align, reference = reference, mask = mask, fill_value = fill_value), images)

# TODO: handle NaNs by having array sum_of_weights and not counting NaNs
def iaverage(images, weights = None):
"""
Streaming average of diffraction images. This is equivalent
Expand Down
13 changes: 9 additions & 4 deletions skued/image/symmetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
from warnings import warn

# TODO: out parameter?
def nfold(im, mod, center = None, mask = None, **kwargs):
def nfold(im, mod, center = None, mask = None, fill_value = 0.0, **kwargs):
"""
Returns an images averaged according to n-fold rotational symmetry.
Keyword arguments are passed to skimage.transform.rotate()
Parameters
----------
Expand All @@ -27,7 +26,11 @@ def nfold(im, mod, center = None, mask = None, **kwargs):
Mask of `image`. The mask should evaluate to `True`
(or 1) on invalid pixels. If None (default), no mask
is used.
fill_value : float, optional
In the case of a mask that overlaps with itself when rotationally averaged,
the overlapping regions will be filled with this value.
kwargs
Keyword arguments are passed to skimage.transform.rotate().
Returns
-------
Expand Down Expand Up @@ -57,7 +60,9 @@ def nfold(im, mod, center = None, mask = None, **kwargs):

stack = np.dstack([rotate(im, angle, center = center, **rotate_kwargs) for angle in angles])
avg = np.nanmean(stack, axis = 2)
return np.nan_to_num(avg)

avg[np.isnan(avg)] = fill_value
return avg

def nfold_symmetry(*args, **kwargs):
warn('nfold_symmetry() is deprecated. Please use nfold in \
Expand Down

0 comments on commit 2d07b16

Please sign in to comment.