pyFAI/sandbox/check_calib.py

218 lines
7.8 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Project: Azimuthal integration
# https://github.com/silx-kit/pyFAI
#
# Copyright (C) 2015-2018 European Synchrotron Radiation Facility, Grenoble, France
#
# Principal author: Jérôme Kieffer (Jerome.Kieffer@ESRF.eu)
#
# 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.
from __future__ import absolute_import, print_function, with_statement, division
# this is a very simple tool that checks the calibratation
import utilstest
import pyFAI, fabio, numpy, sys, os, optparse, time
import pylab
from pyFAI.third_party import six
def shift(input, shift):
"""
Shift an array like scipy.ndimage.interpolation.shift(input, shift, mode="wrap", order=0) but faster
:param input: 2d numpy array
:param shift: 2-tuple of integers
:return: shifted image
"""
re = numpy.zeros_like(input)
s0, s1 = input.shape
d0 = shift[0] % s0
d1 = shift[1] % s1
r0 = (-d0) % s0
r1 = (-d1) % s1
re[d0:, d1:] = input[:r0, :r1]
re[:d0, d1:] = input[r0:, :r1]
re[d0:, :d1] = input[:r0, r1:]
re[:d0, :d1] = input[r0:, r1:]
return re
def shiftFFT(inp, shift, method="fftw"):
"""
Do shift using FFTs
Shift an array like scipy.ndimage.interpolation.shift(input, shift, mode="wrap", order="infinity") but faster
:param input: 2d numpy array
:param shift: 2-tuple of float
:return: shifted image
"""
d0, d1 = inp.shape
v0, v1 = shift
f0 = numpy.fft.ifftshift(numpy.arange(-d0 // 2, d0 // 2))
f1 = numpy.fft.ifftshift(numpy.arange(-d1 // 2, d1 // 2))
m1, m0 = numpy.meshgrid(f1, f0)
e0 = numpy.exp(-2j * numpy.pi * v0 * m0 / float(d0))
e1 = numpy.exp(-2j * numpy.pi * v1 * m1 / float(d1))
e = e0 * e1
if method.startswith("fftw") and (fftw3 is not None):
input = numpy.zeros((d0, d1), dtype=complex)
output = numpy.zeros((d0, d1), dtype=complex)
with sem:
fft = fftw3.Plan(input, output, direction='forward', flags=['estimate'])
ifft = fftw3.Plan(output, input, direction='backward', flags=['estimate'])
input[:, :] = inp.astype(complex)
fft()
output *= e
ifft()
out = input / input.size
else:
out = numpy.fft.ifft2(numpy.fft.fft2(inp) * e)
return abs(out)
def maximum_position(img):
"""
Same as scipy.ndimage.measurements.maximum_position:
Find the position of the maximum of the values of the array.
:param img: 2-D image
:return: 2-tuple of int with the position of the maximum
"""
maxarg = numpy.argmax(img)
s0, s1 = img.shape
return (maxarg // s1, maxarg % s1)
def center_of_mass(img):
"""
Calculate the center of mass of of the array.
Like scipy.ndimage.measurements.center_of_mass
:param img: 2-D array
:return: 2-tuple of float with the center of mass
"""
d0, d1 = img.shape
a0, a1 = numpy.ogrid[:d0, :d1]
img = img.astype("float64")
img /= img.sum()
return ((a0 * img).sum(), (a1 * img).sum())
def measure_offset(img1, img2, method="numpy", withLog=False, withCorr=False):
"""
Measure the actual offset between 2 images
:param img1: ndarray, first image
:param img2: ndarray, second image, same shape as img1
:param withLog: shall we return logs as well ? boolean
:return: tuple of floats with the offsets
"""
method = str(method)
################################################################################
# Start convolutions
################################################################################
shape = img1.shape
logs = []
assert img2.shape == shape
t0 = time.time()
i1f = numpy.fft.fft2(img1)
i2f = numpy.fft.fft2(img2)
res = numpy.fft.ifft2(i1f * i2f.conjugate()).real
t1 = time.time()
################################################################################
# END of convolutions
################################################################################
offset1 = maximum_position(res)
res = shift(res, (shape[0] // 2 , shape[1] // 2))
mean = res.mean(dtype="float64")
maxi = res.max()
std = res.std(dtype="float64")
SN = (maxi - mean) / std
new = numpy.maximum(numpy.zeros(shape), res - numpy.ones(shape) * (mean + std * SN * 0.9))
com2 = center_of_mass(new)
logs.append("MeasureOffset: fine result of the centered image: %s %s " % com2)
offset2 = ((com2[0] - shape[0] // 2) % shape[0] , (com2[1] - shape[1] // 2) % shape[1])
delta0 = (offset2[0] - offset1[0]) % shape[0]
delta1 = (offset2[1] - offset1[1]) % shape[1]
if delta0 > shape[0] // 2:
delta0 -= shape[0]
if delta1 > shape[1] // 2:
delta1 -= shape[1]
if (abs(delta0) > 2) or (abs(delta1) > 2):
logs.append("MeasureOffset: Raw offset is %s and refined is %s. Please investigate !" % (offset1, offset2))
listOffset = list(offset2)
if listOffset[0] > shape[0] // 2:
listOffset[0] -= shape[0]
if listOffset[1] > shape[1] // 2:
listOffset[1] -= shape[1]
offset = tuple(listOffset)
t2 = time.time()
logs.append("MeasureOffset: fine result: %s %s" % offset)
logs.append("MeasureOffset: execution time: %.3fs with %.3fs for FFTs" % (t2 - t0, t1 - t0))
if withLog:
if withCorr:
return offset, logs, new
else:
return offset, logs
else:
if withCorr:
return offset, new
else:
return offset
class CheckCalib(object):
def __init__(self, poni, img):
self.ponifile = poni
self.ai = pyFAI.load(poni)
self.img = fabio.open(img)
self.r = None
self.I = None
self.resynth = None
self.delta = None
def __repr__(self, *args, **kwargs):
return self.ai.__repr__()
def integrate(self):
self.r, self.I = self.ai.integrate1d(self.img.data, 2048, unit="q_nm^-1")
def rebuild(self):
if self.r is None:
self.integrate()
self.resynth = self.ai.calcfrom1d(self.r, self.I, self.img.data.shape, mask=None,
dim1_unit="q_nm^-1", correctSolidAngle=True)
self.delta = self.resynth - self.img.data
self.offset, log = measure_offset(self.resynth, self.img.data, withLog=1)
print(os.linesep.join(log))
print(self.offset)
if __name__ == "__main__":
cc = CheckCalib(sys.argv[1], sys.argv[2])
cc.integrate()
cc.rebuild()
pylab.ion()
pylab.imshow(cc.delta, aspect="auto", interpolation=None, origin="bottom")
# pylab.show()
six.moves.input("Delta image")
pylab.imshow(cc.img.data, aspect="auto", interpolation=None, origin="bottom")
six.moves.input("raw image")
pylab.imshow(cc.resynth, aspect="auto", interpolation=None, origin="bottom")
six.moves.input("rebuild image")
pylab.clf()
pylab.plot(cc.r, cc.I)
six.moves.input("powder pattern")