{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Detector distortion corrections\n", "\n", "This tutorial shows how to correct images for spatial distortion. Some tutorial examples rely on files available in http://www.silx.org/pub/pyFAI/testimages/ and will be downloaded during this tutorial. The required minimum version of pyFAI is 0.12.0.\n", "\n", "## Detector definitions\n", "\n", "PyFAI features an impressive list of 64 detector definitions contributed often by manufacturers and some other reverse engineerd by scientists. \n", "Each of them is defined as an invividual class which contains a way to calculate the mask (invalid pixels, gaps,…) and a method to calculate the pixel positions in Cartesian coordinates. \n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pyFAI version: 2024.9.0-dev0\n", "Number of detectors registered: 296 with 108 unique detectors\n", "\n", "List of all supported detectors:\n", "Detector Quantum 210\t PixelSize= 51µm, 51µm\t BottomRight (3)\n", "Detector Quantum 270\t PixelSize= 64.8µm, 64.8µm\t BottomRight (3)\n", "Detector Quantum 315\t PixelSize= 51µm, 51µm\t BottomRight (3)\n", "Detector Quantum 4\t PixelSize= 82µm, 82µm\t BottomRight (3)\n", "Detector Aarhus\t PixelSize= 24.893µm, 24.893µm\t BottomRight (3)\n", "Detector ApexII%s\t PixelSize= 12µm, 12µm\n", "Detector aca1300\t PixelSize= 3.750e-06, 3.750e-06 m\n", "Detector CirPAD\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)\n", "Undefined detector\n", "Detector Dexela 2923%s\t PixelSize= 75µm, 75µm\n", "Detector Eiger 16M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger 1M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 16M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 1M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 1M-W\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 2M-W\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 4M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 500k\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 CdTe 9M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 16M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 1M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 1M-W\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 250k\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 2M-W\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 4M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 500k\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger2 9M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger 4M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger 500k\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Eiger 9M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector FReLoN\t PixelSize= 5µm, 5µm\t BottomRight (3)\n", "Detector Fairchild%s\t PixelSize= 15µm, 15µm\n", "Detector HF-130k\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector HF-1M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector HF-262k\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector HF-2.4M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector HF-4M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector HF-9.4M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Imxpad S10\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)\n", "Detector Imxpad S140\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)\n", "Detector Imxpad S70\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)\n", "Detector Imxpad S70 V\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)\n", "Detector Jungfrau 500k%s\t PixelSize= 75µm, 75µm\n", "Detector Jungfrau 1M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Jungfrau 4M\t PixelSize= 75µm, 75µm\t BottomRight (3)\n", "Detector Jungfrau 8M%s\t PixelSize= 75µm, 75µm\n", "Detector Jungfrau 16M cor%s\t PixelSize= 75µm, 75µm\n", "Detector Lambda 10M\t PixelSize= 55µm, 55µm\n", "Detector Lambda 250k\t PixelSize= 55µm, 55µm\n", "Detector Lambda 2M\t PixelSize= 55µm, 55µm\n", "Detector Lambda 60k\t PixelSize= 55µm, 55µm\n", "Detector Lambda 750k\t PixelSize= 55µm, 55µm\n", "Detector Lambda 7.5M\t PixelSize= 55µm, 55µm\n", "Detector MAR 345\t PixelSize= 1.000e-04, 1.000e-04 m\n", "Detector MAR 555\t PixelSize= 139µm, 139µm\t BottomRight (3)\n", "Detector Maxipix 1x1\t PixelSize= 55µm, 55µm\n", "Detector Maxipix 2x2\t PixelSize= 55µm, 55µm\n", "Detector Maxipix 5x1\t PixelSize= 55µm, 55µm\n", "Detector Mythen 1280\t PixelSize= 8mm, 5µm\t BottomRight (3)\n", "Detector Perkin detector%s\t PixelSize= 2µm, 2µm\n", "Detector Pilatus 100k\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 1M\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 200k\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 2M\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 300k\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 300kw\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus4 1M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Pilatus4 260k\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Pilatus4 260kw\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Pilatus4 2M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Pilatus4 4M\t PixelSize= 15µm, 15µm\t BottomRight (3)\n", "Detector Pilatus4 1M CdTe\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus4 260k CdTe\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus4 260kw CdTe\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus4 2M CdTe\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus4 4M CdTe\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 6M\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus 900k\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus CdTe 1M\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus CdTe 2M\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus CdTe 300k\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus CdTe 300kw\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Detector Pilatus CdTe 900kw\t PixelSize= 172µm, 172µm\t BottomRight (3)\n", "Hexagonal-pixel detector Pixirad-1\t Pitch= 6.000e-05 m\n", "Hexagonal-pixel detector Pixirad-2\t Pitch= 6.000e-05 m\n", "Hexagonal-pixel detector Pixirad-4\t Pitch= 6.000e-05 m\n", "Hexagonal-pixel detector Pixirad-8\t Pitch= 6.000e-05 m\n", "Detector Pixium 4700%s\t PixelSize= 308µm, 308µm\n", "Detector RapidII\t PixelSize= 1µm, 1µm\t BottomRight (3)\n", "Detector Picam v1\t PixelSize= 1.4µm, 1.4µm\t BottomRight (3)\n", "Detector Picam v2\t PixelSize= 1.12µm, 1.12µm\t BottomRight (3)\n", "Detector Rayonix133\t PixelSize= 6.400e-05, 6.400e-05 m\t BottomRight (3)\n", "Detector Rayonix LX170\t PixelSize= 4.427e-05, 4.427e-05 m\t BottomRight (3)\n", "Detector Rayonix LX255\t PixelSize= 4.427e-05, 4.427e-05 m\t BottomRight (3)\n", "Detector Rayonix MX170\t PixelSize= 4.427e-05, 4.427e-05 m\t BottomRight (3)\n", "Detector Rayonix MX225\t PixelSize= 7.324e-05, 7.324e-05 m\t BottomRight (3)\n", "Detector Rayonix MX225HS\t PixelSize= 7.813e-05, 7.813e-05 m\t BottomRight (3)\n", "Detector Rayonix MX300\t PixelSize= 7.324e-05, 7.324e-05 m\t BottomRight (3)\n", "Detector Rayonix MX300HS\t PixelSize= 7.813e-05, 7.813e-05 m\t BottomRight (3)\n", "Detector Rayonix MX325\t PixelSize= 7.935e-05, 7.935e-05 m\t BottomRight (3)\n", "Detector Rayonix MX340HS\t PixelSize= 8.854e-05, 8.854e-05 m\t BottomRight (3)\n", "Detector Rayonix MX425HS\t PixelSize= 4.427e-05, 4.427e-05 m\t BottomRight (3)\n", "Detector Rayonix SX165\t PixelSize= 3.950e-05, 3.950e-05 m\t BottomRight (3)\n", "Detector Rayonix SX200\t PixelSize= 4.800e-05, 4.800e-05 m\t BottomRight (3)\n", "Detector Rayonix SX30HS\t PixelSize= 1.563e-05, 1.563e-05 m\t BottomRight (3)\n", "Detector Rayonix SX85HS\t PixelSize= 4.427e-05, 4.427e-05 m\t BottomRight (3)\n", "Detector Titan 2k x 2k%s\t PixelSize= 6µm, 6µm\n", "Detector Xpad S540 flat\t PixelSize= 1.300e-04, 1.300e-04 m\t BottomRight (3)(3)\n" ] } ], "source": [ "import time, os, numpy\n", "start_time = time.perf_counter()\n", "import pyFAI, pyFAI.detectors\n", "print(f\"pyFAI version: {pyFAI.version}\")\n", "all_detectors = list(set(pyFAI.detectors.ALL_DETECTORS.values()))\n", "#Sort detectors according to their name\n", "all_detectors.sort(key=lambda i:i.__name__)\n", "nb_det = len(all_detectors)\n", "print(\"Number of detectors registered: %i with %i unique detectors\"%(len(pyFAI.detectors.ALL_DETECTORS),nb_det))\n", "print()\n", "print(\"List of all supported detectors:\")\n", "for i in all_detectors:\n", " print(i())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining a detector from a spline file \n", "For optically coupled CCD detectors, the geometrical distortion is often described by a two-dimensional cubic spline (as in FIT2D) which can be imported into the relevant detector instance and used to calculate the actual pixel position in space (and masked pixels).\n", "\n", "At the ESRF, mainly FReLoN detectors [J.-C. Labiche, ESRF Newsletter 25, 41 (1996)] are used with spline files describing the distortion of the fiber optic taper.\n", "\n", "Let's download such a file and create a detector from it. Users at ESRF may declare a proxy to connect to the internet. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/tmp/pyFAI_testdata_edgar1993a/halfccd.spline\n" ] } ], "source": [ "import os\n", "from silx.resources import ExternalResources\n", "downloader = ExternalResources(\"pyFAI\", \"http://www.silx.org/pub/pyFAI/testimages\", \"PYFAI_DATA\")\n", "spline_file = downloader.getfile(\"halfccd.spline\")\n", "print(spline_file)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Detector FReLoN\t Spline= /tmp/pyFAI_testdata_edgar1993a/halfccd.spline\t PixelSize= 48.42252µm, 46.84483µm\t BottomRight (3)\n", "Shape: 1025, 2048\n" ] } ], "source": [ "hd = pyFAI.detectors.FReLoN(splineFile=spline_file)\n", "print(hd)\n", "print(\"Shape: %i, %i\"% hd.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Note:* the unusual shape of this detector. This is probably a human error when calibrating the detector distortion in FIT2D.\n", "\n", "### Visualizing the mask\n", "Every detector object contains a mask attribute, defining pixels which are invalid. \n", "For FReLoN detector (a spline-files-defined detectors), all pixels having an offset such that the pixel falls out of the initial detector are considered as invalid. \n", "\n", "Masked pixel have non-null values can be displayed like this: " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# %matplotlib widget\n", "#For documentation purpose, `inline` is used to enforce the storage of the image in the notebook\n", "%matplotlib inline\n", "from matplotlib.pyplot import subplots\n", "from pyFAI.gui import jupyter" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "image/png": "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AACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwjVYVbEx7l9qEt2/qaQAAgEbSqoLN0f43qlvkgKaeBgAAaCStKtjIESFHKysZAIDWhHd5AABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgGwQbAABgG40SbL788kv97//+rzp27KiYmBj16dNHW7ZssfqNMZoxY4YSExMVExOj1NRU7dq1K+gYFRUVSk9Pl9PpVFxcnCZOnKjDhw83xnQBAIBNhDzYfPPNN7rqqqvUpk0brVq1Sjt27NAf//hHdejQwRoze/ZszZs3T4sWLVJ+fr7atm2rtLQ0HTt2zBqTnp6uoqIi5eTkaOXKldqwYYMmT54c6ukCAAAbcRhjTCgP+MADD+j999/Xu+++22C/MUZJSUm69957dd9990mSfD6f3G63li5dqnHjxmnnzp3q3bu3Nm/erIEDB0qSVq9ereuvv15ffPGFkpKSTjsPv98vl8ulisrFcjpjJUlHKgs1qMu/VHzkzRBVCwAAQstICsjn88npdJ7x3iG/YvO3v/1NAwcO1M9+9jMlJCSoX79++stf/mL179mzR6WlpUpNTbXaXC6XUlJSlJeXJ0nKy8tTXFycFWokKTU1VWFhYcrPz2/wvFVVVfL7/UEbAABoXUIebP71r39p4cKF6t69u95++23deeed+s1vfqNly5ZJkkpLSyVJbrc7aD+32231lZaWKiEhIag/IiJC8fHx1pjjZWdny+VyWVuXLl1CXRoAAGjmQh5sAoGA+vfvryeeeEL9+vXT5MmTNWnSJC1atCjUpwqSlZUln89nbfv27WvU8wEAgOYn5MEmMTFRvXv3Dmrr1auXSkpKJEkej0eSVFZWFjSmrKzM6vN4PCovLw/qr62tVUVFhTXmeFFRUXI6nUEbAABoXUIebK666ioVFxcHtX366afq1q2bJCk5OVkej0e5ublWv9/vV35+vrxeryTJ6/WqsrJSBQUF1pi1a9cqEAgoJSUl1FMGAAA2ERHqA95zzz268sor9cQT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99f3NSQ7O1sul8vaunTpEuJqAABAc9eowSYjI0Pbt2/XSy+91JinkSRlZWXJ5/NZ2759+xr9nAAAoHmJaKwDZ2ZmauXKldqwYYPOO+88q93j8ai6ulqVlZVBV23Kysrk8XisMZs2bQo6Xv2npurHHC8qKkpRUVEhrgIAALQkIb9iY4xRZmamXn/9da1du1bJyclB/QMGDFCbNm2Um5trtRUXF6ukpERer1eS5PV6tW3bNpWXl1tjcnJy5HQ61bt371BPGQAA2ETIr9hkZGRoxYoVevPNN9W+fXvrnhiXy6WYmBi5XC5NnDhRU6dOVXx8vJxOp+666y55vV5dccUVkqShQ4eqd+/euvXWWzV79myVlpbqd7/7nTIyMrgqAwAATirkwWbhwoWSpB//+MdB7c8//7xuv/12SdJTTz2lsLAwjRkzRlVVVUpLS9OCBf//I9jh4eFauXKl7rzzTnm9XrVt21YTJkzQzJkzQz1dAABgI43+HJumwnNsAABoiZr5c2wAAADOFYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwDYINAACwjYimnsC51jvukPpW3CJJCiig3XVbVFVbcUbHCJhaBQKHGmN6AADgB2jWwWb+/PmaM2eOSktLddlll+mZZ57RoEGDftAxR2/uo1G1hyVJxtQquqhIYf7YMzvIwa908J+9pYDjjHb79nBbvb2jj2oCZ36hrPCbSBV/e/iM9wvIqFgf6tuar854X0kKmCrV1n1zVvsCAHCuOYwxpqkn0ZCXX35Zt912mxYtWqSUlBQ9/fTTevXVV1VcXKyEhITT7u/3++VyuVRRuVhO5xkGl2aoutanutozDzYmUKs2u1cpvPLsgo2jbL/8a90y5sxCXL3a6jZau3mgjtS2Oav9JelgVaRyywIK/MBv1c/DvtT+mm0/6Bj1qmu+UcAcCcmxAADfZSQF5PP55HQ6z3jv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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "jupyter.display(hd.mask, label=\"Mask\")\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Detector definition files as NeXus files\n", "\n", "Any detector object in pyFAI can be saved into an HDF5 file following the NeXus convention [Könnecke et al., 2015, J. Appl. Cryst. 48, 301-305.]. Detector objects can subsequently be restored from disk, making complex detector definitions less error prone. " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "pyFAI.detectors._common.NexusDetector" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(new_det)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\u001b[0;31mSignature:\u001b[0m \u001b[0mnew_det\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mDocstring:\u001b[0m\n", "Nice representation of the instance\n", " \n", "\u001b[0;31mSource:\u001b[0m \n", " \u001b[0;32mdef\u001b[0m \u001b[0m__repr__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtxt\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34mf\"{self.name} detector from NeXus file: {self._filename}\\t\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtxt\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34mf\"PixelSize= {to_eng(self._pixel1)}m, {to_eng(self._pixel2)}m\"\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0morientation\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\n", "\u001b[0;34m\u001b[0m \u001b[0mtxt\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34mf\"\\t {self.orientation.name} ({self.orientation.value})\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mFile:\u001b[0m /home/edgar1993a/miniforge3/envs/ewoks/lib/python3.10/site-packages/pyFAI/detectors/_common.py\n", "\u001b[0;31mType:\u001b[0m method" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "new_det.__repr__??" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "__str__ returned non-string (type NoneType)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[16], line 4\u001b[0m\n\u001b[1;32m 2\u001b[0m hd\u001b[38;5;241m.\u001b[39msave(h5_file)\n\u001b[1;32m 3\u001b[0m new_det \u001b[38;5;241m=\u001b[39m pyFAI\u001b[38;5;241m.\u001b[39mdetector_factory(h5_file)\n\u001b[0;32m----> 4\u001b[0m \u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mnew_det\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 5\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mMask is the same: \u001b[39m\u001b[38;5;124m\"\u001b[39m, numpy\u001b[38;5;241m.\u001b[39mallclose(new_det\u001b[38;5;241m.\u001b[39mmask, hd\u001b[38;5;241m.\u001b[39mmask))\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPixel positions are the same: \u001b[39m\u001b[38;5;124m\"\u001b[39m, numpy\u001b[38;5;241m.\u001b[39mallclose(new_det\u001b[38;5;241m.\u001b[39mget_pixel_corners(), hd\u001b[38;5;241m.\u001b[39mget_pixel_corners()))\n", "\u001b[0;31mTypeError\u001b[0m: __str__ returned non-string (type NoneType)" ] } ], "source": [ "h5_file = \"halfccd.h5\"\n", "hd.save(h5_file)\n", "new_det = pyFAI.detector_factory(h5_file)\n", "print(new_det)\n", "print(\"Mask is the same: \", numpy.allclose(new_det.mask, hd.mask))\n", "print(\"Pixel positions are the same: \", numpy.allclose(new_det.get_pixel_corners(), hd.get_pixel_corners()))\n", "print(\"Number of masked pixels\", new_det.mask.sum())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pixels of an area detector are saved as a four-dimensional dataset: i.e. a two-dimensional array of vertices pointing to every corner of each pixel, generating an array of dimension (Ny, Nx, Nc, 3), where Nx and Ny are the dimensions of the detector, Nc is the number of corners of each pixel, usually four, and the last entry contains the coordinates of the vertex itself (in the order: Z, Y, X). \n", "\n", "This kind of definition, while relying on large description files, can address some of the most complex detector layouts. They will be presented a bit later in this tutorial." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Size of Spline-file: 1183\n", "Size of Nexus-file: 108279290\n" ] } ], "source": [ "print(\"Size of Spline-file:\", os.stat(spline_file).st_size)\n", "print(\"Size of Nexus-file:\", os.stat(h5_file).st_size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The HDF5 file is indeed much larger than the spline file. \n", "\n", "## Modify a detector and saving\n", "\n", "One may want to define a new mask (or flat-field) for its detector and save the mask with the detector definition. \n", "Here, we create a copy of the detector and reset its mask to enable all pixels in the detector and save the new detector instance into another file." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "No pixels are masked 0\n" ] } ], "source": [ "import copy\n", "nomask_file = \"nomask.h5\"\n", "nomask = copy.deepcopy(new_det)\n", "nomask.mask = numpy.zeros_like(new_det.mask)\n", "nomask.save(nomask_file)\n", "nomask = pyFAI.detector_factory(\"nomask.h5\")\n", "print(\"No pixels are masked\",nomask.mask.sum())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Wrap up**\n", "\n", "In this section we have seen how detectors are defined in pyFAI, how they can be created, either from the list of the parametrized ones, or from spline files, or from NeXus detector files. \n", "We have also seen how to save and subsequently restore a detector instance, preserving the modifications made." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Distortion correction\n", "\n", "Once the position of every single pixel in space is known, one can benefit from the regridding engine of pyFAI adapted to image distortion correction tasks. \n", "The *pyFAI.distortion.Distortion* class is the equivalent of the *pyFAI.AzimuthalIntegrator* for distortion. \n", "Provided with a detector definition, it enables the correction of a set of images by using the same kind of look-up tables as for azimuthal integration." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "sequence item 1: expected str instance, NoneType found", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[9], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;21;01mpyFAI\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mdistortion\u001b[39;00m \u001b[38;5;28;01mimport\u001b[39;00m Distortion\n\u001b[1;32m 2\u001b[0m dis \u001b[38;5;241m=\u001b[39m Distortion(nomask)\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mdis\u001b[49m\u001b[43m)\u001b[49m\n", "File \u001b[0;32m/home/edgar1993a/miniforge3/envs/ewoks/lib/python3.10/site-packages/pyFAI/distortion.py:144\u001b[0m, in \u001b[0;36mDistortion.__repr__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 143\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m__repr__\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m--> 144\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mos\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlinesep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mjoin\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mDistortion correction \u001b[39;49m\u001b[38;5;132;43;01m%s\u001b[39;49;00m\u001b[38;5;124;43m on device \u001b[39;49m\u001b[38;5;132;43;01m%s\u001b[39;49;00m\u001b[38;5;124;43m for detector shape \u001b[39;49m\u001b[38;5;132;43;01m%s\u001b[39;49;00m\u001b[38;5;124;43m:\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m%\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmethod\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdevice\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_shape_out\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 145\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdetector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;21;43m__repr__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", "\u001b[0;31mTypeError\u001b[0m: sequence item 1: expected str instance, NoneType found" ] } ], "source": [ "from pyFAI.distortion import Distortion\n", "dis = Distortion(nomask)\n", "print(dis)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### FReLoN detector\n", "\n", "First load the image to be corrected, then correct it for geometric distortion.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "halfccd_img = downloader.getfile(\"halfccd.edf\")\n", "import fabio\n", "raw = fabio.open(halfccd_img).data\n", "cor = dis.correct(raw, dummy=raw.min())\n", "\n", "#Then display raw and corrected imagesimages \n", "fig, ax = subplots(2, figsize=(8,8))\n", "\n", "jupyter.display(raw, label=\"Raw Image\", ax=ax[0])\n", "jupyter.display(cor, label=\"Corrected image\", ax=ax[1])\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nota:** in this case the image size (1024 lines) does not match the detector's number of lines (1025) hence pyFAI complains about it. \n", "Here, pyFAI patched the image on an empty image of the right size so that the processing can occur. \n", "\n", "In this example, the size of the pixels and the shape of the detector are preserved, discarding all pixels falling outside the detector's grid. \n", "\n", "One may want all pixels' intensity to be preserved in the transformation. By allowing the output array to be large enough to accomodate all pixels, the total intensity can be kept. For this, just enable the \"resize\" option in the constructor of *Distortion*:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dis1 = Distortion(hd, resize=True)\n", "cor = dis1.correct(raw)\n", "print(dis1)\n", "print(\"After correction, the image has a different shape\", cor.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, ax = subplots(2,figsize=(8,8))\n", "jupyter.display(raw, label=\"Raw Image\", ax=ax[0])\n", "jupyter.display(cor, label=\"Corrected image\", ax=ax[1])\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example of Pixel-detectors: \n", "\n", "#### XPad Flat detector\n", "\n", "There is a striking example in the cover image of this article: http://scripts.iucr.org/cgi-bin/paper?S1600576715004306 where a detector made of multiple modules is *eating up* some rings. \n", "The first example will be about the regeneration of an \"eyes friendly\" version of this image." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "xpad_file = downloader.getfile(\"LaB6_18.57keV_frame_13.edf\")\n", "xpad = pyFAI.detector_factory(\"Xpad_flat\")\n", "print(xpad)\n", "xpad_dis = Distortion(xpad, resize=True)\n", "\n", "raw = fabio.open(xpad_file).data\n", "cor = xpad_dis.correct(raw)\n", "print(\"Shape as input and output:\", raw.shape, cor.shape)\n", "print(\"Conservation of the total intensity:\", raw.sum(dtype=\"float64\"), cor.sum(dtype=\"float64\"))\n", "\n", "#then display images side by side\n", "fig, ax = subplots(1, 2, figsize=(8,8))\n", "jupyter.display(raw, label=\"Raw Image\", ax=ax[0])\n", "jupyter.display(cor, label=\"Corrected image\", ax=ax[1])\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### WOS XPad detector\n", "\n", "This is a new **WAXS opened for SAXS** pixel detector from ImXPad (available at ESRF-BM02/D2AM CRG beamline). \n", "It looks like two of *XPad_flat* detectors side by side with some modules shifted in order to create a hole to accomodate a flight-tube which gathers the SAXS photons to a second detector further away.\n", "\n", "The detector definition for this specific detector has directly been put down using the metrology informations from the manufacturer and saved as a NeXus detector definition file. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wos_det = downloader.getfile(\"WOS.h5\")\n", "wos_img = downloader.getfile(\"WOS.edf\")\n", "wos = pyFAI.detector_factory(wos_det)\n", "print(wos)\n", "wos_dis = Distortion(wos, resize=True)\n", "\n", "raw = fabio.open(wos_img).data\n", "cor = wos_dis.correct(raw)\n", "print(\"Shape as input: %s and output: %s\"%( raw.shape, cor.shape))\n", "print(\"Conservation of the total intensity: %.4e vs %.4e \"%(raw.sum(dtype=\"float64\"), cor.sum(dtype=\"float64\")))\n", "#then display images side by side\n", "fig, ax = subplots(2, figsize=(8,8))\n", "jupyter.display(raw, label=\"Raw Image\", ax=ax[0])\n", "jupyter.display(cor, label=\"Corrected image\", ax=ax[1])\n", "pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Nota:** Do not use this detector definition file to process data from the WOS@D2AM as it has not (yet) been fully validated and may contain some errors in the pixel positioning.\n", "\n", "## Conclusion\n", "\n", "PyFAI provides a very comprehensive list of detector definitions, is versatile enough to address most area detectors on the market, and features a powerful regridding engine, both combined together into the distortion correction tool which ensures the conservation of the signal during the transformation (the number of photons counted is preserved during the transformation)\n", "\n", "Distortion correction should not be used for pre-processing images prior to azimuthal integration as it re-bins the image, thus induces a broadening of the peaks. The AzimuthalIntegrator object performs all this together with integration, it has hence a better precision.\n", "\n", "This tutorial did not answer the question *how to calibrate the distortion of a given detector ?* which is addressed in another tutorial called **detector calibration**." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Total execution time: {time.perf_counter() - start_time:.3f} s\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.14" } }, "nbformat": 4, "nbformat_minor": 4 }