commit 91ad25aca931e0d15a3a03a7fb0e73f69cb509c9 Author: Sooyoung Cheong <64125280+c-sooyoung@users.noreply.github.com> Date: Fri Aug 7 15:56:42 2026 +0900 initial commit diff --git a/+astra/ASTRA_GPU_wrapper.m b/+astra/ASTRA_GPU_wrapper.m new file mode 100644 index 0000000..28f896f --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper.m @@ -0,0 +1,48 @@ +%ASTRA_GPU_WRAPPER wrapper around the astra toolkit, it automatically +%recompiles the wrapper if some problems with the MEX file are detected +% +% varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,output_array,varargin) +% +% +% ** direction 'fp' or 'bp' - forward or backward projection operator +% ** input_array either projected volume or backprojected projection array +% ** cfg cfg structed created by ASTRA_initialize +% ** vectors projection geometry created by ASTRA_initialize +% +% optional: +% ** output_array either reconstructed volume or projected array +% ** deformation_fields 3x2 or 3x1 cell array if deformation vector fields for nonrigid deformation tomography +% +% +% returns: +% ++ output resulting reconstruction, if output_array ~= [], result will +% be written to output_array directly to avoid memory allocation + + + +function varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin) + varargout = cell(nargout,1); + assert(ismember(direction, {'fp', 'bp'}), 'Wrong option') + + try + % call mex function + [varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:}); + catch err + warning(err.identifier, 'ASTRA wrapper returned the following error: %s', err.message) + if any(strcmp(err.identifier, { 'MATLAB:UndefinedFunction','MATLAB:mex:ErrInvalidMEXFile'})) + path = replace(mfilename('fullpath'), mfilename, ''); + utils.verbose(0, 'Trying to recompile the MEX function ... ') + + mexcuda('-outdir',fullfile(path, 'private'), ... + fullfile(path, 'ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu'), ... + fullfile(path, 'ASTRA_GPU_wrapper/util3d.cu'), ... + fullfile(path, 'ASTRA_GPU_wrapper/par3d_fp.cu'), ... + fullfile(path, 'ASTRA_GPU_wrapper/par3d_bp.cu')); + + [varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:}); + else + utils.report_GPU_usage + rethrow(err) + end + end +end diff --git a/+astra/ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu b/+astra/ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu new file mode 100644 index 0000000..9c8a468 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu @@ -0,0 +1,353 @@ +/* + +*-----------------------------------------------------------------------* +|                                                                       | +|  Except where otherwise noted, this work is licensed under a          | +|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +|  International (CC BY-NC-SA 4.0) license.                             | +|                                                                       | +|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +|                                                                       | +|      Author: CXS group, PSI  | +*-----------------------------------------------------------------------* +You may use this code with the following provisions: + +If the code is fully or partially redistributed, or rewritten in another + computing language this notice should be included in the redistribution. + +If this code, or subfunctions or parts of it, is used for research in a + publication or if it is fully or partially rewritten for another + computing language the authors and institution should be acknowledged + in written form in the publication: “Data processing was carried out + using the “cSAXS matlab package” developed by the CXS group, + Paul Scherrer Institut, Switzerland.” + Variations on the latter text can be incorporated upon discussion with + the CXS group if needed to more specifically reflect the use of the package + for the published work. + +A publication that focuses on describing features, or parameters, that + are already existing in the code should be first discussed with the + authors. + +This code and subroutines are part of a continuous development, they + are provided “as they are” without guarantees or liability on part + of PSI or the authors. It is the user responsibility to ensure its + proper use and the correctness of the results. + + +*/ + + +// Defines the exported functions for the DLL application. +// +// recompile commands +// (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu +// (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu + +/************* INPUTS *****************************/ +/* + string 'fp' or 'bp' - forward / backward projection + single gpuArray volume or data object + struct cfg - contain configuration for astra, created by ASTRA_initialize.m + double array vec - contain projection geometry for astra, created by ASTRA_initialize.m + (optional) + single gpuArray - volume or data object to write the results to +*/ + + +#include "cuda_runtime.h" +#include "device_launch_parameters.h" +#include + +#include + +#include +#include +#include +#include + +#include "mex.h" +#include "gpu/mxGPUArray.h" + +#include "util3d.h" +#include "dims3d.h" +#include "par3d_bp.h" +#include "par3d_fp.h" + + + +void mexFunction(int nlhs, mxArray *plhs[], + int nrhs, mxArray const *prhs[]) +{ + + //mexPrintf("Warning: loading development version of ASTRA\n"); + //mexPrintf("Ninputs:%i\n", nrhs); + + if (!((nrhs == 4) || (nrhs == 5) || (nrhs == 8 ) || (nrhs == 11 ) )) + mexErrMsgTxt("4,5, 8, or 11 input arguments required"); + + + using namespace astraCUDA3d; + char const * const errId = "parallel:gpu:mexGPUExample:InvalidInput"; + char const * const errMsg = "Invalid input to MEX file."; + + + /* Throw an error if the input is not a GPU array. */ + if (!mxIsGPUArray(prhs[1])) { + mexErrMsgIdAndTxt(errId, "The second input must be GPU array"); + } + + + /* Load configuration */ + SDimensions3D dims; + mxArray * tmp; + double * val; + #define SETVAR(name) do {tmp = mxGetField(prhs[2], 0, ""#name""); if (tmp!=NULL) { val = mxGetPr(tmp); dims.name = (unsigned int)val[0]; }} while (0); + SETVAR(iVolX); + SETVAR(iVolY); + SETVAR(iVolZ); + SETVAR(iProjAngles); + SETVAR(iProjU); + SETVAR(iProjV); + SETVAR(iRaysPerDetDim); + SETVAR(iRaysPerVoxelDim); + #undef SETVAR + + + /* Initialize the MathWorks GPU API. */ + mxInitGPU(); + + + /* load confuguration of angles */ + double * my_angles = mxGetPr(prhs[3]); + int Nangles = (int)mxGetM(prhs[3]); + SPar3DProjection* angle = new SPar3DProjection[Nangles]; + +#define SETVAR(name,i,j) do { angle[i].name = my_angles[i+j*Nangles]; } while (0); + for (int i = 0; i < Nangles; i++) + { + SETVAR(fRayX, i, 0); + SETVAR(fRayY, i, 1); + SETVAR(fRayZ, i, 2); + SETVAR(fDetSX, i, 3); + SETVAR(fDetSY, i, 4); + SETVAR(fDetSZ, i, 5); + SETVAR(fDetUX, i, 6); + SETVAR(fDetUY, i, 7); + SETVAR(fDetUZ, i, 8); + SETVAR(fDetVX, i, 9); + SETVAR(fDetVY, i, 10); + SETVAR(fDetVZ, i, 11); + // mexPrintf("---------------------- \n"); + } +#undef SETVAR + + + char * task = mxArrayToString(prhs[0]); + + //mexPrintf("--------- Task %s \n ", task); + + + /* Load input data */ + mxGPUArray const * m_data = mxGPUCreateFromMxArray(prhs[1]); + if ((mxGPUGetClassID(m_data) != mxSINGLE_CLASS)) { + mexErrMsgIdAndTxt(errId, errMsg); + } + float * p_data = (float *)mxGPUGetDataReadOnly(m_data); + + DeformField DF; + if (nrhs == 8 || nrhs == 11 ) { + /* load deformation field */ + DF.use_deform = true; + DF.use_linear_model = false; // assume contant deformation + DF.X0 = mxGPUCreateFromMxArray(prhs[5]); + DF.Y0 = mxGPUCreateFromMxArray(prhs[6]); + DF.Z0 = mxGPUCreateFromMxArray(prhs[7]); + if ((mxGPUGetClassID(DF.X0) != mxSINGLE_CLASS) | + (mxGPUGetClassID(DF.Y0) != mxSINGLE_CLASS) | + (mxGPUGetClassID(DF.Z0) != mxSINGLE_CLASS)) { + mexPrintf("wrong input type: deformation fields has to be single\n"); + mexErrMsgIdAndTxt(errId, errMsg); + } + if (nrhs == 11 ) { + DF.use_linear_model = true; // assume linear deformation + DF.X1 = mxGPUCreateFromMxArray(prhs[8]); + DF.Y1 = mxGPUCreateFromMxArray(prhs[9]); + DF.Z1 = mxGPUCreateFromMxArray(prhs[10]); + if ((mxGPUGetClassID(DF.X1) != mxSINGLE_CLASS) | + (mxGPUGetClassID(DF.Y1) != mxSINGLE_CLASS) | + (mxGPUGetClassID(DF.Z1) != mxSINGLE_CLASS)) { + mexPrintf("wrong input type: deformation fields has to be single\n"); + mexErrMsgIdAndTxt(errId, errMsg); + } + } + } + else + DF.use_deform = false; + + + + if (strcmp(task, "fp")==0) + { + //mexPrintf(" forward projection \n "); + + /* make volume array (no copying) */ + cudaPitchedPtr volData; + volData.ptr = p_data; + volData.pitch = dims.iVolX * sizeof(float); + volData.xsize = dims.iVolX; + volData.ysize = dims.iVolY; + + + mxGPUArray * m_projData; + if(nrhs >= 5 && !mxIsEmpty(prhs[4]) ) + { + /**** copy of the array is the slow operation and also GPU memory is limited *****/ + // m_projData = mxGPUCopyFromMxArray(prhs[4]); + + /* Use ugly trick to write directly to the provided GPU array ... + => Now it is writting directly into the input field !!! DANGEROUS */ + + m_projData = const_cast(mxGPUCreateFromMxArray(prhs[4])); + if ((mxGPUGetClassID(m_projData) != mxSINGLE_CLASS)) { + mexPrintf("m_projData\n"); + mexErrMsgIdAndTxt(errId, errMsg); + } + const mwSize * projSize = mxGPUGetDimensions(m_projData); + + if (dims.iProjU != projSize[0] || + dims.iProjV != projSize[1] || + dims.iProjAngles != projSize[2]) + mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array"); + + //mexPrintf("Writting directly to the input array\n\n"); + + } + else + { + /* allocate projection field */ + int const Ndim = 3; + mwSize projSize[3]; + projSize[0] = (mwSize)dims.iProjU; + projSize[1] = (mwSize)dims.iProjV; + projSize[2] = (mwSize)dims.iProjAngles; + m_projData = mxGPUCreateGPUArray(Ndim, + projSize, + mxSINGLE_CLASS, + mxREAL, + MX_GPU_INITIALIZE_VALUES); + } + + /* make cudaPitchedPtr for projection field */ + cudaPitchedPtr projData; + projData.ptr = (float *)mxGPUGetData(m_projData); + projData.pitch = dims.iProjU * sizeof(float); + projData.xsize = dims.iProjU; + projData.ysize = dims.iProjV; + + //mexPrintf("astraCUDA3d::Par3DFP \n ") ; + astraCUDA3d::Par3DFP(volData, projData, dims, angle, 1.0f, DF); + checkLastError("After Projector"); + + + /* Wrap the result up as a MATLAB gpuArray for return. */ + if (nlhs > 0) + plhs[0] = mxGPUCreateMxArrayOnGPU(m_projData); + mxGPUDestroyGPUArray(m_projData); + mxGPUDestroyGPUArray(m_data); + + + } + else if (strcmp(task, "bp")==0) + { + //mexPrintf(" backward projection \n "); + + + /* make projection field (no copying) */ + cudaPitchedPtr projData; + projData.ptr = p_data; + projData.pitch = dims.iProjU * sizeof(float); + projData.xsize = dims.iProjU; + projData.ysize = dims.iProjAngles; + mxGPUArray* m_volData; + if(nrhs >= 5 && !mxIsEmpty(prhs[4]) ) + { + /**** copy of the array is the slow operation and also GPU memory is limited *****/ + // m_volData = mxGPUCopyFromMxArray(prhs[4]); + + /* Use ugly trick to write directly to the provided GPU array ... + => Now it is writting directly into the input field !!! DANGEROUS */ + + m_volData = const_cast(mxGPUCreateFromMxArray(prhs[4])); + if ((mxGPUGetClassID(m_volData) != mxSINGLE_CLASS)) { + mexPrintf("m_volData\n"); + mexErrMsgIdAndTxt(errId, errMsg); + } + mwSize volSize[3]; + const mwSize * volSize0 = mxGPUGetDimensions(m_volData); + if (mxGPUGetNumberOfDimensions(m_volData)==3) { + volSize[0]=volSize0[0]; + volSize[1]=volSize0[1]; + volSize[2]=volSize0[2]; + } else { + volSize[0]=volSize0[0]; + volSize[1]=volSize0[1]; + volSize[2]=1; + } + + + if (dims.iVolX != volSize[0] || + dims.iVolY != volSize[1] || + dims.iVolZ != volSize[2]) + mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array"); + + } else { + /* allocate volume data */ + int const Ndim = 3; + mwSize volSize[3]; + volSize[0] = (mwSize)dims.iVolX; + volSize[1] = (mwSize)dims.iVolY; + volSize[2] = (mwSize)dims.iVolZ; + m_volData = mxGPUCreateGPUArray(Ndim, + volSize, + mxSINGLE_CLASS, + mxREAL, + MX_GPU_INITIALIZE_VALUES); + } + + /* make volume array pointer*/ + cudaPitchedPtr volData; + volData.ptr = (float *)mxGPUGetData(m_volData); + volData.pitch = dims.iVolX * sizeof(float); + volData.xsize = dims.iVolX; + volData.ysize = dims.iVolY; + + astraCUDA3d::Par3DBP(volData, projData, dims, angle, 1.0f, DF); + checkLastError("After Projector"); + + + /* Wrap the result up as a MATLAB gpuArray for return. */ + if (nlhs > 0) + plhs[0] = mxGPUCreateMxArrayOnGPU(m_volData); + mxGPUDestroyGPUArray(m_volData); + mxGPUDestroyGPUArray(m_data); + + } + else + mexPrintf("No such option"); + + if (DF.use_deform) { + //mexPrintf("Deleted DF"); + mxGPUDestroyGPUArray(DF.X0); + mxGPUDestroyGPUArray(DF.Y0); + mxGPUDestroyGPUArray(DF.Z0); + if (DF.use_linear_model) { + mxGPUDestroyGPUArray(DF.X1); + mxGPUDestroyGPUArray(DF.Y1); + mxGPUDestroyGPUArray(DF.Z1); + } + } + + +} + diff --git a/+astra/ASTRA_GPU_wrapper/astra/GeometryUtil3D.h b/+astra/ASTRA_GPU_wrapper/astra/GeometryUtil3D.h new file mode 100644 index 0000000..1c2a2f9 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/astra/GeometryUtil3D.h @@ -0,0 +1,143 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _INC_ASTRA_GEOMETRYUTIL3D +#define _INC_ASTRA_GEOMETRYUTIL3D + +namespace astra { + +struct SConeProjection { + // the source + double fSrcX, fSrcY, fSrcZ; + + // the origin ("bottom left") of the (flat-panel) detector + double fDetSX, fDetSY, fDetSZ; + + // the U-edge of a detector pixel + double fDetUX, fDetUY, fDetUZ; + + // the V-edge of a detector pixel + double fDetVX, fDetVY, fDetVZ; + + + + + void translate(double dx, double dy, double dz) { + fSrcX += dx; + fSrcY += dy; + fSrcZ += dz; + fDetSX += dx; + fDetSY += dy; + fDetSZ += dz; + + } + void scale(double factor) { + fSrcX *= factor; + fSrcY *= factor; + fSrcZ *= factor; + fDetSX *= factor; + fDetSY *= factor; + fDetSZ *= factor; + fDetUX *= factor; + fDetUY *= factor; + fDetUZ *= factor; + fDetVX *= factor; + fDetVY *= factor; + fDetVZ *= factor; + } +}; + +struct SPar3DProjection { + // the ray direction + double fRayX, fRayY, fRayZ; + + // the origin ("bottom left") of the (flat-panel) detector + double fDetSX, fDetSY, fDetSZ; + + // the U-edge of a detector pixel + double fDetUX, fDetUY, fDetUZ; + + // the V-edge of a detector pixel + double fDetVX, fDetVY, fDetVZ; + + + + + void translate(double dx, double dy, double dz) { + fDetSX += dx; + fDetSY += dy; + fDetSZ += dz; + } + void scale(double factor) { + fRayX *= factor; + fRayY *= factor; + fRayZ *= factor; + fDetSX *= factor; + fDetSY *= factor; + fDetSZ *= factor; + fDetUX *= factor; + fDetUY *= factor; + fDetUZ *= factor; + fDetVX *= factor; + fDetVY *= factor; + fDetVZ *= factor; + } +}; + +void computeBP_UV_Coeffs(const SPar3DProjection& proj, + double &fUX, double &fUY, double &fUZ, double &fUC, + double &fVX, double &fVY, double &fVZ, double &fVC); + +void computeBP_UV_Coeffs(const SConeProjection& proj, + double &fUX, double &fUY, double &fUZ, double &fUC, + double &fVX, double &fVY, double &fVZ, double &fVC, + double &fDX, double &fDY, double &fDZ, double &fDC); + + +SConeProjection* genConeProjections(unsigned int iProjAngles, + unsigned int iProjU, + unsigned int iProjV, + double fOriginSourceDistance, + double fOriginDetectorDistance, + double fDetUSize, + double fDetVSize, + const float *pfAngles); + +SPar3DProjection* genPar3DProjections(unsigned int iProjAngles, + unsigned int iProjU, + unsigned int iProjV, + double fDetUSize, + double fDetVSize, + const float *pfAngles); + + + + +} + +#endif diff --git a/+astra/ASTRA_GPU_wrapper/astra/Globals.h b/+astra/ASTRA_GPU_wrapper/astra/Globals.h new file mode 100644 index 0000000..5858f17 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/astra/Globals.h @@ -0,0 +1,318 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _INC_ASTRA_GLOBALS +#define _INC_ASTRA_GLOBALS + +/*! \mainpage The ASTRA-toolbox + * + * + */ + + +//---------------------------------------------------------------------------------------- + +#ifdef _MSC_VER + +// disable warning: 'fopen' was declared deprecated +#pragma warning (disable : 4996) +// disable warning: C++ exception handler used, but unwind semantics are not enables +#pragma warning (disable : 4530) +// disable warning: no suitable definition provided for explicit template instantiation request +#pragma warning (disable : 4661) + +#endif + +//---------------------------------------------------------------------------------------- +// standard includes +#include +#include +#include +#include +//#include +//#include + +//---------------------------------------------------------------------------------------- +// macro's + +#define ASTRA_TOOLBOXVERSION_MAJOR 1 +#define ASTRA_TOOLBOXVERSION_MINOR 7 +#define ASTRA_TOOLBOXVERSION ((ASTRA_TOOLBOXVERSION_MAJOR)*100 + (ASTRA_TOOLBOXVERSION_MINOR)) +#define ASTRA_TOOLBOXVERSION_STRING "1.7.1" + + +#define ASTRA_ASSERT(a) assert(a) + +#define ASTRA_CONFIG_CHECK(value, type, msg) if (!(value)) { cout << "Configuration Error in " << type << ": " << msg << endl; return false; } + +#define ASTRA_CONFIG_WARNING(type, msg) { cout << "Warning in " << type << ": " << msg << endl; } + + +#define ASTRA_DELETE(a) if (a) { delete a; a = NULL; } +#define ASTRA_DELETE_ARRAY(a) if (a) { delete[] a; a = NULL; } + +#ifdef _MSC_VER + +#ifdef DLL_EXPORTS +#define _AstraExport __declspec(dllexport) +#define EXPIMP_TEMPLATE +#else +#define _AstraExport __declspec(dllimport) +#define EXPIMP_TEMPLATE extern +#endif + +#else + +#define _AstraExport + +#endif + + +//---------------------------------------------------------------------------------------- +// typedefs +namespace astra { + typedef float float32; + typedef double float64; + typedef unsigned short int uint16; + typedef signed short int sint16; + typedef unsigned char uchar8; + typedef signed char schar8; + + typedef int int32; + typedef short int int16; +} + +//---------------------------------------------------------------------------------------- +// globals vars & functions +//namespace astra { +//#define ToolboxVersion 0.1f; + +//float32 getVersion() { return ToolboxVersion; } + +//_AstraExport bool cudaEnabled() { +//#ifdef ASTRA_CUDA +// return true; +//#else +// return false; +//#endif +//} +//} + +//---------------------------------------------------------------------------------------- +// errors +namespace astra { + +typedef enum {ASTRA_SUCCESS, + ASTRA_ERROR_NOT_INITIALIZED, + ASTRA_ERROR_INVALID_FILE, + ASTRA_ERROR_OUT_OF_RANGE, + ASTRA_ERROR_DIMENSION_MISMATCH, + ASTRA_ERROR_EXTERNAL_LIBRARY, + ASTRA_ERROR_ALLOCATION, + ASTRA_ERROR_NOT_IMPLEMENTED} AstraError; +} + + +//---------------------------------------------------------------------------------------- +// variables +namespace astra { + const float32 PI = 3.14159265358979323846264338328f; + const float32 PI32 = 3.14159265358979323846264338328f; + const float32 PIdiv2 = PI / 2; + const float32 PIdiv4 = PI / 4; + const float32 eps = 1e-7f; + + extern _AstraExport bool running_in_matlab; +} + +//---------------------------------------------------------------------------------------- +// math +namespace astra { + + inline float32 cos_73s(float32 x) + { + /* + const float32 c1 = 0.999999953464f; + const float32 c2 = -0.4999999053455f; + const float32 c3 = 0.0416635846769f; + const float32 c4 = -0.0013853704264f; + const float32 c5 = 0.000023233f; + */ + const float c1= (float)0.99940307; + const float c2= (float)-0.49558072; + const float c3= (float)0.03679168; + + float32 x2; + x2 = x * x; + //return (c1 + x2*(c2 + x2*(c3 + x2*(c4 + c5*x2)))); + return (c1 + x2*(c2 + c3 * x2)); + } + + inline float32 fast_cos(float32 x) + { + int quad; + + //x = fmod(x, 2*PI); // Get rid of values > 2* pi + if (x < 0) x = -x; // cos(-x) = cos(x) + quad = int(x/PIdiv2); // Get quadrant # (0 to 3) + switch (quad) { + case 0: return cos_73s(x); + case 1: return -cos_73s(PI-x); + case 2: return -cos_73s(x-PI); + case 3: return cos_73s(2*PI-x); + } + return 0.0f; + } + + inline float32 fast_sin(float32 x){ + return fast_cos(PIdiv2-x); + } + +} + +//---------------------------------------------------------------------------------------- +// structs +namespace astra { + /** + * Struct for storing pixel weigths + **/ + struct SPixelWeight + { + int m_iIndex; + float32 m_fWeight; + }; + + /** + * Struct combining some properties of a detector in 1D detector row + **/ + struct SDetector2D + { + int m_iIndex; + int m_iAngleIndex; + int m_iDetectorIndex; + }; + + /** + * Struct combining some properties of a detector in 2D detector array + **/ + struct SDetector3D + { + int m_iIndex; + int m_iAngleIndex; + int m_iDetectorIndex; + int m_iSliceIndex; + }; +} +//---------------------------------------------------------------------------------------- +// some toys + +// safe reinterpret cast +// template +// To safe_reinterpret_cast(From from) +// { +// BOOST_STATIC_ASSERT(sizeof(From) <= sizeof(To)); +// return reinterpret_cast(from); +// } + +//---------------------------------------------------------------------------------------- +// functions for testing +template +inline void writeArray(T*** arr, int dim1, int dim2, int dim3, const std::string& filename) +{ + std::ofstream out(filename.c_str()); + int i1, i2, i3; + for (i1 = 0; i1 < dim1; ++i1) { + for (i2 = 0; i2 < dim2; ++i2) { + for (i3 = 0; i3 < dim3; ++i3) { + out << arr[i1][i2][i3] << " "; + } + out << std::endl; + } + out << std::endl; + } + out.close(); +} + +template +inline void writeArray(T** arr, int dim1, int dim2, const std::string& filename) +{ + std::ofstream out(filename.c_str()); + for (int i1 = 0; i1 < dim1; i1++) { + for (int i2 = 0; i2 < dim2; i2++) { + out << arr[i1][i2] << " "; + } + out << std::endl; + } + out.close(); +} + +template +inline void writeArray(T* arr, int dim1, const std::string& filename) +{ + std::ofstream out(filename.c_str()); + for (int i1 = 0; i1 < dim1; i1++) { + out << arr[i1] << " "; + } + out.close(); +} +namespace astra { +_AstraExport inline int getVersion() { return ASTRA_TOOLBOXVERSION; } +_AstraExport inline const char* getVersionString() { return ASTRA_TOOLBOXVERSION_STRING; } +#ifdef ASTRA_CUDA +_AstraExport inline bool cudaEnabled() { return true; } +#else +_AstraExport inline bool cudaEnabled() { return false; } +#endif +} +//---------------------------------------------------------------------------------------- +// portability between MSVC and Linux/gcc + +#ifndef _MSC_VER +// #include "swrap.h" +#define EXPIMP_TEMPLATE + +#if !defined(FORCEINLINE) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define FORCEINLINE inline __attribute__((__always_inline__)) +#else +#define FORCEINLINE inline +#endif + +#else + +#define FORCEINLINE __forceinline + +#endif + +//---------------------------------------------------------------------------------------- +// use pthreads on Linux and OSX +#if defined(__linux__) || defined(__MACH__) +#define USE_PTHREADS +#endif + + +#endif diff --git a/+astra/ASTRA_GPU_wrapper/astra/Logging.cpp b/+astra/ASTRA_GPU_wrapper/astra/Logging.cpp new file mode 100644 index 0000000..7f78c92 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/astra/Logging.cpp @@ -0,0 +1,212 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#define CLOG_MAIN +#include "clog.h" + +#include "Logging.h" + +#include + +using namespace astra; + +void CLogger::enableScreen() +{ + m_bEnabledScreen = true; +} + +void CLogger::enableFile() +{ + m_bEnabledFile = true; +} + +void CLogger::enable() +{ + enableScreen(); + enableFile(); +} + +void CLogger::disableScreen() +{ + m_bEnabledScreen = false; +} + +void CLogger::disableFile() +{ + m_bEnabledFile = false; +} + +void CLogger::disable() +{ + disableScreen(); + disableFile(); +} + +void CLogger::debug(const char *sfile, int sline, const char *fmt, ...) +{ + _assureIsInitialized(); + va_list ap, apf; + if(m_bEnabledScreen){ + va_start(ap, fmt); + clog_debug(sfile,sline,0,fmt,ap); + va_end(ap); + } + if(m_bEnabledFile && m_bFileProvided){ + va_start(apf, fmt); + clog_debug(sfile,sline,1,fmt,apf); + va_end(apf); + } +} + +void CLogger::info(const char *sfile, int sline, const char *fmt, ...) +{ + _assureIsInitialized(); + va_list ap, apf; + if(m_bEnabledScreen){ + va_start(ap, fmt); + clog_info(sfile,sline,0,fmt,ap); + va_end(ap); + } + if(m_bEnabledFile && m_bFileProvided){ + va_start(apf, fmt); + clog_info(sfile,sline,1,fmt,apf); + va_end(apf); + } +} + +void CLogger::warn(const char *sfile, int sline, const char *fmt, ...) +{ + _assureIsInitialized(); + va_list ap, apf; + if(m_bEnabledScreen){ + va_start(ap, fmt); + clog_warn(sfile,sline,0,fmt,ap); + va_end(ap); + } + if(m_bEnabledFile && m_bFileProvided){ + va_start(apf, fmt); + clog_warn(sfile,sline,1,fmt,apf); + va_end(apf); + } +} + +void CLogger::error(const char *sfile, int sline, const char *fmt, ...) +{ + _assureIsInitialized(); + va_list ap, apf; + if(m_bEnabledScreen){ + va_start(ap, fmt); + clog_error(sfile,sline,0,fmt,ap); + va_end(ap); + } + if(m_bEnabledFile && m_bFileProvided){ + va_start(apf, fmt); + clog_error(sfile,sline,1,fmt,apf); + va_end(apf); + } +} + +void CLogger::_setLevel(int id, log_level m_eLevel) +{ + switch(m_eLevel){ + case LOG_DEBUG: + clog_set_level(id,CLOG_DEBUG); + break; + case LOG_INFO: + clog_set_level(id,CLOG_INFO); + break; + case LOG_WARN: + clog_set_level(id,CLOG_WARN); + break; + case LOG_ERROR: + clog_set_level(id,CLOG_ERROR); + break; + } +} + +void CLogger::setOutputScreen(int fd, log_level m_eLevel) +{ + _assureIsInitialized(); + if(fd==1||fd==2){ + clog_set_fd(0, fd); + }else{ + error(__FILE__,__LINE__,"Invalid file descriptor"); + } + _setLevel(0,m_eLevel); +} + +void CLogger::setOutputFile(const char *filename, log_level m_eLevel) +{ + if(m_bFileProvided){ + clog_free(1); + m_bFileProvided=false; + } + if(!clog_init_path(1,filename)){ + m_bFileProvided=true; + _setLevel(1,m_eLevel); + } +} + +void CLogger::_assureIsInitialized() +{ + if(!m_bInitialized) + { + clog_init_fd(0, 2); + clog_set_level(0, CLOG_INFO); + clog_set_fmt(0, "%l: %m\n"); + m_bInitialized = true; + } +} + +void CLogger::setFormatFile(const char *fmt) +{ + if(m_bFileProvided){ + clog_set_fmt(1,fmt); + }else{ + error(__FILE__,__LINE__,"No log file specified"); + } +} +void CLogger::setFormatScreen(const char *fmt) +{ + clog_set_fmt(0,fmt); +} + +CLogger::CLogger() +{ + ; +} + +bool CLogger::setCallbackScreen(void (*cb)(const char *msg, size_t len)){ + _assureIsInitialized(); + return clog_set_cb(0,cb)==0; +} + +bool CLogger::m_bEnabledScreen = true; +bool CLogger::m_bEnabledFile = true; +bool CLogger::m_bFileProvided = false; +bool CLogger::m_bInitialized = false; diff --git a/+astra/ASTRA_GPU_wrapper/astra/Logging.h b/+astra/ASTRA_GPU_wrapper/astra/Logging.h new file mode 100644 index 0000000..41d9c32 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/astra/Logging.h @@ -0,0 +1,164 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _INC_ASTRA_LOGGING +#define _INC_ASTRA_LOGGING + +#include "Globals.h" + +//#define ASTRA_DEBUG(...) astra::CLogger::debug(__FILE__,__LINE__, __VA_ARGS__) +//#define ASTRA_INFO(...) astra::CLogger::info(__FILE__,__LINE__, __VA_ARGS__) +//#define ASTRA_WARN(...) astra::CLogger::warn(__FILE__,__LINE__, __VA_ARGS__) +//#define ASTRA_ERROR(...) astra::CLogger::error(__FILE__,__LINE__, __VA_ARGS__) + +// FIXME !!!!!! +#define ASTRA_DEBUG(...) +#define ASTRA_INFO(...) +#define ASTRA_WARN(...) +#define ASTRA_ERROR(...) + +namespace astra +{ + +enum log_level { + LOG_DEBUG, + LOG_INFO, + LOG_WARN, + LOG_ERROR +}; + +class _AstraExport CLogger +{ + CLogger(); + ~CLogger(); + static bool m_bEnabledFile; + static bool m_bEnabledScreen; + static bool m_bFileProvided; + static bool m_bInitialized; + static void _assureIsInitialized(); + static void _setLevel(int id, log_level m_eLevel); + +public: + + /** + * Writes a line to the log file (newline is added). Ignored if logging is turned off. + * + * @param sfile + * The name of the source file making this log call (e.g. __FILE__). + * + * @param sline + * The line number of the call in the source code (e.g. __LINE__). + * + * @param id + * The id of the logger to write to. + * + * @param fmt + * The format string for the message (printf formatting). + * + * @param ... + * Any additional format arguments. + */ + static void debug(const char *sfile, int sline, const char *fmt, ...); + static void info(const char *sfile, int sline, const char *fmt, ...); + static void warn(const char *sfile, int sline, const char *fmt, ...); + static void error(const char *sfile, int sline, const char *fmt, ...); + + /** + * Sets the file to log to, with logging level. + * + * @param filename + * File to log to. + * + * @param m_eLevel + * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). + * + */ + static void setOutputFile(const char *filename, log_level m_eLevel); + + /** + * Sets the screen to log to, with logging level. + * + * @param screen_fd + * Screen file descriptor (1 for stdout, 2 for stderr) + * + * @param m_eLevel + * Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR). + * + */ + static void setOutputScreen(int fd, log_level m_eLevel); + + /** + * Set the format string for log messages. Here are the substitutions you may + * use: + * + * %f: Source file name generating the log call. + * %n: Source line number where the log call was made. + * %m: The message text sent to the logger (after printf formatting). + * %d: The current date, formatted using the logger's date format. + * %t: The current time, formatted using the logger's time format. + * %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). + * %%: A literal percent sign. + * + * The default format string is "%d %t %f(%n): %l: %m\n". + * + * @param fmt + * The new format string, which must be less than 256 bytes. + * You probably will want to end this with a newline (\n). + * + */ + static void setFormatFile(const char *fmt); + static void setFormatScreen(const char *fmt); + + + /** + * Enable logging. + * + */ + static void enable(); + static void enableScreen(); + static void enableFile(); + + /** + * Disable logging. + * + */ + static void disable(); + static void disableScreen(); + static void disableFile(); + + /** + * Set callback function for logging to screen. + * @return whether callback was set succesfully. + * + */ + static bool setCallbackScreen(void (*cb)(const char *msg, size_t len)); + +}; + +} + +#endif /* _INC_ASTRA_LOGGING */ diff --git a/+astra/ASTRA_GPU_wrapper/astra/clog.h b/+astra/ASTRA_GPU_wrapper/astra/clog.h new file mode 100644 index 0000000..62d2f80 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/astra/clog.h @@ -0,0 +1,693 @@ +/* clog: Extremely simple logger for C. + * + * Features: + * - Implemented purely as a single header file. + * - Create multiple loggers. + * - Four log levels (debug, info, warn, error). + * - Custom formats. + * - Fast. + * + * Dependencies: + * - Should conform to C89, C++98 (but requires vsnprintf, unfortunately). + * - POSIX environment. + * + * USAGE: + * + * Include this header in any file that wishes to write to logger(s). In + * exactly one file (per executable), define CLOG_MAIN first (e.g. in your + * main .c file). + * + * #define CLOG_MAIN + * #include "clog.h" + * + * This will define the actual objects that all the other units will use. + * + * Loggers are identified by integers (0 - 15). It's expected that you'll + * create meaningful constants and then refer to the loggers as such. + * + * Example: + * + * const int MY_LOGGER = 0; + * + * int main() { + * int r; + * r = clog_init_path(MY_LOGGER, "my_log.txt"); + * if (r != 0) { + * fprintf(stderr, "Logger initialization failed.\n"); + * return 1; + * } + * clog_info(CLOG(MY_LOGGER), "Hello, world!"); + * clog_free(MY_LOGGER); + * return 0; + * } + * + * The CLOG macro used in the call to clog_info is a helper that passes the + * __FILE__ and __LINE__ parameters for you, so you don't have to type them + * every time. (It could be prettier with variadic macros, but that requires + * C99 or C++11 to be standards compliant.) + * + * Errors encountered by clog will be printed to stderr. You can suppress + * these by defining a macro called CLOG_SILENT before including clog.h. + * + * License: Do whatever you want. It would be nice if you contribute + * improvements as pull requests here: + * + * https://github.com/mmueller/clog + * + * Copyright 2013 Mike Mueller . + * + * As is; no warranty is provided; use at your own risk. + */ + +#ifndef __CLOG_H__ +#define __CLOG_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#else +#define WIN32_LEAN_AND_MEAN +#include +#include +#define open _open +#define close _close +#define write _write +#define snprintf _snprintf +#endif + +/* Number of loggers that can be defined. */ +#define CLOG_MAX_LOGGERS 16 + +/* Format strings cannot be longer than this. */ +#define CLOG_FORMAT_LENGTH 256 + +/* Formatted times and dates should be less than this length. If they are not, + * they will not appear in the log. */ +#define CLOG_DATETIME_LENGTH 256 + +/* Default format strings. */ +#define CLOG_DEFAULT_FORMAT "%d %t %f(%n): %l: %m\n" +#define CLOG_DEFAULT_DATE_FORMAT "%Y-%m-%d" +#define CLOG_DEFAULT_TIME_FORMAT "%H:%M:%S" + +#ifdef __cplusplus +extern "C" { +#endif + +enum clog_level { + CLOG_DEBUG, + CLOG_INFO, + CLOG_WARN, + CLOG_ERROR +}; + +struct clog; + +/** + * Create a new logger writing to the given file path. The file will always + * be opened in append mode. + * + * @param id + * A constant integer between 0 and 15 that uniquely identifies this logger. + * + * @param path + * Path to the file where log messages will be written. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_init_path(int id, const char *const path); + +/** + * Create a new logger writing to a file descriptor. + * + * @param id + * A constant integer between 0 and 15 that uniquely identifies this logger. + * + * @param fd + * The file descriptor where log messages will be written. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_init_fd(int id, int fd); + +/** + * Destroy (clean up) a logger. You should do this at the end of execution, + * or when you are done using the logger. + * + * @param id + * The id of the logger to destroy. + */ +void clog_free(int id); + +#define CLOG(id) __FILE__, __LINE__, id + +/** + * Log functions (one per level). Call these to write messages to the log + * file. The first three arguments can be replaced with a call to the CLOG + * macro defined above, e.g.: + * + * clog_debug(CLOG(MY_LOGGER_ID), "This is a log message."); + * + * @param sfile + * The name of the source file making this log call (e.g. __FILE__). + * + * @param sline + * The line number of the call in the source code (e.g. __LINE__). + * + * @param id + * The id of the logger to write to. + * + * @param fmt + * The format string for the message (printf formatting). + * + * @param ... + * Any additional format arguments. + */ +void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap); +void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap); + +/** + * Set the minimum level of messages that should be written to the log. + * Messages below this level will not be written. By default, loggers are + * created with level == CLOG_DEBUG. + * + * @param id + * The identifier of the logger. + * + * @param level + * The new minimum log level. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_level(int id, enum clog_level level); + +/** + * Set the format string used for times. See strftime(3) for how this string + * should be defined. The default format string is CLOG_DEFAULT_TIME_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_time_fmt(int id, const char *fmt); + +/** + * Set the format string used for dates. See strftime(3) for how this string + * should be defined. The default format string is CLOG_DEFAULT_DATE_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_date_fmt(int id, const char *fmt); + +/** + * Set the format string for log messages. Here are the substitutions you may + * use: + * + * %f: Source file name generating the log call. + * %n: Source line number where the log call was made. + * %m: The message text sent to the logger (after printf formatting). + * %d: The current date, formatted using the logger's date format. + * %t: The current time, formatted using the logger's time format. + * %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR"). + * %%: A literal percent sign. + * + * The default format string is CLOG_DEFAULT_FORMAT. + * + * @param fmt + * The new format string, which must be less than CLOG_FORMAT_LENGTH bytes. + * You probably will want to end this with a newline (\n). + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_fmt(int id, const char *fmt); + +/** + * Set the callback function. + * + * @param cb + * The new callback function. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_cb(int id, void (*cb)(const char *msg, size_t len)); + +/** + * Set the file descriptor. + * + * @param id + * The identifier of the logger. + * + * @param fd + * The new file descriptor. + * + * @return + * Zero on success, non-zero on failure. + */ +int clog_set_fd(int id, int fd); + + +/* + * No need to read below this point. + */ + +/** + * The C logger structure. + */ +struct clog { + + /* The current level of this logger. Messages below it will be dropped. */ + enum clog_level level; + + /* The file being written. */ + int fd; + + /* The format specifier. */ + char fmt[CLOG_FORMAT_LENGTH]; + + /* Date format */ + char date_fmt[CLOG_FORMAT_LENGTH]; + + /* Time format */ + char time_fmt[CLOG_FORMAT_LENGTH]; + + /* Tracks whether the fd needs to be closed eventually. */ + int opened; + + /* Callback function for each log message. */ + void (*cb)(const char *msg, size_t len); +}; + +void _clog_err(const char *fmt, ...); + +#ifdef CLOG_MAIN +struct clog *_clog_loggers[CLOG_MAX_LOGGERS] = { 0 }; +#else +extern struct clog *_clog_loggers[CLOG_MAX_LOGGERS]; +#endif + +#ifdef CLOG_MAIN + +const char *const CLOG_LEVEL_NAMES[] = { + "Debug", + "Info", + "Warning", + "Error", +}; + +int +clog_init_path(int id, const char *const path) +{ + int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666); + if (fd == -1) { + _clog_err("Unable to open %s: %s\n", path, strerror(errno)); + return 1; + } + if (clog_init_fd(id, fd)) { + close(fd); + return 1; + } + _clog_loggers[id]->opened = 1; + return 0; +} + +int +clog_init_fd(int id, int fd) +{ + struct clog *logger; + + if (_clog_loggers[id] != NULL) { + _clog_err("Logger %d already initialized.\n", id); + return 1; + } + + logger = (struct clog *) malloc(sizeof(struct clog)); + if (logger == NULL) { + _clog_err("Failed to allocate logger: %s\n", strerror(errno)); + return 1; + } + + logger->level = CLOG_DEBUG; + logger->fd = fd; + logger->opened = 0; + strcpy(logger->fmt, CLOG_DEFAULT_FORMAT); + strcpy(logger->date_fmt, CLOG_DEFAULT_DATE_FORMAT); + strcpy(logger->time_fmt, CLOG_DEFAULT_TIME_FORMAT); + logger->cb = NULL; + + _clog_loggers[id] = logger; + return 0; +} + +void +clog_free(int id) +{ + if (_clog_loggers[id]) { + if (_clog_loggers[id]->opened) { + close(_clog_loggers[id]->fd); + } + free(_clog_loggers[id]); + _clog_loggers[id]=NULL; + } +} + +int +clog_set_level(int id, enum clog_level level) +{ + if (_clog_loggers[id] == NULL) { + return 1; + } + if ((unsigned) level > CLOG_ERROR) { + return 1; + } + _clog_loggers[id]->level = level; + return 0; +} + +int +clog_set_fd(int id, int fd) +{ + if (_clog_loggers[id] == NULL) { + return 1; + } + _clog_loggers[id]->fd = fd; + return 0; +} + +int +clog_set_time_fmt(int id, const char *fmt) +{ + struct clog *logger = _clog_loggers[id]; + if (logger == NULL) { + _clog_err("clog_set_time_fmt: No such logger: %d\n", id); + return 1; + } + if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { + _clog_err("clog_set_time_fmt: Format specifier too long.\n"); + return 1; + } + strcpy(logger->time_fmt, fmt); + return 0; +} + +int +clog_set_date_fmt(int id, const char *fmt) +{ + struct clog *logger = _clog_loggers[id]; + if (logger == NULL) { + _clog_err("clog_set_date_fmt: No such logger: %d\n", id); + return 1; + } + if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { + _clog_err("clog_set_date_fmt: Format specifier too long.\n"); + return 1; + } + strcpy(logger->date_fmt, fmt); + return 0; +} + +int +clog_set_fmt(int id, const char *fmt) +{ + struct clog *logger = _clog_loggers[id]; + if (logger == NULL) { + _clog_err("clog_set_fmt: No such logger: %d\n", id); + return 1; + } + if (strlen(fmt) >= CLOG_FORMAT_LENGTH) { + _clog_err("clog_set_fmt: Format specifier too long.\n"); + return 1; + } + strcpy(logger->fmt, fmt); + return 0; +} + +int +clog_set_cb(int id, void (*cb)(const char *msg, size_t len)) +{ + struct clog *logger = _clog_loggers[id]; + if (logger == NULL) { + _clog_err("clog_set_cb: No such logger: %d\n", id); + return 1; + } + logger->cb = cb; + return 0; +} + +/* Internal functions */ + +size_t +_clog_append_str(char **dst, char *orig_buf, const char *src, size_t cur_size) +{ + size_t new_size = cur_size; + + while (strlen(*dst) + strlen(src) >= new_size) { + new_size *= 2; + } + if (new_size != cur_size) { + if (*dst == orig_buf) { + *dst = (char *) malloc(new_size); + strcpy(*dst, orig_buf); + } else { + *dst = (char *) realloc(*dst, new_size); + } + } + + strcat(*dst, src); + return new_size; +} + +size_t +_clog_append_int(char **dst, char *orig_buf, long int d, size_t cur_size) +{ + char buf[40]; /* Enough for 128-bit decimal */ + if (snprintf(buf, 40, "%ld", d) >= 40) { + return cur_size; + } + return _clog_append_str(dst, orig_buf, buf, cur_size); +} + +size_t +_clog_append_time(char **dst, char *orig_buf, struct tm *lt, + const char *fmt, size_t cur_size) +{ + char buf[CLOG_DATETIME_LENGTH]; + size_t result = strftime(buf, CLOG_DATETIME_LENGTH, fmt, lt); + + if (result > 0) { + return _clog_append_str(dst, orig_buf, buf, cur_size); + } + + return cur_size; +} + +const char * +_clog_basename(const char *path) +{ + const char *slash = strrchr(path, '/'); + if (slash) { + path = slash + 1; + } +#ifdef _WIN32 + slash = strrchr(path, '\\'); + if (slash) { + path = slash + 1; + } +#endif + return path; +} + +char * +_clog_format(const struct clog *logger, char buf[], size_t buf_size, + const char *sfile, int sline, const char *level, + const char *message) +{ + size_t cur_size = buf_size; + char *result = buf; + enum { NORMAL, SUBST } state = NORMAL; + size_t fmtlen = strlen(logger->fmt); + size_t i; + time_t t = time(NULL); + struct tm *lt = localtime(&t); + + sfile = _clog_basename(sfile); + result[0] = 0; + for (i = 0; i < fmtlen; ++i) { + if (state == NORMAL) { + if (logger->fmt[i] == '%') { + state = SUBST; + } else { + char str[2] = { 0 }; + str[0] = logger->fmt[i]; + cur_size = _clog_append_str(&result, buf, str, cur_size); + } + } else { + switch (logger->fmt[i]) { + case '%': + cur_size = _clog_append_str(&result, buf, "%", cur_size); + break; + case 't': + cur_size = _clog_append_time(&result, buf, lt, + logger->time_fmt, cur_size); + break; + case 'd': + cur_size = _clog_append_time(&result, buf, lt, + logger->date_fmt, cur_size); + break; + case 'l': + cur_size = _clog_append_str(&result, buf, level, cur_size); + break; + case 'n': + cur_size = _clog_append_int(&result, buf, sline, cur_size); + break; + case 'f': + cur_size = _clog_append_str(&result, buf, sfile, cur_size); + break; + case 'm': + cur_size = _clog_append_str(&result, buf, message, + cur_size); + break; + } + state = NORMAL; + } + } + + return result; +} + +void +_clog_log(const char *sfile, int sline, enum clog_level level, + int id, const char *fmt, va_list ap) +{ + /* For speed: Use a stack buffer until message exceeds 4096, then switch + * to dynamically allocated. This should greatly reduce the number of + * memory allocations (and subsequent fragmentation). */ + char buf[4096]; + size_t buf_size = 4096; + char *dynbuf = buf; + char *message; + int result; + struct clog *logger = _clog_loggers[id]; + + if (!logger) { + _clog_err("No such logger: %d\n", id); + return; + } + + if (level < logger->level) { + return; + } + + /* Format the message text with the argument list. */ + result = vsnprintf(dynbuf, buf_size, fmt, ap); + if ((size_t) result >= buf_size) { + buf_size = result + 1; + dynbuf = (char *) malloc(buf_size); + result = vsnprintf(dynbuf, buf_size, fmt, ap); + if ((size_t) result >= buf_size) { + /* Formatting failed -- too large */ + _clog_err("Formatting failed (1).\n"); + free(dynbuf); + return; + } + } + + /* Format according to log format and write to log */ + { + char message_buf[4096]; + message = _clog_format(logger, message_buf, 4096, sfile, sline, + CLOG_LEVEL_NAMES[level], dynbuf); + if (!message) { + _clog_err("Formatting failed (2).\n"); + if (dynbuf != buf) { + free(dynbuf); + } + return; + } + result = write(logger->fd, message, strlen(message)); + if (logger->cb) logger->cb(message,strlen(message)); + if (result == -1) { + _clog_err("Unable to write to log file: %s\n", strerror(errno)); + } + if (message != message_buf) { + free(message); + } + if (dynbuf != buf) { + free(dynbuf); + } +#ifndef _MSC_VER + fsync(logger->fd); +#else + HANDLE h = (HANDLE) _get_osfhandle(logger->fd); + if (h != INVALID_HANDLE_VALUE) { + // This call will fail on a console fd, but that's ok. + FlushFileBuffers(h); + } +#endif + } +} + +void +clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ + _clog_log(sfile, sline, CLOG_DEBUG, id, fmt, ap); +} + +void +clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ + _clog_log(sfile, sline, CLOG_INFO, id, fmt, ap); +} + +void +clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ + _clog_log(sfile, sline, CLOG_WARN, id, fmt, ap); +} + +void +clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap) +{ + _clog_log(sfile, sline, CLOG_ERROR, id, fmt, ap); +} + +void +_clog_err(const char *fmt, ...) +{ +#ifdef CLOG_SILENT + (void) fmt; +#else + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); +#endif +} + +#endif /* CLOG_MAIN */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __CLOG_H__ */ diff --git a/+astra/ASTRA_GPU_wrapper/dims3d.h b/+astra/ASTRA_GPU_wrapper/dims3d.h new file mode 100644 index 0000000..2fbfd78 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/dims3d.h @@ -0,0 +1,68 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _CUDA_CONE_DIMS_H +#define _CUDA_CONE_DIMS_H + +#include "astra/GeometryUtil3D.h" +#include "mex.h" +#include "gpu/mxGPUArray.h" + + +namespace astraCUDA3d { + +using astra::SConeProjection; +using astra::SPar3DProjection; + +struct SDimensions3D { + unsigned int iVolX; + unsigned int iVolY; + unsigned int iVolZ; + unsigned int iProjAngles; + unsigned int iProjU; // number of detectors in the U direction + unsigned int iProjV; // number of detectors in the V direction + unsigned int iRaysPerDetDim; + unsigned int iRaysPerVoxelDim; +}; + +struct DeformField { + const mxGPUArray * X0; + const mxGPUArray * Y0; + const mxGPUArray * Z0; + const mxGPUArray * X1; + const mxGPUArray * Y1; + const mxGPUArray * Z1; + bool use_deform; + bool use_linear_model; + +}; + +} + +#endif + diff --git a/+astra/ASTRA_GPU_wrapper/dllmain.cpp b/+astra/ASTRA_GPU_wrapper/dllmain.cpp new file mode 100644 index 0000000..8a4edd3 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/+astra/ASTRA_GPU_wrapper/par3d_bp.cu b/+astra/ASTRA_GPU_wrapper/par3d_bp.cu new file mode 100644 index 0000000..51da1c8 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/par3d_bp.cu @@ -0,0 +1,483 @@ +/* + +*-----------------------------------------------------------------------* +|                                                                       | +|  Except where otherwise noted, this work is licensed under a          | +|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +|  International (CC BY-NC-SA 4.0) license.                             | +|                                                                       | +|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +|                                                                       | +|      Author: CXS group, PSI  | +*-----------------------------------------------------------------------* +You may use this code with the following provisions: + +If the code is fully or partially redistributed, or rewritten in another + computing language this notice should be included in the redistribution. + +If this code, or subfunctions or parts of it, is used for research in a + publication or if it is fully or partially rewritten for another + computing language the authors and institution should be acknowledged + in written form in the publication: “Data processing was carried out + using the “cSAXS matlab package” developed by the CXS group, + Paul Scherrer Institut, Switzerland.” + Variations on the latter text can be incorporated upon discussion with + the CXS group if needed to more specifically reflect the use of the package + for the published work. + +A publication that focuses on describing features, or parameters, that + are already existing in the code should be first discussed with the + authors. + +This code and subroutines are part of a continuous development, they + are provided “as they are” without guarantees or liability on part + of PSI or the authors. It is the user responsibility to ensure its + proper use and the correctness of the results. + + + +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#include +#include +#include +#include + +#include +#include "util3d.h" + +#ifdef STANDALONE +#include "par3d_fp.h" +#include "testutil.h" +#endif + +#include "dims3d.h" + +typedef texture texture3D; + +static texture3D gT_par3DProjTexture, Xdef0_tex, Ydef0_tex, Zdef0_tex, Xdef1_tex, Ydef1_tex, Zdef1_tex; + +namespace astraCUDA3d { + +#define ZSIZE 6 +static const unsigned int g_volBlockZ = ZSIZE; + +static const unsigned int g_anglesPerBlock = 32; +static const unsigned int g_volBlockX = 16; +static const unsigned int g_volBlockY = 32; + +static const unsigned g_MaxAngles = 1024; + +__constant__ float gC_C[8*g_MaxAngles]; + +#define MAX(x,y) (x>y?x:y); +#define MIN(x,y) (x0?x:-x); + + + + + + +__global__ void dev_par3D_BP(void* D_volData, unsigned int volPitch, + int startAngle, int angleOffset, const SDimensions3D dims, + float fOutputScale, bool use_deform, bool linear_deform_model) +{ + float* volData = (float*)D_volData; + + int endAngle = startAngle + g_anglesPerBlock; + if (endAngle > dims.iProjAngles - angleOffset) + endAngle = dims.iProjAngles - angleOffset; + + // threadIdx: x = rel x + // y = rel y + + // blockIdx: x = x + y + // y = z + + + const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; + const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; + + if (X >= dims.iVolX) + return; + if (Y >= dims.iVolY) + return; + + const int startZ = blockIdx.y * g_volBlockZ; + + const float limX = dims.iVolX; + const float limY = dims.iVolY; + const float limZ = dims.iVolZ; + + float fX = X - 0.5f*limX + 0.5f; + float fY = Y - 0.5f*limY + 0.5f; + float fZ = startZ - 0.5f*limZ + 0.5f; + + // solve by small blocks over all angles + float Z[ZSIZE]; + for(int i=0; i < ZSIZE; i++) + Z[i] = 0.0f; + + + float fAngle = startAngle + angleOffset + 0.5f; + float4 fCu, fCv; + float fU, fV; + float fXn, fYn, fZn; // normalized coordinates + float fXs, fYs, fZs; // shifted coordinates + float angle_ratio ; // ratio from angle / iProjAngles + + for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) + { + + fCu = make_float4(gC_C[8*angle+0], gC_C[8*angle+1], gC_C[8*angle+2], gC_C[8*angle+3]); + fCv = make_float4(gC_C[8*angle+4], gC_C[8*angle+5], gC_C[8*angle+6], gC_C[8*angle+7]); + + angle_ratio = (float)angle / (float)dims.iProjAngles ; + + + if (use_deform) + { + + /* + // FASTER APPROXIMATION FOR SMALL DEFORMATIONS + fXn = X/limX; // normalized coordinates + fYn = Y/limY; + fZn = startZ/limZ; + + + // load deformed coordinates + fXs = fX + tex3D(Xdef0_tex,fXn, fYn, fZn); + fYs = fY + tex3D(Ydef0_tex,fXn, fYn, fZn); + fZs = fZ + tex3D(Zdef0_tex,fXn, fYn, fZn); + + // find location on the detector + fU = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z; + fV = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z; + + for (int idx = 0; idx < ZSIZE; ++idx) { + // get bilinear interpolation back to non-shifted coordinates + Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV); + + // TODO: check if approximation that deformation is constant for Z block is valid !! + fU += fCu.z; + fV += fCv.z; + } + */ + + // ARBITRARY DEFORMATIONS APPROXIMATION + + fXn = X/limX; // normalized coordinates + fYn = Y/limY; + for (int idx = 0; idx < ZSIZE; ++idx) { + fZs = fZ + idx; // Z coordinate + fZn = (startZ+idx)/limZ; // normalized Z coordinate + + // load deformed coordinates + if (!linear_deform_model){ + fXs = fX + tex3D(Xdef0_tex,fXn, fYn, fZn); + fYs = fY + tex3D(Ydef0_tex,fXn, fYn, fZn); + fZs = fZs +tex3D(Zdef0_tex,fXn, fYn, fZn); + } else { + // deformated coordinates with linear interpolation + fXs = fX + (tex3D(Xdef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Xdef1_tex,fXn, fYn, fZn)); + fYs = fY + (tex3D(Ydef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Ydef1_tex,fXn, fYn, fZn)); + fZs = fZs +(tex3D(Zdef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Zdef1_tex,fXn, fYn, fZn)); + } + + // find location on the detector + fU = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z; + fV = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z; + + // get bilinear interpolation back to non-shifted coordinates + Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV); + + // TODO: check if approximation that deformation is constant for Z block is valid !! + fU += fCu.z; + fV += fCv.z; + } + + + + } else { + + + fU = fCu.w + fX * fCu.x + fY * fCu.y + fZ * fCu.z; + fV = fCv.w + fX * fCv.x + fY * fCv.y + fZ * fCv.z; + + for (int idx = 0; idx < ZSIZE; ++idx) { + + Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV); + + fU += fCu.z; + fV += fCv.z; + } + } + + } + + + int endZ = ZSIZE; + if (endZ > dims.iVolZ - startZ) + endZ = dims.iVolZ - startZ; + + for(int i=0; i < endZ; i++) + volData[((startZ+i)*dims.iVolY+Y)*volPitch+X] += Z[i] * fOutputScale; +} + +// supersampling version +__global__ void dev_par3D_BP_SS(void* D_volData, unsigned int volPitch, int startAngle, int angleOffset, const SDimensions3D dims, float fOutputScale) +{ + float* volData = (float*)D_volData; + + int endAngle = startAngle + g_anglesPerBlock; + if (endAngle > dims.iProjAngles - angleOffset) + endAngle = dims.iProjAngles - angleOffset; + + // threadIdx: x = rel x + // y = rel y + + // blockIdx: x = x + y + // y = z + + + // TO TRY: precompute part of detector intersection formulas in shared mem? + // TO TRY: inner loop over z, gather ray values in shared mem + + const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x; + const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y; + + if (X >= dims.iVolX) + return; + if (Y >= dims.iVolY) + return; + + const int startZ = blockIdx.y * g_volBlockZ; + int endZ = startZ + g_volBlockZ; + if (endZ > dims.iVolZ) + endZ = dims.iVolZ; + + float fX = X - 0.5f*dims.iVolX + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim; + float fY = Y - 0.5f*dims.iVolY + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim; + float fZ = startZ - 0.5f*dims.iVolZ + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim; + + const float fSubStep = 1.0f/dims.iRaysPerVoxelDim; + + fOutputScale /= (dims.iRaysPerVoxelDim*dims.iRaysPerVoxelDim*dims.iRaysPerVoxelDim); + + + for (int Z = startZ; Z < endZ; ++Z, fZ += 1.0f) + { + + float fVal = 0.0f; + float fAngle = startAngle + angleOffset + 0.5f; + + for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f) + { + const float fCux = gC_C[8*angle+0]; + const float fCuy = gC_C[8*angle+1]; + const float fCuz = gC_C[8*angle+2]; + const float fCuc = gC_C[8*angle+3]; + const float fCvx = gC_C[8*angle+4]; + const float fCvy = gC_C[8*angle+5]; + const float fCvz = gC_C[8*angle+6]; + const float fCvc = gC_C[8*angle+7]; + + float fXs = fX; + for (int iSubX = 0; iSubX < dims.iRaysPerVoxelDim; ++iSubX) { + float fYs = fY; + for (int iSubY = 0; iSubY < dims.iRaysPerVoxelDim; ++iSubY) { + float fZs = fZ; + for (int iSubZ = 0; iSubZ < dims.iRaysPerVoxelDim; ++iSubZ) { + + const float fU = fCuc + fXs * fCux + fYs * fCuy + fZs * fCuz; + const float fV = fCvc + fXs * fCvx + fYs * fCvy + fZs * fCvz; + + fVal += tex3D(gT_par3DProjTexture, fU, fAngle, fV); + fZs += fSubStep; + } + fYs += fSubStep; + } + fXs += fSubStep; + } + + } + + volData[(Z*dims.iVolY+Y)*volPitch+X] += fVal * fOutputScale; + } + +} + +bool Par3DBP_Array(cudaPitchedPtr D_volumeData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale, bool use_deform, bool linear_deform_model) +{ + + for (unsigned int th = 0; th < dims.iProjAngles; th += g_MaxAngles) { + unsigned int angleCount = g_MaxAngles; + if (th + angleCount > dims.iProjAngles) + angleCount = dims.iProjAngles - th; + + // transfer angles to constant memory + float* tmp = new float[8*dims.iProjAngles]; + + // NB: We increment angles at the end of the loop body. + + + // TODO: Use functions from dims3d.cu for this: + +#define TRANSFER_TO_CONSTANT(expr,name) do { for (unsigned int i = 0; i < angleCount; ++i) tmp[8*i + name] = (expr) ; } while (0) + +#define DENOM (angles[i].fRayX*angles[i].fDetUY*angles[i].fDetVZ - angles[i].fRayX*angles[i].fDetUZ*angles[i].fDetVY - angles[i].fRayY*angles[i].fDetUX*angles[i].fDetVZ + angles[i].fRayY*angles[i].fDetUZ*angles[i].fDetVX + angles[i].fRayZ*angles[i].fDetUX*angles[i].fDetVY - angles[i].fRayZ*angles[i].fDetUY*angles[i].fDetVX) + + TRANSFER_TO_CONSTANT( ( - (angles[i].fRayY*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVY)) / DENOM , 0 ); + TRANSFER_TO_CONSTANT( ( (angles[i].fRayX*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVX)) / DENOM , 1 ); + TRANSFER_TO_CONSTANT( (- (angles[i].fRayX*angles[i].fDetVY - angles[i].fRayY*angles[i].fDetVX) ) / DENOM , 2 ); + TRANSFER_TO_CONSTANT( (-(angles[i].fDetSY*angles[i].fDetVZ - angles[i].fDetSZ*angles[i].fDetVY)*angles[i].fRayX + (angles[i].fRayY*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVY)*angles[i].fDetSX - (angles[i].fRayY*angles[i].fDetSZ - angles[i].fRayZ*angles[i].fDetSY)*angles[i].fDetVX) / DENOM , 3 ); + + TRANSFER_TO_CONSTANT( ((angles[i].fRayY*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUY) ) / DENOM , 4 ); + TRANSFER_TO_CONSTANT( (- (angles[i].fRayX*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUX) ) / DENOM , 5 ); + TRANSFER_TO_CONSTANT( ((angles[i].fRayX*angles[i].fDetUY - angles[i].fRayY*angles[i].fDetUX) ) / DENOM , 6 ); + TRANSFER_TO_CONSTANT( ((angles[i].fDetSY*angles[i].fDetUZ - angles[i].fDetSZ*angles[i].fDetUY)*angles[i].fRayX - (angles[i].fRayY*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUY)*angles[i].fDetSX + (angles[i].fRayY*angles[i].fDetSZ - angles[i].fRayZ*angles[i].fDetSY)*angles[i].fDetUX ) / DENOM , 7 ); + +#undef TRANSFER_TO_CONSTANT +#undef DENOM + + cudaMemcpyToSymbol(gC_C, tmp, angleCount*8*sizeof(float), 0, cudaMemcpyHostToDevice); + + delete[] tmp; + + checkLastError("after cudaMemcpyToSymbol"); + + + dim3 dimBlock(g_volBlockX, g_volBlockY); + + dim3 dimGrid(((dims.iVolX+g_volBlockX-1)/g_volBlockX)*((dims.iVolY+g_volBlockY-1)/g_volBlockY), (dims.iVolZ+g_volBlockZ-1)/g_volBlockZ); + + // timeval t; + // tic(t); + + for (unsigned int i = 0; i < angleCount; i += g_anglesPerBlock) { + // printf("Calling BP: %d, %dx%d, %dx%d to %p\n", i, dimBlock.x, dimBlock.y, dimGrid.x, dimGrid.y, (void*)D_volumeData.ptr); + if (dims.iRaysPerVoxelDim == 1) + dev_par3D_BP<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), i, th, dims, fOutputScale, use_deform, linear_deform_model); + else + dev_par3D_BP_SS<<>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), i, th, dims, fOutputScale); + } + + cudaTextForceKernelsCompletion(); + checkLastError("after cudaTextForceKernelsCompletion"); + + angles = angles + angleCount; + // printf("%f\n", toc(t)); + + } + + + + return true; +} + +bool Par3DBP(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale, DeformField DF) +{ + // transfer projections to array + + checkLastError("before allocateVolumeArray"); + + cudaArray* cuArray = allocateProjectionArray(dims); + checkLastError("after allocateVolumeArray"); + + transferProjectionsToArray(D_projData, cuArray, dims); + + checkLastError("after transferProjectionsToArray"); + + bindDataTexture(cuArray, gT_par3DProjTexture, cudaAddressModeBorder, false); + checkLastError("after bindProjDataTexture"); + + cudaArray * cuArrX0, *cuArrY0, *cuArrZ0, *cuArrX1, *cuArrY1, *cuArrZ1 ; + + + if (DF.use_deform) { + // mexPrintf("transferDeformationToArray\n"); + + cuArrX0 = transferDeformationToArray(DF.X0); + cuArrY0 = transferDeformationToArray(DF.Y0); + cuArrZ0 = transferDeformationToArray(DF.Z0); + bindDataTexture(cuArrX0, Xdef0_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrY0, Ydef0_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrZ0, Zdef0_tex,cudaAddressModeClamp, true); + if (DF.use_linear_model) { + cuArrX1 = transferDeformationToArray(DF.X1); + cuArrY1 = transferDeformationToArray(DF.Y1); + cuArrZ1 = transferDeformationToArray(DF.Z1); + bindDataTexture(cuArrX1, Xdef1_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrY1, Ydef1_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrZ1, Zdef1_tex,cudaAddressModeClamp, true); + } + } + + bool ret = Par3DBP_Array(D_volumeData, dims, angles, fOutputScale, DF.use_deform, DF.use_linear_model); + + checkLastError("after Par3DBP_Array"); + + cudaUnbindTexture(gT_par3DProjTexture); + checkLastError("after cudaUnbindTexture"); + + cudaFreeArray(cuArray); + + checkLastError("after cudaFreeArray"); + + + if (DF.use_deform) { + cudaFreeArray(cuArrX0); + cudaFreeArray(cuArrY0); + cudaFreeArray(cuArrZ0); + cudaUnbindTexture(Xdef0_tex); + cudaUnbindTexture(Ydef0_tex); + cudaUnbindTexture(Zdef0_tex); + if (DF.use_linear_model) { + cudaFreeArray(cuArrX1); + cudaFreeArray(cuArrY1); + cudaFreeArray(cuArrZ1); + cudaUnbindTexture(Xdef1_tex); + cudaUnbindTexture(Ydef1_tex); + cudaUnbindTexture(Zdef1_tex); + } + checkLastError("unbind deforms"); + } + + + return ret; +} + + +} + diff --git a/+astra/ASTRA_GPU_wrapper/par3d_bp.h b/+astra/ASTRA_GPU_wrapper/par3d_bp.h new file mode 100644 index 0000000..f7a97ef --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/par3d_bp.h @@ -0,0 +1,47 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _CUDA_PAR3D_BP_H +#define _CUDA_PAR3D_BP_H + +namespace astraCUDA3d { + +_AstraExport bool Par3DBP_Array(cudaPitchedPtr D_volumeData, + cudaArray *D_projArray, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale); + +_AstraExport bool Par3DBP(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale, DeformField DF); + + +} + +#endif diff --git a/+astra/ASTRA_GPU_wrapper/par3d_fp.cu b/+astra/ASTRA_GPU_wrapper/par3d_fp.cu new file mode 100644 index 0000000..f203953 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/par3d_fp.cu @@ -0,0 +1,929 @@ +/* + +*-----------------------------------------------------------------------* +|                                                                       | +|  Except where otherwise noted, this work is licensed under a          | +|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +|  International (CC BY-NC-SA 4.0) license.                             | +|                                                                       | +|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +|                                                                       | +|      Author: CXS group, PSI  | +*-----------------------------------------------------------------------* +You may use this code with the following provisions: + +If the code is fully or partially redistributed, or rewritten in another + computing language this notice should be included in the redistribution. + +If this code, or subfunctions or parts of it, is used for research in a + publication or if it is fully or partially rewritten for another + computing language the authors and institution should be acknowledged + in written form in the publication: “Data processing was carried out + using the “cSAXS matlab package” developed by the CXS group, + Paul Scherrer Institut, Switzerland.” + Variations on the latter text can be incorporated upon discussion with + the CXS group if needed to more specifically reflect the use of the package + for the published work. + +A publication that focuses on describing features, or parameters, that + are already existing in the code should be first discussed with the + authors. + +This code and subroutines are part of a continuous development, they + are provided “as they are” without guarantees or liability on part + of PSI or the authors. It is the user responsibility to ensure its + proper use and the correctness of the results. + + + +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#include +#include +#include +#include + +#include +#include "util3d.h" + +#include "mex.h" +#include "gpu/mxGPUArray.h" + + + +#ifdef STANDALONE +#include "testutil.h" +#endif + +#include "dims3d.h" + +typedef texture texture3D; + +static texture3D gT_par3DVolumeTexture, Xdef0_tex, Ydef0_tex, Zdef0_tex, Xdef1_tex, Ydef1_tex, Zdef1_tex; + +#define MAX(x,y) (x>y?x:y); +#define MIN(x,y) (x +__global__ void par3D_FP_t(float* D_projData, unsigned int projPitch, + unsigned int startSlice, + unsigned int startAngle, unsigned int endAngle, + const SDimensions3D dims, float fOutputScale, const bool use_deform, const bool linear_deform_model) +{ + COORD c; + + + int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; + if (angle >= endAngle) + return; + + + + const float fRayX = gC_RayX[angle]; + const float fRayY = gC_RayY[angle]; + const float fRayZ = gC_RayZ[angle]; + const float fDetUX = gC_DetUX[angle]; + const float fDetUY = gC_DetUY[angle]; + const float fDetUZ = gC_DetUZ[angle]; + const float fDetVX = gC_DetVX[angle]; + const float fDetVY = gC_DetVY[angle]; + const float fDetVZ = gC_DetVZ[angle]; + const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; + const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; + const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; + + + + if (c.c0(fRayX, fRayY, fRayZ) == 0) + return; + + const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; + + if (detectorU >= dims.iProjU) + return; + + + const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; + int endDetectorV = startDetectorV + g_detBlockV; + if (endDetectorV > dims.iProjV) + endDetectorV = dims.iProjV; + + int endSlice = startSlice + g_blockSlices; + if (endSlice > c.nSlices(dims)) + endSlice = c.nSlices(dims); + + // FIXME + /*if (endSlice < startSlice - 1) + return;*/ + + float angle_ratio = (float)angle / (float)dims.iProjAngles ; + + + for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) + { + /* Trace ray in direction Ray to (detectorU,detectorV) from */ + /* X = startSlice to X = endSlice */ + + const float fDetX = fDetSX + (detectorU*fDetUX + detectorV*fDetVX); + const float fDetY = fDetSY + (detectorU*fDetUY + detectorV*fDetVY); + const float fDetZ = fDetSZ + (detectorU*fDetUZ + detectorV*fDetVZ); + + /* (x) ( 1) ( 0) */ + /* ray: (y) = (ay) * x + (by) */ + /* (z) (az) (bz) */ + + const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); + const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); + + const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale; + + float fVal = 0.0f; + + //float f0 = startSlice + 0.5f; + //float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; + //float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; + + bool is_inside; + int lim0, lim1, lim2; + lim0 = c.nSlices(dims); + lim1 = c.nDim1(dims); + lim2 = c.nDim2(dims); + const float offset = 0.5*lim0; + + // calculate minimal distance needed to get the subprojection, important for laminography and large projection size + + int startSlice_tmp = startSlice; + int endSlice_tmp = endSlice; + + if (a1 > 0) + { + startSlice_tmp = MAX(startSlice_tmp, floor((-0.5*lim1-b1-0.5f)/a1+offset-1.0f)); + endSlice_tmp = MIN(endSlice_tmp, ceil((+0.5*lim1-b1+0.5f)/a1+offset+1.0f)); + } + else if (a1 < 0) + { + startSlice_tmp = MAX(startSlice_tmp, floor((+0.5*lim1-b1+0.5f)/a1+offset-1.0f)); + endSlice_tmp = MIN(endSlice_tmp, ceil((-0.5*lim1-b1-0.5f)/a1+offset+1.0f)); + } + if (a2 > 0) + { + startSlice_tmp = MAX(startSlice_tmp, floor((-0.5*lim2-b2-0.5f)/a2+offset-1.0f)); + endSlice_tmp = MIN(endSlice_tmp, ceil((+0.5*lim2-b2+0.5f)/a2+offset+1.0f)); + } + else if (a2 < 0) + { + startSlice_tmp = MAX(startSlice_tmp, floor((+0.5*lim2-b2+0.5f)/a2+offset-1.0f)); + endSlice_tmp = MIN(endSlice_tmp, ceil((-0.5*lim2-b2-0.5f)/a2+offset+1.0f)); + } + + endSlice_tmp = MIN(endSlice_tmp, endSlice); + endSlice_tmp = MAX(endSlice_tmp, 0); + + startSlice_tmp = MAX(startSlice_tmp, startSlice); + startSlice_tmp = MIN(startSlice_tmp, endSlice_tmp); + + + + float f0 = startSlice_tmp + 0.5f; + float f1 = a1 * (startSlice_tmp - offset+0.5f) + b1 + 0.5f*c.nDim1(dims); + float f2 = a2 * (startSlice_tmp - offset+0.5f) + b2 + 0.5f*c.nDim2(dims); + + + float f0s, f1s, f2s; // shifted coordinates + float f0n, f1n, f2n; // normalized coordinates + + // 87% of the execution time + for (int s = startSlice_tmp; s < endSlice_tmp; ++s) + { + if (use_deform) { + f0n = f0/lim0; // normalized coordinates + f1n = f1/lim1; + f2n = f2/lim2; + + // load deformed coordinates + if (!linear_deform_model) { + f0s = f0 - c.texD0x(f0n, f1n, f2n); + f1s = f1 - c.texD0y(f0n, f1n, f2n); + f2s = f2 - c.texD0z(f0n, f1n, f2n); + } else { + f0s = f0 - (c.texD0x(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1x(f0n, f1n, f2n)); + f1s = f1 - (c.texD0y(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1y(f0n, f1n, f2n)); + f2s = f2 - (c.texD0z(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1z(f0n, f1n, f2n)); + } + // get trilinear interpolation in the shifted coordinates + fVal += c.tex(f0s, f1s, f2s); + } else { + + is_inside = (f0 > 0 && f1 > 0 && f2 > 0 && f0 < lim0 && f1 < lim1 && f2 < lim2 ); + // fVal += (is_inside ? c.tex(f0, f1, f2) : 0); // skip textures on boundaries + //fVal += c.tex(f0, f1, f2) == 0; + //fVal += is_inside == 0; + + fVal += c.tex(f0, f1, f2); // fastest seems to be let texture memory to handle boundaries + } + + // move to the next pixel + f0 += 1.0f; + f1 += a1; + f2 += a2; + } + + fVal *= fDistCorr; + + // !! 10% of the execution time + //D_projData[(detectorV*dims.iProjAngles + angle)*projPitch + detectorU] += fVal; + atomicAdd(&D_projData[(detectorV*dims.iProjAngles + angle)*projPitch + detectorU], fVal); + + } +} + +// Supersampling version +template +__global__ void par3D_FP_SS_t(float* D_projData, unsigned int projPitch, + unsigned int startSlice, + unsigned int startAngle, unsigned int endAngle, + const SDimensions3D dims, float fOutputScale) +{ + COORD c; + + int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; + if (angle >= endAngle) + return; + + const float fRayX = gC_RayX[angle]; + const float fRayY = gC_RayY[angle]; + const float fRayZ = gC_RayZ[angle]; + const float fDetUX = gC_DetUX[angle]; + const float fDetUY = gC_DetUY[angle]; + const float fDetUZ = gC_DetUZ[angle]; + const float fDetVX = gC_DetVX[angle]; + const float fDetVY = gC_DetVY[angle]; + const float fDetVZ = gC_DetVZ[angle]; + const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; + const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; + const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; + + + + const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; + const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; + int endDetectorV = startDetectorV + g_detBlockV; + if (endDetectorV > dims.iProjV) + endDetectorV = dims.iProjV; + + int endSlice = startSlice + g_blockSlices; + if (endSlice > c.nSlices(dims)) + endSlice = c.nSlices(dims); + + const float fSubStep = 1.0f/dims.iRaysPerDetDim; + + for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) + { + + float fV = 0.0f; + + float fdU = detectorU - 0.5f + 0.5f*fSubStep; + for (int iSubU = 0; iSubU < dims.iRaysPerDetDim; ++iSubU, fdU+=fSubStep) { + float fdV = detectorV - 0.5f + 0.5f*fSubStep; + for (int iSubV = 0; iSubV < dims.iRaysPerDetDim; ++iSubV, fdV+=fSubStep) { + + /* Trace ray in direction Ray to (detectorU,detectorV) from */ + /* X = startSlice to X = endSlice */ + + const float fDetX = fDetSX + fdU*fDetUX + fdV*fDetVX; + const float fDetY = fDetSY + fdU*fDetUY + fdV*fDetVY; + const float fDetZ = fDetSZ + fdU*fDetUZ + fdV*fDetVZ; + + /* (x) ( 1) ( 0) */ + /* ray: (y) = (ay) * x + (by) */ + /* (z) (az) (bz) */ + + + const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); + const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); + + const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale; + + float fVal = 0.0f; + + float f0 = startSlice + 0.5f; + float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; + float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; + + + + for (int s = startSlice; s < endSlice; ++s) + { + fVal += c.tex(f0, f1, f2); + f0 += 1.0f; + f1 += a1 ; + // f2 += a2; + } + + fVal *= fDistCorr; + fV += fVal; + + } + } + + D_projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fV / (dims.iRaysPerDetDim * dims.iRaysPerDetDim); + } +} + + +__device__ float dirWeights(float fX, float fN) { + if (fX <= -0.5f) // outside image on left + return 0.0f; + if (fX <= 0.5f) // half outside image on left + return (fX + 0.5f) * (fX + 0.5f); + if (fX <= fN - 0.5f) { // inside image + float t = fX + 0.5f - floorf(fX + 0.5f); + return 1; // t*t + (1 - t)*(1 - t); + } + if (fX <= fN + 0.5f) // half outside image on right + return (fN + 0.5f - fX) * (fN + 0.5f - fX); + return 0.0f; // outside image on right +} + +template +__global__ void par3D_FP_SumSqW_t(float* D_projData, unsigned int projPitch, + unsigned int startSlice, + unsigned int startAngle, unsigned int endAngle, + const SDimensions3D dims, float fOutputScale) +{ + COORD c; + + int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y; + if (angle >= endAngle) + return; + + const float fRayX = gC_RayX[angle]; + const float fRayY = gC_RayY[angle]; + const float fRayZ = gC_RayZ[angle]; + const float fDetUX = gC_DetUX[angle]; + const float fDetUY = gC_DetUY[angle]; + const float fDetUZ = gC_DetUZ[angle]; + const float fDetVX = gC_DetVX[angle]; + const float fDetVY = gC_DetVY[angle]; + const float fDetVZ = gC_DetVZ[angle]; + const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX; + const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY; + const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ; + + + + const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x; + const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV; + int endDetectorV = startDetectorV + g_detBlockV; + if (endDetectorV > dims.iProjV) + endDetectorV = dims.iProjV; + + int endSlice = startSlice + g_blockSlices; + if (endSlice > c.nSlices(dims)) + endSlice = c.nSlices(dims); + + for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV) + { + /* Trace ray in direction Ray to (detectorU,detectorV) from */ + /* X = startSlice to X = endSlice */ + + const float fDetX = fDetSX + detectorU*fDetUX + detectorV*fDetVX; + const float fDetY = fDetSY + detectorU*fDetUY + detectorV*fDetVY; + const float fDetZ = fDetSZ + detectorU*fDetUZ + detectorV*fDetVZ; + + /* (x) ( 1) ( 0) */ + /* ray: (y) = (ay) * x + (by) */ + /* (z) (az) (bz) */ + + const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ); + const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ); + const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ); + + const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale; + + float fVal = 0.0f; + + float f0 = startSlice + 0.5f; + float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f; + float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f; + + for (int s = startSlice; s < endSlice; ++s) + { + fVal += dirWeights(f1, c.nDim1(dims)) * dirWeights(f2, c.nDim2(dims)) * fDistCorr * fDistCorr; + f0 += 1.0f; + f1 += a1; + f2 += a2; + } + + D_projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fVal; + } +} + +// Supersampling version +// TODO + + +bool Par3DFP_Array_internal(cudaPitchedPtr D_projData, + const SDimensions3D& dims, unsigned int angleCount, const SPar3DProjection* angles, + float fOutputScale, const bool use_deform, const bool linear_deform_model) +{ + + + + // transfer angles to constant memory + float* tmp = new float[dims.iProjAngles]; + +#define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < angleCount; ++i) tmp[i] = (float)angles[i].f##name ; cudaMemcpyToSymbol(gC_##name, tmp, angleCount*sizeof(float), 0, cudaMemcpyHostToDevice); } while (0) + + TRANSFER_TO_CONSTANT(RayX); + TRANSFER_TO_CONSTANT(RayY); + TRANSFER_TO_CONSTANT(RayZ); + TRANSFER_TO_CONSTANT(DetSX); + TRANSFER_TO_CONSTANT(DetSY); + TRANSFER_TO_CONSTANT(DetSZ); + TRANSFER_TO_CONSTANT(DetUX); + TRANSFER_TO_CONSTANT(DetUY); + TRANSFER_TO_CONSTANT(DetUZ); + TRANSFER_TO_CONSTANT(DetVX); + TRANSFER_TO_CONSTANT(DetVY); + TRANSFER_TO_CONSTANT(DetVZ); + +#undef TRANSFER_TO_CONSTANT + + delete[] tmp; + + std::list streams; + dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles + + // Run over all angles, grouping them into groups of the same + // orientation (roughly horizontal vs. roughly vertical). + // Start a stream of grids for each such group. + + unsigned int blockStart = 0; + unsigned int blockEnd = 0; + int blockDirection = 0; + + + for (unsigned int a = 0; a <= angleCount; ++a) { + int dir = -1; + if (a != dims.iProjAngles) { + float dX = fabsf(angles[a].fRayX); + float dY = fabsf(angles[a].fRayY); + float dZ = fabsf(angles[a].fRayZ); + + if (dX >= dY && dX >= dZ) + dir = 0; + else if (dY >= dX && dY >= dZ) + dir = 1; + else + dir = 2; + } + + if (a == angleCount || dir != blockDirection) { + // block done + + blockEnd = a; + if (blockStart != blockEnd) { + + dim3 dimGrid( + ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), + (blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock); + // TODO: check if we can't immediately + // destroy the stream after use + + + cudaStream_t stream; + cudaStreamCreate(&stream); + streams.push_back(stream); + + //mexPrintf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y); + //mexPrintf(" Nelements %i ", (dims.iProjU)*(dims.iProjV)*(dims.iProjAngles)); + + + if (blockDirection == 0) { + for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model); + else + par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + } else if (blockDirection == 1) { + for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model); + else + par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + } else if (blockDirection == 2) { + for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model); + else + par3D_FP_SS_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + } + + } + + blockDirection = dir; + blockStart = a; + } + } + + cudaThreadSynchronize(); + + for (std::list::iterator iter = streams.begin(); iter != streams.end(); ++iter) + cudaStreamDestroy(*iter); + + + streams.clear(); + + cudaTextForceKernelsCompletion(); + + return true; +} + +bool Par3DFP(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale, DeformField DF) +{ + + + + checkLastError("before allocateVolumeArray"); + /*printFreeMemory(); + mexPrintf("Allocate memory\n");*/ + // transfer volume to array + + + if (dims.iVolX*dims.iVolY*dims.iVolZ * 4 > 1024e6) + { + mexPrintf("Volume exceeded maximal size of texture 1024MB \n"); + return 1; + } + + cudaArray* cuArray = allocateVolumeArray(dims); + //mexPrintf("Allocate memory done\n"); + //printFreeMemory(); + + checkLastError("after allocateVolumeArray"); + + //mexPrintf("transferVolumeToArray\n"); + + transferVolumeToArray(D_volumeData, cuArray, dims); + + + checkLastError("after transferVolumeToArray\n \n "); + //printFreeMemory(); + + bindDataTexture(cuArray, gT_par3DVolumeTexture,cudaAddressModeBorder, false); + + //mexPrintf("bindDataTexture done \n"); + + + checkLastError("after bindDataTexture"); + //printFreeMemory(); + + //mexPrintf("preoparation finieshe \n"); + cudaArray * cuArrX0, *cuArrY0, *cuArrZ0, *cuArrX1, *cuArrY1, *cuArrZ1 ; + + + if (DF.use_deform) { + // mexPrintf("transferDeformationToArray\n"); + + cuArrX0 = transferDeformationToArray(DF.X0); + cuArrY0 = transferDeformationToArray(DF.Y0); + cuArrZ0 = transferDeformationToArray(DF.Z0); + bindDataTexture(cuArrX0, Xdef0_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrY0, Ydef0_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrZ0, Zdef0_tex,cudaAddressModeClamp, true); + if (DF.use_linear_model) { + cuArrX1 = transferDeformationToArray(DF.X1); + cuArrY1 = transferDeformationToArray(DF.Y1); + cuArrZ1 = transferDeformationToArray(DF.Z1); + bindDataTexture(cuArrX1, Xdef1_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrY1, Ydef1_tex,cudaAddressModeClamp, true); + bindDataTexture(cuArrZ1, Zdef1_tex,cudaAddressModeClamp, true); + } + } + + + bool ret; + + // ONLY A LIMITED RANGE OF ANGLES IS AVAILIBLE INSIDE !!!!! + checkLastError("before allocateVolumeArray"); + + + // 97% of time spent in Par3DFP_Array_internal + ret = Par3DFP_Array_internal(D_projData, + dims, dims.iProjAngles, angles, + fOutputScale, DF.use_deform, DF.use_linear_model); + checkLastError("after allocateVolumeArray"); + + cudaFreeArray(cuArray); + checkLastError("after cudaFreeArray"); + + // THIS WAS BUG IN ASTRA !!!! + cudaUnbindTexture(gT_par3DVolumeTexture); + checkLastError("cudaUnbindTexture"); + + if (DF.use_deform) { + cudaFreeArray(cuArrX0); + cudaFreeArray(cuArrY0); + cudaFreeArray(cuArrZ0); + cudaUnbindTexture(Xdef0_tex); + cudaUnbindTexture(Ydef0_tex); + cudaUnbindTexture(Zdef0_tex); + if (DF.use_linear_model) { + cudaFreeArray(cuArrX1); + cudaFreeArray(cuArrY1); + cudaFreeArray(cuArrZ1); + cudaUnbindTexture(Xdef1_tex); + cudaUnbindTexture(Ydef1_tex); + cudaUnbindTexture(Zdef1_tex); + } + checkLastError("unbind deforms"); + } + + + + + + + return ret; +} + + + +bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale) +{ + // transfer angles to constant memory + float* tmp = new float[dims.iProjAngles]; + +#define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < dims.iProjAngles; ++i) tmp[i] = angles[i].f##name ; cudaMemcpyToSymbol(gC_##name, tmp, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice); } while (0) + + TRANSFER_TO_CONSTANT(RayX); + TRANSFER_TO_CONSTANT(RayY); + TRANSFER_TO_CONSTANT(RayZ); + TRANSFER_TO_CONSTANT(DetSX); + TRANSFER_TO_CONSTANT(DetSY); + TRANSFER_TO_CONSTANT(DetSZ); + TRANSFER_TO_CONSTANT(DetUX); + TRANSFER_TO_CONSTANT(DetUY); + TRANSFER_TO_CONSTANT(DetUZ); + TRANSFER_TO_CONSTANT(DetVX); + TRANSFER_TO_CONSTANT(DetVY); + TRANSFER_TO_CONSTANT(DetVZ); + +#undef TRANSFER_TO_CONSTANT + + delete[] tmp; + + std::list streams; + dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles + + // Run over all angles, grouping them into groups of the same + // orientation (roughly horizontal vs. roughly vertical). + // Start a stream of grids for each such group. + + unsigned int blockStart = 0; + unsigned int blockEnd = 0; + int blockDirection = 0; + + // timeval t; + // tic(t); + + for (unsigned int a = 0; a <= dims.iProjAngles; ++a) { + int dir; + if (a != dims.iProjAngles) { + float dX = fabsf(angles[a].fRayX); + float dY = fabsf(angles[a].fRayY); + float dZ = fabsf(angles[a].fRayZ); + + if (dX >= dY && dX >= dZ) + dir = 0; + else if (dY >= dX && dY >= dZ) + dir = 1; + else + dir = 2; + } + + if (a == dims.iProjAngles || dir != blockDirection) { + // block done + + blockEnd = a; + if (blockStart != blockEnd) { + + dim3 dimGrid( + ((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV), +(blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock); + // TODO: check if we can't immediately + // destroy the stream after use + cudaStream_t stream; + cudaStreamCreate(&stream); + streams.push_back(stream); + + //printf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y); + + if (blockDirection == 0) { + for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + else +#if 0 + par3D_FP_SS_SumSqW_dirX<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); +#else + assert(false); +#endif + } else if (blockDirection == 1) { + for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + else +#if 0 + par3D_FP_SS_SumSqW_dirY<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); +#else + assert(false); +#endif + } else if (blockDirection == 2) { + for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices) + if (dims.iRaysPerDetDim == 1) + par3D_FP_SumSqW_t<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); + else +#if 0 + par3D_FP_SS_SumSqW_dirZ<<>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale); +#else + assert(false); +#endif + } + + } + + blockDirection = dir; + blockStart = a; + } + } + + for (std::list::iterator iter = streams.begin(); iter != streams.end(); ++iter) + cudaStreamDestroy(*iter); + + streams.clear(); + + cudaTextForceKernelsCompletion(); + + + // printf("%f\n", toc(t)); + + return true; +} + +} + diff --git a/+astra/ASTRA_GPU_wrapper/par3d_fp.h b/+astra/ASTRA_GPU_wrapper/par3d_fp.h new file mode 100644 index 0000000..0ce9d29 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/par3d_fp.h @@ -0,0 +1,51 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#ifndef _CUDA_PAR3D_FP_H +#define _CUDA_PAR3D_FP_H + +namespace astraCUDA3d { + +_AstraExport bool Par3DFP_Array(cudaArray *D_volArray, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale); + +_AstraExport bool Par3DFP(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale, DeformField DF); + +_AstraExport bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData, + cudaPitchedPtr D_projData, + const SDimensions3D& dims, const SPar3DProjection* angles, + float fOutputScale); + +} + +#endif diff --git a/+astra/ASTRA_GPU_wrapper/stdafx.cpp b/+astra/ASTRA_GPU_wrapper/stdafx.cpp new file mode 100644 index 0000000..0d6ea5d --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ConsoleApplication2.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/+astra/ASTRA_GPU_wrapper/stdafx.h b/+astra/ASTRA_GPU_wrapper/stdafx.h new file mode 100644 index 0000000..677e68a --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/+astra/ASTRA_GPU_wrapper/targetver.h b/+astra/ASTRA_GPU_wrapper/targetver.h new file mode 100644 index 0000000..90e767b --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/+astra/ASTRA_GPU_wrapper/util3d.cu b/+astra/ASTRA_GPU_wrapper/util3d.cu new file mode 100644 index 0000000..171cdc2 --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/util3d.cu @@ -0,0 +1,688 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp + 2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + +#include +#include +#include "util3d.h" +#include + +#include +#include "cuda_runtime.h" +#include "device_launch_parameters.h" + +//#include "../2d/util.h" + +#include "astra/Logging.h" +#include "mex.h" + + +namespace astraCUDA3d { + + + cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims) + { + cudaExtent extentV; + extentV.width = dims.iVolX*sizeof(float); + extentV.height = dims.iVolY; + extentV.depth = dims.iVolZ; + + cudaPitchedPtr volData; + + cudaError err = cudaMalloc3D(&volData, extentV); + if (err != cudaSuccess) { + astraCUDA3d::reportCudaError(err); + ASTRA_ERROR("Failed to allocate %dx%dx%d GPU buffer", dims.iVolX, dims.iVolY, dims.iVolZ); + volData.ptr = 0; + // TODO: return 0 somehow? + } + + return volData; + } + cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims) + { + cudaExtent extentP; + extentP.width = dims.iProjU*sizeof(float); + extentP.height = dims.iProjAngles; + extentP.depth = dims.iProjV; + + cudaPitchedPtr projData; + + cudaError err = cudaMalloc3D(&projData, extentP); + if (err != cudaSuccess) { + mexPrintf("Failed to allocate %dx%dx%d GPU buffer", dims.iProjU, dims.iProjAngles, dims.iProjV); + projData.ptr = 0; + // TODO: return 0 somehow? + } + + return projData; + } + bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims) + { + char* t = (char*)D_data.ptr; + cudaError err; + + for (unsigned int z = 0; z < dims.iVolZ; ++z) { + err = cudaMemset2D(t, D_data.pitch, 0, dims.iVolX*sizeof(float), dims.iVolY); + ASTRA_CUDA_ASSERT(err); + t += D_data.pitch * dims.iVolY; + } + return true; + } + bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims) + { + char* t = (char*)D_data.ptr; + cudaError err; + + for (unsigned int z = 0; z < dims.iProjV; ++z) { + err = cudaMemset2D(t, D_data.pitch, 0, dims.iProjU*sizeof(float), dims.iProjAngles); + ASTRA_CUDA_ASSERT(err); + t += D_data.pitch * dims.iProjAngles; + } + + return true; + } + bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) + { + if (!pitch) + pitch = dims.iVolX; + + cudaPitchedPtr ptr; + ptr.ptr = (void*)data; // const cast away + ptr.pitch = pitch*sizeof(float); + ptr.xsize = dims.iVolX*sizeof(float); + ptr.ysize = dims.iVolY; + + cudaExtent extentV; + extentV.width = dims.iVolX*sizeof(float); + extentV.height = dims.iVolY; + extentV.depth = dims.iVolZ; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = ptr; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = D_data; + p.extent = extentV; + p.kind = cudaMemcpyHostToDevice; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + + bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) + { + if (!pitch) + pitch = dims.iProjU; + + cudaPitchedPtr ptr; + ptr.ptr = (void*)data; // const cast away + ptr.pitch = pitch*sizeof(float); + ptr.xsize = dims.iProjU*sizeof(float); + ptr.ysize = dims.iProjAngles; + + cudaExtent extentV; + extentV.width = dims.iProjU*sizeof(float); + extentV.height = dims.iProjAngles; + extentV.depth = dims.iProjV; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = ptr; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = D_data; + p.extent = extentV; + p.kind = cudaMemcpyHostToDevice; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + + bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) + { + if (!pitch) + pitch = dims.iVolX; + + cudaPitchedPtr ptr; + ptr.ptr = data; + ptr.pitch = pitch*sizeof(float); + ptr.xsize = dims.iVolX*sizeof(float); + ptr.ysize = dims.iVolY; + + cudaExtent extentV; + extentV.width = dims.iVolX*sizeof(float); + extentV.height = dims.iVolY; + extentV.depth = dims.iVolZ; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_data; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = ptr; + p.extent = extentV; + p.kind = cudaMemcpyDeviceToHost; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch) + { + if (!pitch) + pitch = dims.iProjU; + + cudaPitchedPtr ptr; + ptr.ptr = data; + ptr.pitch = pitch*sizeof(float); + ptr.xsize = dims.iProjU*sizeof(float); + ptr.ysize = dims.iProjAngles; + + cudaExtent extentV; + extentV.width = dims.iProjU*sizeof(float); + extentV.height = dims.iProjAngles; + extentV.depth = dims.iProjV; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_data; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = ptr; + p.extent = extentV; + p.kind = cudaMemcpyDeviceToHost; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + + bool duplicateVolumeData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims) + { + cudaExtent extentV; + extentV.width = dims.iVolX*sizeof(float); + extentV.height = dims.iVolY; + extentV.depth = dims.iVolZ; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_src; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = D_dst; + p.extent = extentV; + p.kind = cudaMemcpyDeviceToDevice; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + bool duplicateProjectionData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims) + { + cudaExtent extentV; + extentV.width = dims.iProjU*sizeof(float); + extentV.height = dims.iProjAngles; + extentV.depth = dims.iProjV; + + cudaPos zp = { 0, 0, 0 }; + + cudaMemcpy3DParms p; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_src; + p.dstArray = 0; + p.dstPos = zp; + p.dstPtr = D_dst; + p.extent = extentV; + p.kind = cudaMemcpyDeviceToDevice; + + cudaError err; + err = cudaMemcpy3D(&p); + ASTRA_CUDA_ASSERT(err); + + return err == cudaSuccess; + } + + + + // TODO: Consider using a single array of size max(proj,volume) (per dim) + // instead of allocating a new one each time + + cudaArray* allocateVolumeArray(const SDimensions3D& dims) + { + cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(); + cudaArray* cuArray; + cudaExtent extentA; + extentA.width = dims.iVolX; + extentA.height = dims.iVolY; + extentA.depth = dims.iVolZ; + cudaError err = cudaMalloc3DArray(&cuArray, &channelDesc, extentA); + if (err != cudaSuccess) { + mexPrintf("Failed to allocate %dx%dx%d GPU array", dims.iVolX, dims.iVolY, dims.iVolZ); + return 0; + } + + return cuArray; + } + cudaArray* allocateProjectionArray(const SDimensions3D& dims) + { + cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(); + cudaArray* cuArray; + cudaExtent extentA; + extentA.width = dims.iProjU; + extentA.height = dims.iProjAngles; + extentA.depth = dims.iProjV; + cudaError err = cudaMalloc3DArray(&cuArray, &channelDesc, extentA); + + if (err != cudaSuccess) { + mexPrintf("Failed to allocate %dx%dx%d GPU array", dims.iProjU, dims.iProjAngles, dims.iProjV); + return 0; + } + + return cuArray; + } + + bool bindDataTexture(const cudaArray* array, texture3D & Texture, cudaTextureAddressMode bordermode, bool normalized) + { + cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(); + Texture.addressMode[0] = bordermode; + Texture.addressMode[1] = bordermode; + Texture.addressMode[2] = bordermode; + Texture.filterMode = cudaFilterModeLinear; + Texture.normalized = normalized; + + cudaError err = cudaBindTextureToArray(Texture, array, channelDesc); + + checkLastError("cudaBindTextureToArray cudaMemcpy3D"); + ASTRA_CUDA_ASSERT(err); + + //mexPrintf("Max texture size !!! %i %i %i", cudaDeviceProp.maxTexture3D[0], cudaDeviceProp.maxTexture3D[1], cudaDeviceProp.maxTexture3D[2]); + return true; + } + + cudaArray * transferDeformationToArray(const mxGPUArray * m_img) + { + mwSize const * dimensions = mxGPUGetDimensions(m_img); + mwSize Ndim = mxGPUGetNumberOfDimensions(m_img); + int M = (int)dimensions[0]; + int N = (int)dimensions[1]; + int O = Ndim > 2 ? (int)dimensions[2] : 1; + + SDimensions3D dims; + dims.iVolX = M; + dims.iVolY = N; + dims.iVolZ = O; + + //mexPrintf("Deformation field size: %i %i %i \n", M,N,O); + + + cudaArray* array = allocateVolumeArray(dims); + + // get the values into float array + const float * img =(const float *)mxGPUGetDataReadOnly(m_img); + + if (array == 0) + return 0; + + if (M * sizeof(float) > 2048) { + mexPrintf("Volume is too large to be transfered to GPU array"); + return 0; + } + + // make volume array (no copying) + cudaPitchedPtr volume; + volume.ptr = (float *)img; + volume.pitch = M * sizeof(float); + volume.xsize = M; + volume.ysize = N; + + + transferVolumeToArray(volume, array,dims); + + // if (!checkLastError("transferDeformToArray cudaMemcpy3D")) + // return false; + + return array; + } + + + bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims) + { + cudaExtent extentA; + extentA.width = dims.iVolX; + extentA.height = dims.iVolY; + extentA.depth = dims.iVolZ; + + cudaMemcpy3DParms p; + cudaPos zp = { 0, 0, 0 }; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_volumeData; + p.dstArray = array; + p.dstPtr.ptr = 0; + p.dstPtr.pitch = 0; + p.dstPtr.xsize = 0; + p.dstPtr.ysize = 0; + p.dstPos = zp; + p.extent = extentA; + p.kind = cudaMemcpyDeviceToDevice; + + cudaError err = cudaMemcpy3D(&p); + + checkLastError("transferVolumeToArray cudaMemcpy3D"); + ASTRA_CUDA_ASSERT(err); + // TODO: check errors + return true; + } + + bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims) + { + cudaExtent extentA; + extentA.width = dims.iProjU; + extentA.height = dims.iProjAngles; + extentA.depth = dims.iProjV; + + cudaMemcpy3DParms p; + cudaPos zp = { 0, 0, 0 }; + p.srcArray = 0; + p.srcPos = zp; + p.srcPtr = D_projData; + p.dstArray = array; + p.dstPtr.ptr = 0; + p.dstPtr.pitch = 0; + p.dstPtr.xsize = 0; + p.dstPtr.ysize = 0; + p.dstPos = zp; + p.extent = extentA; + p.kind = cudaMemcpyDeviceToDevice; + + cudaError err = cudaMemcpy3D(&p); + checkLastError("transferProjectionsToArray cudaMemcpy3D"); + + ASTRA_CUDA_ASSERT(err); + + // TODO: check errors + + return true; + } + + + bool cudaTextForceKernelsCompletion() + { + cudaError_t returnedCudaError = cudaThreadSynchronize(); + + if (returnedCudaError != cudaSuccess) { + //FIXME + fprintf(stderr, "Failed to force completion of cuda kernels: %d: %s. \n ", returnedCudaError, cudaGetErrorString(returnedCudaError)); + ASTRA_ERROR("Failed to force completion of cuda kernels: %d: %s.\n ", returnedCudaError, cudaGetErrorString(returnedCudaError)); + return false; + } + + return true; + } + + void reportCudaError(cudaError_t err) + { + if (err != cudaSuccess) { + mexPrintf("CUDA error %d: %s.", err, cudaGetErrorString(err)); + mexErrMsgTxt("ASTRA failed, reboot GPU"); + } + } + + + + // + //float dotproduct3d(cudapitchedptr data, unsigned int x, unsigned int y, + // unsigned int z) + //{ + // return astraCUDA3d::dotproduct2d((float*)data.ptr, data.pitch/sizeof(float), x, y*z); + //} + + + + int calcNextPowerOfTwo(int _iValue) + { + int iOutput = 1; + while (iOutput < _iValue) + iOutput *= 2; + return iOutput; + } + + double tic() + { + return clock(); + } + + double toc(double tstart) + { + return (clock() - tstart) / CLOCKS_PER_SEC; + } + + + + void printFreeMemory() + { + // show memory usage of GPU + size_t free_byte; + size_t total_byte; + cudaError_t cuda_status = cudaMemGetInfo(&free_byte, &total_byte); + + if (cudaSuccess != cuda_status){ + mexPrintf("Error: cudaMemGetInfo fails, %s \n", cudaGetErrorString(cuda_status)); + } + double free_db = (double)free_byte; + double total_db = (double)total_byte; + double used_db = total_db - free_db; + mexPrintf("GPU memory usage: used = %g, free = %g MB, total = %g MB\n", + used_db / 1024.0 / 1024.0, free_db / 1024.0 / 1024.0, total_db / 1024.0 / 1024.0); + } + + + int checkLastError(char * msg) + { + cudaError_t cudaStatus = cudaGetLastError(); + if (cudaStatus != cudaSuccess) { + char err[512]; + sprintf(err, "astraCUDA3d failed %s: %s. \n", msg, cudaGetErrorString(cudaStatus)); + mexErrMsgTxt(err); + //mexPrintf(err); + //mexPrintf("assert \n"); + //ASTRA_CUDA_ASSERT(cudaStatus); + } + return 0; + } + + int dumpArray(char* filename, int width, int height, float *buffer) + { + FILE * f; + int i, j; + f = fopen(filename, "w"); + for (i = 0; i < height; i++) + { + for (j = 0; j < width; j++) + { + fprintf(f, "%3.2g\t", buffer[i*width + j]); + // fprintf(f, "%i %i\t", i, j); + + //fprintf(f, "%3.2g\t", 1); + } + fprintf(f, "\n"); + } + fclose(f); + return 0; + } + + int dumpCudaArray(cudaPitchedPtr Data, int start, int end, char * filename) + { + + char fname[32], msg[32]; + int width = Data.xsize / sizeof(float); + int height = Data.ysize; + int slice_size = width*height*sizeof(float); + float* buffer = new float[width*height]; + for (int i = start; i < end; i++) { + cudaMemcpy(buffer, ((float*)Data.ptr) + slice_size*i, slice_size, cudaMemcpyDeviceToHost); + sprintf(fname, filename, i); + sprintf(msg, filename, i); + fprintf(stdout, "%s\n", msg); + dumpArray(fname, width, height, buffer); + } + return 0; + } + + int writeImageCudaArray(cudaPitchedPtr Data, int start, int end, char * filename) + { + + char fname[32]; + int width = Data.xsize / sizeof(float); + int height = Data.ysize; + int slice_size = width*height*sizeof(float); + float* buffer = new float[width*height]; + for (int i = start; i < end; i++) { + cudaMemcpy(buffer, ((float*)Data.ptr) + slice_size*i, slice_size, cudaMemcpyDeviceToHost); + sprintf(fname, filename, i); + writeImage(fname, width, height, buffer); + } + return 0; + } + + int writeImage(char * fname, int w, int h, float * data) + { + // normalize image + float max = 0; + for (int i = 0; i < w*h; i++) + if (data[i] > max) + max = data[i]; + + float **x; + /* allocate the array */ + x = (float **)malloc(h * sizeof *x); + for (int i = 0; i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + img[(x + y*w) * 3 + 2] = (unsigned char)(r); + img[(x + y*w) * 3 + 1] = (unsigned char)(g); + img[(x + y*w) * 3 + 0] = (unsigned char)(b); + } + } + + unsigned char bmpfileheader[14] = { 'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 }; + unsigned char bmpinfoheader[40] = { 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0 }; + unsigned char bmppad[3] = { 0, 0, 0 }; + + bmpfileheader[2] = (unsigned char)(filesize); + bmpfileheader[3] = (unsigned char)(filesize >> 8); + bmpfileheader[4] = (unsigned char)(filesize >> 16); + bmpfileheader[5] = (unsigned char)(filesize >> 24); + + bmpinfoheader[4] = (unsigned char)(w); + bmpinfoheader[5] = (unsigned char)(w >> 8); + bmpinfoheader[6] = (unsigned char)(w >> 16); + bmpinfoheader[7] = (unsigned char)(w >> 24); + bmpinfoheader[8] = (unsigned char)(h); + bmpinfoheader[9] = (unsigned char)(h >> 8); + bmpinfoheader[10] = (unsigned char)(h >> 16); + bmpinfoheader[11] = (unsigned char)(h >> 24); + + f = fopen(fname, "wb"); + fwrite(bmpfileheader, 1, 14, f); + fwrite(bmpinfoheader, 1, 40, f); + for (int i = 0; i < h; i++) + { + fwrite(img + (w*(h - i - 1) * 3), 3, w, f); + fwrite(bmppad, 1, (4 - (w * 3) % 4) % 4, f); + } + fclose(f); + + + fprintf(stdout, "Saved image %s\n", fname); + + return 0; + + } +} \ No newline at end of file diff --git a/+astra/ASTRA_GPU_wrapper/util3d.h b/+astra/ASTRA_GPU_wrapper/util3d.h new file mode 100644 index 0000000..3cf277f --- /dev/null +++ b/+astra/ASTRA_GPU_wrapper/util3d.h @@ -0,0 +1,121 @@ +/* +----------------------------------------------------------------------- +Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp +2014-2015, CWI, Amsterdam + +Contact: astra@uantwerpen.be +Website: http://sf.net/projects/astra-toolbox + +This file is part of the ASTRA Toolbox. + + +The ASTRA Toolbox is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The ASTRA Toolbox is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with the ASTRA Toolbox. If not, see . + +----------------------------------------------------------------------- +$Id$ +*/ + + +#include +#include + +#ifdef _MSC_VER + +#ifdef DLL_EXPORTS +#define _AstraExport __declspec(dllexport) +#define EXPIMP_TEMPLATE +#else +#define _AstraExport __declspec(dllimport) +#define EXPIMP_TEMPLATE extern +#endif + +#else + +#define _AstraExport + +#endif + +//#include "dims.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define ASTRA_CUDA_ASSERT(err) do { if (err != cudaSuccess) { astraCUDA3d::reportCudaError(err); assert(err == cudaSuccess); } } while(0) + + +#ifndef _CUDA_UTIL3D_H +#define _CUDA_UTIL3D_H + +#include +#include "dims3d.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +//#include "../2d/util.h" + + + + +namespace astraCUDA3d { + + typedef texture texture3D; + + cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims); + cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims); + bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims); + bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims); + bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); + bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); + bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); + bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0); + bool duplicateVolumeData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims); + bool duplicateProjectionData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims); + + bool transferVolumeToArray_1D(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims); + + bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims); + bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims); + bool zeroProjectionArray(cudaArray* array, const SDimensions3D& dims); + bool zeroVolumeArray(cudaArray* array, const SDimensions3D& dims); + cudaArray* allocateProjectionArray(const SDimensions3D& dims); + cudaArray* allocateVolumeArray(const SDimensions3D& dims); + cudaArray* transferDeformationToArray(const mxGPUArray * m_img); + bool bindDataTexture(const cudaArray* array, texture3D & Texture, cudaTextureAddressMode bordermode, bool normalized); + + //float dotProduct3D(cudaPitchedPtr data, unsigned int x, unsigned int y, unsigned int z); + + int calcNextPowerOfTwo(int _iValue); + + + bool cudaTextForceKernelsCompletion(); + void reportCudaError(cudaError_t err); + + double toc(double tstart); + double tic(); + int checkLastError(char * msg); + void printFreeMemory(); + + int dumpArray(char* filename, int width, int height, float *buffer); + int dumpCudaArray(cudaPitchedPtr projData, int syart, int end, char * filename); + int writeImage(char * fname, int w, int h, float * data); + int writeBMPImage(char * fname, int w, int h, float ** red, float ** green, float ** blue); + int writeImageCudaArray(cudaPitchedPtr Data, int start, int end, char * filename); + + + +} + +#endif diff --git a/+astra/ASTRA_find_optimal_split.m b/+astra/ASTRA_find_optimal_split.m new file mode 100644 index 0000000..9d7744f --- /dev/null +++ b/+astra/ASTRA_find_optimal_split.m @@ -0,0 +1,103 @@ +% ASTRA_FIND_OPTIMAL_SPLIT Find optimal split of data and make the blocks sufficiently small for +% limited GPU memory +% +% split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator) +% +% Inputs: +% **cfg - config structure generated by ASTRA_initialize +% **num_gpu - number of gpu to split the data +% **angle_blocks - number of angular blocks (ie in SART method or FSC) +% **propagator - which propagator should be assumed : FWD, BACK, both (default) +% Outputs: +% ++split - volume / angle split - [split_x,split_y,split_z,split_angles] + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + + +function split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator) + + if gpuDeviceCount == 0 + split = 1; + return + end + + gpu = gpuDevice; + if nargin < 2 || num_gpu == 0 + num_gpu = 1; + end + if nargin < 3 + angle_blocks = 1; + end + if nargin < 4 + propagator = 'both'; + end + + Nangles = cfg.iProjAngles / angle_blocks; + if isfield(cfg, 'Grouping') + Nangles = min(Nangles, cfg.Grouping); + end + + split = [1,1,1]; + if ismember(lower(propagator), {'fwd','both'}) + split = max(split, ceil([cfg.iVolX, cfg.iVolY, cfg.iVolZ] / 4096 )); % texture memory limit + split = max(split, [1,1,ceil( (cfg.iVolX*cfg.iVolY*cfg.iVolZ*4) / 1.024e9 / prod(split)/num_gpu)]); % texture memory limit + end + split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)/ prod(split)/num_gpu) / (gpu.AvailableMemory/2 - min(1.1e9, cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)) )]); % gpu memory limit + split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ )/ prod(split)/num_gpu) / double(intmax('int32')) )]); % maximal array on GPU limit + + if ismember(lower(propagator), {'back','both'}) + % if projection would be larger than 4096x4096 -> split the reconstruction volume + split = max(split, [cfg.iProjU, cfg.iProjU, cfg.iProjV]/4096 ); % texture memory limit + end + + % projection size limitation + astra allows only < 1024 angles + split(4) = max(ceil(Nangles/1024), ceil( (cfg.iProjU*cfg.iProjV*min(1024,Nangles)*4) / gpu.TotalMemory/num_gpu)); % gpu memory limit + + % RAM limits + if cfg.iVolX*cfg.iVolY*cfg.iVolZ > 2e6 + freemem = 0.8 * utils.check_available_memory; + split(4) = max(split(4), num_gpu*(4*(cfg.iProjU*cfg.iProjV*cfg.iProjAngles... + + cfg.iVolX*cfg.iVolY*cfg.iVolZ*(1+1/prod(split(1:3)))))... + / (freemem * 1e6) ); % RAM memory limit + end + split(4) = ceil(split(4)); + split(1:3) = 2.^nextpow2(split(1:3)); +% if any(split ~= 1) +% fprintf('Automatically splitting to %ix%ix%ix(%i) cubes \n', split); +% end + +end \ No newline at end of file diff --git a/+astra/ASTRA_initialize.m b/+astra/ASTRA_initialize.m new file mode 100644 index 0000000..13fa401 --- /dev/null +++ b/+astra/ASTRA_initialize.m @@ -0,0 +1,299 @@ +% ASTRA_INITIALIZE Generate inputs needed for astra MEX wrapper +% +% [cfg, vectors] = ASTRA_initialize(Npix, size_projection,angles,lamino_angle, tilt_angle, pixel_scale, rotation_center) +% +% Inputs for angular geometry (!! all angles are expected in degress !!): +% **Npix - size of tomogram +% **size_projection - size of sinogram (Nlayers, width, Nangles) +% **angles - rotation angles of projections in degrees +% *optional*: +% **lamino_angle - laminography angle / angles in degrees. lamino_angle == +% 90 is standard tomography , default = 90 +% **tilt_angle - tilt of camera with respect to the rotation axis coordinates, in degrees, default = 0 +% **pixel_scale - scale of pixels in tomogram compares to the +% projection pixel size, default = 1 +% **rotation_center - center of rotation coordinates, default = size_projection/2 +% **skewness_angle - distorsion of parallel axis by [1, sind(alpha); 0, 1] +% +% Inputs for rotation matrix geometry: +% **Npix - size of tomogram +% **size_projection - size of sinogram (Nlayers, width, Nangles) +% **rotation_matrix - R is a 3x3xn matrix, for n projections +% +% Outputs: +% ++cfg - config structure for ASTRA mex wrapper +% ++vectors - parameter vector for each angle + + +%*-----------------------------------------------------------------------* +%| | +%| Except where otherwise noted, this work is licensed under a | +%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 | +%| International (CC BY-NC-SA 4.0) license. | +%| | +%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) | +%| | +%| Author: CXS group, PSI | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + + + + +function [cfg, vectors] = ASTRA_initialize(Npix, size_projection, varargin) + use_rotmat = length(varargin) == 1 && size(varargin{1},1) == 3 && size(varargin{1},2) == 3 ; + if use_rotmat + % rotation matrices were provided + rot_mat = varargin{1}; + Nangles = size(rot_mat,3); + r.rotation_center = size_projection/2; % only centered geometry is supported when rotation matrix is provided + else + % angles and other parameters were provided + par = inputParser; + par.KeepUnmatched = true; + %% ALL angles are assumed in degrees + par.addRequired('angles') % rotation angles of projections in degrees + par.addOptional('lamino_angle', 90, @isnumeric) % laminography angle / angles in degrees. lamino_angle == 90 is standard tomography , default = 90 + par.addOptional('tilt_angle', 0, @isnumeric) % tilt of camera with respect to the rotation axis coordinates, in + par.addOptional('pixel_scale', [1,1], @isnumeric) % scale of pixels in tomogram compares to the projection pixel size, default = 1 + par.addOptional('rotation_center', size_projection/2, @isnumeric) % center of rotation cooridinates, default = size_projection/2 + par.addOptional('skewness_angle', 0) % distorsion of parallel axis by [1, sind(alpha); 0, 1] + par.addParameter('show_geometry', false) % plot also a geometry for each projection + par.parse(varargin{:}) + r = par.Results; + Nangles = length(r.angles); + end + + + % angles should be sorted in order to maximize performance of the astra + % toolbox -> better use of texture memory + + assert(math.isint(Npix), 'Npix is not integer'); + assert(math.isint(size_projection), 'size_projection is not integer'); + + if isscalar(Npix) + Npix(2) = Npix; + end + if length(Npix) == 2 && all(r.lamino_angle == 90) + Npix(3) = size_projection(1); % default behaviour is to have same number of layers in reconstruction and in laminography + elseif length(Npix) == 2 && any(r.lamino_angle ~= 90) + error('All three dimensions of the volume size has to be specified for the laminograhy geometry') + end + + cfg.iVolX = Npix(1); + cfg.iVolY = Npix(2); + cfg.iVolZ = Npix(3); + cfg.iProjAngles = Nangles; + cfg.iProjU = size_projection(2); + cfg.iProjV = size_projection(1); + cfg.iRaysPerDet = 1; + cfg.iRaysPerDetDim = 1; + cfg.iRaysPerVoxelDim = 1; + source_distance = 1; % currenlty not implemented in the ASTRA wrapper + + + if use_rotmat + [vectors] = astra_convert_R_vectors(rot_mat,Nangles); + else + % compatibility with iradonfast + r.angles = r.angles + 90; + cfg.lamino_angle = r.lamino_angle; + cfg.pixel_scale = r.pixel_scale; + cfg.tilt_angle = r.tilt_angle; + cfg.skewness_angle = r.skewness_angle; + vectors = ASTRA_get_geometry(r.angles, r.lamino_angle, r.tilt_angle, source_distance,r.pixel_scale,r.skewness_angle,r.show_geometry); + end + + %%%% apply geometry correction to shift reconstruction into center %%%%%%%%%%%%%%%%%%%%%%%%%% + vectors(:,4:6) = vectors(:,4:6) -(vectors(:,10:12).*(r.rotation_center(:,1) )+vectors(:,7:9).*(r.rotation_center(:,2) )); + +end + +function vectors = ASTRA_get_geometry(angles, lamino_angle, tilt_angle, source_distance,pixel_scale,skewness_angle,show) + + Nangles = numel(angles); + % angles should be sorted in order to maximize performance of the astra + % toolbox -> better use of texture memory + + angles = deg2rad(angles(:)); + lamino_angle = pi/2 - deg2rad(lamino_angle); + tilt_angle = deg2rad(tilt_angle); + skewness_angle = deg2rad(skewness_angle); + + if isscalar(lamino_angle) + lamino_angle = lamino_angle .* ones(Nangles,1); + end + if isscalar(tilt_angle) + tilt_angle = tilt_angle .* ones(Nangles,1); + end + if isscalar(skewness_angle) + skewness_angle = skewness_angle .* ones(Nangles,1); + end + if isscalar(pixel_scale) || numel(pixel_scale) == 2 + pixel_scale = bsxfun(@times, pixel_scale , ones(Nangles,2)); + end + + % We generate the same geometry as the circular one above. + vectors = zeros(Nangles, 12); + % ray direction + vectors(:,1) = sin(angles).*cos(lamino_angle); + vectors(:,2) = -cos(angles).*cos(lamino_angle); + vectors(:,3) = sin(lamino_angle); + + vectors(:,1:3) = vectors(:,1:3) .*source_distance; + % center of detector + vectors(:,4:6) = 0; + % vector from detector pixel (0,0) to (0,1) + vectors(:,7) = cos(angles)./pixel_scale(:,1); + vectors(:,8) = sin(angles)./pixel_scale(:,1); + vectors(:,9) = 0/pixel_scale(:,1); + + % vector from detector pixel (0,0) to (1,0) + + % cross(vectors(i,1:3), vectors(i,7:9)) + % dot(vectors(i,1:3), vectors(i,7:9)) + + vectors(:,10) = - sin(lamino_angle).*sin(angles)./pixel_scale(:,2); + vectors(:,11) = sin(lamino_angle).*cos(angles)./pixel_scale(:,2); + vectors(:,12) = cos(lamino_angle)./pixel_scale(:,2); + + % Rodrigues' rotation formula - rotate detector in plane + % perpendicular to the beam axis + if any(tilt_angle ~= 0) + for i = 1:Nangles + vectors(i,7:9)=vectors(i,7:9).*cos(tilt_angle(i)) + ... + cross(vectors(i,1:3), vectors(i,7:9)).*sin(tilt_angle(i)) + ... + (vectors(i,1:3)*dot(vectors(i,1:3),vectors(i,7:9))).*(1-cos(tilt_angle(i))); + vectors(i,10:12)=vectors(i,10:12).*cos(tilt_angle(i)) + ... + cross(vectors(i,1:3), vectors(i,10:12)).*sin(tilt_angle(i)) + ... + (vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(tilt_angle(i))); + end + end + + % search also for skewness => the same as rotation, but rotate + % only one axis of the detector !! + if any(skewness_angle ~= 0) + for i = 1:Nangles + vectors(i,10:12)=vectors(i,10:12).*cos(skewness_angle(i)/2) + ... + cross(vectors(i,1:3), vectors(i,10:12)).*sin(skewness_angle(i)/2) + ... + (vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(skewness_angle(i)/2)); + end + end + + %% PLOT THE CURRENT SETUP + + if show + for i = 1:Nangles + draw_projection_geometry(vectors(i,:)) + end + end + + + + +end + +function [vectors] = astra_convert_R_vectors(rot_mat,Nangles) + +% R is defined as in the arbitrary projection code by Manuel +% This integrates along z (2nd index) and so this code follows this +% convention. +pixel_scale(1:Nangles,1:2) = [1]; + + +convert_matrix = [0 0 1 + 0 1 0 + 1 0 0]; + +for ii=1:size(rot_mat,3) + rot_mat(:,:,ii)=rot_mat(:,:,ii)*convert_matrix; +end + +vectors = zeros(Nangles, 12); + for i = 1:Nangles + % before starting to mess around: works with a correction matrix + % with the magnetic contrast, but not for the laminography + vectors(i,1) = -rot_mat(3,1,i); + vectors(i,2) = -rot_mat(3,3,i); + vectors(i,3) = -rot_mat(3,2,i); + + vectors(i,4:6) = 0; + + vectors(i,7) = rot_mat(1,1,i)/pixel_scale(i,1); + vectors(i,8) = rot_mat(1,3,i)/pixel_scale(i,1); + vectors(i,9) = rot_mat(1,2,i)/pixel_scale(i,1); + + vectors(i,10) = rot_mat(2,1,i)/pixel_scale(i,2); + vectors(i,11) = rot_mat(2,3,i)/pixel_scale(i,2); + vectors(i,12) = rot_mat(2,2,i)/pixel_scale(i,2); + end +end + + +function draw_projection_geometry(vectors) + % show geometry saved in the "vectors" matrix + ray = vectors(1:3); + c_center = vectors(4:6)-ray; + c_origin = c_center - vectors( 7:9)/2-vectors( 10:12)/2; + + k = 6; + n = 2^k-1; + [x,y,z] = sphere(n); + c = hadamard(2^k); + s = 0.5; + figure(15) + surf(vectors(4)+s*x,vectors(5)+s*y,vectors(6)+s*z,c); + shading flat + colormap([1 1 0; 0 1 1]) + + hold all + plot3d_vec(c_origin, vectors( 7:9), 'r'); + plot3d_vec(c_origin, vectors( 10:12), 'r'); + plot3d_vec(c_origin+vectors( 7:9), vectors( 10:12), 'r'); + plot3d_vec(c_origin+vectors( 10:12), vectors( 7:9), 'r'); + % draw "pixels" on a 10x10 grid + for x = linspace(0,1,10) + plot3d_vec(c_origin+x*vectors( 10:12), vectors( 7:9), 'r:'); + plot3d_vec(c_origin+x*vectors( 7:9),vectors( 10:12), 'r:'); + end + plotting.mArrow3(c_center+ray*2,c_center, 'color', 'blue', 'stemWidth',0.02,'facealpha',0.5); + + hold off + axis([-1,1,-1,1,-1,1]) + drawnow + +end + +function h_out = plot3d_vec(x0, vec, varargin) + h = plot3(x0(1)+[0,vec(1)], ... + x0(2)+[0,vec(2)], ... + x0(3)+[0,vec(3)], varargin{:}); + if nargout > 1 + h_out = h; + end + +end + diff --git a/+astra/Atx_partial.m b/+astra/Atx_partial.m new file mode 100644 index 0000000..87578f6 --- /dev/null +++ b/+astra/Atx_partial.m @@ -0,0 +1,339 @@ +% AX_PARTIAL backprojector that allows to split the full volume into smaller pieces +% composed tomography back-projector based on ASTRA toolbox +% can be used either for data in RAM or on GPU (automatically decided from class of volData) +% * volume is split based on "split" parameter, 1 == no splitting +% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits) +% +% vol_full = Atx_partial(projData, cfg, vectors,split,varargin) +% +% Inputs: +% **projData - array Nx x Ny x Nz of projected volume +% **cfg - config structure generated by ASTRA_initialize +% **vectors - orientation of projections generated by ASTRA_initialize +% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ] +% *optional* +% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object +% **GPU - GPU id to be used for reconstruction +% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug +% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option) +% +% *returns* +% ++vol_full - projection of volData +% +% recompile commands +% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu +% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + +function vol_full = Atx_partial(projData, cfg, vectors,split,varargin) + + + import utils.* + import math.* + + par = inputParser; + par.KeepUnmatched = true; + + par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object + par.addOptional('GPU', []) % GPUs id to be used in reconstruction + par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster + par.addOptional('verbose', 0) % + + par.parse(varargin{:}) + r = par.Results; + + if isempty(r.deformation_fields); r.deformation_fields = {}; end + + %% check the inputs + check memory availibility on GPU + assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible') + + if ~( (isa(projData, 'gpuArray') && strcmp(classUnderlying(projData), 'single')) || ... + isa(projData, 'single') ) || ~isreal(projData) + error('Only single precision real input array supported') + end + + if ~isempty(r.deformation_fields) + assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array') + + for i = 1:numel(r.deformation_fields) + if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ... + isa(r.deformation_fields{i}, 'single')) + error('Only single precision for deformation fields is supported') + end + r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small + end + r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field + end + + + projSize = size(projData); + cfg.iProjAngles = size(vectors,1); % ignore value in config + + gpu = gpuDevice(); + if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1) + % switch and !! reset !! GPU + if isa(projData, 'gpuArray'), error('Switching GPUs will reset content'); end + gpu = gpuDevice(r.GPU(1)); + end + + + assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Volume is empty'); + assert(numel(projSize) > 0, 'Projections are empty'); + + % input parameters check + if ismatrix(projData); projSize = [projSize,1]; end + assert(max(cfg.iProjU, cfg.iProjV) <= 4096, 'Sinogram exceed maximal size allowed by GPU (4096)') + assert(all(projSize==[cfg.iProjV,cfg.iProjU,cfg.iProjAngles]), 'Wrong inputs size') + assert(all(size(vectors)==[cfg.iProjAngles,12]), 'Wrong vectors size') + + split_projections = cfg.iProjAngles > 1024 || ... + numel(projData)*4 > 1024e6 || ... ; % Data array is to large, try splitting + (length(split) > 3 && split(4) > 1); % split contains also angular split + + % be sure that ASTRA wrapper is feeded by doubles !! + for i = fieldnames(cfg)' + cfg.(i{1}) = double(cfg.(i{1})); + end + vectors = double(vectors); + split=double(split); + + if all(split == 1) && ~split_projections + assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial'); + %% in the simplest case call ASTRA_GPU_wrapper directly + if isa(projData, 'gpuArray'), r.keep_on_GPU = true; end % assume that the data should stay on GPU if provided + projData = matlab2astra(gpuArray(projData)); + vol_full = astra.ASTRA_GPU_wrapper('bp',projData, cfg, vectors,[],r.deformation_fields{:}); + if ~r.keep_on_GPU; vol_full = gather(vol_full); end + return + end + + %% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes + + split = ceil(max(1,split)); + if isscalar(split) + split = split .* ones(3,1); + end + + if length(split) < 4 + split(4) = 1; + end + + + %% PREPARE VOLUME %% + + Npix_full = [cfg.iVolX,cfg.iVolY,cfg.iVolZ]; + Npix_small = Npix_full(:)./reshape(split(1:3),[],1); + + cfg.iVolX = Npix_small(1); + cfg.iVolY = Npix_small(2); + cfg.iVolZ = Npix_small(3); + + assert( all(mod(Npix_small,1)==0), sprintf('Volume array cannot be divided to %i %i %i cubes', split)) + assert(prod(Npix_small)*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial'); + + + req_vol_memory = 4*(cfg.iVolX*cfg.iVolY*cfg.iVolZ) * (1+any(split(1:3)>1)); + + keep_volume_on_GPU = isa(projData, 'gpuArray') ||... + (gpu.AvailableMemory * 0.5 > req_vol_memory) && ... + ( (cfg.iVolX*cfg.iVolY*cfg.iVolZ) < intmax('int32') ); + + % preallocate large array for results + if keep_volume_on_GPU || prod(split(1:3)) == 1 + % transfer to GPU now + vol_full = gpuArray.zeros(Npix_full, 'single'); + else + % keep the volume in RAM + vol_full = zeros(Npix_full, 'single'); + end + + %% PREPARE PROJECTIONS %% + + % fix if the number of projections is > 1024, or arrays are too large + Nproj_groups = max(split(4),ceil(max([cfg.iProjAngles/1024, ... + cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / min(1024e6,gpu.AvailableMemory-1.024e9), ... + cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))]))); + + % avoid some rounding issues during splitting + Nproj_groups = ceil(cfg.iProjAngles / floor(cfg.iProjAngles/Nproj_groups)); + + + if r.verbose > 1 + fprintf('Size of the full volume: %i %i %i\n', Npix_full) + fprintf('Size of the subvolume: %i %i %i\n', Npix_small) + fprintf('Size of the data: %i %i %i\n', size(projData)) + fprintf('Free GPU memory: %3.2g%%\n', gpu.AvailableMemory/gpu.TotalMemory*100) + end + + if Nproj_groups > 1 + % fix if the number of projections is > 1024 (limitation of the ASTRA code ) + % also if the dataset is too large (>1024MB), do automatic splitting along angles + + for i = 1:Nproj_groups + ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups); + ind = ind(ind <= cfg.iProjAngles); + proj_ind{i} = ind; + vectors_tmp{i} = vectors(ind,:); + cfg_tmp{i} = cfg; + cfg_tmp{i}.iProjAngles = length(ind); + end + cfg = cfg_tmp; vectors = vectors_tmp; + clear projData_tmp + else + Nproj_groups = 1; + cfg = {cfg}; + vectors = {vectors}; + end + + if ~isempty(r.deformation_fields) && any(split(1:3) > 1) + error('Deformation field splitting not implemented') + end + + if gpu.AvailableMemory > 4*(numel(projData)+ ... + cfg{1}.iProjU* cfg{1}.iProjV*cfg{1}.iProjAngles * (Nproj_groups>1) ... + +any(split>1)*prod(Npix_small)*(2 + ~isa(vol_full, 'gpuArray'))) && ... + numel(projData) < intmax('int32') + projData = gpuArray(projData); % move small blocks directly on GPU + end + + inParpool = ~isempty(getCurrentTask()); + iter = 1; + for k = 1:Nproj_groups + if Nproj_groups > 1 + if length(proj_ind{k}) > 1 && ~inParpool && ~isa(projData, 'gpuArray') && exist('+tomo/get_from_array.m', 'file') + % use custom made MEX function from +tomo package, usually + % faster but use a lot of CPU + projData_small = tomo.get_from_array(projData, [], proj_ind{k}); % load subblock from projections + else + projData_small = projData(:,:,proj_ind{k}); % split the projections if needed + end + else + projData_small = projData; + end + if gpu.AvailableMemory < 2*4*numel(projData_small) + % in case of low GPU memory, transpose data in RAM + projData_small = gpuArray(matlab2astra(projData_small)); % transfer projections to GPU if not there yet + else + % move subblocks of the data on GPU + projData_small = gpuArray(projData_small); + projData_small = matlab2astra(projData_small); + end + if numel(projData_small) * 4 > 1024e6 + error('Data exceeded maximal size of texture memory 1024MB, Increase "split" to reduce the projection size') + + end + + if gpu.AvailableMemory < prod(Npix_small)*4 && prod(split(1:3)) ~= 1 + % memory needed to make CUDA array for texture memory + pause(0.1) + !nvidia-smi + whos + error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,prod(Npix_small)*4/1e9, gpu.Index, gpuDeviceCount) + end + for z = 1:split(3) + for x = 1:split(1) + for y =1:split(2) + if r.verbose > 0 + progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20); + end + pos = [x,y,z]; + for n = 1:3 + % find optimal shift of the subvolume + if mod(split(n),2)==1 %% odd + shift(n) = (pos(n) - ceil(split(n)/2))*Npix_small(n); + else + shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Npix_small(n); + end + end + + vectors_tmp = vectors{k}; + vectors_tmp(:,4:6) = bsxfun(@minus, vectors_tmp(:,4:6), shift); + if prod(split(1:3))== 1 + req_mem = numel(projData_small)*4 ; + else + req_mem = 2*numel(projData_small)*4 ; + end + if gpu.AvailableMemory < req_mem + error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount) + end + try + if prod(split(1:3))== 1 + % no splitting, vol_small == vol_full -> write + % the backprojection directly to vol_full + % without copying -> no output arguments are needed + astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,vol_full,r.deformation_fields{:}); + else + vol_small = astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,[],r.deformation_fields{:}); + % if some volume split is needed, add the subvolume + % to the full volume + if ~keep_volume_on_GPU + vol_small = gather(vol_small); % return to RAM + end + if keep_volume_on_GPU || isa(vol_full, 'gpuArray') || ~exist('+tomo/add_to_3D_volume.m', 'file') + % return results to the large array + % use matlab to do the copying on GPU + vol_full =add_to_3D(vol_full, vol_small,([x,y,z]-1).*Npix_small'); + else + % otherwise use paralelized CPU code + tomo.add_to_3D_volume(vol_full,vol_small, ([x,y,z]-1).*Npix_small', true); + end + end + catch err + if strcmpi(err.identifier,'parallel:gpu:array:OOM') + warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index) + gpuDevice + end + reset(gpuDevice) + rethrow(err) + end + + iter = iter+1; + if r.verbose > 0 + progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20); + end + end + end + end + end + + if ~r.keep_on_GPU + vol_full = gather(vol_full); + end + + +end diff --git a/+astra/Ax_partial.m b/+astra/Ax_partial.m new file mode 100644 index 0000000..0ca9de1 --- /dev/null +++ b/+astra/Ax_partial.m @@ -0,0 +1,385 @@ +% AX_PARTIAL forward projector that allows to split the full volume into smaller pieces +% composed tomography projector based on ASTRA toolbox +% can be used either for data in RAM or on GPU (automatically decided from class of volData) +% * volume is split based on "split" parameter, 1 == no splitting +% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits) +% +% projData = Ax_partial(volData, cfg, vectors,split, varargin) +% +% Inputs: +% **volData - array Nx x Ny x Nz of projected volume +% **cfg - config structure generated by ASTRA_initialize +% **vectors - orientation of projections generated by ASTRA_initialize +% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ] +% *optional* +% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object +% **GPU - GPU id to be used for reconstruction +% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug +% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option) +% +% *returns* +% ++projData - projection of volData +% +% recompile commands +% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu +% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + +function projData = Ax_partial(volData, cfg, vectors,split, varargin) + + + import utils.* + import math.* + + par = inputParser; + par.KeepUnmatched = true; + + par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object + par.addOptional('GPU', []) % GPUs id to be used in reconstruction + par.addOptional('verbose', 0) % verbose = 0 : quiet, verbose : standard info , verbose = 2: debug + par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster + + par.parse(varargin{:}) + r = par.Results; + + if isempty(r.deformation_fields); r.deformation_fields = {}; end + + %% check the inputs + check memory availibility on GPU + assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible') + + if ~( (isa(volData, 'gpuArray') && strcmp(classUnderlying(volData), 'single')) || ... + isa(volData, 'single') ) || ~isreal(volData) + error('Only single precision real input array supported') + end + if ~isempty(r.deformation_fields) + assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array') + for i = 1:numel( r.deformation_fields) + if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ... + isa(r.deformation_fields{i}, 'single')) + error('Only single precision for deformation fields is supported') + end + r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small + end + r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field + end + + + gpu = gpuDevice(); + if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1) + % switch and !! reset !! GPU + if isa(volData, 'gpuArray'), error('Switching GPUs will reset content'); end + gpu = gpuDevice(r.GPU(1)); + end + + + split = ceil(max(1,split)); + if ismatrix(volData) + split = [split([1, min(2,end)]),1]; + assert(all(size(volData)==[cfg.iVolX,cfg.iVolY]), 'Wrong inputs size') + else + assert(all(size(volData)==[cfg.iVolX,cfg.iVolY,cfg.iVolZ]), 'Wrong inputs size') + end + + keep_on_GPU = isa(volData, 'gpuArray') || r.keep_on_GPU; + + %% backprojector that allows to split the full volume into smaller pieces + assert(all(size(vectors,2)==12), 'Wrong "vectors" size') + assert(~isempty(vectors), 'Empty input "vectors"') + assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Inputs volume is empty'); + assert(cfg.iProjU*cfg.iProjV*cfg.iProjAngles > 0, 'Projections are empty'); + + % be sure that ASTRA wrapper is feeded by doubles !! + for i = fieldnames(cfg)' + cfg.(i{1}) = double(cfg.(i{1})); + end + vectors = double(vectors); + split=double(split); + + + cfg.iProjAngles = size(vectors,1); + + assert(cfg.iProjAngles > 1, 'Number of processed angles must be > 1') + + + % fix if the number of projections is > 1024, or arrays are too large + Nproj_groups = max(1,ceil(max([cfg.iProjAngles/1024, ... % ASTRA constant memory limit + cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / gpu.AvailableMemory, ... % availible memory limit + cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))]))); % maximal array size allowed by CUDA limit + if length(split) > 3 + Nproj_groups = max(split(4), Nproj_groups); + end + + if all(split == 1) && Nproj_groups == 1 + if numel(volData)*4 > 1024e6 % exceeded texture memory + nsubVol = ceil(numel(volData)*4 / 1024e6); + nsubVol = 2^nextpow2(nsubVol); +% split = ceil([sqrt(nsubVol),sqrt(nsubVol),1]); + split = [1,1,nsubVol]; + if r.verbose>0; disp(['Volume array is larger than 1024MB, auto-splitting ', num2str(split)]); end + else + %% in the simple case call ASTRA_GPU_wrapper directly + volData = gpuArray(volData); + % call ASTRA + projData = astra.ASTRA_GPU_wrapper('fp',volData, cfg, vectors,[],r.deformation_fields{:}); + clear volData + if gpu.AvailableMemory < 4*numel(projData) + projData = gather(projData); % prevent out of memory errors during next step + end + projData = astra2matlab(projData); + if ~keep_on_GPU; projData = gather(projData); end + return + end + end + + + %% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes + + + if isscalar(split) + split = split .* ones(ndims(volData),1); + end + assert(numel(volData)*4/prod(split) <= 1024e6, 'Volume array exceeded 1024MB, use more splitting') + + Nvol_full = size(volData); + if numel(Nvol_full)<3 + Nvol_full(3)=1; + end + Nvol_sub = Nvol_full'./reshape(split(1:3),[],1); + + assert(all(mod(Nvol_sub,1)==0), sprintf('Volume size %ix%ix%i is not dividable by split %ix%ix%i',size(volData),split(1:3))) + + + if ismatrix(volData) + split(3) = 1; + Nvol_sub(3) = 1; + end + + cfg.iVolX = cfg.iVolX/split(1); + cfg.iVolY = cfg.iVolY/split(2); + cfg.iVolZ = cfg.iVolZ/split(3); + + cfg_orig = cfg; + + if Nproj_groups > 1 + % split the projections along the angles + for i = 1:Nproj_groups + ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups); + ind = ind(ind <= cfg.iProjAngles); + vectors_tmp{i} = vectors(ind,:); + cfg_tmp{i} = cfg; + cfg_tmp{i}.iProjAngles = length(ind); + end + cfg = cfg_tmp; vectors = vectors_tmp; + else + Nproj_groups = 1; + cfg = {cfg}; + vectors = {vectors}; + end + clear ind + + + if r.verbose > 1 + fprintf('Size of the full volume: %i %i %i\n', size(volData)) + fprintf('Size of the subvolume: %i %i %i\n', Nvol_sub) + fprintf('Size of the one sinogram block: %i %i %i\n', cfg{1}.iProjU, cfg{1}.iProjV, cfg{1}.iProjAngles) + end + + %%!!!! note that in rare cases astra my fail if sinogram width is too small + + assert(prod(Nvol_sub) * 4 <= 1024e6, 'Volume exceeded maximal size of texture 1024MB') + + % estimate required memory + (use only if 2x more memory is available) + required_mem = 2*(prod(Nvol_sub)*(2+~isa(volData, 'gpuArray')) + cfg_orig.iProjU * cfg_orig.iProjV * cfg_orig.iProjAngles )*4; + % keep projections on GPU only of there is enough memory + keep_projections_on_GPU = Nproj_groups == 1 || gpu.AvailableMemory > required_mem; + + if cfg{1}.iProjU * cfg{1}.iProjV * cfg{1}.iProjAngles > intmax('int32') + error('Projection size exceeded maximum size allowed on GPU') + end + if (keep_on_GPU || prod(split(1:3)) == 1) && numel(volData) < intmax('int32') % keep volume on GPU + volData = gpuArray(volData); + end + + inParpool = ~isempty(getCurrentTask()); + iter = 1; + for m = 1:Nproj_groups + % split angularly (solve smaller groups of angles) + % allocate memory for the projections + projData{m} = gpuArray.zeros(cfg{m}.iProjU, cfg{m}.iProjV, cfg{m}.iProjAngles, 'single'); + % split into volume cubes + for i = 1:split(1) + for j = 1:split(2) + for k = 1:split(3) + if r.verbose > 0 + progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20); + end + pos = [i,j,k]; + for n = 1:3 + ind{n} = (1+(pos(n)-1)*Nvol_sub(n)):(pos(n)*Nvol_sub(n)); + %% find optimal shift of the subvolume + if mod(split(n),2)==1 %% odd + shift(n) = (pos(n) - ceil(split(n)/2))*Nvol_sub(n); + else + shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Nvol_sub(n); + end + end + + % extract subvolume to be processed + if any(split(1:3)~=1) && isa(volData, 'gpuArray') + vol_small = volData(ind{:}); % take only small subvolume + elseif any(split(1:3)~=1) + vol_small = zeros(Nvol_sub','single'); + utils.get_from_3D_projection(vol_small,volData,[ind{1}(1),ind{2}(1)]-1,ind{3}); + else + vol_small = volData; % avoid data copying of possible + end + vol_small = gpuArray(vol_small); + + % split deformation field for nonrigid tomography + if ~isempty(r.deformation_fields) + for ii = 1:3 + N_deform = size(r.deformation_fields{ii}) ./ reshape(split(1:3),[],1)'; + for jj = 1:3 + ind_def{jj} = linspace(1+(pos(jj)-1)*N_deform(jj), pos(jj)*N_deform(jj), size(r.deformation_fields{ii},jj)); + end + [X,Y,Z]= meshgrid(ind_def{:}); + deformation_fields_sub{ii} = interp3(r.deformation_fields{ii},X,Y,Z); + end + else + deformation_fields_sub = {}; + end + + vec = vectors{m}; + vec(:,4:6) = bsxfun(@minus, vec(:,4:6), shift); + + req_mem = 2*numel(vol_small)*4 ; + if gpu.AvailableMemory < req_mem + !nvidia-smi + whos + error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i, projection group %i/%i, keep_proj_on_GPU=%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount, m , Nproj_groups, keep_projections_on_GPU) + end + + try + % avoid memory allocation, write directly to projData{m} -> no output arguments are needed + astra.ASTRA_GPU_wrapper('fp',vol_small, cfg{m}, vec,projData{m}, deformation_fields_sub{:}); + vol_small = []; % soft mem clean + catch err + if strcmpi(err.identifier,'parallel:gpu:array:OOM') + warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index) + gpuDevice + reset(gpuDevice) + end + + rethrow(err) + end + + iter = iter+1; + if r.verbose>0 + progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20); + end + end + end + end + if ~keep_projections_on_GPU + projData{m} = gather(projData{m}); + end + end + clear volData vol_small + + + % permute / concatenate + if gpu.AvailableMemory < 4*numel(projData{1})*max(2,Nproj_groups) + projData = gather_all(projData); + end + + projData = astra2matlab(projData); + + if gpu.AvailableMemory < 8*numel(projData{1})*Nproj_groups + projData = gather_all(projData); + end + % concatenate the projected data align the angular (3rd) axis + projData = merge_projections(projData); + + if ~keep_on_GPU + projData = gather(projData); + elseif numel(projData) < intmax('int32') && gpu.AvailableMemory < 4*numel(projData) + % return to GPU if requested by 'keep_on_GPU' parameter + projData = gpuArray(projData); + end + +end + +function x = gather_all(x) + for i = 1:length(x) + x{i} = gather(x{i}); + end +end + + +function projData = merge_projections(projData_blocks) + Nblocks = length(projData_blocks); + if Nblocks > 1 + if isa(projData_blocks{1}, 'gpuArray') + projData = cat(3, projData_blocks{:}); + else + % faster and more memory efficient version + proj_size = [size(projData_blocks{1},1),size(projData_blocks{1},2),sum(cellfun(@(x)size(x,3), projData_blocks))]; + for ii = 1:10 + try + projData = zeros(proj_size, 'single'); + break + catch err + end + pause(1) + end + if ii == 10 + warning('Unsufficient memory to allocate %3.2gGB RAM', prod(proj_size)*4/1e9) + utils.check_available_memory + rethrow(err) + end + offset = 0; + for ii = 1:Nblocks + tomo.set_to_array(projData, projData_blocks{ii}, offset); + offset = offset + size(projData_blocks{ii},3); + end + end + else + projData = projData_blocks{1}; + end +end + + diff --git a/+astra/example.m b/+astra/example.m new file mode 100644 index 0000000..af4aa37 --- /dev/null +++ b/+astra/example.m @@ -0,0 +1,90 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% example script for ASTRA wrappers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% recompile commands +% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu +% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu + + +% volume settings +Npix_vol = [300, 300, 100] ; +Nangles = 400; +Npix_proj = [400, 400]; + +% create "data" +angles = linspace(0, 360, Nangles); +lamino_angle = 60; +volData = ones(Npix_vol, 'single'); + +% generate geometry +[cfg, vectors] = astra.ASTRA_initialize(Npix_vol, Npix_proj, angles, lamino_angle); +% find optimal split, for small volumes below 600^3 no split is needed +split = astra.ASTRA_find_optimal_split(cfg); + +% generate projections +projData = astra.Ax_partial(volData, cfg, vectors, split); + +figure(1) +% plot the projections +subplot(1,2,1) +plotting.imagesc3D(projData); axis off image; colormap bone +title('Angular geometry') + +% do backprojection projections !!! not FBP !!! +backprojData = astra.Atx_partial(projData, cfg, vectors, split); + + +% generate rotation matrix, note that the expected angles needs to be +% adjusted to provide same results are the previous example +R3 = utils.get_rotation_matrix_3D((90-lamino_angle)*ones(Nangles,1), -angles, zeros(Nangles,1)); + +% generate geometry +[cfgR, vectorsR] = astra.ASTRA_initialize(Npix_vol, Npix_proj, R3); + +% generate projections +projDataR = astra.Ax_partial(volData, cfgR, vectorsR, split); + +figure(1) +% plot the projections +subplot(1,2,2) +% plot the projections +plotting.imagesc3D(projDataR); axis off image; colormap bone +title('Rotation matrix geometry') + + + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. diff --git a/+astra/iradon_gpu_wrapper.m b/+astra/iradon_gpu_wrapper.m new file mode 100644 index 0000000..e5854e9 --- /dev/null +++ b/+astra/iradon_gpu_wrapper.m @@ -0,0 +1,54 @@ +% IRADON_GPU_WRAPPER back projector that allows to split the full volume +% into smaller pieces, tomography projector is based on ASTRA toolbox can be used +% either for data in RAM or on GPU (automatically decided from class of volData) +% +% vol = iradon_gpu_wrapper(sinogram, cfg, vectors) +% +% Inputs: +% **projData - array Nlayers x width_sinogram x Nangles of back-projected data +% **cfg - config structure generated by ASTRA_initialize +% **vectors - orientation of projections generated by ASTRA_initialize +% Outputs: +% ++vol - backprojected volume +% +% recompile commands +% (Linux) mexcuda -largeArrayDims -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu +% (Windows) mexcuda -largeArrayDims -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function vol = iradon_gpu_wrapper(sinogram, cfg, vectors) + vol = ASTRA_GPU_wrapper('bp', sinogram, cfg, vectors); +end \ No newline at end of file diff --git a/+astra/private/add_to_3D.m b/+astra/private/add_to_3D.m new file mode 100644 index 0000000..56efe60 --- /dev/null +++ b/+astra/private/add_to_3D.m @@ -0,0 +1,58 @@ +% FUNCTION full_array = add_to_3D(full_array, small_array, position) +% add one small 3D block into large 3D array +% Inputs: +% full_array +% small_array +% position - offset from (1,1,1) coordinate in pixels + +% *-----------------------------------------------------------------------* +% |                                                                       | +% |  Except where otherwise noted, this work is licensed under a          | +% |  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +% |  International (CC BY-NC-SA 4.0) license.                             | +% |                                                                       | +% |  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +% |                                                                       | +% |      Author: CXS group, PSI  | +% *-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. +% +% +% +function full_array = add_to_3D(full_array, small_array, position) + + position = round(position); + N_f = size(full_array); + N_s = size(small_array); + + for i = 1:ndims(full_array) + ind_f{i} = unique(min(N_f(i),max(1,position(i)+(1:N_s(i))))); + ind_s{i} = unique(min(N_s(i),max(1,ind_f{i}-position(i)))); + end + + full_array(ind_f{:}) = full_array(ind_f{:}) + small_array(ind_s{:}); + + +end diff --git a/+astra/private/astra2matlab.m b/+astra/private/astra2matlab.m new file mode 100644 index 0000000..57d24d7 --- /dev/null +++ b/+astra/private/astra2matlab.m @@ -0,0 +1,51 @@ +% FUNCTION sinogram = astra2matlab(sinogram) +% simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order +% Inputs: +% sinogram - real value sinogram + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% + +function sinogram = astra2matlab(sinogram) + % simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order + + if isnumeric(sinogram) + [U,V,A] = size(sinogram); + sinogram = permute(reshape(sinogram, [U,A,V]), [3,1,2]); + elseif iscell(sinogram) + for ii = 1:length(sinogram) + sinogram{ii} = astra2matlab(sinogram{ii}); + end + end +end \ No newline at end of file diff --git a/+astra/private/matlab2astra.m b/+astra/private/matlab2astra.m new file mode 100644 index 0000000..e562eea --- /dev/null +++ b/+astra/private/matlab2astra.m @@ -0,0 +1,52 @@ +% FUNCTION sinogram = astra2matlab(sinogram) +% simple function to reshape sinogram from Matlab (FORTRAN) order to ASTRA (C++) order +% Inputs: +% sinogram - real value sinogram + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% + + +function sinogram = matlab2astra(sinogram) + + if isnumeric(sinogram) + [U,V,A] = size(sinogram); + sinogram = reshape(permute(sinogram, [2,3,1]),[U,V,A]); + elseif iscell(sinogram) + for ii = 1:length(sinogram) + sinogram{ii} = matlab2astra(sinogram{ii}); + end + end + +end \ No newline at end of file diff --git a/+beamline/OMNY_get_scan_numbers.m b/+beamline/OMNY_get_scan_numbers.m new file mode 100644 index 0000000..7ee34e6 --- /dev/null +++ b/+beamline/OMNY_get_scan_numbers.m @@ -0,0 +1,59 @@ +% OMNY_get_scan_numbers( OMNY_angles_file, scannums ) +% OMNY_angles_file - File with Scan number, angle target, angle readout +% tomo_id - Index specifying the range of scan numbers +% +% out - returns scan numbers for tomo_id + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ out ] = OMNY_get_scan_numbers( OMNY_angles_file, tomo_id ) + +fid = fopen(OMNY_angles_file); + +ln = fgetl(fid); +if numel(strsplit(ln, ' '))<=6 + error('OMNY file does contain tomo_ids') +end + +outmat = textscan(fid,'%f %f %f %f %f %f %s'); +fclose(fid); + + +ind = find(outmat{4}==tomo_id); +out = outmat{1}(ind); +out = out'; + +end + diff --git a/+beamline/WAXS_standards.fig b/+beamline/WAXS_standards.fig new file mode 100644 index 0000000..6e0c47d Binary files /dev/null and b/+beamline/WAXS_standards.fig differ diff --git a/+beamline/beamstop_mask.m b/+beamline/beamstop_mask.m new file mode 100644 index 0000000..435c8c9 --- /dev/null +++ b/+beamline/beamstop_mask.m @@ -0,0 +1,229 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: beamstop_mask.m,v $ +% +% $Revision: 1.8 $ $Date: 2011/08/23 17:17:53 $ +% $Author: $ +% $Tag: $ +% +% Description: +% remove a polygonic region from the valid pixel mask +% +% Note: +% This is a template. The coordinates of the polygon have to be manually +% edited. +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read +% +% history: +% +% May 19th 2010: +% add XyCoord and xCoord, yCoord command line parameters +% +% May 9th 2008: 1st documented version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ bmask_ind ] = beamstop_mask(filename,varargin) +import beamline.pilatus_valid_pixel_roi +import beamline.prep_valid_mask +import io.image_read +import plotting.display_valid_mask + +% set default values for the variable input arguments: +% valid pixel mask +filename_valid_mask = '~/Data10/analysis/data/pilatus_valid_mask.mat'; +% do not update the valid pixel mask +save_data = 0; +% figure number for display +fig_no = 220; +% mask corners +xy_coord = []; %#ok +x_coord = []; +y_coord = []; + +% check minimum number of input arguments +if (nargin < 1) + display_help(filename_valid_mask,save_data,fig_no); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + display_help(filename_valid_mask,save_data,fig_no); + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'SaveData' + save_data = value; + case 'FilenameValidMask' + filename_valid_mask = value; + case 'xyCoord' + xy_coord = value; + x_coord = xy_coord(:,1); + y_coord = xy_coord(:,2); + case 'xCoord' + x_coord = value; + case 'yCoord' + y_coord = value; + otherwise + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + end +end + +% read file for test display +frame = image_read(filename,vararg_remain); +frame.data = double(frame.data); +dimensions = size(frame.data); +if (numel(dimensions) > 2) + frame.data = mean(frame.data,3); + dimensions = size(frame.data); +end + +% get indices to pixels within beam stop +if ((isempty(x_coord)) || (isempty(y_coord))) + bmask_ind = 1:(dimensions(1)*dimensions(2)); +else + [bmask] = uint8(1 - roipoly( dimensions(1), dimensions(2), x_coord, y_coord )); + bmask_ind = find(bmask == 0); +end + +% plot the result +figure(5); +frame_plot = frame.data; +frame_plot(frame_plot < 1) = 1; +% plot the masked region with lower intensity +frame_plot(bmask_ind) = 0.1 * frame_plot(bmask_ind); +imagesc(log10(frame_plot)); +axis xy; +axis equal; +axis tight; +colorbar; +title('beamstop mask shape'); + +% show the current valid pixel mask +display_valid_mask('FilenameValidMask',filename_valid_mask,'FigNo',fig_no+1,... + 'NoHelp',1); +title('current valid pixel mask'); + +% load ind_valid, the indices of the valid pixels +fprintf('loading %s\n',filename_valid_mask); +load(filename_valid_mask); +% cut out the current region of interest +valid_mask = pilatus_valid_pixel_roi(valid_mask,'RoiSize',size(frame.data)); + +% remove beam-stop pixels from it +valid_mask.indices = setdiff(valid_mask.indices,bmask_ind); %#ok + +if (save_data) + % create a backup of the mask + if (exist(filename_valid_mask,'file')) + filename_valid_mask_backup = [ filename_valid_mask '.bak' ]; + fprintf('Copying the current mask %s to %s\n',filename_valid_mask,... + filename_valid_mask_backup); + copyfile(filename_valid_mask,filename_valid_mask_backup); + end + + % save the updated mask + fprintf('saving updated mask %s\n',filename_valid_mask); + save(filename_valid_mask,'valid_mask'); + + % display the new mask + display_valid_mask('FilenameValidMask',filename_valid_mask,'FigNo',fig_no+2,... + 'NoHelp',1); +else + % mark the valid pixels as 1, leave the invalid at 0 + pframe = zeros(valid_mask.framesize); + pframe(valid_mask.indices) = 1; + + % plot the result + figure(fig_no+2); + imagesc(pframe); + axis xy; + axis equal; + axis tight; + colorbar; + title('valid pixels'); + title('updated valid pixel mask (not saved!)'); + set(gcf,'Name','valid pixels'); +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [] = display_help(filename_valid_mask,save_data,fig_no) + +fprintf('Usage:\n'); +fprintf('%s(filename_for_display, [[,],...]);\n',mfilename) +fprintf('The specified file is used to display the beamstop mask with reduced intensity.\n'); +fprintf('The optional , pairs are:\n'); +fprintf('''xyCoord'',[ x1 y1; x2 y2; ...] coordinates of the beamstop mask\n'); +fprintf('''xCoord'',[ x1 x2 ...] x-coordinates of the beamstop mask, alternative to specifying xy pairs, may be useful if roipoly is used\n'); +fprintf('''yCoord'',[ y1 y2 ...] y-coordinates of the beamstop mask, alternative to specifying xy pairs, may be useful if roipoly is used\n'); +fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices ind_valid,\n'); +fprintf(' default is %s\n',filename_valid_mask); +fprintf('''SaveData'',<0-no,1-yes> 0 for displaying the result without updating the mask, default is %d\n',... + save_data); +fprintf('''FigNo'', number of the figure in which the result is displayed, default is %d\n',... + fig_no); +fprintf('\n'); +fprintf('A valid pixel mask can be created using the macro prep_valid_mask.\n') +fprintf('You will find a valid pixel mask in %s but you may consider to measure a new one.\n',... + filename_valid_mask); +fprintf('\n'); diff --git a/+beamline/choose_beamstop_mask.m b/+beamline/choose_beamstop_mask.m new file mode 100644 index 0000000..4e484f2 --- /dev/null +++ b/+beamline/choose_beamstop_mask.m @@ -0,0 +1,224 @@ +% [mask_coord,bmask_ind] = choose_beamstop_mask(filename,varargin) + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [mask_coord,bmask_ind] = choose_beamstop_mask(filename,varargin) +import beamline.beamstop_mask +import plotting.image_show + +% set default values for the variable input arguments: +% File with mask coordinates +filename_coord = '~/Data10/analysis/data/mask_coordinates.mat'; +% border size +border = 3; +% save coordinates +save_coord = 1; +% select corrdinates +select_points = 1; +% do not read prevously saved coordinates +read_coord = 1; +% start with an empty set of coordinates +mask_coord = []; +bmask_ind = []; +% figure number for display +fig_no_sel = 555 ; +% run 'beamstop_mask' at the end +create_mask = 1; +% Arguments to be passed to imageshow +imageshow_args = {}; + + +% check minimum number of input arguments +if (nargin < 1) + fprintf('\n'); + fprintf('Usage:\n'); + fprintf('%s(filename,[[,],...]);\n',mfilename); + fprintf('\n'); + fprintf('The optional , pairs are:\n'); + fprintf('''Border'', size of the ''sticky'' edge border, default is %d, 0 to disable\n',border); + fprintf('''SaveCoord'',<0-no,1-yes> 1 for saving the coordinates, default is %d\n',save_coord); + fprintf('''FilenameCoord'', Matlab file with the mask coordinates,\n'); + fprintf(' default is %s\n',filename_coord); + fprintf('''SelectPoints'',<0-no,1-yes> 1 for selecting the points in the image, default is %d\n',select_points); + fprintf(' if 0, points should be either read from the file or \n'); + fprintf(' supplied as options for ''beamstop_mask'' function\n'); + fprintf('''ReadCoord'',<0-no,1-yes> 1 for reading the coordinates from the file, default is %d\n',read_coord); + fprintf('''CreateMask'',<0-no,1-yes> 1 for running ''beamstop_mask'', default is %d\n',create_mask); + fprintf('''FigNoSel'', number of the figure in which the coordinates are selected, default is %d\n',... + fig_no_sel); + fprintf('''ImageShowArgs'', cell additional parameters to be passed to image_show, default is an empty cell {} \n'); + fprintf('\n'); + fprintf('Additional , pairs recognized by ''beamstop_mask'' can be specified.\n'); + error('At least the filename has to be specified as input parameter.'); +end + + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + display_help(filename_coord,save_coord,create_mask,select_points,border,read_coord,fig_no_sel); + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'SaveCoord' + save_coord = value; + case 'Border' + border = value; + case 'SelectPoints' + select_points = value; + case 'ReadCoord' + read_coord = value; + case 'FilenameCoord' + filename_coord = value; + case 'FigNoSel' + fig_no_sel = value; + case 'xyCoord' + mask_coord = value; + case 'CreateMask' + create_mask = value; + case 'ImageShowArgs' + imageshow_args = value; + otherwise + vararg_remain{end+1} = name; + vararg_remain{end+1} = value; + end +end + + + + + +% Read the coordinates from the file +if (read_coord == 1) + if (exist(filename_coord,'file')) + load(filename_coord); + % don't use if there are less than two points in the mask + % makes it impossible to add new points + if size(mask_coord,1) < 2 + mask_coord = []; + end + else + fprintf('Mask coordinates file %s was not found.\n',filename_coord); + fprintf('Continuing with no starting mask.\n'); + end +end + + +% select/change the mask coordinates in the image +if (select_points == 1) + + [qq] = image_show(filename,'FigNo',fig_no_sel, imageshow_args{:}); + +% h=impoly(gca,mask_coord); +% mask_coord=getPosition(h); +% addNewPositionCallback(h,@(pos)eval('mask_coord=pos;')); +% % wait for the changes while the image is open +% waitfor(fig_no_sel) + msgbox({'Instructions:', '1) Create a closed polygon around the beamstop (don''t double-click when you finish)'... + , '2) Adjust the corners of polygon if needed',... + '3) Double-click on polygon to finish'},'Choose beamstop mask'); + h = impoly(gca,mask_coord); + mask_coord = wait(h); + + % move points to the edge + if border + im_dim(1) = size(qq.data,2); + im_dim(2) = size(qq.data,1); + + for jj = 1:size(mask_coord,1) + for kk = 1:2 + + if mask_coord(jj,kk) < border + mask_coord(jj,kk) = 0; + end + + if abs(mask_coord(jj,kk) - im_dim(kk)) < border + mask_coord(jj,kk) = im_dim(kk); + end + end + end + end + + % mask coordinates should be integers + mask_coord = round(mask_coord); +end + +% save the mask coordinates file, if specified +if save_coord + % create a backup of the mask coordinates file + if (exist(filename_coord,'file')) + filename_coord_backup = [ filename_coord '.bak' ]; + fprintf('Copying the current mask coordinates file %s to %s\n',filename_coord,... + filename_coord_backup); + copyfile(filename_coord,filename_coord_backup); + end + fprintf('saving updated mask coordinates file %s\n',filename_coord); + save(filename_coord,'mask_coord'); +end + +% run the beamstop_mask function, if specified +if create_mask + vararg_remain{end+1} = 'xyCoord'; + vararg_remain{end+1} = mask_coord; + bmask_ind = beamstop_mask(filename,vararg_remain{:},imageshow_args{:}); +end + + + + + + + diff --git a/+beamline/create_mask.m b/+beamline/create_mask.m new file mode 100644 index 0000000..e63093e --- /dev/null +++ b/+beamline/create_mask.m @@ -0,0 +1,236 @@ +%CREATE_MASK +% create a binary mask for the current figure +% The following arguments have to be given as name/value pairs. However, +% they can also be set within the GUI. +% +% *optional* +% ** mask initial mask; either a file, an array or a structure (indicies + asize) +% ** fig pass figure handle; default: current figure +% ** ind convert mask to indicies +% ** file save mask to disk; specify path + filename +% +% returns: +% ++ out 2D binary mask or structure containing the asize and the indicies +% +% EXAMPLE: +% img = io.image_read('~/Data10/pilatus_1/S00000-00999/S00170/*.cbf'); % load image stack +% plotting.imagesc3D(log10(img.data)); axis equal tight xy; % plot image stack +% beamline.create_mask(); % open the GUI and create the mask +% +% see also: beamline.mask2ind + +% Academic License Agreement +% +% Source Code +% +% Introduction +% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR") +% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS +% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM"). +% +% Terms and Conditions of the LICENSE +% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions +% hereinafter set out and until termination of this license as set forth below. +% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements +% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the +% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.” +% 3. THE PROGRAM 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 OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT +% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE +% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM. +% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively, +% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same +% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for +% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged +% in the commercial use, application or exploitation of works similar to the PROGRAM. +% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into +% another computing language: +% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul +% Scherrer Institut, Switzerland." +% +% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map: +% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008). +% (doi: 10.1126/science.1158573), +% for maximum likelihood: +% P. Thibault and M. Guizar-Sicairos, Maximum-likelihood refinement for coherent diffractive imaging, New J. Phys. 14, 063004 (2012). +% (doi: 10.1088/1367-2630/14/6/063004), +% for mixed coherent modes: +% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806), +% and/or for multislice: +% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016). +% (doi: 10.1364/OE.24.029089). +% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the +% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case. +% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE +% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to +% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies: +% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017. +% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein. +% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate +% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents, +% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software +% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program. +% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before +% the courts of Zürich, Switzerland. + +function [out] = create_mask(varargin) + +check_input_mask = @(x) ischar(x) || (isnumeric(x)|| islogical(x)) || isstruct(x); + +par = inputParser; +par.addParameter('mask', [], check_input_mask) +par.addParameter('fig', [], @ishandle) +par.addParameter('ind', false, @islogical) +par.addParameter('file', [], @ischar) +par.parse(varargin{:}) +vars = par.Results; + +% Check screen size +try + scrsz = get(0,'ScreenSize'); +catch + scrsz = [1 1 2560 1024]; +end + +% get fig +if isempty(vars.fig) + fig = gcf; +end + +current_pos = fig.Position; +new_fig_pos(2:4) = current_pos(2:4); + +if current_pos(1)+current_pos(3)/2 - scrsz(3)/2 > 0 + % figure to the left + new_fig_pos(1) = current_pos(1)-current_pos(3); +else + % figure to the right + new_fig_pos(1) = current_pos(1)+current_pos(3); +end + + +% get axis +ax = gca; +% get current data size +if ~isempty(ax.Children) + asize = size(ax.Children.CData); +else + fig = gcf; + close(fig) + error('Failed to connect to figure instance.') +end + +% prepare mask +if isempty(vars.mask) + mask = ones(asize); +else + if ischar(vars.mask) + % load a mask from disk + try + f = load(vars.mask); + mask = f.mask; + clear f + catch + fprintf('Failed to load mask. Using empty mask instead.\n') + mask = ones(asize); + end + elseif isnumeric(vars.mask) || islogical(vars.mask) + mask = vars.mask; + elseif isstruct(vars.mask) + mask = beamline.ind2mask(vars.mask); + else + error('Unknown mask data format.') + end + assert(all(size(mask)==asize), 'Mask size and data size does not match') + +end +pause(0.1) + + +% apply mask +CData_orig = ax.Children.CData; +if ax.isprop('img') + img_orig = ax.img; + ax.img = ax.img .* mask; + mask_dims = ndims(img_orig); + if mask_dims==3 + mask3D = true; + else + mask3D = false; + end + mask_dims = size(img_orig,3); +else + mask3D = false; + mask_dims = 1; +end +ax.Children.CData = ax.Children.CData .* mask; + +s = create_mask_GUI_export('mask', mask, 'mask3D', mask3D, 'mask_dims', mask_dims); +s.figure1.UserData.ax = ax; +s.figure1.UserData.fig = fig; +s.figure1.UserData.asize = asize; +s.figure1.UserData.CData = CData_orig; +if ax.isprop('img') + s.figure1.UserData.img_orig = img_orig; + if s.figure1.UserData.mask3D + orig_fig_listener = s.figure1.UserData.ax.slider_handle.listener('Value','PostSet',@(src, evnt)orig_fig_slice_update(s)); + end + +end + +try + while ~s.figure1.UserData.done + mask = s.figure1.UserData.mask; + pause(0.1) + end + + set(groot,'CurrentFigure',fig); + ax.Children.CData = CData_orig; + if ax.isprop('img') + ax.img = img_orig; + end +catch + if ~isprop(s, 'figure1') + fprintf('Lost connection to GUI.\n') + end +end + +try + if s.figure1.UserData.mask3D + delete(orig_fig_listener) + end + delete(s.figure1) +catch +end + + +% if needed, convert 2D mask to indicies +if vars.ind + out = beamline.mask2ind(mask); +else + out = mask; +end + + + +% save to disk +if ~isempty(vars.file) + valid_mask = out; + try + utils.savefast_safe(vars.file, 'valid_mask'); + catch + fprintf('Failed to save mask to disk.'); + end +end + + +end + +function orig_fig_slice_update(s) +val = s.figure1.UserData.ax.slider_handle.Value; +set(s.axes1.slider_handle, 'Value', val); +set(s.axes1.edit_handle, 'String', num2str(val)); +s.axes1.update_fig(s.axes1); + +end + diff --git a/+beamline/energy2mark.m b/+beamline/energy2mark.m new file mode 100644 index 0000000..1a4c0e5 --- /dev/null +++ b/+beamline/energy2mark.m @@ -0,0 +1,57 @@ +% function mark = energy2mark(E,calib_file) +% After calibrating using beamline.mark_interpolation_setup you can use +% this function to input a desired energy and a linear interpolation will +% determine and give you the mark +% Inputs +% E Array of energies in keV +% calib_file Name of the file with the calibration +% +% Outputs +% mark Array of marks corresponding to input energies + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function mark = energy2mark(E,calib_file) + +if ~nargin<2 + calib_file = 'mark_calib.mat'; +end + +calib = load(calib_file); + +mark = interp1(calib.E,calib.marks,E,'linear'); + + + diff --git a/+beamline/find_capillary.m b/+beamline/find_capillary.m new file mode 100644 index 0000000..8deda56 --- /dev/null +++ b/+beamline/find_capillary.m @@ -0,0 +1,84 @@ +% [varargout] = find_capillary(varargin) + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [varargout] = find_capillary(varargin) +import io.spec_read + + vararg = varargin; + vararg{end+1} = 'UnhandledParError'; + vararg{end+1} = 0; + [S] = spec_read(vararg{1},vararg{2:end}); + vararg = vararg(2:end); + for jj = 1:2:length(vararg) + name = vararg{jj}; + value = vararg{jj+1}; + switch name + case 'Counter' + counter = S.(value); + end + end + + arrout = regexp(S.S,' +','split'); + motor = S.(arrout{4}); + threshold = .1 * max(counter); + + motor = motor(counter>threshold); + counter = counter(counter>threshold); + + + threshold = .9 * max(counter); + i_i = find(counter>threshold,1,'first'); + i_f = find(counter>threshold,1,'last'); + + p = polyfit(motor(counter>threshold),counter(counter>threshold),1); + dy = polyval(p,motor)-counter; + COM = sum(motor(i_i:i_f).*dy(i_i:i_f))/sum(dy(i_i:i_f)); + + do_plot = 0; + if (do_plot) + figure(1) + plot(motor,dy) + hold on + area(motor(i_i:i_f),dy(i_i:i_f)) + plot([1 1]*COM,[0 max(dy(i_i:i_f))],'r','LineWidth',2) + hold off + end + + varargout{1} = COM; + + + +end diff --git a/+beamline/find_capillary_wrapper.m b/+beamline/find_capillary_wrapper.m new file mode 100644 index 0000000..c4e01e5 --- /dev/null +++ b/+beamline/find_capillary_wrapper.m @@ -0,0 +1,69 @@ +% this script contains the necessary loop for 'find_capillary.m' to be called +% as function of 'spec' +% written by (last change: 2011-06-16) +% in case of bugs, problems, and suggestions for improvements, please contact +% CXS group +% +% note that EPICS communication works only on local machines at the beamline, i.e., +% NOT on the compute nodes +% run this (or related scripts that use EPICS for communication), for instance, on +% x12sa-cons-1 + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +lastscan = 0; + +while(1) + scall = sprintf('caget ''X12SA-ES1-DOUBLE-00'''); + [err,io] = system(scall); + arrout = regexp(io,' +','split'); + scannr = str2double(arrout{2}); + if (scannr > lastscan) + try + COM = +beamline.find_capillary('..','ScanNr',scannr,'Counter','diode') + catch + fprintf('Failed find capillary, pausing 5 sec and retrying\n') + pause(5) + COM = +beamline.find_capillary('..','ScanNr',scannr,'Counter','diode') + end + else + pause(1); + end + scall = sprintf('caputq X12SA-ES1-DOUBLE-02 %f',COM); + [err,io] = system(scall); + scall = sprintf('caputq X12SA-ES1-DOUBLE-01 %d',scannr); + [err,io] = system(scall); + lastscan = scannr; +end diff --git a/+beamline/find_specDatFile.m b/+beamline/find_specDatFile.m new file mode 100644 index 0000000..21a22d0 --- /dev/null +++ b/+beamline/find_specDatFile.m @@ -0,0 +1,100 @@ +% function specDatFile = find_specDatFile(specDatFile) +% find location of the spec file in the provided folder / path +% if the variable specDatFile is not a complete path to a file +% try to guess where a spec data file can be found, by +% - look for directories called 'spec' or 'dat-files' +% - look for files called '*.dat' +% - take the newest one + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function specDatFile = find_specDatFile(specDatFile) + while (exist(specDatFile,'file') ~= 2) +% if the variable specDatFile is not a complete path to a file +% try to guess where a spec data file can be found, by +% - look for directories called 'spec' or 'dat-files' +% - look for files called '*.dat' +% - take the newest one + compare_str = specDatFile; + fname = dir(specDatFile); + if (exist(specDatFile,'dir')) + if (specDatFile(end) ~= '/') + specDatFile = strcat(specDatFile,'/'); + end + + for ii=1:numel(fname) + if (regexp(fname(ii).name,'.dat$')) + specDatFile = strcat(specDatFile,'*.dat'); + fname = []; + break; + end + end + for ii=1:numel(fname) + if (strcmp(fname(ii).name,'dat-files')) + specDatFile = strcat(specDatFile,fname(ii).name); + fname = []; + break; + end + end + for ii=1:numel(fname) + if (strcmp(fname(ii).name,'specES1')) + specDatFile = strcat(specDatFile,fname(ii).name); + break; + end + if (strcmp(fname(ii).name,'spec')) + specDatFile = strcat(specDatFile,fname(ii).name); + break; + end + end + else + if (numel(fname)>0) + [~,ii] = max(cell2mat({fname.datenum})); + specDatFile = regexprep(specDatFile,'\*\.dat$',fname(ii).name); + else + error('''%s'' cannot be found.', specDatFile); + break + end + end + if (strcmp(specDatFile,compare_str)) + break + end + end + + if ~exist(specDatFile, 'file') || exist(specDatFile, 'dir') + error('Spec dat file not found in the provided path %s', specDatFile) + end + +end diff --git a/+beamline/flux_estimate.m b/+beamline/flux_estimate.m new file mode 100644 index 0000000..ac97702 --- /dev/null +++ b/+beamline/flux_estimate.m @@ -0,0 +1,380 @@ +import beamline.identify_eaccount +import beamline.radial_integ +import io.spec_read +import utils.get_fil_trans +import utils.get_gas_trans +import utils.pixel_to_q + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EDIT HERE: +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% where the data is saved (Data10, Data20, p-account) +raw_data = '~/Data10'; +e_account = identify_eaccount; +%uncomment for using during online analysis in the e-account +BasePath = raw_data; +%uncomment for using during offline analysis +% BasePath= sprintf('%s/%s', raw_data, e_account); +%_____________________________________________________________ +% needed for flux calculation using bim2 +use_bim2 = 1; +% Remember to activate Elletra +scan_dark=43; % scan with closed shutter (set offset in Elettra device such that counts are almost zero) +scan=44; % scan with open shutter and BIM2 diode in the beam +air_path = 10; % air path distance between the exposure box window and the sample in mm +Elettra_bitmode = 24; +Elettra_range = 2.5e-3; %[A] +%_____________________________________________________________ +% needed for flux calculation using the glassy carbon L14 +use_GCL14 = 1; +%glassy carbon +Glassy_carbon=34; % the scan number of glassy carbon +%air scattering +Air = 42; %scan number of air scattering +DB = 42; %direct beam, use air scattering in case there is no direct beam measurement +%parameters for calculating the flux +DetDist_mm = 2.1397e3; %in mm +used_bs = 1; % 1 for diode in the beam stop + % 2 for cyberstar diode +%_____________________________________________________________ +% also scale WAXS data? +use_WAXS = 0; % 1 to scale the WAXS data, 0 not used +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% calculations +if use_bim2 + % calculation + %read the spec files + S=spec_read(BasePath,'ScanNr',scan); + S_dark=spec_read(BasePath,'ScanNr',scan_dark); + + %read the energy in keV + energy = S_dark.mokev; + + %other important parameters + epsilon_Si=3.66; % energy required to generate an electron-hole pair in Si [eV] + e_charge=1.6e-19; %electron change [C] + + % from http://henke.lbl.gov/optical_constants/filter2.html, considering + % mica KAl3Si3O11.8H1.8F0.2 Density=2.76 Thickness= 4. microns + % 4 um mica at the exposure box exit + [val, ~ ] = get_fil_trans( 'mica', energy, 4); + trans_factor_d1 = val(2); + + + %calculate the transmission of the air path + % between the exposure box exit and the sample + %from http://henke.lbl.gov/optical_constants/gastrn2.html considering + % air N1.562O.42C.0003Ar.0094 Pressure=760 torr. T = 297 K, Path = 1 cm + [ val ] = get_gas_trans('air', energy, air_path/10,760, 297 ); + ap_trans = val(2); + + + % from http://henke.lbl.gov/optical_constants/filter2.html, considering + % Silicon Density=2.33 Thickness= 10. microns + % contributes positively to the calculated flux + t_d1=10; % thickness Si sensor in microns + [ val, ~ ] = get_fil_trans( 'Si', energy, t_d1); + trans_si = val(2); + + + + %calculate the counts difference between open and closed shutter + counts_d1 = mean(S.transd) - mean(S_dark.transd); %photons + fprintf('The average counts at the transmission diode is %7.2f photons \n',counts_d1) + + %calculate the current + curr_d1 = counts_d1 * Elettra_range/(2^(Elettra_bitmode-1)); + fprintf('The current at the transmission diode is %7.2f microA \n',curr_d1*1e6) + + %calculate the flux + flux_BIM2= ((curr_d1 * epsilon_Si)/(e_charge* (energy*1e3) *(1-trans_si)))*(trans_factor_d1 * ap_trans); + fprintf('The flux based on bim2 is %7.2e photons/s \n',flux_BIM2) +end + +%% +if use_GCL14 + + % determination of the flux + %thickness of the sample + thickness_L14 = 0.1; % in cm for L14 + + %calculate the relative transmittance + %load the diode value for the air + S_air = spec_read(BasePath,'ScanNr',Air); + %scale in case the exposure times are different + exp_time = S_air.sec(1,1); + scale_air = 1/exp_time; + + %read the scep data for the glassy carbon sample + S = spec_read(BasePath,'ScanNr',Glassy_carbon); + energy = S.mokev; % in kev + pixel_size = 0.172; % in mm + %normalize the measurement to the correct + exp_time = S.sec(1,1); + scale_GC = 1/exp_time; + + %calculation of transmittance + if used_bs == 1 + transmittance = (mean(S.diode)/mean(S.bpm4i))/(mean(S_air.diode)/mean(S_air.bpm4i)); + elseif used_bs == 2 + transmittance = (mean(S.cyb)/mean(S.bpm4i))/(mean(S_air.cyb)/mean(S_air.bpm4i)); + else + error('Please inform the beam stop used in used_bs'); + end + + + + + %define the name of the measurement + scan_num = Glassy_carbon; + if scan_num > 9999 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration/S%05d-%05d/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, init, last_number, e_account, scan_num); + else + filename = sprintf('%s/analysis/radial_integration/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, e_account, scan_num); + if exist(filename, 'file') ~= 2 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration/S%05d-%05d/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, init, last_number, e_account, scan_num); + end + end + Data_GC = importdata(filename); + I_GC_m = mean(Data_GC.I_all,3); + I_GC_m = sum(I_GC_m.*Data_GC.norm_sum,2)./sum(Data_GC.norm_sum, 2); + q_GC_m = pixel_to_q(Data_GC.radius, pixel_size, DetDist_mm, energy); + + + %estimate the direct beam measurement + S_DB = spec_read(BasePath,'ScanNr',DB); + %scale in case the exposure times are different + exp_time = S_DB.sec(1,1); + scale_DB = 1/exp_time; + + + %plot the measured standard + scan_num = DB; + if scan_num > 9999 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration/S%05d-%05d/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, init, last_number, e_account, scan_num); + else + filename = sprintf('%s/analysis/radial_integration/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, e_account, scan_num); + if exist(filename, 'file') ~= 2 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration/S%05d-%05d/%s_1_%05d_00000_00000_integ.mat', ... + BasePath, init, last_number, e_account, scan_num); + end + end + Data_DB = importdata(filename); + I_DB= mean(Data_DB.I_all,3); + I_DB = sum(I_DB.*Data_DB.norm_sum,2)./sum(Data_DB.norm_sum, 2); + + + %hold on; + %q_GC_m = (q_GC_m*10); + %normalize by the exposure time + I_GC_m = ((((I_GC_m*scale_GC)*1/transmittance )-(I_DB*scale_DB))*1/thickness_L14); + + %compare to calibrated standard + %values provided for the calibrated Glassy carbon sample + q_GC_c = [0.0120496340976507;0.0125044551874072;0.0129732051637538;0.0134583002623949;0.0139593140871214;0.0144769572942580;0.0150120826715035;0.0155646902169131;0.0161354905862074;0.0167244837770547;0.0173329489709293;0.0179618810858233;0.0186112801185458;0.0192811460656564;0.0199724738441620;0.0206865426336400;0.0214223575088364;0.0221826189635588;0.0229661899401404;0.0237744917475841;0.0246092299570281;0.0254699781665768;0.0263573048941064;0.0272720629196088;0.0282159578107348;0.0291889895567488;0.0301914424089283;0.0312250219314847;0.0322902966359584;0.0333882614266597;0.0345199112062792;0.0356860987444304;0.0368869661524214;0.0381246453778608;0.0393994206591611;0.0407117183640269;0.0420638125635520;0.0434554189640091;0.0448893801546903;0.0463648433112569;0.0478845088842486;0.0494488032221519;0.0510572998839037;0.0527151155326772;0.0544201181456095;0.0561750081476676;0.0579803539937366;0.0598374347869336;0.0617482402768343;0.0637136231591258;0.0657351467748547;0.0678138059336549;0.0699520167405197;0.0721506318538254;0.0744114988336479;0.0767360388361561;0.0791253887439922;0.0815812539462713;0.0841040606468480;0.0867000623577208;0.0893671269066933;0.0921071017413062;0.0949225449342727;0.0978154460147494;0.100787225969034;0.103839874273604;0.106976091018953;0.110196728587036;0.113505339771704;0.116902066242041;0.120391029223623;0.123972796689442;0.127650494872454;0.131426112929190;0.135299792309430;0.139280344160401;0.143364214499320;0.147555239889708;0.151856261927069;0.156270548500290;0.160799093399002;0.165445448584523;0.170213165908177;0.175104375861299;0.180120640330274;0.185266789926598;0.190545381136233;0.195958259692354;0.201509545135676;0.207201793489021;0.213037134240793;0.219023239380533;0.225158684973226;0.231448584401029;0.237896203237904;0.244506227971290;0.251280076107924;0.258222433557780;0.265337275310852;0.272627723322335;0.280098746656204;0.287753608607507;0.290087652062856;0.293304736269482;0.296521618321574;0.299738296022361;0.302954767175882;0.306171029586988;0.309387081061351;0.312602919405477;0.315818542426710;0.319033947933244;0.322249133734126;0.325464097639273;0.328678837459470;0.331893351006389;0.335107636092589;0.338321690531530;0.341535512137577;0.344749098726013;0.347962448113043;0.351175558115807;0.354388426552383;0.357601051241800;0.360813430004044;0.364025560660067;0.367237441031796;0.370449068942139;0.373660442214998;0.376871558675271;0.380082416148865;0.383293012462703;0.386503345444732;0.389713412923933;0.392923212730325;0.396132742694977;0.399342000650017;0.402550984428637;0.405759691865102;0.408968120794761;0.412176269054052;0.415384134480512;0.418591714912783;0.421799008190624;0.425006012154916;0.428212724647670;0.431419143512038;0.434625266592318;0.437831091733966;0.441036616783597;0.444241839589003;0.447446757999153;0.450651369864203;0.453855673035507;0.457059665365622;0.460263344708318;0.463466708918585;0.466669755852640;0.469872483367936;0.473074889323173;0.476276971578300;0.479478727994529;0.482680156434336;0.485881254761478;0.489082020840992;0.492282452539209;0.495482547723759;0.498682304263582;0.501881720028930;0.505080792891380;0.508279520723843;0.511477901400565;0.514675932797143;0.517873612790525;0.521070939259026;0.524267910082328;0.527464523141494;0.530660776318971;0.533856667498600;0.537052194565627;0.540247355406704;0.543442147909900;0.546636569964711;0.549830619462064;0.553024294294327;0.556217592355316;0.559410511540301;0.562603049746017;0.565795204870670;0.568986974813943;0.572178357477005;0.575369350762521;0.578559952574654;0.581750160819079;0.584939973402985;0.588129388235086;0.591318403225627;0.594507016286392;0.597695225330712;0.600883028273470;0.604070423031114;0.607257407521657;0.610443979664689;0.613630137381386;0.616815878594512;0.620001201228432;0.623186103209116;0.626370582464145;0.629554636922725;0.632738264515686;0.635921463175494;0.639104230836259;0.642286565433740;0.645468464905351;0.648649927190174;0.651830950228960;0.655011531964139;0.658191670339828;0.661371363301837;0.664550608797674;0.667729404776558;0.670907749189420;0.674085639988915;0.677263075129425;0.680440052567068;0.683616570259708;0.686792626166954;0.689968218250177;0.693143344472510;0.696318002798857;0.699492191195902;0.702665907632112;0.705839150077748;0.709011916504869;0.712184204887341;0.715356013200841;0.718527339422871;0.721698181532753;0.724868537511648;0.728038405342554;0.731207783010319;0.734376668501645;0.737545059805092;0.740712954911092;0.743880351811948;0.747047248501847;0.750213642976862;0.753379533234962;0.756544917276018;0.759709793101807;0.762874158716023;0.766038012124281;0.769201351334123;0.772364174355027;0.775526479198411;0.778688263877642;0.781849526408042;0.785010264806893;0.788170477093445;0.791330161288921;0.794489315416526;0.797647937501451;0.800806025570882;0.803963577654003;0.807120591782006;0.810277065988094;0.813432998307490;0.816588386777444;0.819743229437236;0.822897524328184;0.826051269493653;0.829204462979055;0.832357102831862;0.835509187101608;0.838660713839898;0.841811681100411;0.844962086938909;0.848111929413243;0.851261206583356;0.854409916511293;0.857558057261207;0.860705626899361;0.863852623494139;0.866999045116048;0.870144889837729;0.873290155733956;0.876434840881649;0.879578943359877;0.882722461249862;0.885865392634988;0.889007735600807;0.892149488235043;0.895290648627597;0.898431214870558;0.901571185058203;0.904710557287006;0.907849329655645;0.910987500265001;0.914125067218175;0.917262028620484;0.920398382579469;0.923534127204906;0.926669260608805;0.929803780905419;0.932937686211250;0.936070974645051;0.939203644327838;0.942335693382891;0.945467119935758;0.948597922114265;0.951728098048522;0.954857645870923;0.957986563716156;0.961114849721207;0.964242502025366;0.967369518770233;0.970495898099721;0.973621638160064;0.976746737099822;0.979871193069885;0.982995004223480;0.986118168716174;0.989240684705883;0.992362550352873;0.995483763819770;0.998604323271560;1.00172422687560;1.00484347280162;1.00796205922172;1.01107998431039;1.01419724624452;1.01731384320337;1.02042977336862;1.02354503492435;1.02665962605703;1.02977354495557;1.03288678981129;1.03599935881792;1.03911125017165;1.04222246207107;1.04533299271724;1.04844284031363;1.05155200306620;1.05466047918332;1.05776826687586;1.06087536435712;1.06398176984290;1.06708748155144;1.07019249770348;1.07329681652225;1.07640043623343;1.07950335506523;1.08260557124835;1.08570708301598;1.08880788860381;1.09190798625008;1.09500737419550;1.09810605068332;1.10120401395931;1.10430126227179;1.10739779387158;1.11049360701207;1.11358869994917;1.11668307094135;1.11977671824962;1.12286964013756;1.12596183487130;1.12905330071955;1.13214403595357;1.13523403884722;1.13832330767690;1.14141184072162;1.14449963626298;1.14758669258515;1.15067300797492;1.15375858072165;1.15684340911734;1.15992749145657;1.16301082603654;1.16609341115706;1.16917524512058;1.17225632623216;1.17533665279948;1.17841622313287;1.18149503554529;1.18457308835233;1.18765037987225;1.19072690842594;1.19380267233694;1.19687766993146;1.19995189953835;1.20302535948915;1.20609804811805;1.20916996376192;1.21224110476031;1.21531146945542;1.21838105619218;1.22144986331817;1.22451788918368;1.22758513214169;1.23065159054788;1.23371726276062;1.23678214714101;1.23984624205283;1.24290954586259;1.24597205693952;1.24903377365556;1.25209469438539;1.25515481750638;1.25821414139868;1.26127266444514;1.26433038503136;1.26738730154567;1.27044341237917;1.27349871592567;1.27655321058177;1.27960689474680;1.28265976682286;1.28571182521480;1.28876306833024;1.29181349457958;1.29486310237597;1.29791189013534;1.30095985627641;1.30400699922068;1.30705331739241;1.31009880921867;1.31314347312932;1.31618730755700;1.31923031093715;1.32227248170803;1.32531381831067;1.32835431918892;1.33139398278945;1.33443280756172;1.33747079195802;1.34050793443346;1.34354423344595;1.34657968745625;1.34961429492792;1.35264805432736;1.35568096412381;1.35871302278933;1.36174422879884;1.36477458063007;1.36780407676360;1.37083271568289;1.37386049587419;1.37688741582665;1.37991347403226;1.38293866898585;1.38596299918512;1.38898646313064;1.39200905932582;1.39503078627698;1.39805164249326;1.40107162648669;1.40409073677219;1.40710897186754;1.41012633029340;1.41314281057332;1.41615841123372;1.41917313080391;1.42218696781611;1.42519992080540;1.42821198830976;1.43122316887009;1.43423346103016;1.43724286333665;1.44025137433914;1.44325899259012;1.44626571664498;1.44927154506203;1.45227647640248;1.45528050923045;1.45828364211298;1.46128587362004;1.46428720232451;1.46728762680218;1.47028714563178;1.47328575739495;1.47628346067627;1.47928025406325;1.48227613614633;1.48527110551887;1.48826516077719;1.49125830052052;1.49425052335104;1.49724182787389;1.50023221269712;1.50322167643175;1.50621021769172;1.50919783509396;1.51218452725830;1.51517029280755;1.51815513036748;1.52113903856680;1.52412201603717;1.52710406141322;1.53008517333255;1.53306535043570;1.53604459136618;1.53902289477047;1.54200025929801;1.54497668360121;1.54795216633545;1.55092670615908;1.55390030173342;1.55687295172277;1.55984465479439;1.56281540961853;1.56578521486842;1.56875406922025;1.57172197135321;1.57468891994946;1.57765491369415;1.58061995127541;1.58358403138436;1.58654715271510;1.58950931396471;1.59247051383329;1.59543075102390;1.59839002424259;1.60134833219843;1.60430567360347;1.60726204717273;1.61021745162427;1.61317188567911;1.61612534806130;1.61907783749785;1.62202935271880;1.62497989245718;1.62792945544904;1.63087804043339;1.63382564615229;1.63677227135077;1.63971791477690;1.64266257518171;1.64560625131929;1.64854894194669;1.65149064582400;1.65443136171431;1.65737108838372;1.66030982460134;1.66324756913928;1.66618432077270;1.66912007827973;1.67205484044154;1.67498860604230;1.67792137386920;1.68085314271246;1.68378391136529;1.68671367862394;1.68964244328766;1.69257020415873;1.69549696004244;1.69842270974710;1.70134745208405;1.70427118586765;1.70719390991525;1.71011562304725;1.71303632408708;1.71595601186116;1.71887468519895;1.72179234293293;1.72470898389861;1.72762460693450;1.73053921088216;1.73345279458615;1.73636535689408;1.73927689665656;1.74218741272723;1.74509690396276;1.74800536922285;1.75091280737020;1.75381921727057;1.75672459779271;1.75962894780841;1.76253226619250;1.76543455182281;1.76833580358020;1.77123602034858;1.77413520101486;1.77703334446897;1.77993044960388;1.78282651531559;1.78572154050312;1.78861552406850;1.79150846491680;1.79440036195611;1.79729121409756;1.80018102025528;1.80306977934643;1.80595749029120;1.80884415201282;1.81172976343751;1.81461432349454;1.81749783111618;1.82038028523776;1.82326168479758;1.82614202873702;1.82902131600044;1.83189954553523;1.83477671629182;1.83765282722364;1.84052787728715;1.84340186544183;1.84627479065018;1.84914665187771;1.85201744809296;1.85488717826749;1.85775584137587;1.86062343639569;1.86348996230756;1.86635541809510;1.86921980274495;1.87208311524676;1.87494535459320;1.87780651977996;1.88066660980573;1.88352562367222;1.88638356038415;1.88924041894925;1.89209619837827;1.89495089768496;1.89780451588608;1.90065705200139;1.90350850505369;1.90635887406874;1.90920815807536;1.91205635610532;1.91490346719343;1.91774949037749;1.92059442469832;1.92343826919972;1.92628102292850;1.92912268493447;1.93196325427044;1.93480272999222;1.93764111115861;1.94047839683142;1.94331458607543;1.94614967795845;1.94898367155125;1.95181656592761;1.95464836016430;1.95747905334108;1.96030864454069;1.96313713284887;1.96596451735434;1.96879079714881;1.97161597132697;1.97444003898649;1.97726299922804;1.98008485115525;1.98290559387474;1.98572522649611;1.98854374813193;1.99136115789776;1.99417745491212;1.99699263829651;1.99980670717540;2.00261966067624;2.00543149792944;2.00824221806838;2.01105182022941;2.01386030355184;2.01666766717795;2.01947391025299;2.02227903192515;2.02508303134560;2.02788590766846;2.03068766005082;2.03348828765269;2.03628778963708;2.03908616516993;2.04188341342012;2.04467953355950;2.04747452476286;2.05026838620794;2.05306111707542;2.05585271654892;2.05864318381502;2.06143251806322;2.06422071848596;2.06700778427864;2.06979371463957;2.07257850877001;2.07536216587413;2.07814468515905;2.08092606583482;2.08370630711441;2.08648540821371;2.08926336835154;2.09204018674965;2.09481586263269;2.09759039522824;2.10036378376680;2.10313602748177;2.10590712560949;2.10867707738918;2.11144588206298;2.11421353887595;2.11698004707603;2.11974540591410;2.12250961464390;2.12527267252209;2.12803457880823;2.13079533276478;2.13355493365708;2.13631338075336;2.13907067332476;2.14182681064528;2.14458179199184;2.14733561664421;2.15008828388506;2.15283979299994;2.15559014327727;2.15833933400834;2.16108736448734;2.16383423401131;2.16657994188015;2.16932448739666;2.17206786986647;2.17481008859809;2.17755114290289;2.18029103209511;2.18302975549182;2.18576731241296;2.18850370218132;2.19123892412255;2.19397297756512;2.19670586184038;2.19943757628250;2.20216812022849;2.20489749301821;2.20762569399436;2.21035272250246;2.21307857789086;2.21580325951075;2.21852676671615;2.22124909886389;2.22397025531364;2.22669023542787;2.22940903857189;2.23212666411381;2.23484311142455;2.23755837987785;2.24027246885026;2.24298537772112;2.24569710587260;2.24840765268964;2.25111701755999;2.25382519987421;2.25653219902564;2.25923801441041;2.26194264542744;2.26464609147844;2.26734835196789;2.27004942630307;2.27274931389402;2.27544801415358;2.27814552649733;2.28084185034365;2.28353698511367;2.28623093023130;2.28892368512319;2.29161524921877;2.29430562195022;2.29699480275248;2.29968279106324;2.30236958632294;2.30505518797475;2.30773959546461;2.31042280824119;2.31310482575591;2.31578564746289;2.31846527281903;2.32114370128393;2.32382093231993;2.32649696539210;2.32917179996822;2.33184543551879;2.33451787151703;2.33718910743889;2.33985914276300;2.34252797697073;2.34519560954614;2.34786203997599;2.35052726774974;2.35319129235956;2.35585411330031;2.35851573006953;2.36117614216746;2.36383534909702;2.36649335036382;2.36915014547614;2.37180573394495;2.37446011528388;2.37711328900924;2.37976525464002;2.38241601169784;2.38506555970702;2.38771389819453;2.39036102668997;2.39300694472564;2.39565165183645;2.39829514755998;2.40093743143644;2.40357850300870;2.40621836182226;2.40885700742525;2.41149443936843;2.41413065720522;2.41676566049162;2.41939944878629;2.42203202165049;2.42466337864812;2.42729351934567;2.42992244331226;2.43255015011959;2.43517663934201;2.43780191055643;2.44042596334239;2.44304879728200;2.44567041195998;2.44829080696363;2.45090998188285;2.45352793631011;2.45614466984046;2.45876018207154;2.46137447260355;2.46398754103927;2.46659938698405;2.46921001004578;2.47181940983495;2.47442758596457;2.47703453805024;2.47964026571008;2.48224476856477;2.48484804623755;2.48745009835419;2.49005092454298;2.49265052443478;2.49524889766296;2.49784604386342;2.50044196267460;2.50303665373744;2.50563011669542;2.50822235119452;2.51081335688325;2.51340313341261;2.51599168043611;2.51857899760978;2.52116508459213;2.52374994104418;2.52633356662942;2.52891596101385;2.53149712386596;2.53407705485670;2.53665575365952;2.53923321995034;2.54180945340753;2.54438445371198;2.54695822054699;2.54953075359836;2.55210205255433;2.55467211710562;2.55724094694536;2.55980854176916;2.56237490127508;2.56494002516360;2.56750391313765;2.57006656490260;2.57262798016624;2.57518815863879;2.57774710003291;2.58030480406367;2.58286127044855;2.58541649890745;2.58797048916270;2.59052324093900;2.59307475396350;2.59562502796570;2.59817406267754;2.60072185783333;2.60326841316978;2.60581372842598;2.60835780334341;2.61090063766592;2.61344223113975;2.61598258351350;2.61852169453814;2.62105956396702;2.62359619155583;2.62613157706264;2.62866572024786;2.63119862087425;2.63373027870693;2.63626069351336;2.63878986506334;2.64131779312900;2.64384447748481;2.64636991790758;2.64889411417643;2.65141706607280;2.65393877338047;2.65645923588553;2.65897845337636;2.66149642564367;2.66401315248048;2.66652863368210;2.66904286904613;2.67155585837247;2.67406760146333;2.67657809812318;2.67908734815878;2.68159535137919;2.68410210759571;2.68660761662195;2.68911187827376;2.69161489236927;2.69411665872887;2.69661717717520;2.69911644753317;2.70161446962991;2.70411124329485;2.70660676835960;2.70910104465805;2.71159407202633;2.71408585030277;2.71657637932796;2.71906565894470;2.72155368899800;2.72404046933512;2.72652599980551;2.72901028026082;2.73149331055495;2.73397509054395;2.73645562008610;2.73893489904188;2.74141292727394;2.74388970464714;2.74636523102850;2.74883950628724;2.75131253029475;2.75378430292460;2.75625482405253;2.75872409355642;2.76119211131634;2.76365887721452;2.76612439113533;2.76858865296529;2.77105166259308;2.77351341990951;2.77597392480755;2.77843317718228;2.78089117693092;2.78334792395284;2.78580341814951;2.78825765942452;2.79071064768361;2.79316238283458;2.79561286478740;2.79806209345409;2.80051006874882;2.80295679058782;2.80540225888944;2.80784647357411;2.81028943456436;2.81273114178478;2.81517159516205;2.81761079462495;2.82004874010429;2.82248543153299;2.82492086884600;2.82735505198035;2.82978798087512;2.83221965547146;2.83465007571253;2.83707924154359;2.83950715291190;2.84193380976677;2.84435921205955;2.84678335974362;2.84920625277438;2.85162789110926;2.85404827470771;2.85646740353118;2.85888527754316;2.86130189670913;2.86371726099656;2.86613137037495;2.86854422481579;2.87095582429254;2.87336616878068;2.87577525825766;2.87818309270290;2.88058967209781;2.88299499642579;2.88539906567218;2.88780187982431;2.89020343887145;2.89260374280485;2.89500279161771;2.89740058530517;2.89979712386432;2.90219240729421;2.90458643559581;2.90697920877203;2.90937072682773;2.91176098976967;2.91414999760655;2.91653775034900;2.91892424800954;2.92130949060263;2.92369347814463;2.92607621065379;2.92845768815029;2.93083791065618;2.93321687819543;2.93559459079388;2.93797104847927;2.94034625128120;2.94272019923118;2.94509289236258;2.94746433071064;2.94983451431246;2.95220344320703;2.95457111743516;2.95693753703955;2.95930270206474;2.96166661255712;2.96402926856492;2.96639067013821;2.96875081732891;2.97110971019076;2.97346734877933;2.97582373315202;2.97817886336805;2.98053273948846;2.98288536157610;2.98523672969564;2.98758684391354;2.98993570429808;2.99228331091932;2.99462966384914;2.99697476316119;2.99931860893091;3.00166120123554;3.00400254015408;3.00634262576731;3.00868145815780;3.01101903740987;3.01335536360961;3.01569043684487;3.01802425720525;3.02035682478213;3.02268813966861;3.02501820195955;3.02734701175154;3.02967456914293;3.03200087423378;3.03432592712591;3.03664972792282;3.03897227672979;3.04129357365378;3.04361361880348;3.04593241228928;3.04824995422329;3.05056624471932;3.05288128389289;3.05519507186120;3.05750760874314;3.05981889465932;3.06212892973201;3.06443771408515;3.06674524784439;3.06905153113703;3.07135656409205;3.07366034684009;3.07596287951346;3.07826416224614;3.08056419517373;3.08286297843351;3.08516051216440;3.08745679650696;3.08975183160340;3.09204561759757;3.09433815463492;3.09662944286256;3.09891948242923;3.10120827348526;3.10349581618262;3.10578211067490;3.10806715711728;3.11035095566656;3.11263350648114;3.11491480972101;3.11719486554776;3.11947367412459;3.12175123561626;3.12402755018913;3.12630261801114;3.12857643925179;3.13084901408218;3.13312034267495;3.13539042520432;3.13765926184609;3.13992685277758;3.14219319817769;3.14445829822687;3.14672215310710;3.14898476300192;3.15124612809639;3.15350624857714;3.15576512463230;3.15802275645154;3.16027914422605;3.16253428814855;3.16478818841326;3.16704084521594;3.16929225875384;3.17154242922571;3.17379135683183;3.17603904177395;3.17828548425533;3.18053068448072;3.18277464265635;3.18501735898995;3.18725883369071;3.18949906696932;3.19173805903792;3.19397581011012;3.19621232040103;3.19844759012719;3.20068161950659;3.20291440875870;3.20514595810444;3.20737626776615;3.20960533796765;3.21183316893418;3.21405976089242;3.21628511407047;3.21850922869788;3.22073210500562;3.22295374322608;3.22517414359306;3.22739330634179;3.22961123170890;3.23182791993244;3.23404337125183;3.23625758590794;3.23847056414301;3.24068230620066;3.24289281232593;3.24510208276521;3.24731011776632;3.24951691757841;3.25172248245204;3.25392681263911;3.25612990839293;3.25833176996813;3.26053239762073;3.26273179160809;3.26492995218895;3.26712687962336;3.26932257417275;3.27151703609988;3.27371026566885;3.27590226314509;3.27809302879538;3.28028256288781;3.28247086569180;3.28465793747810;3.28684377851876;3.28902838908718;3.29121176945802;3.29339391990729;3.29557484071229;3.29775453215161;3.29993299450515;3.30211022805410;3.30428623308094;3.30646100986943;3.30863455870462;3.31080687987284;3.31297797366170;3.31514784036006;3.31731648025808;3.31948389364715;3.32165008081996;3.32381504207043;3.32597877769375;3.32814128798635;3.33030257324592;3.33246263377138;3.33462146986291;3.33677908182190;3.33893546995101;3.34109063455409;3.34324457593626]; + I_GC_c = [37.4996383921856;37.2548332078092;36.4283288124236;36.2171017455181;36.0594526535833;35.9115039243978;35.7206516758486;35.5457687526235;35.4645745008631;35.3593982858726;35.2646446480797;34.5439024389316;34.4496042048382;34.3367536550321;34.2494160725991;34.1552878382091;34.0225318267624;33.8700294479800;33.7322197521813;33.6578192244874;33.5595559196515;33.4289145601177;33.3042268238371;33.1786408925464;33.0235231925436;32.9220890682906;32.7933488016634;32.0956203754132;32.0134149698111;31.9402817946214;31.7996141727154;31.7154953296368;31.6228560667772;31.5414505004883;31.4524481113140;31.3508797425490;31.2854652404785;31.1914306053749;31.1408305534950;31.0959938489474;31.0430858318622;31.0641636481652;30.9690412374643;30.8992959536039;30.8582724791307;30.8052206773024;30.7651990010188;30.7142602480375;30.6480002036461;30.5753599313589;30.5380638562716;30.4429228856013;30.3636076266949;30.2560176849365;30.1547018931462;30.0181754919199;29.8537241862370;29.6561602812547;29.4377786196195;29.1376347175011;28.8615410878108;28.5411135600163;28.1562579961923;28.2315394618897;27.2097022716816;27.2126863964973;26.6591948256764;26.0754377785032;25.4554875012226;24.7569221427885;24.0479588379221;23.2468096224346;22.4772266670018;21.6680129830842;20.8113131927969;19.9163549442443;19.0327712249840;18.0965621637316;17.2038128508614;16.2679657267658;15.3605304774854;14.4543927151244;13.5951173112066;12.6881058999300;11.8622364660335;10.8011194375845;10.2782222618139;9.52750501741191;8.61494293579688;7.93357564852788;7.30044454794664;6.77625944064214;6.16035426579989;5.64497525875385;5.16162362465492;4.72441913531377;4.31454612658574;3.95227755033053;3.59874264093546;3.29018119665293;3.00322911372552;2.74924242496490;2.67832939744743;2.58007078333710;2.49067190473411;2.39487275388869;2.34205549260251;2.25324465762885;2.17310866891045;2.08711825841638;2.01926423902903;1.94911575979623;1.88229705191627;1.82389184767408;1.76412613413988;1.71169094593207;1.65296411902820;1.60233168550444;1.55217055879365;1.50621289274187;1.46380628112730;1.41082620607445;1.37340160284771;1.33974584799371;1.29936150512739;1.26254058638749;1.21817236723899;1.18855787548849;1.15051551711141;1.13178209580990;1.09564126963491;1.05689699918740;1.03223779321436;1.00921412322167;0.982011108924821;0.954196649142988;0.933257621487925;0.910027226521827;0.889860352287722;0.862006058599062;0.838316685025028;0.818082680760775;0.797788343684465;0.782549083868602;0.761265928948320;0.739661568945698;0.730028877798775;0.713922496518267;0.695299867344911;0.678843048156554;0.660838460326802;0.649057366696747;0.629896406621617;0.617109000418718;0.603455210139803;0.589989001697014;0.580678570990164;0.567285610839356;0.557683612799142;0.542622027987168;0.532108400474406;0.518258635461546;0.509293319512140;0.497352793118331;0.488202205124504;0.478828182354006;0.468886500917918;0.460221030314538;0.448104587202362;0.441216649094231;0.432536416075870;0.425109193866992;0.417060076396248;0.408307219708715;0.401393103801021;0.394546945404697;0.385522557899928;0.380612106178446;0.372607525564145;0.366296726081248;0.361762870734754;0.352927542887272;0.347774303544552;0.340362363686539;0.336202140636285;0.327517845542412;0.323161000427133;0.318350108024248;0.312773068088334;0.308253896940966;0.303326886374816;0.298524243296601;0.294257140015375;0.289435833533733;0.284379271618311;0.279920089267782;0.275588732481385;0.272275805408322;0.265975135649143;0.263913217302554;0.260995613065508;0.256283712638261;0.252464453872433;0.249572932713027;0.245678091117762;0.241957725947313;0.239883404681421;0.234639557559059;0.232384547125292;0.227869074445529;0.225798291447451;0.222371988936042;0.219979802092999;0.217518861184104;0.214980917512402;0.211843541970048;0.208961118292305;0.205994100051124;0.203824360208272;0.201058749381979;0.198683961225536;0.196581103304325;0.194255444598214;0.190596526903666;0.188467527386347;0.186598882088540;0.184907665108340;0.182435297445616;0.181351841464676;0.178632855644918;0.177518067175766;0.175030999770603;0.172310029478859;0.170335199522834;0.168928597141419;0.166885872093665;0.165378744039184;0.162626121329917;0.161118313665291;0.160414100848547;0.158590051316222;0.156832935400278;0.156720543960870;0.153592032136451;0.152817084573196;0.151441497989464;0.150090135985959;0.147760548925723;0.146990053306342;0.145922931001558;0.144860247120584;0.142764303904499;0.141755847632909;0.139879437827240;0.139006281832558;0.137331368086629;0.136060935867709;0.135112276901657;0.133737412181438;0.132116486936951;0.131524506276471;0.129908938183876;0.129735945273417;0.128738876495355;0.127101736233621;0.125510445428119;0.124532502369922;0.123586137299227;0.123161959455318;0.121932384227846;0.121726636603451;0.120038483099947;0.119580595712703;0.118836345839512;0.118875274780644;0.117957003927912;0.117231934438890;0.117059080942472;0.115178614399837;0.115408995761404;0.114051913989668;0.112516650678456;0.112578676816154;0.111024072389246;0.110785091511723;0.110345368051266;0.109314517337608;0.108717646038948;0.107061949103043;0.107075749973032;0.106601948598256;0.106306467600103;0.106530862745948;0.104609512936625;0.104019410346268;0.103473019493828;0.102810683308282;0.103122654686751;0.102310852595660;0.101484779161486;0.100574736122695;0.100334514652975;0.100189416951435;0.0994006899845934;0.0995791723669450;0.0979918953345411;0.0980639032940743;0.0981049551962847;0.0968831939397526;0.0967611915226487;0.0963822372721850;0.0962876724305304;0.0952951574008161;0.0950295077721273;0.0947363604415038;0.0948099539292648;0.0935799641716028;0.0932506161361304;0.0930830488988461;0.0932544616728714;0.0922055604856590;0.0914887152837670;0.0910342850529264;0.0906809361576627;0.0905641558872569;0.0899075772522877;0.0894893472328375;0.0895345083260128;0.0890018711739819;0.0886677856440173;0.0884756564678029;0.0879021214587174;0.0871529872155350;0.0876217668441652;0.0870052170433574;0.0869378114765917;0.0864945424420139;0.0867712346628615;0.0863888290073545;0.0865546106716726;0.0861348137265473;0.0855556387669311;0.0852856369758596;0.0858610676070817;0.0854605609491165;0.0850367474199608;0.0858718400808859;0.0854087862542802;0.0844500084810677;0.0843096850621483;0.0842918940182374;0.0844154965458165;0.0841082724311412;0.0840178149145389;0.0845054320758153;0.0833217999731521;0.0834777038132775;0.0827014079554586;0.0835435281260447;0.0847244466143707;0.0843391846805502;0.0841858807160885;0.0836703647209445;0.0841771297185195;0.0836196840839768;0.0833674730752508;0.0831777565275481;0.0840851899758925;0.0835353600864580;0.0833638810049888;0.0832928535653042;0.0835227786250826;0.0835279621628701;0.0831666287817926;0.0837227560029837;0.0836623980193662;0.0834248900506670;0.0835816333432480;0.0831849997677054;0.0827399553001356;0.0830817951283610;0.0842297784965481;0.0833999644874767;0.0835684204459715;0.0836453961781198;0.0834805890932144;0.0835470391183697;0.0834784253131107;0.0834183610223144;0.0841040348641747;0.0836065704251993;0.0839642083432425;0.0840850204633514;0.0850036747498199;0.0842248856254011;0.0843493223349913;0.0840527418343790;0.0842100505171298;0.0849402319950868;0.0843651530985329;0.0850988634565171;0.0853595021218781;0.0858445689602491;0.0854631132262984;0.0860438952815239;0.0859599726910152;0.0869955212390877;0.0860446609448370;0.0863361686601534;0.0867955138459666;0.0873334221515559;0.0870525917108529;0.0873156102189771;0.0874941864329727;0.0882440639358392;0.0885075810624663;0.0885248187521614;0.0891661326475738;0.0889495048148492;0.0893703403051117;0.0895546874486657;0.0902630932048841;0.0903029186428313;0.0907883265204857;0.0906467282229316;0.0911414158888007;0.0917738856341661;0.0916922619185359;0.0915203478873470;0.0922884973034796;0.0926033216712055;0.0931138918545560;0.0935911211306301;0.0936761818857671;0.0944248633828444;0.0948723820317554;0.0949468494003478;0.0954707621579024;0.0961279073472088;0.0962942185501833;0.0962053035211515;0.0966963507939852;0.0979202342844086;0.0980179641064233;0.0983733040196930;0.0986667768940910;0.0997478309271865;0.0998404286449695;0.100449357453439;0.100691676454507;0.101515291174958;0.101793108434166;0.102433177237233;0.103000229279223;0.103531218296803;0.104597478632081;0.104982689214794;0.104778765990754;0.106326827227661;0.106435668737810;0.106973613402831;0.107609565479959;0.108238666449599;0.108694691946741;0.110153897298624;0.110511372533923;0.111018660824552;0.111586810055312;0.112316769366801;0.112870487906306;0.113558568220466;0.114402567317150;0.115342464582022;0.115932153476461;0.117135352061785;0.117500073144297;0.118120015725177;0.119250678028916;0.120246207284863;0.120791227081139;0.122155177744817;0.122882983315590;0.123445015950130;0.124151839333055;0.125352315927278;0.126035099889789;0.126932948080035;0.128461797686879;0.129475140040189;0.130313104545368;0.131493852694764;0.132453620357563;0.133032552880140;0.134300047957459;0.135772027038256;0.136399424971278;0.137788996987622;0.138414776598931;0.139668011592967;0.141375595561552;0.142527124306941;0.143349299990772;0.144551451285932;0.145890295723163;0.147056746963049;0.148365658021746;0.149810704440913;0.151454004226644;0.152793846794285;0.153896830798280;0.155336881296646;0.157197219323716;0.158102403766416;0.160037937048823;0.160855417027481;0.162492032818723;0.163699210517460;0.165387514803600;0.167351960205536;0.168693255472803;0.170764446658187;0.172367763133848;0.174212097864683;0.175663294685604;0.177898421906694;0.179362848900446;0.179249970086033;0.181849881766393;0.183928880268139;0.185997186934555;0.188219542074020;0.190422409526596;0.192784709327254;0.193541064028063;0.195486546591224;0.196686887249801;0.199192614012702;0.200953345393903;0.203610785093689;0.204512692389041;0.206479550973491;0.208478573776161;0.210582263115830;0.212006979202808;0.214664343497077;0.215780240429842;0.218140890258223;0.219480926197594;0.221027251994341;0.223643635887271;0.225810809371972;0.227323362672101;0.229044394284067;0.230754598888442;0.232778602884360;0.234155865246407;0.235993221814910;0.238217985042940;0.239566814189230;0.240515343137583;0.241903344073455;0.244432323559211;0.245866322547838;0.246420910819076;0.248555718878753;0.249884475693517;0.250449999353858;0.251585519201414;0.252779565557130;0.254497722247741;0.255126440084873;0.256349613524879;0.257489971672725;0.257103657393491;0.258312544801924;0.258381990142922;0.259223429176171;0.258758551389667;0.259825997418901;0.260045694679529;0.259891475244714;0.260628482730240;0.260481540795873;0.260504639306441;0.260606652237674;0.260112524477504;0.259923926359401;0.259232156881148;0.259294659466811;0.259191891412954;0.257727247293731;0.257189441657865;0.255892029760549;0.255247284510122;0.254240165882962;0.253651667905458;0.251931132081184;0.250857834404964;0.249122991871621;0.248469565424385;0.246640776838502;0.244169987093193;0.242791967622438;0.240988748408351;0.239654572900907;0.237806385230646;0.235634746220506;0.233529474082008;0.231884595658197;0.229808106535271;0.227120439235811;0.225409909927598;0.222370773577963;0.220274578688395;0.217400590465258;0.215727397482599;0.213149284290822;0.210088118986334;0.207434588941018;0.204573359001446;0.202472437179484;0.199576822762138;0.196714116829165;0.193775002659220;0.191718727974613;0.188278620507074;0.186098140413177;0.182770599430838;0.179771405221572;0.177078675541496;0.174937396784584;0.171554989464862;0.168912232489398;0.165642615444593;0.163284899808782;0.160040840268530;0.157431185588883;0.154487887505419;0.152644031718029;0.149379139483998;0.146642173793427;0.144304869732841;0.141394927354025;0.139183848728402;0.136378238241086;0.133718180962964;0.131522165101923;0.128782999912383;0.126141535362966;0.123990811307635;0.120707778997368;0.118656407995089;0.116558736256450;0.114272405430663;0.111810389716351;0.110110689986860;0.107875433164136;0.105677559858969;0.103758408389972;0.101753851837463;0.0994364280068472;0.0976464360860966;0.0955470808123102;0.0936690528301063;0.0915869018640204;0.0903336018113043;0.0882722203537735;0.0867212214182326;0.0847499737669528;0.0833088709576811;0.0817807817067562;0.0802478752655140;0.0782050168539257;0.0767931118978623;0.0754228620532900;0.0742539905701452;0.0725798106190140;0.0714703242958179;0.0702803735932788;0.0688743990444592;0.0677203687643831;0.0662732155383156;0.0651844245641772;0.0642754403572801;0.0631498280480671;0.0615585494621173;0.0608664059298069;0.0598001541793686;0.0585623921787418;0.0578122164103816;0.0568700084516389;0.0553298267231375;0.0546772685999258;0.0542393927358034;0.0531471401890058;0.0519588455632168;0.0514520098524078;0.0508679152986645;0.0497543442260019;0.0490053758306410;0.0486221850812871;0.0477231729508574;0.0470082652718414;0.0465084943584076;0.0459828679034788;0.0451689947971547;0.0442919128388797;0.0439463498151099;0.0434173195478428;0.0429818054771213;0.0423388355370746;0.0415610036461803;0.0409245784827451;0.0406800346909924;0.0401708683639568;0.0394690011218357;0.0394587909771062;0.0384952715270525;0.0381257551941984;0.0379600738941668;0.0372883034338003;0.0370663930260093;0.0367242473394865;0.0360296001708497;0.0357660313097922;0.0354671924805957;0.0351198186132375;0.0344023914544330;0.0343153616956022;0.0338589697377667;0.0335647783704276;0.0331790324921377;0.0331615113610083;0.0327207980420024;0.0323823954955347;0.0322471477861945;0.0318426733861727;0.0315626544993144;0.0311135326887795;0.0308818165440491;0.0306123852933966;0.0303084081142922;0.0301377397792610;0.0300907380027190;0.0297080529061949;0.0294565130500449;0.0292923134209813;0.0288977724792032;0.0290164359349684;0.0286109546405044;0.0289926109619715;0.0282480306823265;0.0280175586059109;0.0279775505002816;0.0275033393387664;0.0273768434931974;0.0271741309617271;0.0269192380968549;0.0266548784356806;0.0267615185580666;0.0265603315136120;0.0263184626546715;0.0261522597669838;0.0258223278910966;0.0259040816252261;0.0257296228340069;0.0254926620239851;0.0256389227253211;0.0249929291910680;0.0249728931678840;0.0247245787541135;0.0248721362457691;0.0246985039639338;0.0244385338138334;0.0246143215183240;0.0241495595799066;0.0241247720819240;0.0241939643910717;0.0237849599530596;0.0237941704489560;0.0237521197867216;0.0235285289273351;0.0234570331555563;0.0233220870385309;0.0233070372264960;0.0230581193128611;0.0231295022663461;0.0232227228689549;0.0230077753704454;0.0229288119722936;0.0231661757738245;0.0227478748706787;0.0228136579317384;0.0227804463185621;0.0223818087897547;0.0225266154294518;0.0224489831136479;0.0222667554030470;0.0221847844582676;0.0220647200942889;0.0220395032920402;0.0217114821642993;0.0219871901824814;0.0217397871415369;0.0218450921535550;0.0218008450259407;0.0218344714234402;0.0215694654005418;0.0215075150782776;0.0212987985735320;0.0214431131901444;0.0212305136966542;0.0213297237129501;0.0212614495440215;0.0217968742192471;0.0213976187831031;0.0209758211806359;0.0210844154369079;0.0208376402614487;0.0211509225289370;0.0213021876370094;0.0218966399423126;0.0205487396011189;0.0207026795969442;0.0213921673559823;0.0204155125383724;0.0204633379170904;0.0200609684942755;0.0203388414982041;0.0203435677384577;0.0202575859628054;0.0202851951327361;0.0202584230260972;0.0203313586566435;0.0202485470999655;0.0199307003806070;0.0202848816407111;0.0197481404573840;0.0200180144068593;0.0197751220401197;0.0196937051860860;0.0194422718030303;0.0196606155102091;0.0197320803574530;0.0196775592553173;0.0195612658853753;0.0195992321102882;0.0193309335190488;0.0193400583268185;0.0194387430392444;0.0191746906263867;0.0193883106751985;0.0192554895012652;0.0192868113895302;0.0194438283497712;0.0192658445089028;0.0191799866828671;0.0190471864785960;0.0192623669083169;0.0193548560337128;0.0191622056097198;0.0190559010857016;0.0191646345804209;0.0187859801846454;0.0190984067974883;0.0190340818820691;0.0190262681786453;0.0190074757431962;0.0189581788323853;0.0189775105099474;0.0186714493488168;0.0187788258984221;0.0187438133093437;0.0188215813252638;0.0188003652478934;0.0188825051964057;0.0185983047092690;0.0188468176110961;0.0187942831650983;0.0185860248153894;0.0185843402667667;0.0185372378208103;0.0186243592434270;0.0187239700124557;0.0186597144201739;0.0185671743389061;0.0185596309228783;0.0183603842432739;0.0186161755077576;0.0184799585137113;0.0183481104659061;0.0183461182202108;0.0184279628954496;0.0182539557775897;0.0185258838459466;0.0183587881756707;0.0186093728958480;0.0183411159604321;0.0182791254880500;0.0186630761480609;0.0181298667157161;0.0182459612078933;0.0184675831579458;0.0185955748824505;0.0184154570717442;0.0184624266977795;0.0183126033171461;0.0183970370356682;0.0185419906367076;0.0184031608369824;0.0184541739351863;0.0182770065824583;0.0186566948813977;0.0182691447156686;0.0181024413861789;0.0185529050686361;0.0184730534222183;0.0185084257754729;0.0184833263226635;0.0183864933922039;0.0183482356438532;0.0184138270247567;0.0186752125982310;0.0185238592093305;0.0187434838030276;0.0185972783247171;0.0181357392004642;0.0187132227771188;0.0187158945149902;0.0189366971777676;0.0187536555698207;0.0189765925461928;0.0187758429576026;0.0188145984637302;0.0188396037600377;0.0188413862580502;0.0188459430550750;0.0190759678635653;0.0189786901373551;0.0186481443425060;0.0188695988653146;0.0191491672037817;0.0193394446522504;0.0190723177645248;0.0190897099764720;0.0197237878371987;0.0191725315164984;0.0193703298064157;0.0190905799610329;0.0194582912077836;0.0191897423333521;0.0195108815204790;0.0196133443225207;0.0195421135290876;0.0201424324731172;0.0197604328780863;0.0194711128103248;0.0197812065126590;0.0202185478118173;0.0200238140517035;0.0201681282748319;0.0205191728038324;0.0203431656384889;0.0205936516580411;0.0207153992669838;0.0207206707806900;0.0204223851686011;0.0210798794903056;0.0210369952477117;0.0212385673171060;0.0214759869480948;0.0218305289405694;0.0221439840865384;0.0232682859724466;0.0237881365803310;0.0227849939049099;0.0228405695096319;0.0228195279399666;0.0230476486838428;0.0234747644325930;0.0238319906717991;0.0240618057295245;0.0240681163842894;0.0240141494245520;0.0240705409051771;0.0244996976470823;0.0248041127128012;0.0252508323675244;0.0259381278022552;0.0261231091207576;0.0265283422294468;0.0266333082389331;0.0271837099981487;0.0271763761859966;0.0283153637930755;0.0283883946009908;0.0285393812905249;0.0296620117506671;0.0300153022512822;0.0310947767873625;0.0319544052552341;0.0328155829922504;0.0331283560454105;0.0332238582293423;0.0346697362944266;0.0351597622626639;0.0360396379232369;0.0370153248842645;0.0383083139502351;0.0390561120022396;0.0406137456807503;0.0413040506236033;0.0428944285042096;0.0434234956462627;0.0451778494452900;0.0461830896487462;0.0469085254408741;0.0492392655574735;0.0495403179811676;0.0512851486948006;0.0528796363243245;0.0538077672258900;0.0561724637940814;0.0580105609043118;0.0599778835515627;0.0617718673618281;0.0616875458865694;0.0622170402901965;0.0653136053641224;0.0680881850545449;0.0693734423056632;0.0712072811158143;0.0718066821787546;0.0742352895174523;0.0735710941758569;0.0752698185398832;0.0788573342481060;0.0798270625391214;0.0806950489829287;0.0791980500723784;0.0805883508314134;0.0827628891664726;0.0834189421971264;0.0856124722254894;0.0839728949121338;0.0829123442924816;0.0860383899100689;0.0851982209745146;0.0897845006936449;0.0873187129810341;0.0888710435361596;0.0860206979105856;0.0853806243200176;0.0860069437035836;0.0884730603939587;0.0891589554076845;0.0911733861891870;0.0861840852174086;0.0902825163581452;0.0858807373807453;0.0859093474917708;0.0842868756434246;0.0871752511017805;0.0831516318926400;0.0832023998963708;0.0863235308663344;0.0816162923212966;0.0822953877499070;0.0813145085528648;0.0857358144630724;0.0801335035826561;0.0816771050204893;0.0858537247575922;0.0779650141175828;0.0784686978017259;0.0777296491927410;0.0767711441447507;0.0768091386100438;0.0758565155957630;0.0746455690517293;0.0730356907861769;0.0725700185469693;0.0730154488255198;0.0706916252165585;0.0702932891711562;0.0693647807202049;0.0696637556239349;0.0688300976069800;0.0671026054992115;0.0661618440460318;0.0653930304940334;0.0636208596998396;0.0639784034222365;0.0635229980704250;0.0623671078908284;0.0636393741862071;0.0611647215570232;0.0613685831463423;0.0602813250585447;0.0599577571307978;0.0593124415473514;0.0589698604148216;0.0579031515354112;0.0576563734653825;0.0576102970273536;0.0562779062829001;0.0565768260968255;0.0564576134285343;0.0547751054008383;0.0538450533243285;0.0536064668509639;0.0533224619491000;0.0518254662023279;0.0523011630072659;0.0516270561189482;0.0517028170452727;0.0499300632458147;0.0510264266766338;0.0502485827312818;0.0505876790311235;0.0502110242476833;0.0500473147949757;0.0491911407345692;0.0476975090423956;0.0483856879935516;0.0469366149549049;0.0453442993889730;0.0462023027358431;0.0463876562848192;0.0453418296004854;0.0444586257598817;0.0445587267898697;0.0451806253054321;0.0443530800112586;0.0441915944442830;0.0445684958026137;0.0418947171411499;0.0441932722684921;0.0445756131632978;0.0436317802141775;0.0427427454263860;0.0426922927217532;0.0415835282106632;0.0411889052039778;0.0410213188276570;0.0422992472442955;0.0412247369996285;0.0411719078579223;0.0407179272032039;0.0402909378390404;0.0397923907599835;0.0400005482577337;0.0386561450792916;0.0390622401845554;0.0386415696772356;0.0390949596575275;0.0386859596994390;0.0384390111146331;0.0383528156908703;0.0389125312182718;0.0375412660102177;0.0365562411844043;0.0401222106537155;0.0375443465195469;0.0370207350895898;0.0385787683121572;0.0363112885761093;0.0376749688091075;0.0380773846718442;0.0365970300223128;0.0353199987716234;0.0366100764337691;0.0347705940290818;0.0359591666134441;0.0341095252262050;0.0359824512084458;0.0362504606412126;0.0346785880250509;0.0368545668631960;0.0350404429632715;0.0347629248751471;0.0358759237820286;0.0337229423357313;0.0351449217310496;0.0345992732995441;0.0370188029009108;0.0336262016120379;0.0377555442233060;0.0351219932628978;0.0368512970054316;0.0360720304482021;0.0311529300960851;0.0305857332354005;0.0335578236015645;0.0270776921474693;0.0344479515485556;0.0376524121577235;0.0359300145802958;0.0309831285148927;0.0366590293688815;0.0388807887245713;0.0354092897313060]; + + + + %determine the factor to scale the intensity in absolute scale + C = []; + %create a vector to fit the region from q 0.07 to 0.08 A + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + q_range = [q_GC_c(1):0.01:q_GC_m(end-1000)]; %good region for 2 and 7 m flight tube + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + for i = 1:length(q_range) + [~, index_m] = min(abs(q_GC_m - q_range(i))); + [~, index_c] = min(abs(q_GC_c - q_range(i))); + C = [C I_GC_c(index_c)/I_GC_m(index_m)]; %determined in cm/s + end + dev_SAXS = std(C); + C_SAXS = mean(C); + calibration_factor_SAXS = C_SAXS; %in s/photons + + + if (use_WAXS) + scan_num = Glassy_carbon; + if scan_num > 9999 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, init, last_number, e_account, scan_num); + else + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, e_account, scan_num); + if exist(filename, 'file') ~= 2 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, init, last_number, e_account, scan_num); + end + end + WAXS_GC = importdata(filename); + I_GC_WAXS= median(squeeze(WAXS_GC.I_all),2); + scan_num = DB; + if scan_num > 9999 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, init, last_number, e_account, scan_num); + else + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, e_account, scan_num); + if exist(filename, 'file') ~= 2 + init = floor(scan_num/10000)*10000; + last_number = init+9999; + filename = sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat',... + BasePath, init, last_number, e_account, scan_num); + end + end + Data_DB_WAXS= importdata(filename); + I_DB_WAXS = median(squeeze(Data_DB_WAXS.I_all),2); + q_GC_WAXS = WAXS_GC.q; + I_GC_WAXS = ((((I_GC_WAXS*scale_GC)*1/transmittance )-(I_DB_WAXS*scale_DB))*1/thickness_L14); + + %determine the factor to scale the intensity in absolute scale + C=[]; + %create a vector to fit the region from q 0.07 to 0.08 A + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + q_range = [q_GC_WAXS(1):0.01:q_GC_WAXS(300) ]; %good region for 2 and 7 m flight tube + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + for i = 1:length(q_range) + [~, index_m] = min(abs(q_GC_WAXS - q_range(i))); + [~, index_c] = min(abs(q_GC_c - q_range(i))); + C = [C I_GC_c(index_c)/I_GC_WAXS(index_m)]; %determined in cm/s + end + dev_WAXS = std(C); + C_WAXS = mean(C); + calibration_factor_WAXS = C_WAXS; %in s/photons + else + calibration_factor_WAXS=1; + dev_WAXS = 0; + C_WAXS = 1; + end + + + %calculate the efficiency of the detector + % from http://henke.lbl.gov/optical_constants/filter2.html, considering + %silicon Si Density=2.33 Thickness=320. microns + [ val, ~ ] = get_fil_trans( 'Si', energy, 320); + TransmissionSi1 = val(2); + p = 1-(TransmissionSi1); + + %contribution from the FT windows to the flux + % from http://henke.lbl.gov/optical_constants/filter2.html, considering + % mica KAl3Si3O11.8H1.8F0.2 Density=2.76 Thickness= 7. microns + [ val, ~ ] = get_fil_trans( 'mica', energy, 7); + mica = val(2); + + %from http://henke.lbl.gov/optical_constants/filter2.html, considering + % mylar C10H8O4 Density=1.43 Thickness=300. microns + [ val, ~ ] = get_fil_trans( 'mylar', energy, 300); + mylar = val(2); + + % define the length of the air path in mm + % remove the air path in vacuum from the DetDist_mm + % values from https://intranet.psi.ch/CSAXS/NewFlightTube + if DetDist_mm < 5e3 + air_path = DetDist_mm - 2051.5; %for 2m flight tube in mm + else + air_path = DetDist_mm - 7030.2; %for 3m flight tube in mm + end + + %calculate the transmission of the air path + + %from http://henke.lbl.gov/optical_constants/gastrn2.html considering + %N1.562O.42C.0003Ar.0094 Pressure=760. T = 297 K, Path = 1 cm + % air N1.562O.42C.0003Ar.0094 Pressure=760 torr. T = 297 K, Path = 1 cm + [ val ] = get_gas_trans('air', energy, air_path/10,760, 297 ); + ap_trans = val(2); + + + %define the solid angle: we consider the first pixel close to the beam stop + %as it would have the highest intensity + angle = (atan(pixel_size/DetDist_mm)); %in radians + sol_angle = angle^2; %in steradian (sr): square radians + + %plot the comparison + loglog(q_GC_c, I_GC_c,'k', 'LineWidth', 4); + hold on + I_GC_m = I_GC_m*calibration_factor_SAXS; % in cm-1 + loglog(q_GC_m, I_GC_m, 'r', 'LineWidth', 2); + + if (use_WAXS) + loglog(q_GC_WAXS, I_GC_WAXS*C_WAXS, 'LineWidth', 2); + end + + xlabel('Scattering vector q (A^{-1})'); + ylabel('Differential scattering cross section (cm^{-1})'); + axis tight + grid on; + + if (use_WAXS) + legend('Standard GCL14', sprintf('Corrected SAXS GCL14 x %.2d',C_SAXS ), ... + sprintf('Corrected WAXS GCL14 x %.2d',C_WAXS ), 'Location','southwest') + else + legend('Standard GCL14', sprintf('Corrected SAXS GCL14 x %.2d',C_SAXS ),... + 'Location','southwest') + end + + set(gca,'fontsize',12) + + %calculate the flux + positive_cont = (mica * mylar * ap_trans * p); + flux_GC = 1/(calibration_factor_SAXS*sol_angle * positive_cont); %flux in photons/s + + + %display the results + fprintf('The approximated flux from GCL14 is %5.2e photons/s \n', flux_GC); + fprintf('The calibration factor for corrected intensity is C_{SAXS} = %.2d ± %.2d. \n ', calibration_factor_SAXS, dev_SAXS); + if (use_WAXS) + fprintf('The calibration factor for corrected intensity is C_{WAXS} = %.2d ± %.2d. \n ', calibration_factor_WAXS, dev_WAXS); + end +end + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. \ No newline at end of file diff --git a/+beamline/identify_eaccount.m b/+beamline/identify_eaccount.m new file mode 100644 index 0000000..4f003d8 --- /dev/null +++ b/+beamline/identify_eaccount.m @@ -0,0 +1,77 @@ +% Return the current e-account user name in case this function is executed +% at the X12SA beamline, [] otherwise. + +% Filename: $RCSfile: identify_eaccount.m,v $ +% +% $Revision: 1.1 $ $Date: 2010/04/28 18:00:56 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Return the current e-account user name in case this function is executed +% at the X12SA beamline, [] otherwise. +% +% Note: +% none +% +% Dependencies: +% identify_system.m +% +% +% history: +% +% April 28th, 2010: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [return_user_name] = identify_eaccount() +import utils.identify_system + +persistent user_name; + +if (isempty(user_name)) + user_name = []; + % at the cSAXS beamline return the name of the current user as + % e-account name + sys_id = identify_system(); + if (strcmp(sys_id,'X12SA')) + [st,un] = system('echo $USER'); + if ((st == 0) && (length(un) > 1)) + user_name = un(1:end-1); + end + end +end + +return_user_name = user_name; diff --git a/+beamline/ind2mask.m b/+beamline/ind2mask.m new file mode 100644 index 0000000..c48dd02 --- /dev/null +++ b/+beamline/ind2mask.m @@ -0,0 +1,80 @@ +%IND2MASK +% convert mask structure (indices + framesize) to a 2D binary mask +% +% ** s mask structure; must contain s.indices and s.framesize +% +% returns: +% ++ mask 2D binary mask +% +% see also: beamline.mask2ind + +% Academic License Agreement +% +% Source Code +% +% Introduction +% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR") +% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS +% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM"). +% +% Terms and Conditions of the LICENSE +% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions +% hereinafter set out and until termination of this license as set forth below. +% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements +% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the +% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.” +% 3. THE PROGRAM 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 OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT +% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE +% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM. +% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively, +% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same +% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for +% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged +% in the commercial use, application or exploitation of works similar to the PROGRAM. +% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into +% another computing language: +% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul +% Scherrer Institut, Switzerland." +% +% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map: +% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008). +% (doi: 10.1126/science.1158573), +% for maximum likelihood: +% P. Thibault and M. Guizar-Sicairos, Maximum-likelihood refinement for coherent diffractive imaging, New J. Phys. 14, 063004 (2012). +% (doi: 10.1088/1367-2630/14/6/063004), +% for mixed coherent modes: +% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806), +% and/or for multislice: +% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016). +% (doi: 10.1364/OE.24.029089). +% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the +% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case. +% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE +% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to +% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies: +% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017. +% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein. +% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate +% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents, +% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software +% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program. +% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before +% the courts of Zürich, Switzerland. + +function [mask] = ind2mask(s) + +if ~isfield(s, 'framesize') + error('Please specify your frame size.') +end + +if ~isfield(s, 'indices') + error('Please specify the indices.') +end + +mask = reshape(zeros(s.framesize),1,[]); +mask(s.indices) = 1; +mask = reshape(mask, s.framesize); + +end + diff --git a/+beamline/integrate_range.m b/+beamline/integrate_range.m new file mode 100644 index 0000000..3008b4a --- /dev/null +++ b/+beamline/integrate_range.m @@ -0,0 +1,183 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: integrate_range.m,v $ +% +% $Revision: 1.7 $ $Date: 2012/09/02 15:13:04 $ +% $Author: $ +% $Tag: $ +% +% Description: +% azimuthal integration of a range of scans +% +% Note: +% Call without arguments for a brief help text. +% The integration masks need to be prepared first using prep_integ_masks.m +% +% Dependencies: +% - compile_x12sa_filename +% - find_files +% - radial_integ +% +% history: +% +% May 19th 2010: 1st documented version +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [] = integrate_range(scan_no_from,scan_no_to,scan_no_step,varargin) +import beamline.prep_integ_masks +import beamline.radial_integ +import utils.compile_x12sa_filename +import utils.find_files + +% set default values for the variable input arguments: +% select PILATUS 2M +pilatus_det_no = 1; +% writing cbf files +file_extension = 'cbf'; +save_format = '-v6'; + +if (nargin < 2) + fprintf('\nUsage:\n'); + fprintf('%s(scan_no_from,scan_no_to,scan_no_step [[,,] ...]);\n',mfilename); + fprintf('integrates the scans within the range [scan_no_from, scan_no_to].\n'); + fprintf('The optional , pairs are:\n'); + fprintf('''PilatusDetNo'', Detector number, 1 for 2M, default is %d\n',pilatus_det_no); + fprintf('''FileExtension'', default is %s\n',file_extension); + fprintf('''SaveFormat'', default is %s\n',save_format); + fprintf('Example:\n'); + fprintf('%s(100,500);\n',mfilename); + fprintf('Additional , pairs recognized by radial_integ can be specified.\n'); + error('At least the scan number range has to be specified as input argument.'); +end +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 4) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% parse the variable input arguments: +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'PilatusDetNo' + pilatus_det_no = value; + case 'SaveFormat' + save_format = value; + case 'FileExtension' + file_extension = value; + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + otherwise + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + end +end + +vararg_remain{end+1} = 'UnhandledParError'; +vararg_remain{end+1} = 0; + +vararg_remain_x12sa_filename = vararg_remain; +vararg_remain_x12sa_filename{end+1} = 'DetectorNumber'; +vararg_remain_x12sa_filename{end+1} = pilatus_det_no; + +vararg_remain_x12sa_filename_wildcard = vararg_remain_x12sa_filename; +vararg_remain_x12sa_filename_wildcard{end+1} = 'PointWildcard'; +vararg_remain_x12sa_filename_wildcard{end+1} = 1; +vararg_remain_x12sa_filename_wildcard{end+1} = 'SubExpWildcard'; +vararg_remain_x12sa_filename_wildcard{end+1} = 1; + + +% highest number of an existing scan +scan_no_exists = scan_no_from -1; +scan_no_check = scan_no_from; + +for scan_no = scan_no_from:scan_no_step:scan_no_to + % wait until the first file of the next scan is available + while (scan_no >= scan_no_exists) + scan_no_check = scan_no_check +1; + if (scan_no_check > scan_no + 100) + scan_no_check = scan_no +1; + fprintf('Pausing for 1 minute.\n'); + pause(60); + + % check if the data directory and first file exists + filename_mask = compile_x12sa_filename(scan_no,0,vararg_remain_x12sa_filename); + [ddir fnames] = find_files(filename_mask); + if (~isempty(fnames)) + % integrate all data available until now + filename_mask = [ compile_x12sa_filename(scan_no,-1,vararg_remain_x12sa_filename) '*_' num2str(pilatus_det_no) '_' ['*' file_extension] ]; + radial_integ(filename_mask,vararg_remain); + end + end + filename_next = compile_x12sa_filename(scan_no_check,0,vararg_remain_x12sa_filename); + [ddir fnames] = find_files(filename_next); + if (~isempty(fnames)) + scan_no_exists = scan_no_check; + end + end + + % integrate scan if a following scan has been started, i.e., + % if the current one must be finished + if (scan_no < scan_no_exists) + % check if the data directory and first file exists + filename_mask = compile_x12sa_filename(scan_no,0,vararg_remain_x12sa_filename); + [ddir fnames] = find_files(filename_mask); + if (isempty(fnames)) + fprintf('Skipping scan no %d: no data found\n',scan_no); + continue; + end + + % integrate all data, i.e., all points and sub exposures + filename_mask = [ compile_x12sa_filename(scan_no,0,vararg_remain_x12sa_filename_wildcard) ]; + radial_integ(filename_mask,vararg_remain); + end +end diff --git a/+beamline/intensity_calibration.m b/+beamline/intensity_calibration.m new file mode 100644 index 0000000..219a8e6 --- /dev/null +++ b/+beamline/intensity_calibration.m @@ -0,0 +1,184 @@ +% Description: +% Return beam intensity in photons / sec based on calibration with +% a glassy carbon sample and and air +% +% Dependencies: +% spec_read + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSA% Description: +% Return beam intensity in photons / sec based on calibration with +% a glassy carbon sample and and air +% +% Dependencies: +% spec_readXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ diodescale ] = intensity_calibration(specfile, air_scanno, gc_scanno, det_dist_mm, varargin) +import io.spec_read +import utils.find_files + +pixel_size_mm = 0.172; +gc_file = 'glassycarbon_L14_xsection.dat'; +binned_path = '~/Data10/analysis/radial_integration/'; + +if (nargin < 4) + fprintf('\nUsage:\n'); + fprintf('%s(specfile, air_scanno, gc_scanno, det_dist_mm, [[,,] ...]);\n\n',mfilename); + fprintf('specfile is the full path to the SPEC dat-file.\n'); + fprintf('air_scanno and gc_scanno are SPEC scan numbers for empty and Glassy Carbon L14 measurements.\n'); + fprintf('det_dist_mm is the sample to detector distance in mm.\n'); + fprintf('\nThe optional , pairs are:\n'); + fprintf('''PixelSize_mm'', Size of detector pixel in mm, default is %s\n', pixel_size_mm); + fprintf('''CrossSectionFile'', Full path to file containing the cross section of the standard, default is ''%s''\n', gc_file); + fprintf('''BinnedPath'', Directory containing the radially binned detector frames, default is ''%s''\n', binned_path); + fprintf('\n'); + error('Not enough input arguments.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 5) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'PixelSize_mm' + pixel_size_mm = value; + case 'CrossSectionFile' + gc_file = value; + case 'BinnedPath' + binned_path= value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + + +s_air = spec_read(specfile, 'ScanNr', air_scanno); +s_gc = spec_read(specfile, 'ScanNr', gc_scanno); + +[dd, air_intfile] = find_files(sprintf(strcat(binned_path, 'e*_1_%05d_00000_00000_integ.mat'), air_scanno)); +airint = load(strcat(dd, air_intfile.name)); +[dd, gc_intfile] = find_files(sprintf(strcat(binned_path, 'e*_1_%05d_00000_00000_integ.mat'), gc_scanno)); +gcint = load(strcat(dd, gc_intfile.name)); + +lambda = 12.39852 / s_gc.mokev; +q_gc = 4*pi * sin(0.5*atan(gcint.radius*pixel_size_mm/det_dist_mm)) / lambda; +gc_transmission = (sum(s_gc.diode)/sum(s_gc.sec)) / (sum(s_air.diode)/sum(s_air.sec)); +gc_time = sum(s_gc.sec); +I_gc = sum(sum(gcint.I_all, 3), 2) - (sum(s_gc.diode)/sum(s_air.diode)) * sum(sum(airint.I_all, 3), 2); +Ierr_gc = sqrt(sum(sum(gcint.I_std.^2, 3), 2) + (sum(s_gc.diode)/sum(s_air.diode)).^2 * sum(sum(airint.I_std.^2, 3), 2)); + +gc = load(gc_file); +qmin = max(q_gc(1), gc(1,1)); +qmax = min(q_gc(end), gc(end,1)); +qind = find(qmin < gc(:,1) & gc(:,1) < qmax); +q = gc(qind, 1); +tth = 2*asin(lambda * q / (4*pi)); + +% Cross section per q-bin. Factors for thickness, transmission and solid angle +xsection_scale = (1 / 10) * 1/gc_transmission * pixel_size_mm^2 / (4*pi*det_dist_mm^2); +gc_xsection = xsection_scale * gc(qind,2); +gc_xsection_err = xsection_scale * gc(qind,3); + +% interpolate and scale w. angle dependent pixel solid angle and tilt +gc_exp = interp1(q_gc, I_gc, q) ./ (cos(tth).^3); +gc_exp_err = interp1(q_gc, Ierr_gc, q) ./ (cos(tth).^3); + +s2 = gc_xsection_err.^2 + gc_exp_err.^2; +% Solve for scaling factor, weigh with combined variance^-1 +wscale = sum(gc_xsection.*gc_exp./s2) / sum(gc_exp.^2 ./ s2); +fitchi = sum((gc_xsection - wscale*gc_exp).^2./(gc_xsection_err.^2 + (wscale*gc_exp_err).^2)); +clf() +hold on +errband(q, gc_xsection, gc_xsection_err, 'r'); +errband(q, wscale*gc_exp, wscale*gc_exp_err, 'b'); +legend('Cross section', ' 1 std', 'Experimental', ' 1 std'); +hold off + +% scaling without error weighing +% scale = gc_exp \ gc_xsection; +%semilogy(q, gc_xsection, '*', q, scale*gc_exp) + +% incoming flux (photons/s) determined for each q-channel: +%plot((1/gc_time) * gc_exp ./ gc_xsection) +inphotons = 1/gc_time * mean(gc_exp./gc_xsection); +diodescale = inphotons / (sum(s_gc.diode)/gc_time/gc_transmission); +fprintf('Glassy carbon transmission: %g\n', gc_transmission); +fprintf('Chi^2 to known cross-section: %g\n', fitchi); +fprintf('Flux on sample: %g ph/sec\n', inphotons); +fprintf('Scaling factor for diode readings: %g\n', diodescale); + + +function errband(x, y, yerr, colour); +%function errband(x, y, yerr, colour); +% +% Plot an error band between y-yerr and y+yerr. +% The colour defaults to blue. + +if nargin<4 + colour='b'; +end + +x = x(:).'; +y = y(:).'; +yerr = yerr(:).'; + +lower = y-yerr; +upper = y+yerr; + +hold on +plot(x, y, colour); +h = fill([x, fliplr(x)], [upper, fliplr(lower)], colour); +alpha(h, 0.5); diff --git a/+beamline/is_scan_finished.m b/+beamline/is_scan_finished.m new file mode 100644 index 0000000..a72cdcd --- /dev/null +++ b/+beamline/is_scan_finished.m @@ -0,0 +1,51 @@ +% out = is_scan_finished(specDatFile,scanno) +% Detect 'X# ' in spec file to detect the end of a scan +% From spec compile post_scan.mac + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function out = is_scan_finished(specDatFile,scanno) + + specDatFile = beamline.find_specDatFile(specDatFile); + + cmd = sprintf('grep -n ''#X %d'' %s', scanno,specDatFile); + [~,sysout] = system(cmd); + arrout = regexp(sysout,'[:\n]','split'); + if any(strcmp(arrout,sprintf('#X %d',scanno))) + out = true; + else + out = false; + end +end diff --git a/+beamline/is_scan_started.m b/+beamline/is_scan_started.m new file mode 100644 index 0000000..8e56934 --- /dev/null +++ b/+beamline/is_scan_started.m @@ -0,0 +1,56 @@ +% out = is_scan_started(specDatFile,scanno) +% Detect 'X# ' in spec file to detect the end of a scan +% From spec compile post_scan.mac + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function out = is_scan_started(specDatFile,scanno) + + specDatFile = beamline.find_specDatFile(specDatFile); + + cmd = sprintf('grep -n ''#S %d'' %s', scanno,specDatFile); + [~,sysout] = system(cmd); + arrout = regexp(sysout,'[:\n ]','split'); + indS = find(strcmp(regexp(sysout,'[:\n ]','split'),'#S')==1); % Indices where #S is found + for ii=indS % Loop over all #S found, this is to make sure we dont recognize S# 191 when looking for S# 19 + if strcmp(arrout(ii+1),sprintf('%d',scanno)) + out = true; + return + end + end + % If not found + out = false; + return +end diff --git a/+beamline/mark_interpolation_setup.m b/+beamline/mark_interpolation_setup.m new file mode 100644 index 0000000..0220a36 --- /dev/null +++ b/+beamline/mark_interpolation_setup.m @@ -0,0 +1,60 @@ +% If you do scans with different marks for energy you can use this code to +% calibrate the marks vs energy +% Run this code, it will save a calibration and then you can use +% energy2mark function + +% User input +scans = [134:163]; +marks = [0:0.2:5.99]; + +data = io.spec_read('~/Data10/','ScanNr',scans); + +clear E +for ii = 1:numel(data) + E(ii) = data{ii}.mokev; +end + +% Remove the last +E = E(1:end-1); +marks = marks(1:end-1); + +figure(1); +plot(E,marks,'o-') +xlabel('E (keV)') +ylabel('marks') + +save('mark_calib.mat','marks','E'); + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. \ No newline at end of file diff --git a/+beamline/mask2ind.m b/+beamline/mask2ind.m new file mode 100644 index 0000000..f83f30c --- /dev/null +++ b/+beamline/mask2ind.m @@ -0,0 +1,74 @@ +%MASK2IND +% convert a 2D binary mask to indices +% +% ** mask 2D binary mask +% +% returns: +% ++ s mask structure; must contain s.indices and s.framesize +% +% see also: beamline.ind2mask + +% Academic License Agreement +% +% Source Code +% +% Introduction +% • This license agreement sets forth the terms and conditions under which the PAUL SCHERRER INSTITUT (PSI), CH-5232 Villigen-PSI, Switzerland (hereafter "LICENSOR") +% will grant you (hereafter "LICENSEE") a royalty-free, non-exclusive license for academic, non-commercial purposes only (hereafter "LICENSE") to use the cSAXS +% ptychography MATLAB package computer software program and associated documentation furnished hereunder (hereafter "PROGRAM"). +% +% Terms and Conditions of the LICENSE +% 1. LICENSOR grants to LICENSEE a royalty-free, non-exclusive license to use the PROGRAM for academic, non-commercial purposes, upon the terms and conditions +% hereinafter set out and until termination of this license as set forth below. +% 2. LICENSEE acknowledges that the PROGRAM is a research tool still in the development stage. The PROGRAM is provided without any related services, improvements +% or warranties from LICENSOR and that the LICENSE is entered into in order to enable others to utilize the PROGRAM in their academic activities. It is the +% LICENSEE’s responsibility to ensure its proper use and the correctness of the results.” +% 3. THE PROGRAM 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 OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. IN NO EVENT SHALL THE LICENSOR, THE AUTHORS OR THE COPYRIGHT +% HOLDERS BE LIABLE FOR ANY CLAIM, DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION WITH THE PROGRAM OR THE USE +% OF THE PROGRAM OR OTHER DEALINGS IN THE PROGRAM. +% 4. LICENSEE agrees that it will use the PROGRAM and any modifications, improvements, or derivatives of PROGRAM that LICENSEE may create (collectively, +% "IMPROVEMENTS") solely for academic, non-commercial purposes and that any copy of PROGRAM or derivatives thereof shall be distributed only under the same +% license as PROGRAM. The terms "academic, non-commercial", as used in this Agreement, mean academic or other scholarly research which (a) is not undertaken for +% profit, or (b) is not intended to produce works, services, or data for commercial use, or (c) is neither conducted, nor funded, by a person or an entity engaged +% in the commercial use, application or exploitation of works similar to the PROGRAM. +% 5. LICENSEE agrees that it shall make the following acknowledgement in any publication resulting from the use of the PROGRAM or any translation of the code into +% another computing language: +% "Data processing was carried out using the cSAXS ptychography MATLAB package developed by the Science IT and the coherent X-ray scattering (CXS) groups, Paul +% Scherrer Institut, Switzerland." +% +% Additionally, any publication using the package, or any translation of the code into another computing language should cite for difference map: +% P. Thibault, M. Dierolf, A. Menzel, O. Bunk, C. David, F. Pfeiffer, High-resolution scanning X-ray diffraction microscopy, Science 321, 379–382 (2008). +% (doi: 10.1126/science.1158573), +% for maximum likelihood: +% P. Thibault and M. Guizar-Sicairos, Maximum-likelihood refinement for coherent diffractive imaging, New J. Phys. 14, 063004 (2012). +% (doi: 10.1088/1367-2630/14/6/063004), +% for mixed coherent modes: +% P. Thibault and A. Menzel, Reconstructing state mixtures from diffraction measurements, Nature 494, 68–71 (2013). (doi: 10.1038/nature11806), +% and/or for multislice: +% E. H. R. Tsai, I. Usov, A. Diaz, A. Menzel, and M. Guizar-Sicairos, X-ray ptychography with extended depth of field, Opt. Express 24, 29089–29108 (2016). +% (doi: 10.1364/OE.24.029089). +% 6. Except for the above-mentioned acknowledgment, LICENSEE shall not use the PROGRAM title or the names or logos of LICENSOR, nor any adaptation thereof, nor the +% names of any of its employees or laboratories, in any advertising, promotional or sales material without prior written consent obtained from LICENSOR in each case. +% 7. Ownership of all rights, including copyright in the PROGRAM and in any material associated therewith, shall at all times remain with LICENSOR, and LICENSEE +% agrees to preserve same. LICENSEE agrees not to use any portion of the PROGRAM or of any IMPROVEMENTS in any machine-readable form outside the PROGRAM, nor to +% make any copies except for its internal use, without prior written consent of LICENSOR. LICENSEE agrees to place the following copyright notice on any such copies: +% © All rights reserved. PAUL SCHERRER INSTITUT, Switzerland, Laboratory for Macromolecules and Bioimaging, 2017. +% 8. The LICENSE shall not be construed to confer any rights upon LICENSEE by implication or otherwise except as specifically set forth herein. +% 9. DISCLAIMER: LICENSEE shall be aware that Phase Focus Limited of Sheffield, UK has an international portfolio of patents and pending applications which relate +% to ptychography and that the PROGRAM may be capable of being used in circumstances which may fall within the claims of one or more of the Phase Focus patents, +% in particular of patent with international application number PCT/GB2005/001464. The LICENSOR explicitly declares not to indemnify the users of the software +% in case Phase Focus or any other third party will open a legal action against the LICENSEE due to the use of the program. +% 10. This Agreement shall be governed by the material laws of Switzerland and any dispute arising out of this Agreement or use of the PROGRAM shall be brought before +% the courts of Zürich, Switzerland. + + +function [s] = mask2ind(mask) + +s.framesize = size(mask); + +s.indices = find(reshape(mask, 1, [])~=0); + + +end + diff --git a/+beamline/next_scan_started.m b/+beamline/next_scan_started.m new file mode 100644 index 0000000..03313fa --- /dev/null +++ b/+beamline/next_scan_started.m @@ -0,0 +1,65 @@ +%NEXT_SCAN_STARTED if the next scan has started, the first return value is +% true. +% [started, scanNr] = next_scan_started(specDatFile,scanno); +% +% ** specDatFile path to the SPEC file / spec directory +% ** scanno current scan number +% +% returns: +% ++ started true if scanno is not the last scan +% ++ nextScanno next scan number +% ++ specDatFile specDatFile used to determine if the next scan has started +% +% see also: beamline.find_specDatFile + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [started, nextScanno, specDatFile] = next_scan_started(dataPath,scanno) + + specDatFile = beamline.find_specDatFile(dataPath); + + cmd = sprintf('grep ''#S '' %s | grep -A 1 ''#S %d '' | tail -1', specDatFile, scanno); + [~,sysout] = system(cmd); + arrout = regexp(sysout,'[:\n ]','split'); + nextScanno = str2double(arrout{2}); + + if nextScanno>scanno + started = true; + else + started = false; + end + +end diff --git a/+beamline/pilatus_valid_pixel_roi.m b/+beamline/pilatus_valid_pixel_roi.m new file mode 100644 index 0000000..5960b9b --- /dev/null +++ b/+beamline/pilatus_valid_pixel_roi.m @@ -0,0 +1,207 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: pilatus_valid_pixel_roi.m,v $ +% +% $Revision: 1.5 $ $Date: 2011/05/19 16:44:24 $ +% $Author: $ +% $Tag: $ +% +% Description: +% cut out of the valid pixel mask for the full detector the one for the +% current region of interest +% +% Note: +% The location of the ROI is not stored in the data files. It is deduced +% from the known readoiut modes. So far only the 1x2 module mode is +% implemented. +% +% Dependencies: +% none +% +% history: +% +% January 31st 2009: add arbitrary ROIs via named parameters +% +% May 9th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [valid_mask] = pilatus_valid_pixel_roi(valid_mask,varargin) + +% sub-detector readout size +roi_size = []; + +% alternatively: +% from/to row 0 means all rows +row_from = 0; +row_to = 0; +% from/to column 0 means all lines +column_from = 0; +column_to = 0; + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if ((nargin < 1) || (rem(no_of_in_arg,2) ~= 1)) + fprintf('Usage:\n') + fprintf('%s(valid_mask,[[,], ...]);\n',mfilename); + fprintf('The name value pairs are:\n'); + fprintf('''RoiSize'',[ ] size of sub-detector readout ROIs\n'); + fprintf('Alternatively:\n'); + fprintf('''RowFrom'',<0-max> region of interest definition, 0 for full frame\n'); + fprintf('''RowTo'',<0-max> region of interest definition, 0 for full frame\n'); + fprintf('''ColumnFrom'',<0-max> region of interest definition, 0 for full frame\n'); + fprintf('''ColumnTo'',<0-max> region of interest definition, 0 for full frame\n'); + fprintf('''ROI'',s[ ]\n'); + fprintf(' region of interest definition of all four coordinates together\n'); +end + + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'ROI' + if (length(value) ~= 4) + error('The ROI parameter needs a vector of length four as argument.'); + end + column_from = value(1); + row_from = value(2); + column_to = value(3); + row_to = value(4); + case 'RowFrom' + row_from = value; + roi_size = []; + case 'RowTo' + row_to = value; + roi_size = []; + case 'ColumnFrom' + column_from = value; + roi_size = []; + case 'ColumnTo' + column_to = value; + roi_size = []; + case 'RoiSize' + roi_size = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + + +% if (valid_mask.framesize ~= [1679 1475]) +% error('can not handle the source size (%d,%d)',... +% valid_mask.framesize(1),valid_mask.framesize(2)); +% end + + +if (isempty(roi_size)) + if (row_from < 1) + row_from = 1; + end + if (row_to < 1) + row_to = valid_mask.framesize(1); + end + if (column_from < 1) + column_from = 1; + end + if (column_to < 1) + column_to = valid_mask.framesize(2); + end + % nothing to do + if ((row_from == 1) && (column_from == 1) && ... + (row_to == valid_mask.framesize(1)) && (column_to == valid_mask.framesize(2))) + return; + end + + x_from = column_from; + y_from = row_from; + roi_size = [ row_to-row_from+1 column_to-column_from+1 ]; +else + % nothing to do + if (valid_mask.framesize == roi_size) + return; + end + + % determine the location of the ROI from its size via the known modi + x_from = 0; + y_from = 0; + % two modules + if (roi_size == [407 487]) + x_from = 495; + y_from = 637; + end + if (roi_size == [831 1475]) + x_from = 1; + y_from = 425; + end + if (roi_size == [831 981]) + x_from = 495; + y_from = 425; + end + if ((x_from == 0) || (y_from == 0)) + error('can not handle the ROI size (%d,%d)',roi_size(1),roi_size(2)); + end +end + + + + +% create the full valid pixel mask +vpm = zeros(valid_mask.framesize); +vpm(valid_mask.indices) = 1; +% cut out the region of interest +vpm = vpm(y_from:(y_from+roi_size(1)-1), ... + x_from:(x_from+roi_size(2)-1)); + +% return the indices of valid pixels within this ROI +valid_mask.indices = find(vpm == 1); +valid_mask.framesize = size(vpm); diff --git a/+beamline/prep_integ_masks.m b/+beamline/prep_integ_masks.m new file mode 100644 index 0000000..2b1a25d --- /dev/null +++ b/+beamline/prep_integ_masks.m @@ -0,0 +1,456 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: prep_integ_masks.m,v $ +% +% $Revision: 1.9 $ $Date: 2016/01/21 14:51:57 $ +% $Author: $ +% $Tag: $ +% +% Description: +% prepare masks for the radial integration of SAXS patterns +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read +% - pilatus_valid_pixel_roi +% +% history: +% +% September 4th 2009: +% correct in help text one of the RadiusFrom to RadiusTo +% +% May 9th 2008: 1st documented version +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ integ_masks ] = prep_integ_masks(filename, center_xy, varargin) +import beamline.pilatus_valid_pixel_roi +import io.image_read +import plotting.display_valid_mask +import utils.pixel_to_q + +% set number of radii +no_of_radii = 0; +% number of angular segments per radius +no_of_segments = 1; +% pixel size in mm +pixel_size_mm = [];%.172; +% detector distance in mm +det_dist_mm = [];%2000; +calculate_q=0; %only calculate q if exact detector distance is given +% wavelength (unit inconsequential, will be reflected in q) +lambda = [];%1; +% output directory for the masks +out_dir = '~/Data10/analysis/data/'; +filename_valid_mask = [ out_dir 'pilatus_valid_mask.mat' ]; +filename_integ_masks = [ out_dir 'pilatus_integration_masks.mat' ]; +% output figure number +fig_no = 240; +% save integration masks +save_data = 1; +% display valid pixel mask +display_valid_mask_flag = 1; +% detector number +det_no= 1; +% angular range to be excluded to cut out the beam stop +bs_angle_from = 0; +bs_angle_to = 0; + +% check minimum number of input arguments +if (nargin < 2) + fprintf('\nUsage:\n'); + fprintf('[integ_masks]=%s( filename, center_xy [[,,]...]);\n',mfilename); + fprintf('Prepare the masks for an efficient radial integration.\n'); + fprintf('The optional angular range in degree can be used to cut out a beam stop.\n'); + fprintf('Angle 0 is horizontally to the left, positive in counterclockwise direction.\n'); + fprintf('The specified data file is loaded and some of the integration masks are plotted into that frame.\n'); + fprintf('\n'); + fprintf('The optional , pairs are:\n'); + + fprintf('''NormalXY'',[x y] pixel coordinates, from where the detector normal points\n'); + fprintf(' toward the sample. Default is equal to center_xy\n'); + fprintf('''PixelSize_mm'', pixel size in mm, default is %.3f\n',pixel_size_mm); + fprintf('''DetDist_mm'', detector distance in mm, default is %.1f\n',det_dist_mm); + fprintf('''Wavelength'', wavelength. The units chosen here will determine the units of q\n'); + fprintf(' The defaults is %.1f\n',lambda); + fprintf('''NoOfRadii'', radial integration start radius, default is %d\n',no_of_radii); + fprintf(' or ,, defining the limits of radius bins\n'); + fprintf('''NoOfSegments'', Number of angular segments. If an integer, this number of equally wide azimuthal\n') + fprintf(' bins over 360 degrees are created. default is %d\n',no_of_segments); + fprintf(' or ,, defining the limits of angular bins\n'); + fprintf('''SaveData'',<0-no,1-yes> save the integration masks, default is %d\n',save_data); + fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices ind_valid,\n'); + fprintf(' default is %s\n',filename_valid_mask); + fprintf('''FilenameIntegMasks'', output file name for the structure integ_masks,\n'); + fprintf(' default is %s\n',filename_integ_masks); + fprintf('''FigNo'', number of the figure in which the result is displayed\n'); + fprintf('''DetNo'', number of detector 1 for SAXS and 2 for WAXS\n'); + fprintf(' Default is 1 (SAXS)\n'); + fprintf('''BeamstopAngleFrom'', exclude an angular region from the integration, default for the start value is %d\n',... + bs_angle_from); + fprintf('''BeamstopAngleTo'', exclude an angular region from the integration, default for the end value is %d\n',... + bs_angle_to); + fprintf('\n'); + fprintf('\n'); + fprintf('The file name should be the name of a single file without wildcards\n'); + fprintf('that is displayed as an example.\n'); + fprintf('The image file has no other function beyond being displayed as example.\n'); + fprintf('Example:\n'); + fprintf('[integ_masks]=%s(''~/Data10/pilatus/image.cbf'',[512 512]);\n',... + mfilename); + + error('At least the filename and the beam center have to be specified as input parameter.'); +end + +% check number of center coordinates +if (length(center_xy) ~= 2) + error('The beam center needs to be specified as a two component vector [cen_x cen_y].\n'); +end +center_x = center_xy(1); +center_y = center_xy(2); +norm_x = center_x; +norm_y = center_y; + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'NormalXY' + if (numel(value)==2) + norm_x = value(1); + norm_y = value(2); + end + case 'PixelSize_mm' + pixel_size_mm = value; + case 'DetDist_mm' + det_dist_mm = value; + calculate_q=1; + case 'Wavelength_nm' + lambda = value; + case 'Wavelength' + lambda = value/10; + case 'NoOfRadii' + no_of_radii = value; + case 'NoOfSegments' + no_of_segments = value; + case 'FilenameValidMask' + filename_valid_mask = value; + case 'FilenameIntegMasks' + filename_integ_masks = value; + case 'SaveData' + save_data = value; + case 'DisplayValidMask' + display_valid_mask_flag = value; + case 'FigNo' + fig_no = value; + case 'DetNo' + det_no = value; + case 'BeamstopAngleFrom' + bs_angle_from = value; + case 'BeamstopAngleTo' + bs_angle_to = value; + otherwise + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + + +% MGS - The fact that the detector number is dictating whether to have a +% radius variable or q is not ideal. Additional arguments should be given +% for this such as calculate_q or save_radius_var. +% initialize return arguments +if (det_no == 1)||(det_no == 3) + calculate_radius = true; +elseif (det_no == 2) + calculate_radius = false; +else + error('Only det_no 1, 2, and 3 are recognized') +end +if ( calculate_radius ) && (calculate_q) +integ_masks = struct('radius',[], 'indices',[], 'norm_sum', [],'q',[]); +elseif (calculate_radius) && (calculate_q == 0) + integ_masks = struct('radius',[], 'indices',[], 'norm_sum', []); +elseif (~calculate_radius) + integ_masks = struct('indices',[], 'norm_sum', [],'q',[]); +end + + +% check radius +%if (exist('r_from','var')) +if (size(no_of_radii)>1) + if (no_of_radii(1) < 1) + %if (r_from < 1) + error('The minimum radius is 1, %d is invalid.',r_from); + end + %end + %if (exist('r_from','var') && exist('r_to','var')) + if (no_of_radii(end) < no_of_radii(1)) + %if ((r_to ~= 0) && (r_to < r_from)) + error('The maximum radius must be greater than the minimum one, %d is invalid.\n',no_of_radii(end)); + end +end + +% check number of angular segments +if (no_of_segments < 1) + error('The number of angular segments must be at least 1'); +end +if (numel(no_of_segments)>1) + angular_segments = no_of_segments; + no_of_segments = numel(no_of_segments)-1; +else + angular_segments = 360/no_of_segments * (0:no_of_segments); +end +angular_segments = mod(angular_segments, 360); + +% check beamstop region +if ((bs_angle_from < 0.0) || (bs_angle_to > 360.0)) + error('The angular range for the beam stop region is 0 to 360 degree.'); +end +if (bs_angle_to < bs_angle_from) + error('The maximum beam stop angle must be less than or equal to the minimum one.\n'); +end + +% load the indices of valid pixels +fprintf('loading the valid pixel mask %s\n',filename_valid_mask); +load(filename_valid_mask); +dim_x = valid_mask.framesize(2); +dim_y = valid_mask.framesize(1); + + +if (~isempty(filename)) + % load test frame + frame = image_read(filename,vararg); + % select the first frame for display + frame.data = frame.data(:,:,1); + % in case of less than full detector readout cut out the right part of + % the valid pixel mask + valid_mask = pilatus_valid_pixel_roi(valid_mask,'RoiSize',size(frame.data)); + dim_x = size(frame.data,2); + dim_y = size(frame.data,1); +end + +% plot valid pixel mask +if (display_valid_mask_flag) + figure(fig_no); + vpm = zeros(dim_y,dim_x); + vpm(valid_mask.indices) = 1; + imagesc(vpm); + axis xy; + axis equal; + axis tight; + title('valid pixels'); + set(gcf,'Name','valid pixels'); + drawnow; +end + +if calculate_radius + %if (exist('r_to','var')) + % choose maximum radius, if specified via r_to=0 + if (no_of_radii < 1) + no_of_radii = max( [ sqrt(center_x^2+center_y^2) ... + sqrt((dim_x-center_x)^2+center_y^2) ... + sqrt(center_x^2+(dim_y-center_y)^2) ... + sqrt((dim_x-center_x)^2+(dim_y-center_y)^2) ] ); + end + if size(no_of_radii)==1 + no_of_radii=1:1:no_of_radii; + end +end + +fprintf('preparing the integration masks ...\n'); + +% create an array of the (x,y) coordinates relative to the beam center and +% convert it to polar coordinates +% +if calculate_radius % For SAXS detector - MGS, should be fixed, why is it neded different calculation for different detectors? + % angular range to be excluded to cut out the beam stop + + [ x, y ] = meshgrid( (1:dim_x)-center_x, (1:dim_y)-center_y ); + [ theta, rho ] = cart2pol( x, y ); + % convert angular range from -pi/pi to 0/360 + theta = (theta/pi +1) * 180.0; + + % prepare circular masks of the integer width r_step (in pixel) + integ_masks.radius = no_of_radii; + r_step=no_of_radii(2)-no_of_radii(1); + if calculate_q + integ_masks.q = pixel_to_q(no_of_radii,pixel_size_mm,det_dist_mm, 12.39852/lambda); + end + no_of_radii = length(integ_masks.radius); + integ_masks.indices = cell( no_of_radii, no_of_segments ); + integ_masks.norm_sum = zeros( no_of_radii, no_of_segments ); + seg_inds = cell(no_of_segments,1); + for ind_seg = 1:no_of_segments + seg_from = angular_segments(ind_seg); + seg_to = angular_segments(ind_seg+1); + if (seg_from >= seg_to) + ind_curr = find( ((theta > seg_from) | (theta <= seg_to) ) & ... + ((theta <= bs_angle_from) | (theta >= bs_angle_to)) ); + else + ind_curr = find( ((theta > seg_from) & (theta <= seg_to) ) & ... + ((theta <= bs_angle_from) | (theta >= bs_angle_to)) ); + end + % only take valid pixels into account + ind_curr = intersect(ind_curr, valid_mask.indices); + seg_inds{ind_seg} = ind_curr; + end + + for ind_r=1:no_of_radii + if (rem(ind_r,100) == 0) + fprintf('%4d / %d',ind_r,no_of_radii); + if (ind_r <= no_of_radii-100) + fprintf(', '); + end + end + r_inds = find( (rho >= integ_masks.radius(ind_r)) & ... + (rho < integ_masks.radius(ind_r)+r_step) ); + for ind_seg = 1:no_of_segments + integ_masks.indices{ind_r, ind_seg} = intersect( r_inds, seg_inds{ind_seg} ); + % calculate the normalization value (sum of the pixels within the mask) + integ_masks.norm_sum(ind_r, ind_seg) = ... + length( integ_masks.indices{ind_r, ind_seg} ); + end + end + fprintf('\n'); + +else + [ x, y ] = meshgrid( (1:dim_x)-norm_x, (1:dim_y)-norm_y ); + if (norm_x == center_x && norm_y == center_y) + [ theta, rho ] = cart2pol( x, y ); + q = 4*pi/lambda*sin(atan2(rho,det_dist_mm/pixel_size_mm)/2); + % convert angular range from -pi/pi to 0/360 + theta = theta/pi*180.0; + else + if (norm_x ~= center_x) + angle = atan((norm_x - center_x) / (det_dist_mm/pixel_size_mm)); + z = -x*sin(angle) + det_dist_mm/pixel_size_mm*cos(angle); + x = x*cos(angle) + det_dist_mm/pixel_size_mm*sin(angle); + else + fprintf('not implemented yet!!!\n'); + exit + end + q = 4*pi/lambda*sin(atan2(sqrt(x.^2 + y.^2),z)/2); + theta = atan2(y,x)/pi*180; + end + t_1d = reshape(theta(valid_mask.indices),1,[]); + q_1d = reshape(q(valid_mask.indices),1,[]); + + t_ed = linspace( -180, 180,1e0+1); + integ_masks.theta = t_ed(1:end-1); + integ_masks.theta_end = t_ed(end); + q_ed = linspace(min(q_1d),max(q_1d),1e3+1); + integ_masks.q = q_ed(1:end-1); + integ_masks.q_end = q_ed(end); + + [~,t_bin] = histc(t_1d,t_ed); + [~,q_bin] = histc(q_1d,q_ed); + + integ_masks.indices = cell(numel(q_ed)-1,numel(t_ed)-1); + integ_masks.norm_sum = zeros(size(integ_masks.indices)); + for q_i=1:numel(q_ed)-1 + for t_i=1:numel(t_ed)-1 + integ_masks.indices{q_i,t_i} = ... + valid_mask.indices(and(q_bin==q_i,t_bin==t_i)); + integ_masks.norm_sum(q_i,t_i) = numel(integ_masks.indices{q_i,t_i}); + end + end +end + + +% save integration masks +if (save_data) + fprintf('Saving center_xy, no_of_segments, integ_masks to %s\n',... + filename_integ_masks); + if angular_segments(end) == 0 + angular_segments(end) = 360; + end + phi_det = (angular_segments(2:end) + angular_segments(1:end-1))/2; %% Center of the angular sector in degrees + save(filename_integ_masks,'center_xy','no_of_segments','integ_masks','angular_segments','phi_det'); +end + +% display some integration circles +if (~isempty(filename)) + figure(fig_no+1); + hold off; + clf; + frame_plot = double(frame.data); + frame_plot( frame_plot < 1 ) = 1; + + plot_step = round(length(integ_masks.indices)/50); + if (plot_step < 2) + plot_step = 2; + end + for (ind_r = 1:plot_step:size(integ_masks.indices,1)) + for (ind_seg = 1:2:no_of_segments) + frame_plot(integ_masks.indices{ind_r,ind_seg}) = 10^(6*ind_seg/no_of_segments); + end + end + + imagesc(log10(frame_plot)); + axis xy; + axis equal; + axis tight; + colorbar; + title([ 'integration segment test plot for ' strrep(filename,'_','\_') ]); + set(gcf,'Name','integration masks'); +end diff --git a/+beamline/prep_valid_mask.m b/+beamline/prep_valid_mask.m new file mode 100644 index 0000000..bd0457e --- /dev/null +++ b/+beamline/prep_valid_mask.m @@ -0,0 +1,345 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: prep_valid_mask.m,v $ +% +% $Revision: 1.8 $ $Date: 2016/01/21 15:07:41 $ +% $Author: guizar_m $ +% $Tag: $ +% +% Description: +% prepare a list of the linear indices for the valid pixels +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read +% +% history: +% +% May 15th 2010, Oliver Bunk: +% add command line argument for ThresholdMedian +% +% September 4th 2009, Oliver Bunk: +% use find_files rather than dir to find the files +% +% May 9th 2008, Oliver Bunk: 1st documented version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + +function [valid_mask] = prep_valid_mask(data_dir, varargin) +import io.image_read +import plotting.display_valid_mask +import utils.find_files + +% initialize return arguments +valid_mask = struct('indices',[], 'framesize',[]); + +% set default values for the variable input arguments: +% use all cbf files +filename_mask = '*.cbf'; +% filename for loading and saving the valid pixel mask +filename_valid_mask = '~/Data10/analysis/data/pilatus_valid_mask.mat'; +% below this threshold intensity a pixel is considered to be dark +threshold_dark = 1; +% above this threshold intensity a pixel is considered to be hot +threshold_hot = 20; +% this value times the square root of the intensity is used as hot pixel +% threshold +threshold_median = 5.0; +% replace the existing mask +extend = 'no'; +% save the mask +save_data = 1; +% display result in this figure +fig_no = 200; + +% check minimum number of input arguments +if (nargin < 1) + fprintf('\nUsage:\n'); + fprintf('[valid_mask]=%s(data_dir [[,,]...]);\n',mfilename); + fprintf('Prepare a list of the linear indices for the valid pixels.\n'); + fprintf('To get reliable data a series of at least 10 frames should be analyzed.\n'); + fprintf('The direct beam region will be regarded as invalid since it is out of the\n'); + fprintf('range for valid pixels. To ''repair'' this one should take a second series of\n'); + fprintf('exposures at a different detector position and call this macro with the ''Extend'',''or''\n'); + fprintf('option.\n'); + fprintf('\n'); + fprintf('The optional , pairs are:\n'); + fprintf('''FilenameMask'', specify the files to be used from the data directory, empty string for all, default is ''%s''\n',... + filename_mask); + fprintf('''ThresholdDark'', pixels permanently below this value are considered to be dark, default is %d\n',... + threshold_dark); + fprintf('''ThresholdHot'', pixels at least once above this value are considered to be hot, default is %d\n',... + threshold_hot); + fprintf('''ThresholdMedian'', pixels of intensity I above the constant ThresholdHot and above\n'); + fprintf(' ThresholdMedian times (I+sqrt(I)) are considered to be hot, 0 to deactivate this additional threshold,\n'); + fprintf(' default is %.1f\n',... + threshold_median); + fprintf('''SaveData'',<0-no,1-yes> save the valid pixel mask, default is %d\n',save_data); + fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices,\n'); + fprintf(' default is %s\n',filename_valid_mask); + fprintf('''Extend'',<''and'', ''or'' or ''no''> update an existing mask using the specified conjunction, default is %s\n',... + extend); + fprintf('''FigNo'', number of the figure in which the result is displayed, default is %d\n',... + fig_no); + fprintf('\n'); + fprintf('Examples:\n'); + fprintf('[valid_mask]=%s(''~/Data10/pilatus/air_scattering/'');\n',... + mfilename); + fprintf('[valid_mask]=%s(''~/Data10/pilatus/air_scattering_det_pos_2/'',''Extend'',''or'');\n',... + mfilename); + + error('At least the data directory has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = no_of_in_arg -1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments: +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'ThresholdDark' + threshold_dark = value; + case 'ThresholdHot' + threshold_hot = value; + case 'ThresholdMedian' + threshold_median = value; + case 'FilenameMask' + filename_mask = value; + case 'FilenameValidMask' + filename_valid_mask = value; + case 'SaveData' + save_data = value; + case 'FigNo' + fig_no = value; + case 'Extend' + extend = value; + otherwise + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + end +end + +vararg_remain{end+1} = 'UnhandledParError'; +vararg_remain{end+1} = 0; + +% check extend parameter +if ((~strcmp(extend,'no')) && ... + (~strcmp(extend,'and')) && (~strcmp(extend,'or'))) + error('extend must be ''and'', ''or'' or ''no''\n'); +end + +% set some default values for the plot window +set(0, 'DefaultAxesfontsize', 12); +set(0, 'DefaultAxeslinewidth', 1, 'DefaultAxesfontsize', 12); +set(0, 'DefaultLinelinewidth', 1); + +% get all matching filenames +if (data_dir(end) ~= '/') + data_dir(end+1) = '/'; +end +[data_dir,fnames,vararg_remain] = ... + find_files( [ data_dir filename_mask ], vararg_remain ); + +if (length(fnames) < 1) + error('No matching files found for %s%s.\n',data_dir,filename_mask); +end + +if (~strcmp(extend,'no')) + if exist(filename_valid_mask,'file') + fprintf('loading the existing valid mask %s\n', ... + filename_valid_mask); + load(filename_valid_mask); + ind_existing_valid = valid_mask.indices; + else + fprintf('no prior valid mask %s found\n', ... + filename_valid_mask); + ind_existing_valid = ''; + end +end + + +% process the frames +ind_hot = []; +ind_dark = []; +fprintf('data directory is %s\n',data_dir); +for (f_ind=1:length(fnames)) + fprintf('%3d/%3d: reading %s%s\n',f_ind,length(fnames),... + data_dir,fnames(f_ind).name); + [frame] = image_read([data_dir fnames(f_ind).name ],vararg_remain); + frame.data = double(frame.data); + for (frame_ind = 1:size(frame.data,3)) + % median filtered data for comparison + if (threshold_median ~= 0) + data_med = frame.data(:,:,frame_ind); + + % add pixels at the module boundary to ease median filtering + ind = find(data_med == 0); + data_med_shift = circshift(data_med,[2 2]); + data_med(ind) = data_med_shift(ind); + + ind = find(data_med == 0); + data_med_shift = circshift(data_med,[-2 -2]); + data_med(ind) = data_med_shift(ind); + + ind = find(data_med == 0); + data_med_shift = circshift(data_med,[-2 2]); + data_med(ind) = data_med_shift(ind); + + ind = find(data_med == 0); + data_med_shift = circshift(data_med,[2 -2]); + data_med(ind) = data_med_shift(ind); + + % median filter the data + data_med = medfilt2(data_med,[5 5]); + + % the square root of the intensity estimates the standard deviation + data_med_sqrt = data_med.^0.5; + end + + if (f_ind == 1) + framesize1 = size(frame.data,1); + framesize2 = size(frame.data,2); + framesize = framesize1 * framesize2; + end + + % check that the file have identical dimensions + if ((framesize1 ~= size(frame.data,1)) || ... + (framesize2 ~= size(frame.data,2))) + error('The previous file(s) had %d x %d pixels, this frame has %d x %d pixels',... + framesize1,framesize2,size(frame.data,1),size(frame.data,2)); + end + + % pixels are considered to be dark if the intensity is below the + % constant threshold + ind = find(frame.data(:,:,frame_ind) < threshold_dark); + fprintf('%6d dark pixels below %10.3e counts, ', ... + length(ind),threshold_dark); + if (f_ind == 1) + ind_dark = ind; + else + % dark pixels must be dark in all frames + ind_dark = intersect(ind_dark,ind); + end + + % hot pixels are hot if they are above the threshold + ind = find(frame.data(:,:,frame_ind) > threshold_hot); + % and, if active, above the intensity plus a threshold times the square + % root of the intensity as an estimation of the countin statistics + % error + if (threshold_median ~= 0.0) + ind = intersect(ind,find((frame.data(:,:,frame_ind) > data_med+threshold_median*data_med_sqrt))); + fprintf('%4d hot pixels above %d and %.1f * sqrt(intensity) counts\n', ... + length(ind),threshold_hot,threshold_median); + else + fprintf('%4d hot pixels above %d counts\n', ... + length(ind),threshold_hot); + end + % for hot pixels it is enough to be above the threshold in one frame + ind_hot = union(ind_hot,ind); + end +end + +% calculate the complementary masks of the valid pixels +valid_mask.indices = setdiff(1:framesize,union(ind_dark,ind_hot)); + +fprintf('In total %d dark and %d hot pixels found.\n',... + length(ind_dark),length(ind_hot)); +fprintf('%d valid pixels remain.\n',length(valid_mask.indices)); + +if (~strcmp(extend,'no')) + fprintf('Extending the existing valid pixel mask of %d pixels\n',... + length(ind_existing_valid)); + if (strcmp(extend,'and')) + fprintf('using the and conjugation\n'); + valid_mask.indices = ... + intersect(valid_mask.indices,ind_existing_valid); + else + fprintf('using the or conjugation\n'); + if ~isempty(ind_existing_valid) + valid_mask.indices = ... + union(valid_mask.indices,ind_existing_valid); + end + end + fprintf('The combined mask has %d valid pixels.\n',... + length(valid_mask.indices)); +end + +% store the frame size in the return data +valid_mask.framesize = [framesize1 framesize2]; + +if (save_data) + % create a backup of the mask + if (exist(filename_valid_mask,'file')) + filename_mask_backup = [ filename_valid_mask '.bak' ]; + fprintf('Copying the current mask %s to %s\n',filename_valid_mask,... + filename_mask_backup); + copyfile(filename_valid_mask,filename_mask_backup); + end + + % save the masks + fprintf('Saving valid_mask to %s\n',filename_valid_mask); + save(filename_valid_mask,'valid_mask'); +end + +% plot new valid pixel mask +if (fig_no > 0) + display_valid_mask('FilenameValidMask',filename_valid_mask,... + 'NoHelp',1,'FigNo',fig_no); +end diff --git a/+beamline/private/create_mask_GUI_export.m b/+beamline/private/create_mask_GUI_export.m new file mode 100644 index 0000000..ea7d843 --- /dev/null +++ b/+beamline/private/create_mask_GUI_export.m @@ -0,0 +1,1352 @@ +function varargout = create_mask_GUI_export(varargin) +% CREATE_MASK_GUI_EXPORT MATLAB code for create_mask_GUI_export.fig +% CREATE_MASK_GUI_EXPORT, by itself, creates a new CREATE_MASK_GUI_EXPORT or raises the existing +% singleton*. +% +% H = CREATE_MASK_GUI_EXPORT returns the handle to a new CREATE_MASK_GUI_EXPORT or the handle to +% the existing singleton*. +% +% CREATE_MASK_GUI_EXPORT('CALLBACK',hObject,eventData,handles,...) calls the local +% function named CALLBACK in CREATE_MASK_GUI_EXPORT.M with the given input arguments. +% +% CREATE_MASK_GUI_EXPORT('Property','Value',...) creates a new CREATE_MASK_GUI_EXPORT or raises the +% existing singleton*. Starting from the left, property value pairs are +% applied to the GUI before create_mask_GUI_export_OpeningFcn gets called. An +% unrecognized property name or invalid value makes property application +% stop. All inputs are passed to create_mask_GUI_export_OpeningFcn via varargin. +% +% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one +% instance to run (singleton)". +% +% See also: GUIDE, GUIDATA, GUIHANDLES + +% Edit the above text to modify the response to help create_mask_GUI_export + +% Last Modified by GUIDE v2.5 29-Oct-2018 10:23:50 + +% Begin initialization code - DO NOT EDIT +gui_Singleton = 1; +gui_State = struct('gui_Name', mfilename, ... + 'gui_Singleton', gui_Singleton, ... + 'gui_OpeningFcn', @create_mask_GUI_export_OpeningFcn, ... + 'gui_OutputFcn', @create_mask_GUI_export_OutputFcn, ... + 'gui_LayoutFcn', @create_mask_GUI_export_LayoutFcn, ... + 'gui_Callback', []); +if nargin && ischar(varargin{1}) + gui_State.gui_Callback = str2func(varargin{1}); +end + +if nargout + [~, s] = gui_mainfcn(gui_State, varargin{:}); + + varargout{1} = s; +else + gui_mainfcn(gui_State, varargin{:}); +end +% End initialization code - DO NOT EDIT + +% --- Executes just before create_mask_GUI_export is made visible. +function create_mask_GUI_export_OpeningFcn(hObject, eventdata, handles, varargin) +% This function has no output args, see OutputFcn. +% hObject handle to figure +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +% varargin command line arguments to create_mask_GUI_export (see VARARGIN) + +s.done = false; +s.apply = false; + +if(nargin > 3) + for index = 1:2:(nargin-3) + if nargin-3==index, break, end + s.(varargin{index}) = varargin{index+1}; + end +end + + + + +% Choose default command line output for create_mask_GUI_export +handles.output = hObject; +handles.figure1.UserData = utils.update_param(handles.figure1.UserData, s); + +% Update handles structure +guidata(hObject, handles); + + +% This sets up the initial plot - only do when we are invisible +% so window can get raised using create_mask_GUI_export. +if handles.figure1.UserData.mask3D + mask_questdlg = questdlg('Detected a 3D dataset. Do you want to create a 3D mask?', '3D mask', 'Yes', 'No', 'Yes'); + switch mask_questdlg + case 'Yes' + handles.figure1.UserData.mask3D = true; + case 'No' + handles.figure1.UserData.mask3D = false; + end +end + +if handles.figure1.UserData.mask3D + handles.figure1.UserData.mask_asize = [size(handles.figure1.UserData.mask,1) size(handles.figure1.UserData.mask,2) 1 handles.figure1.UserData.mask_dims]; + handles.figure1.UserData.mask_temp = reshape(repmat(handles.figure1.UserData.mask, [1, 1, handles.figure1.UserData.mask_dims]), handles.figure1.UserData.mask_asize); +else + handles.figure1.UserData.mask_asize = size(handles.figure1.UserData.mask); + handles.figure1.UserData.mask_temp = handles.figure1.UserData.mask; +end + +if strcmp(get(hObject,'Visible'),'off') + if ~handles.figure1.UserData.mask3D + imagesc(handles.figure1.UserData.mask); axis equal tight xy + else + plotting.imagesc3D(squeeze(prod(handles.figure1.UserData.mask_temp,3))); axis equal tight xy + set(handles.figure1.UserData.apply_to_stack_toggle, 'Visible', 'on') + handles.figure1.UserData.stack = true; + handles.figure1.UserData.listener_orig = handles.axes1.slider_handle.listener('Value','PostSet',@(src, evnt)fig_slice_update(handles)); + end +end + +% listener for imagesc3D - update slice +function fig_slice_update(s) +val = s.axes1.slider_handle.Value; +set(s.figure1.UserData.ax.slider_handle, 'Value', val); +set(s.figure1.UserData.ax.edit_handle, 'String', num2str(val)); +s.figure1.UserData.ax.update_fig(s.figure1.UserData.ax); + + +% --- Outputs from this function are returned to the command line. +function varargout = create_mask_GUI_export_OutputFcn(hObject, eventdata, handles) +% varargout cell array for returning output args (see VARARGOUT); +% hObject handle to figure +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Get default command line output from handles structure +varargout{1} = handles.output; +varargout{2} = handles; + + +% --- Executes on button press in pushbutton1. +% add more pixels +function pushbutton1_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + + +handles.figure1.UserData.mask_temp(:,:,end+1,:) = ones(handles.figure1.UserData.mask_asize); + + +ax = handles.figure1.UserData.ax; +if isprop(ax, 'user_title') + current_title = ax.user_title; +else + current_title = ''; +end + + % update title + title(ax,'Select the bad pixels. Press ESC to abort.','Interpreter', 'none'); + + + % create rectangle + h = imrect(ax); + + if ~isempty(h) + asize = handles.figure1.UserData.asize; + % get positions and create the mask + sel_pos = round(h.getPosition); + pos = sel_pos; + + pos(1) = min(max(1, sel_pos(1)), asize(2)); + pos(2) = min(max(1, sel_pos(2)), asize(1)); + pos(3) = min(max(0, sel_pos(3)), asize(2)-pos(1)); + pos(4) = min(max(0, sel_pos(4)), asize(1)-pos(2)); + + update_mask3D(handles, pos) + update_image(handles) + update_mask_GUI(handles) + + % delete rectangle and update mask + h.delete; + end + % revert changes to the title + if ax.isprop('update_title') && ax.update_title && ~isempty(ax.vars.title_list) + %%% imagesc3D + % get slice + slice = round(get(ax.slider_handle,'Value')); + slice = max(1, min(length(vars.order), slice)); + % write title + if isempty(vars.title_list) + ax.update_title = false; + if ~isempty(ax.user_title) + title_text = sprintf(ax.user_title, slice); + title(ax, title_text, 'Interpreter', 'none'); + end + ax.update_title = true; + else + if ~isempty(vars.title_list) + title(ax, vars.title_list{slice}, 'Interpreter', 'none') + end + end + else + %%% normal imagesc + title(ax, current_title); + end + +% --------------------------------------------------------------------- +function update_mask_GUI(handles) +if handles.figure1.UserData.mask3D + slice = handles.figure1.UserData.ax.slider_handle.Value; + handles.axes1.Children.CData = squeeze(prod(handles.figure1.UserData.mask_temp(:,:,:,slice),3)); + handles.axes1.img = squeeze(prod(handles.figure1.UserData.mask_temp,3)); +else + handles.axes1.Children.CData = squeeze(prod(handles.figure1.UserData.mask_temp,3)); +end +handles.figure1.UserData.mask = handles.axes1.Children.CData; + +function update_mask3D(handles, pos) +if handles.figure1.UserData.mask3D && ~handles.figure1.UserData.stack + % I guess one can change the if condition to projection/noprojection + slice = handles.figure1.UserData.ax.slider_handle.Value; + handles.figure1.UserData.mask_temp(pos(2):pos(2)+pos(4),pos(1):pos(1)+pos(3), end, slice) = 0; +else + handles.figure1.UserData.mask_temp(pos(2):pos(2)+pos(4),pos(1):pos(1)+pos(3),end,:) = 0; +end + +function update_image(handles) + +ax = handles.figure1.UserData.ax; +if ndims(handles.figure1.UserData.mask_temp)==3 + ax.Children(end).CData = handles.figure1.UserData.CData .* prod(handles.figure1.UserData.mask_temp,3); +elseif ndims(handles.figure1.UserData.mask_temp)==4 + slice = handles.figure1.UserData.ax.slider_handle.Value; + ax.Children(end).CData = handles.figure1.UserData.CData .* prod(handles.figure1.UserData.mask_temp(:,:,:,slice),3); +else + ax.Children(end).CData = handles.figure1.UserData.CData .*handles.figure1.UserData.mask_temp; +end + +if isfield(handles.figure1.UserData, 'img_orig') + if ndims(handles.figure1.UserData.mask_temp)>=3 + ax.img = handles.figure1.UserData.img_orig .* squeeze(prod(handles.figure1.UserData.mask_temp,3)); + else + ax.img = handles.figure1.UserData.img_orig .* handles.figure1.UserData.mask_temp; + end +end + + +% -------------------------------------------------------------------- +function FileMenu_Callback(hObject, eventdata, handles) +% hObject handle to FileMenu (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + + +% -------------------------------------------------------------------- +function OpenMenuItem_Callback(hObject, eventdata, handles) +% hObject handle to OpenMenuItem (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +file = uigetfile('*.fig'); +if ~isequal(file, 0) + open(file); +end + +% -------------------------------------------------------------------- +function PrintMenuItem_Callback(hObject, eventdata, handles) +% hObject handle to PrintMenuItem (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +printdlg(handles.figure1) + +% -------------------------------------------------------------------- +function CloseMenuItem_Callback(hObject, eventdata, handles) +% hObject handle to CloseMenuItem (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +selection = questdlg(['Close ' get(handles.figure1,'Name') '?'],... + ['Close ' get(handles.figure1,'Name') '...'],... + 'Yes','No','Yes'); +if strcmp(selection,'No') + return; +end +handles.figure1.UserData.done = true; +pause(0.2) + + +% --- Executes on selection change in popupmenu1. +function popupmenu1_Callback(hObject, eventdata, handles) +% hObject handle to popupmenu1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) + +% Hints: contents = get(hObject,'String') returns popupmenu1 contents as cell array +% contents{get(hObject,'Value')} returns selected item from popupmenu1 +keyboard + +% --- Executes during object creation, after setting all properties. +function popupmenu1_CreateFcn(hObject, eventdata, handles) +% hObject handle to popupmenu1 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles empty - handles not created until after all CreateFcns called + +% Hint: popupmenu controls usually have a white background on Windows. +% See ISPC and COMPUTER. +if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) + set(hObject,'BackgroundColor','white'); +end + +set(hObject, 'String', {'plot(rand(5))', 'plot(sin(1:0.01:25))', 'bar(1:.5:10)', 'plot(membrane)', 'surf(peaks)'}); + + +% --- Executes on button press in pushbutton2. +% undo +function pushbutton2_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton2 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +if size(handles.figure1.UserData.mask_temp,3)>1 + handles.figure1.UserData.mask_temp(:,:,end,:) = []; + update_mask_GUI(handles) + update_image(handles) +end + + +% --- Executes on button press in pushbutton3. +% load mask +function pushbutton3_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton3 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +[fname, fpath] = uigetfile('*.mat'); +fname_full = fullfile(fpath, fname); + +if ~isempty(fname_full) + try + f = load(fname_full); + if isfield(f, 'mask') + % load binary mask + if ndims(f.mask)~=ndims(squeeze(handles.figure1.UserData.mask_temp(:,:,1,:))) || any(size(f.mask) ~= size(squeeze(handles.figure1.UserData.mask_temp(:,:,1,:)))) + fprintf('ERROR. Mask dimensions don''t match.\n') + end + try + if handles.figure1.UserData.mask3D && size(f.mask,3)==1 && handles.figure1.UserData.stack + % apply mask to 3D stack + f.mask = repmat(f.mask, [1 1 size( handles.figure1.UserData.mask_temp,4)]); + handles.figure1.UserData.mask_temp(:,:,end+1,:) = f.mask; + elseif handles.figure1.UserData.mask3D && size(f.mask,3)==1 && ~handles.figure1.UserData.stack + % get current slider value and apply mask to single + % slice + slice = handles.figure1.UserData.ax.slider_handle.Value; + handles.figure1.UserData.mask_temp(:,:,end+1,:) = handles.figure1.UserData.mask_temp(:,:,end,:); + handles.figure1.UserData.mask_temp(:,:,end,slice) = f.mask; + else + % 2D case + handles.figure1.UserData.mask_temp(:,:,end+1,:) = f.mask; + end + + catch + fprintf('Failed to append mask.\n') + end + elseif isfield(f, 'valid_mask') + % load indices for mask + if numel(f.valid_mask.framesize)~=ndims(squeeze(handles.figure1.UserData.mask_temp(:,:,1,:))) || any(f.valid_mask.framesize ~= size(squeeze(handles.figure1.UserData.mask_temp(:,:,1,:)))) + fprintf('ERROR. Mask dimensions don''t match.\n') + end + try + if handles.figure1.UserData.mask3D && numel(f.valid_mask.framesize)==2 && handles.figure1.UserData.stack + % apply mask to 3D stack + mask = repmat(beamline.ind2mask(f.valid_mask), [1 1 size( handles.figure1.UserData.mask_temp,4)]); + handles.figure1.UserData.mask_temp(:,:,end+1,:) = mask; + elseif handles.figure1.UserData.mask3D && numel(f.valid_mask.framesize)==2 && ~handles.figure1.UserData.stack + % get current slider value and apply mask to single + % slice + slice = handles.figure1.UserData.ax.slider_handle.Value; + handles.figure1.UserData.mask_temp(:,:,end+1,:) = handles.figure1.UserData.mask_temp(:,:,end,:); + handles.figure1.UserData.mask_temp(:,:,end,slice) = beamline.ind2mask(f.valid_mask); + else + % 2D case + handles.figure1.UserData.mask_temp(:,:,end+1,:) = beamline.ind2mask(f.valid_mask); + end + catch + fprintf('Failed to append mask.\n') + end + + end + + catch + fprintf('Failed to load file.\n') + end + +end +update_mask_GUI(handles) +update_image(handles) + + +% handles.figure1.UserData = s; + + + + +% --- Executes on button press in pushbutton4. +% save mask +function pushbutton4_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton4 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +[fname, fpath, ftype] = uiputfile({'*.mat',... + 'Binary mask (*.mat)'; + '*.mat', 'Indicies (*.mat)'}, 'Save mask', 'new_mask.mat'); +fname_full = fullfile(fpath, fname); +if ~isempty(fname_full) + try + switch ftype + case 1 + % binary mask + mask = squeeze(prod(handles.figure1.UserData.mask_temp,3)); + save(fname_full, 'mask'); + case 2 + % indices + mask = squeeze(prod(handles.figure1.UserData.mask_temp,3)); + valid_mask = beamline.mask2ind(mask); + save(fname_full, 'valid_mask'); + end + fprintf('Saved mask to %s.\n', fname_full) + catch ME + fprintf('Failed to save file.\n') + end +end + + +% --- Executes on button press in pushbutton4. +% reset +function revert_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton4 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +handles.figure1.UserData.mask_temp = ones(handles.figure1.UserData.mask_asize); +update_mask_GUI(handles); +update_image(handles); + + +% --- Executes on button press in quit. +% quit +function quit_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton4 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +CloseMenuItem_Callback(hObject, eventdata, handles) + + +% --- Executes on button press in quit. +% toggle button stack +function toggle_button_stack_Callback(hObject, eventdata, handles) +% hObject handle to pushbutton4 (see GCBO) +% eventdata reserved - to be defined in a future version of MATLAB +% handles structure with handles and user data (see GUIDATA) +val = get(hObject, 'Value'); +if val + set(hObject, 'BackgroundColor', [0.956 0.3137 0.2588]) + handles.figure1.UserData.stack = true; +else + set(hObject, 'BackgroundColor', [1 1 1]) + handles.figure1.UserData.stack = false; +end + + +% --- Creates and returns a handle to the GUI figure. +function h1 = create_mask_GUI_export_LayoutFcn(policy) +% policy - create a new figure or use a singleton. 'new' or 'reuse'. + +persistent hsingleton; +if strcmpi(policy, 'reuse') & ishandle(hsingleton) + h1 = hsingleton; + return; +end +% load create_mask_GUI_export.mat + + +appdata = []; +% appdata.GUIDEOptions = mat{1}; +appdata.GUIDEOptions.syscolorfig = true; + +appdata.lastValidTag = 'figure1'; +appdata.GUIDELayoutEditor = []; +appdata.initTags = struct(... + 'handle', [], ... + 'tag', 'figure1'); + +h1 = figure(... +'PaperUnits',get(0,'defaultfigurePaperUnits'),... +'Units','pixels',... +'Position',[986 614 803 503],... +'Visible',get(0,'defaultfigureVisible'),... +'Color',get(0,'defaultfigureColor'),... +'CloseRequestFcn',get(0,'defaultfigureCloseRequestFcn'),... +'CurrentAxesMode','manual',... +'CurrentObjectMode','manual',... +'CurrentPointMode','manual',... +'SelectionTypeMode','manual',... +'ResizeFcn',blanks(0),... +'IntegerHandle','off',... +'NextPlot',get(0,'defaultfigureNextPlot'),... +'Alphamap',get(0,'defaultfigureAlphamap'),... +'WindowButtonDownFcn',blanks(0),... +'WindowButtonUpFcn',blanks(0),... +'WindowButtonMotionFcn',blanks(0),... +'WindowScrollWheelFcn',blanks(0),... +'WindowKeyPressFcn',blanks(0),... +'WindowKeyReleaseFcn',blanks(0),... +'MenuBar','none',... +'ToolBar','figure',... +'Pointer',get(0,'defaultfigurePointer'),... +'PointerShapeHotSpot',get(0,'defaultfigurePointerShapeHotSpot'),... +'Name','Mask selection',... +'NumberTitle','off',... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','figure1',... +'UserData',[],... +'WindowStyle',get(0,'defaultfigureWindowStyle'),... +'DockControls',get(0,'defaultfigureDockControls'),... +'Resize','off',... +'PaperPosition',get(0,'defaultfigurePaperPosition'),... +'PaperSize',get(0,'defaultfigurePaperSize'),... +'PaperType',get(0,'defaultfigurePaperType'),... +'InvertHardcopy',get(0,'defaultfigureInvertHardcopy'),... +'PaperOrientation',get(0,'defaultfigurePaperOrientation'),... +'ScreenPixelsPerInchMode','manual',... +'KeyPressFcn',blanks(0),... +'KeyReleaseFcn',blanks(0),... +'HandleVisibility','callback'); + +appdata = []; +appdata.lastValidTag = 'axes1'; + +h2 = axes(... +'Parent',h1,... +'FontUnits','normalized',... +'Units',get(0,'defaultaxesUnits'),... +'CameraPosition',[0.5 0.5 9.16025403784439],... +'CameraPositionMode','manual',... +'CameraTarget',[0.5 0.5 0.5],... +'CameraUpVector',get(0,'defaultaxesCameraUpVector'),... +'CameraViewAngle',get(0,'defaultaxesCameraViewAngle'),... +'Projection',get(0,'defaultaxesProjection'),... +'LabelFontSizeMultiplier',get(0,'defaultaxesLabelFontSizeMultiplier'),... +'AmbientLightColor',get(0,'defaultaxesAmbientLightColor'),... +'WarpToFill','off',... +'WarpToFillMode',get(0,'defaultaxesWarpToFillMode'),... +'DataAspectRatio',get(0,'defaultaxesDataAspectRatio'),... +'PlotBoxAspectRatio',get(0,'defaultaxesPlotBoxAspectRatio'),... +'FontName',get(0,'defaultaxesFontName'),... +'FontAngle',get(0,'defaultaxesFontAngle'),... +'FontWeight',get(0,'defaultaxesFontWeight'),... +'FontSmoothing',get(0,'defaultaxesFontSmoothing'),... +'TickLabelInterpreter',get(0,'defaultaxesTickLabelInterpreter'),... +'XLim',get(0,'defaultaxesXLim'),... +'YLim',get(0,'defaultaxesYLim'),... +'ZLim',get(0,'defaultaxesZLim'),... +'CLim',get(0,'defaultaxesCLim'),... +'ALim',get(0,'defaultaxesALim'),... +'Layer',get(0,'defaultaxesLayer'),... +'TickLength',get(0,'defaultaxesTickLength'),... +'GridLineStyle',get(0,'defaultaxesGridLineStyle'),... +'MinorGridLineStyle',get(0,'defaultaxesMinorGridLineStyle'),... +'XAxisLocation',get(0,'defaultaxesXAxisLocation'),... +'XTick',[0 0.2 0.4 0.6 0.8 1],... +'XTickLabelRotation',get(0,'defaultaxesXTickLabelRotation'),... +'XScale',get(0,'defaultaxesXScale'),... +'XTickLabel',{ '0 '; '0.2'; '0.4'; '0.6'; '0.8'; '1 ' },... +'XMinorTick',get(0,'defaultaxesXMinorTick'),... +'YAxisLocation',get(0,'defaultaxesYAxisLocation'),... +'YTick',[0 0.2 0.4 0.6 0.8 1],... +'YTickLabelRotation',get(0,'defaultaxesYTickLabelRotation'),... +'YScale',get(0,'defaultaxesYScale'),... +'YTickLabel',{ '0 '; '0.2'; '0.4'; '0.6'; '0.8'; '1 ' },... +'YMinorTick',get(0,'defaultaxesYMinorTick'),... +'ZTick',[0 0.5 1],... +'ZTickLabelRotation',get(0,'defaultaxesZTickLabelRotation'),... +'ZScale',get(0,'defaultaxesZScale'),... +'ZTickLabel',blanks(0),... +'ZMinorTick',get(0,'defaultaxesZMinorTick'),... +'BoxStyle',get(0,'defaultaxesBoxStyle'),... +'LineWidth',get(0,'defaultaxesLineWidth'),... +'Color',get(0,'defaultaxesColor'),... +'ClippingStyle',get(0,'defaultaxesClippingStyle'),... +'CameraMode',get(0,'defaultaxesCameraMode'),... +'DataSpaceMode',get(0,'defaultaxesDataSpaceMode'),... +'ColorSpaceMode',get(0,'defaultaxesColorSpaceMode'),... +'DecorationContainerMode',get(0,'defaultaxesDecorationContainerMode'),... +'ChildContainerMode',get(0,'defaultaxesChildContainerMode'),... +'XRulerMode',get(0,'defaultaxesXRulerMode'),... +'XBaselineMode',get(0,'defaultaxesXBaselineMode'),... +'YRulerMode',get(0,'defaultaxesYRulerMode'),... +'YBaselineMode',get(0,'defaultaxesYBaselineMode'),... +'ZRulerMode',get(0,'defaultaxesZRulerMode'),... +'ZBaselineMode',get(0,'defaultaxesZBaselineMode'),... +'AmbientLightSourceMode',get(0,'defaultaxesAmbientLightSourceMode'),... +'XGrid',get(0,'defaultaxesXGrid'),... +'XMinorGrid',get(0,'defaultaxesXMinorGrid'),... +'YGrid',get(0,'defaultaxesYGrid'),... +'YMinorGrid',get(0,'defaultaxesYMinorGrid'),... +'ZGrid',get(0,'defaultaxesZGrid'),... +'ZMinorGrid',get(0,'defaultaxesZMinorGrid'),... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','axes1',... +'UserData',[],... +'HitTest',get(0,'defaultaxesHitTest'),... +'PickableParts',get(0,'defaultaxesPickableParts'),... +'Position',[0.11083 0.125248 0.587795 0.7952286],... +'ActivePositionProperty','position',... +'LooseInset',[0.13 0.11 0.095 0.075],... +'ColorOrderIndex',get(0,'defaultaxesColorOrderIndex'),... +'LineStyleOrder',get(0,'defaultaxesLineStyleOrder'),... +'LineStyleOrderIndex',get(0,'defaultaxesLineStyleOrderIndex'),... +'FontSize',0.090702947845805,... +'TitleFontWeight',get(0,'defaultaxesTitleFontWeight'),... +'TitleFontSizeMultiplier',get(0,'defaultaxesTitleFontSizeMultiplier'),... +'SortMethod','childorder',... +'TickDir',get(0,'defaultaxesTickDir'),... +'MinorGridColor',get(0,'defaultaxesMinorGridColor'),... +'Clipping',get(0,'defaultaxesClipping'),... +'NextPlot',get(0,'defaultaxesNextPlot'),... +'Box',get(0,'defaultaxesBox'),... +'ChildrenMode','manual',... +'Visible',get(0,'defaultaxesVisible'),... +'HandleVisibility',get(0,'defaultaxesHandleVisibility')); + +h3 = get(h2,'title'); + +set(h3,... +'Parent',h2,... +'Units','data',... +'FontUnits','normalized',... +'DecorationContainer',[],... +'DecorationContainerMode','auto',... +'Color',[0 0 0],... +'ColorMode','auto',... +'Position',[0.500000730279374 1.00006515710682 0.5],... +'PositionMode','auto',... +'String',blanks(0),... +'Interpreter','tex',... +'Rotation',0,... +'RotationMode','auto',... +'FontName','Helvetica',... +'FontSize',0.0951293759512938,... +'FontAngle','normal',... +'FontWeight','normal',... +'HorizontalAlignment','center',... +'HorizontalAlignmentMode','auto',... +'VerticalAlignment','bottom',... +'VerticalAlignmentMode','auto',... +'EdgeColor','none',... +'LineStyle','-',... +'LineWidth',0.5,... +'BackgroundColor','none',... +'Margin',2,... +'Clipping','off',... +'Layer','middle',... +'LayerMode','auto',... +'FontSmoothing','on',... +'FontSmoothingMode','auto',... +'IncludeRenderer','on',... +'IsContainer','off',... +'IsContainerMode','auto',... +'HelpTopicKey',blanks(0),... +'ButtonDownFcn',blanks(0),... +'BusyAction','queue',... +'Interruptible','on',... +'CreateFcn', {@local_CreateFcn, blanks(0), ''} ,... +'DeleteFcn',blanks(0),... +'Tag',blanks(0),... +'UserData',[],... +'HitTest','on',... +'PickableParts','visible',... +'PickablePartsMode','auto',... +'DimensionNames',{ 'X' 'Y' 'Z' },... +'DimensionNamesMode','auto',... +'XLimInclude','on',... +'YLimInclude','on',... +'ZLimInclude','on',... +'CLimInclude','on',... +'ALimInclude','on',... +'Description','Axes Title',... +'DescriptionMode','auto',... +'Visible','on',... +'Serializable','on',... +'HandleVisibility','off',... +'TransformForPrintFcnImplicitInvoke','on',... +'TransformForPrintFcnImplicitInvokeMode','auto'); + +h4 = get(h2,'xlabel'); + +set(h4,... +'Parent',h2,... +'Units','data',... +'FontUnits','normalized',... +'DecorationContainer',[],... +'DecorationContainerMode','auto',... +'Color',[0.15 0.15 0.15],... +'ColorMode','auto',... +'Position',[0.500000476837158 -0.136405433827787 0],... +'PositionMode','auto',... +'String',blanks(0),... +'Interpreter','tex',... +'Rotation',0,... +'RotationMode','auto',... +'FontName','Helvetica',... +'FontSize',0.0951293759512938,... +'FontAngle','normal',... +'FontWeight','normal',... +'HorizontalAlignment','center',... +'HorizontalAlignmentMode','auto',... +'VerticalAlignment','top',... +'VerticalAlignmentMode','auto',... +'EdgeColor','none',... +'LineStyle','-',... +'LineWidth',0.5,... +'BackgroundColor','none',... +'Margin',2,... +'Clipping','off',... +'Layer','back',... +'LayerMode','auto',... +'FontSmoothing','on',... +'FontSmoothingMode','auto',... +'IncludeRenderer','on',... +'IsContainer','off',... +'IsContainerMode','auto',... +'HelpTopicKey',blanks(0),... +'ButtonDownFcn',blanks(0),... +'BusyAction','queue',... +'Interruptible','on',... +'CreateFcn', {@local_CreateFcn, blanks(0), ''} ,... +'DeleteFcn',blanks(0),... +'Tag',blanks(0),... +'UserData',[],... +'HitTest','on',... +'PickableParts','visible',... +'PickablePartsMode','auto',... +'DimensionNames',{ 'X' 'Y' 'Z' },... +'DimensionNamesMode','auto',... +'XLimInclude','on',... +'YLimInclude','on',... +'ZLimInclude','on',... +'CLimInclude','on',... +'ALimInclude','on',... +'Description','NumericRuler Label',... +'DescriptionMode','auto',... +'Visible','on',... +'Serializable','on',... +'HandleVisibility','off',... +'TransformForPrintFcnImplicitInvoke','on',... +'TransformForPrintFcnImplicitInvokeMode','auto'); + +h5 = get(h2,'ylabel'); + +set(h5,... +'Parent',h2,... +'Units','data',... +'FontUnits','normalized',... +'DecorationContainer',[],... +'DecorationContainerMode','auto',... +'Color',[0.15 0.15 0.15],... +'ColorMode','auto',... +'Position',[-0.182371949750624 0.500000476837158 0],... +'PositionMode','auto',... +'String',blanks(0),... +'Interpreter','tex',... +'Rotation',90,... +'RotationMode','auto',... +'FontName','Helvetica',... +'FontSize',0.0951293759512938,... +'FontAngle','normal',... +'FontWeight','normal',... +'HorizontalAlignment','center',... +'HorizontalAlignmentMode','auto',... +'VerticalAlignment','bottom',... +'VerticalAlignmentMode','auto',... +'EdgeColor','none',... +'LineStyle','-',... +'LineWidth',0.5,... +'BackgroundColor','none',... +'Margin',2,... +'Clipping','off',... +'Layer','back',... +'LayerMode','auto',... +'FontSmoothing','on',... +'FontSmoothingMode','auto',... +'IncludeRenderer','on',... +'IsContainer','off',... +'IsContainerMode','auto',... +'HelpTopicKey',blanks(0),... +'ButtonDownFcn',blanks(0),... +'BusyAction','queue',... +'Interruptible','on',... +'CreateFcn', {@local_CreateFcn, blanks(0), ''} ,... +'DeleteFcn',blanks(0),... +'Tag',blanks(0),... +'UserData',[],... +'HitTest','on',... +'PickableParts','visible',... +'PickablePartsMode','auto',... +'DimensionNames',{ 'X' 'Y' 'Z' },... +'DimensionNamesMode','auto',... +'XLimInclude','on',... +'YLimInclude','on',... +'ZLimInclude','on',... +'CLimInclude','on',... +'ALimInclude','on',... +'Description','NumericRuler Label',... +'DescriptionMode','auto',... +'Visible','on',... +'Serializable','on',... +'HandleVisibility','off',... +'TransformForPrintFcnImplicitInvoke','on',... +'TransformForPrintFcnImplicitInvokeMode','auto'); + +h6 = get(h2,'zlabel'); + +set(h6,... +'Parent',h2,... +'Units','data',... +'FontUnits','normalized',... +'DecorationContainer',[],... +'DecorationContainerMode','auto',... +'Color',[0.15 0.15 0.15],... +'ColorMode','auto',... +'Position',[0 0 0],... +'PositionMode','auto',... +'Interpreter','tex',... +'InterpreterMode','auto',... +'Rotation',0,... +'RotationMode','auto',... +'FontName','Helvetica',... +'FontNameMode','auto',... +'FontSize',0.103930461073318,... +'FontSizeMode','auto',... +'FontAngle','normal',... +'FontAngleMode','auto',... +'FontWeight','normal',... +'FontWeightMode','auto',... +'HorizontalAlignment','left',... +'HorizontalAlignmentMode','auto',... +'VerticalAlignment','middle',... +'VerticalAlignmentMode','auto',... +'EdgeColor','none',... +'EdgeColorMode','auto',... +'LineStyle','-',... +'LineStyleMode','auto',... +'LineWidth',0.5,... +'LineWidthMode','auto',... +'BackgroundColor','none',... +'BackgroundColorMode','auto',... +'Margin',3,... +'MarginMode','auto',... +'Clipping','off',... +'ClippingMode','auto',... +'Layer','middle',... +'LayerMode','auto',... +'FontSmoothing','on',... +'FontSmoothingMode','auto',... +'IncludeRenderer','on',... +'IsContainer','off',... +'IsContainerMode','auto',... +'HG1EraseMode','auto',... +'BusyAction','queue',... +'Interruptible','on',... +'HitTest','on',... +'HitTestMode','auto',... +'PickableParts','visible',... +'PickablePartsMode','auto',... +'DimensionNames',{ 'X' 'Y' 'Z' },... +'DimensionNamesMode','auto',... +'XLimInclude','on',... +'XLimIncludeMode','auto',... +'YLimInclude','on',... +'YLimIncludeMode','auto',... +'ZLimInclude','on',... +'ZLimIncludeMode','auto',... +'CLimInclude','on',... +'CLimIncludeMode','auto',... +'ALimInclude','on',... +'ALimIncludeMode','auto',... +'Description','NumericRuler Label',... +'DescriptionMode','auto',... +'Visible','off',... +'VisibleMode','auto',... +'Serializable','on',... +'SerializableMode','auto',... +'HandleVisibility','off',... +'HandleVisibilityMode','auto',... +'TransformForPrintFcnImplicitInvoke','on',... +'TransformForPrintFcnImplicitInvokeMode','auto'); + +appdata = []; +appdata.lastValidTag = 'pushbutton1'; + +h7 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Add more pixels',... +'Position',[613 413 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('pushbutton1_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','pushbutton1',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'pushbutton2'; + +h8 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Undo',... +'Position',[613 344 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('pushbutton2_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','pushbutton2',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'pushbutton5'; + +h8 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Reset',... +'Position',[613 275 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('revert_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','pushbutton2',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'pushbutton3'; + +h9 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Load mask',... +'Position',[613 206 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('pushbutton3_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','pushbutton3',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'pushbutton4'; + +h10 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Save mask',... +'Position',[613 137 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('pushbutton4_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','pushbutton4',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'quit'; + +h11 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'ListboxTop',0,... +'String','Quit',... +'Position',[613 68 146 48],... +'BackgroundColor',[0.831 0.816 0.784],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('quit_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'ButtonDownFcn',blanks(0),... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ,... +'DeleteFcn',blanks(0),... +'Tag','quit',... +'UserData',[],... +'KeyPressFcn',blanks(0),... +'FontSize',8,... +'FontSizeMode',get(0,'defaultuicontrolFontSizeMode')); + +appdata = []; +appdata.lastValidTag = 'quit'; + +h12 = uicontrol(... +'Parent',h1,... +'FontUnits',get(0,'defaultuicontrolFontUnits'),... +'Units','pixels',... +'String',{ 'Apply to stack' },... +'Style','togglebutton',... +'Position',[613 472 146 22],... +'Callback',@(hObject,eventdata)create_mask_GUI_export('toggle_button_stack_Callback',hObject,eventdata,guidata(hObject)),... +'Children',[],... +'Tag','togglebutton1',... +'Visible', 'off',... +'Value', 1,... +'BackgroundColor', [0.956 0.3137 0.2588],... +'CreateFcn', {@local_CreateFcn, blanks(0), appdata} ); + + +h1.UserData.apply_to_stack_toggle = h12; + +hsingleton = h1; + + +% --- Set application data first then calling the CreateFcn. +function local_CreateFcn(hObject, eventdata, createfcn, appdata) + +if ~isempty(appdata) + names = fieldnames(appdata); + for i=1:length(names) + name = char(names(i)); + setappdata(hObject, name, getfield(appdata,name)); + end +end + +if ~isempty(createfcn) + if isa(createfcn,'function_handle') + createfcn(hObject, eventdata); + else + eval(createfcn); + end +end + + +% --- Handles default GUIDE GUI creation and callback dispatch +function varargout = gui_mainfcn(gui_State, varargin) + +gui_StateFields = {'gui_Name' + 'gui_Singleton' + 'gui_OpeningFcn' + 'gui_OutputFcn' + 'gui_LayoutFcn' + 'gui_Callback'}; +gui_Mfile = ''; +for i=1:length(gui_StateFields) + if ~isfield(gui_State, gui_StateFields{i}) + error(message('MATLAB:guide:StateFieldNotFound', gui_StateFields{ i }, gui_Mfile)); + elseif isequal(gui_StateFields{i}, 'gui_Name') + gui_Mfile = [gui_State.(gui_StateFields{i}), '.m']; + end +end + +numargin = length(varargin); + +if numargin == 0 + % CREATE_MASK_GUI_EXPORT + % create the GUI only if we are not in the process of loading it + % already + gui_Create = true; +elseif local_isInvokeActiveXCallback(gui_State, varargin{:}) + % CREATE_MASK_GUI_EXPORT(ACTIVEX,...) + vin{1} = gui_State.gui_Name; + vin{2} = [get(varargin{1}.Peer, 'Tag'), '_', varargin{end}]; + vin{3} = varargin{1}; + vin{4} = varargin{end-1}; + vin{5} = guidata(varargin{1}.Peer); + feval(vin{:}); + return; +elseif local_isInvokeHGCallback(gui_State, []) + % CREATE_MASK_GUI_EXPORT('CALLBACK',hObject,eventData,handles,...) + gui_Create = false; +else + % CREATE_MASK_GUI_EXPORT(...) + % create the GUI and hand varargin to the openingfcn + gui_Create = true; +end + +if ~gui_Create + % In design time, we need to mark all components possibly created in + % the coming callback evaluation as non-serializable. This way, they + % will not be brought into GUIDE and not be saved in the figure file + % when running/saving the GUI from GUIDE. + designEval = false; + if (numargin>1 && ishghandle(varargin{2})) + fig = varargin{2}; + while ~isempty(fig) && ~ishghandle(fig,'figure') + fig = get(fig,'parent'); + end + + designEval = isappdata(0,'CreatingGUIDEFigure') || (isscalar(fig)&&isprop(fig,'GUIDEFigure')); + end + + if designEval + beforeChildren = findall(fig); + end + + % evaluate the callback now + varargin{1} = gui_State.gui_Callback; + if nargout + [varargout{1:nargout}] = feval(varargin{:}); + else + feval(varargin{:}); + end + + % Set serializable of objects created in the above callback to off in + % design time. Need to check whether figure handle is still valid in + % case the figure is deleted during the callback dispatching. + if designEval && ishghandle(fig) + set(setdiff(findall(fig),beforeChildren), 'Serializable','off'); + end +else + if gui_State.gui_Singleton + gui_SingletonOpt = 'reuse'; + else + gui_SingletonOpt = 'new'; + end + + % Check user passing 'visible' P/V pair first so that its value can be + % used by oepnfig to prevent flickering + gui_Visible = 'auto'; + gui_VisibleInput = ''; + for index=1:2:length(varargin) + if length(varargin) == index || ~ischar(varargin{index}) + break; + end + + % Recognize 'visible' P/V pair + len1 = min(length('visible'),length(varargin{index})); + len2 = min(length('off'),length(varargin{index+1})); + if ischar(varargin{index+1}) && strncmpi(varargin{index},'visible',len1) && len2 > 1 + if strncmpi(varargin{index+1},'off',len2) + gui_Visible = 'invisible'; + gui_VisibleInput = 'off'; + elseif strncmpi(varargin{index+1},'on',len2) + gui_Visible = 'visible'; + gui_VisibleInput = 'on'; + end + end + end + + % Open fig file with stored settings. Note: This executes all component + % specific CreateFunctions with an empty HANDLES structure. + + + % Do feval on layout code in m-file if it exists + gui_Exported = ~isempty(gui_State.gui_LayoutFcn); + % this application data is used to indicate the running mode of a GUIDE + % GUI to distinguish it from the design mode of the GUI in GUIDE. it is + % only used by actxproxy at this time. + setappdata(0,genvarname(['OpenGuiWhenRunning_', gui_State.gui_Name]),1); + if gui_Exported + gui_hFigure = feval(gui_State.gui_LayoutFcn, gui_SingletonOpt); + + % make figure invisible here so that the visibility of figure is + % consistent in OpeningFcn in the exported GUI case + if isempty(gui_VisibleInput) + gui_VisibleInput = get(gui_hFigure,'Visible'); + end + set(gui_hFigure,'Visible','off') + + % openfig (called by local_openfig below) does this for guis without + % the LayoutFcn. Be sure to do it here so guis show up on screen. + movegui(gui_hFigure,'onscreen'); + else + gui_hFigure = local_openfig(gui_State.gui_Name, gui_SingletonOpt, gui_Visible); + % If the figure has InGUIInitialization it was not completely created + % on the last pass. Delete this handle and try again. + if isappdata(gui_hFigure, 'InGUIInitialization') + delete(gui_hFigure); + gui_hFigure = local_openfig(gui_State.gui_Name, gui_SingletonOpt, gui_Visible); + end + end + if isappdata(0, genvarname(['OpenGuiWhenRunning_', gui_State.gui_Name])) + rmappdata(0,genvarname(['OpenGuiWhenRunning_', gui_State.gui_Name])); + end + + % Set flag to indicate starting GUI initialization + setappdata(gui_hFigure,'InGUIInitialization',1); + + % Fetch GUIDE Application options + gui_Options = getappdata(gui_hFigure,'GUIDEOptions'); + % Singleton setting in the GUI MATLAB code file takes priority if different + gui_Options.singleton = gui_State.gui_Singleton; + + if ~isappdata(gui_hFigure,'GUIOnScreen') + % Adjust background color + if gui_Options.syscolorfig + set(gui_hFigure,'Color', get(0,'DefaultUicontrolBackgroundColor')); + end + + % Generate HANDLES structure and store with GUIDATA. If there is + % user set GUI data already, keep that also. + data = guidata(gui_hFigure); + handles = guihandles(gui_hFigure); + if ~isempty(handles) + if isempty(data) + data = handles; + else + names = fieldnames(handles); + for k=1:length(names) + data.(char(names(k)))=handles.(char(names(k))); + end + end + end + guidata(gui_hFigure, data); + end + + % Apply input P/V pairs other than 'visible' + for index=1:2:length(varargin) + if length(varargin) == index || ~ischar(varargin{index}) + break; + end + + len1 = min(length('visible'),length(varargin{index})); + if ~strncmpi(varargin{index},'visible',len1) + try set(gui_hFigure, varargin{index}, varargin{index+1}), catch break, end + end + end + + % If handle visibility is set to 'callback', turn it on until finished + % with OpeningFcn + gui_HandleVisibility = get(gui_hFigure,'HandleVisibility'); + if strcmp(gui_HandleVisibility, 'callback') + set(gui_hFigure,'HandleVisibility', 'on'); + end + + feval(gui_State.gui_OpeningFcn, gui_hFigure, [], guidata(gui_hFigure), varargin{:}); + + if isscalar(gui_hFigure) && ishghandle(gui_hFigure) + % Handle the default callbacks of predefined toolbar tools in this + % GUI, if any + guidemfile('restoreToolbarToolPredefinedCallback',gui_hFigure); + + % Update handle visibility + set(gui_hFigure,'HandleVisibility', gui_HandleVisibility); + + % Call openfig again to pick up the saved visibility or apply the + % one passed in from the P/V pairs + if ~gui_Exported + gui_hFigure = local_openfig(gui_State.gui_Name, 'reuse',gui_Visible); + elseif ~isempty(gui_VisibleInput) + set(gui_hFigure,'Visible',gui_VisibleInput); + end + if strcmpi(get(gui_hFigure, 'Visible'), 'on') + figure(gui_hFigure); + + if gui_Options.singleton + setappdata(gui_hFigure,'GUIOnScreen', 1); + end + end + + % Done with GUI initialization + if isappdata(gui_hFigure,'InGUIInitialization') + rmappdata(gui_hFigure,'InGUIInitialization'); + end + + % If handle visibility is set to 'callback', turn it on until + % finished with OutputFcn + gui_HandleVisibility = get(gui_hFigure,'HandleVisibility'); + if strcmp(gui_HandleVisibility, 'callback') + set(gui_hFigure,'HandleVisibility', 'on'); + end + gui_Handles = guidata(gui_hFigure); + else + gui_Handles = []; + end + + if nargout + [varargout{1:nargout}] = feval(gui_State.gui_OutputFcn, gui_hFigure, [], gui_Handles); + else + feval(gui_State.gui_OutputFcn, gui_hFigure, [], gui_Handles); + end + + if isscalar(gui_hFigure) && ishghandle(gui_hFigure) + set(gui_hFigure,'HandleVisibility', gui_HandleVisibility); + end +end + +function gui_hFigure = local_openfig(name, singleton, visible) + +% openfig with three arguments was new from R13. Try to call that first, if +% failed, try the old openfig. +if nargin('openfig') == 2 + % OPENFIG did not accept 3rd input argument until R13, + % toggle default figure visible to prevent the figure + % from showing up too soon. + gui_OldDefaultVisible = get(0,'defaultFigureVisible'); + set(0,'defaultFigureVisible','off'); + gui_hFigure = matlab.hg.internal.openfigLegacy(name, singleton); + set(0,'defaultFigureVisible',gui_OldDefaultVisible); +else + % Call version of openfig that accepts 'auto' option" + gui_hFigure = matlab.hg.internal.openfigLegacy(name, singleton, visible); +% %workaround for CreateFcn not called to create ActiveX +% peers=findobj(findall(allchild(gui_hFigure)),'type','uicontrol','style','text'); +% for i=1:length(peers) +% if isappdata(peers(i),'Control') +% actxproxy(peers(i)); +% end +% end +end + +function result = local_isInvokeActiveXCallback(gui_State, varargin) + +try + result = ispc && iscom(varargin{1}) ... + && isequal(varargin{1},gcbo); +catch + result = false; +end + +function result = local_isInvokeHGCallback(gui_State, varargin) + +try + fhandle = functions(gui_State.gui_Callback); + result = ~isempty(findstr(gui_State.gui_Name,fhandle.file)) || ... + (ischar(varargin{1}) ... + && isequal(ishghandle(varargin{2}), 1) ... + && (~isempty(strfind(varargin{1},[get(varargin{2}, 'Tag'), '_'])) || ... + ~isempty(strfind(varargin{1}, '_CreateFcn'))) ); +catch + result = false; +end + + diff --git a/+beamline/private/radial_integ_mex.cpp b/+beamline/private/radial_integ_mex.cpp new file mode 100644 index 0000000..a02256e --- /dev/null +++ b/+beamline/private/radial_integ_mex.cpp @@ -0,0 +1,179 @@ +// RADIAL_INTEG_MEX perform radial integration for a 2D frame +// +// ** ind_r_max int32 +// ** no_of_segments int32 +// ** norm_sum double +// ** indices cell +// ** frame_data 2D or 3D array, double +// +// return: +// ++ frame_I 2D or 3D array, double +// ++ frame_std 2D or 3D array, double +// +// +// Example: +// [frame_I(:,:,ind_frame), frame_std(:,:,ind_frame)] = radial_integ_mex(int32(ind_r_max),int32(no_of_segments), integ_masks.norm_sum, integ_masks.indices, frame_data); +// +// compile with: +// mex 'CFLAGS="\$CFLAGS -O3 -std=c++17 -fopenmp"' LDFLAGS="\$LDFLAGS -fopenmp" radial_integ_mex.cpp +// +// MATLAB code: +// for (ind_r = 1:ind_r_max) +// for (ind_seg = 1:no_of_segments) +// if (integ_masks.norm_sum(ind_r,ind_seg) > 0) +// frame_I_one_frame(ind_r,ind_seg) = ... +// mean(frame_data(integ_masks.indices{ind_r,ind_seg})); +// frame_std_one_frame(ind_r,ind_seg) = ... +// std(frame_data(integ_masks.indices{ind_r,ind_seg})); +// else +// % mark unknown intensities +// frame_I_one_frame(ind_r,ind_seg) = -1; +// frame_std_one_frame(ind_r,ind_seg) = -1; +// end +// end +// end + +// *-----------------------------------------------------------------------* +// |                                                                       | +// |  Except where otherwise noted, this work is licensed under a          | +// |  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +// |  International (CC BY-NC-SA 4.0) license.                             | +// |                                                                       | +// |  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +// |                                                                       | +// |      Author: CXS group, PSI  | +// *-----------------------------------------------------------------------* +// You may use this code with the following provisions: +// +// If the code is fully or partially redistributed, or rewritten in another +// computing language this notice should be included in the redistribution. +// +// If this code, or subfunctions or parts of it, is used for research in a +// publication or if it is fully or partially rewritten for another +// computing language the authors and institution should be acknowledged +// in written form in the publication: “Data processing was carried out +// using the “cSAXS matlab package” developed by the CXS group, +// Paul Scherrer Institut, Switzerland.” +// Variations on the latter text can be incorporated upon discussion with +// the CXS group if needed to more specifically reflect the use of the package +// for the published work. +// +// A publication that focuses on describing features, or parameters, that +// are already existing in the code should be first discussed with the +// authors. +// +// This code and subroutines are part of a continuous development, they +// are provided “as they are” without guarantees or liability on part +// of PSI or the authors. It is the user responsibility to ensure its +// proper use and the correctness of the results. + +#include "mex.h" +#include "matrix.h" +#include +#include +#include + +void mexFunction( int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + double *norm_sum; + double *frame_data; + uint ind_r_max, no_of_segments; + const mwSize *pDims; + int nDimNum; + int maxSlice; + + /* check for proper number of arguments */ + if(nrhs!=5) { + mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs","Five inputs required."); + } + if(nlhs!=2) { + mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nlhs","Two output containers are required."); + } + /* make sure the first two input arguments are of type int */ + if( !mxIsClass(prhs[0], "int32")) { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:notInteger","r_max must be of type integer."); + } + if( !mxIsClass(prhs[1], "int32")) { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:notInteger","no_of_segments must be of type integer."); + } + if( !mxIsClass(prhs[2], "double")) { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:notInteger","norm_sum must be of type double."); + } + if( !mxIsCell(prhs[3])) { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:notInteger","indices must be of type cell."); + } + if( !mxIsClass(prhs[4], "double")) { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:notInteger","frame_data must be of type double."); + } + ind_r_max = mxGetScalar(prhs[0]); + no_of_segments = mxGetScalar(prhs[1]); + norm_sum = mxGetPr(prhs[2]); + + frame_data = mxGetPr(prhs[4]); + + nDimNum = mxGetNumberOfDimensions(prhs[4]); + pDims = mxGetDimensions(prhs[4]); + + if (nDimNum==2){ + plhs[0] = mxCreateNumericMatrix((mwSize)ind_r_max, (mwSize)no_of_segments, mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericMatrix((mwSize)ind_r_max, (mwSize)no_of_segments, mxDOUBLE_CLASS, mxREAL); + maxSlice = 1; + } else if (nDimNum==3) { + maxSlice = pDims[2]; + mwSize dims[3] = {(mwSize)ind_r_max,(mwSize)no_of_segments,(mwSize)maxSlice}; + plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL); + plhs[1] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL); + } else { + mexErrMsgIdAndTxt("cxsSoftware:radialIntegMex:dimsError","frame_data must be 2D or 3D."); + } + + double* outputMatrixMean = (double *)mxGetData(plhs[0]); + double* outputMatrixStdDev = (double *)mxGetData(plhs[1]); + + #pragma omp parallel for collapse(2) + for (uint slID=0; slID < maxSlice; slID++){ + for (uint ind_r=0; ind_r 0){ + mxArray *subarray = mxGetCell(prhs[3], ind_r+ind_seg*ind_r_max); + double *indicesSub = mxGetPr(subarray); + uint dim = mxGetNumberOfElements(subarray); + + + for (uint ii=0; ii0){ + tmpMean /= dim; + } + for (uint ii=0; ii0 && dim>1){ + tmpStdDev = sqrt(tmpStdDev/(dim-1)); + } + + + } else { + tmpMean = -1; + tmpStdDev = -1; + } + + outputMatrixMean[ind_r+ind_seg*ind_r_max+slID*(ind_r_max)*(no_of_segments)] = tmpMean; + outputMatrixStdDev[ind_r+ind_seg*ind_r_max+slID*(ind_r_max)*(no_of_segments)] = tmpStdDev; + } + } + + } + + + + + +} \ No newline at end of file diff --git a/+beamline/radial_integ.m b/+beamline/radial_integ.m new file mode 100644 index 0000000..f5d6a36 --- /dev/null +++ b/+beamline/radial_integ.m @@ -0,0 +1,710 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: radial_integ.m,v $ +% +% $Revision: 1.12 $ $Date: 2016/01/21 15:11:50 $ +% $Author: $ +% $Tag: $ +% +% Description: +% radial integration of 2D data read from file(s) +% +% Note: +% Call without arguments for a brief help text. +% The integration masks need to be prepared first using prep_integ_masks.m +% +% Dependencies: +% - image_read +% +% history: +% +% February 18 2015: +% updated to use new function names of parallel toolbox in Matlab 2014b +% +% July 22nd 2010: +% add simple parallel processing using parfor +% +% April 28th 2010: +% use default_parameter_value +% +% June 5th 2008: 1st documented version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ I,vararg_remain ] = radial_integ(filename_masks,varargin) +import beamline.prep_integ_masks +import io.image_read +import plotting.plot_radial_integ +import utils.default_parameter_value +import utils.find_files +import utils.abspath + +% initialize return arguments +I = struct('I_all',[], 'I_std',[],'filenames_all',[],'q',[],'radius',[]); + +% set default values for the variable input arguments: +outdir_data = default_parameter_value(mfilename,'OutdirData'); +filename_integ_masks = default_parameter_value(mfilename,'FilenameIntegMasks'); +r_max_forced = default_parameter_value(mfilename,'rMaxForced'); +fig_no = default_parameter_value(mfilename,'FigNo'); +save_combined_I = default_parameter_value(mfilename,'SaveCombinedI'); +recursive = default_parameter_value(mfilename,'Recursive'); +use_find = default_parameter_value(mfilename,'UseFind'); +unhandled_par_error = default_parameter_value(mfilename,'UnhandledParError'); +parallel_tasks_max = 1; %default_parameter_value(mfilename,'ParTasksMax'); +save_format = '-v6'; +use_mex = true; +c_reader = true; +useStack = true; + +% check minimum number of input arguments +if (nargin < 1) + fprintf('\nUsage:\n'); + fprintf('%s(filename_mask, [[,,] ...]);\n',mfilename); + fprintf('filename_mask can be something like ''*.cbf'' or ''image.cbf'' or\n'); + fprintf('a cell array of filenames or filename masks like {''dir1/*.cbf'',''dir2/*.cbf''}.\n'); + fprintf('The optional , pairs are:\n'); + fprintf('''OutdirData'', save the integrated intensities to files in this directory, '''' for no saving, default is %s\n',outdir_data); + fprintf('''FilenameIntegMasks'', Matlab file containing the integration masks, default is ''%s''\n',filename_integ_masks); + fprintf('''rMaxForced'', stop integration at this maximum r even if the integration masks reach further, default is 0 - do not stop\n'); + fprintf('''FigNo'',
number of the figure for an online plot of the intensities in case parallel processing is not used, 0 for no plot, default is %d\n',fig_no); + fprintf('''SaveFormat'', default is %s\n',save_format); + fprintf('''SaveCombinedI'',<0-no, 1-yes> save intensities from all specified files found in one directory in a single file, default is yes\n'); + fprintf('''Recursive'',<0-no, 1-yes> recursively integrate files in all matching sub-directories, default is yes\n'); + fprintf('''ParTasksMax'', specify the maximum number of CPU cores to use, 1 to deactivate the use of parallel computing, default is %d\n',parallel_tasks_max); + fprintf('''UseFind'',<0-no, 1-yes> use Linux/Unix command find to interprete the filename mask, default is yes\n'); + fprintf('''UseMex'', <0-no, 1-yes> use radial_integ_mex; usually faster than MATLAB, default is yes\n'); + fprintf('''CReader'', <0-no, 1-yes> use the fast measurement reader; usually faster than image_read, default is yes\n'); + fprintf('''UseStack'', <0-no, 1-yes> load all detector frames into memory before calling the radial_integ functions; default is yes\n'); + fprintf('''UnhandledParError'',<0-no,1-yes> exit in case not all named parameters are used/known, default is %d\n',unhandled_par_error); + fprintf('Examples:\n'); + fprintf('%s(''~/Data10/pilatus/mydatadir/*.cbf'',''OutdirData'',''~/Data10/analysis/radial_integ/'');\n',mfilename); + fprintf('Additional , pairs recognized by image_read can be specified.\n'); + error('At least the filename mask has to be specified as input argument.'); +end + + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'OutdirData' + outdir_data = value; + case 'SaveFormat' + save_format = value; + case 'FilenameIntegMasks' + filename_integ_masks = value; + case 'rMaxForced' + r_max_forced = value; + case 'FigNo' + fig_no = value; + case 'SaveCombinedI' + save_combined_I = value; + case 'Recursive' + recursive = value; + case 'UseFind' + use_find = value; + case 'UnhandledParError' + unhandled_par_error = value; + case 'ParTasksMax' + parallel_tasks_max = value; + case 'UseMex' + use_mex = value; + case 'CReader' + c_reader = value; + case 'UseStack' + useStack = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); + +% do not exit in image_par in case of unhandled parameters +if (~unhandled_par_error) + vararg{end+1} = 'UnhandledParError'; + vararg{end+1} = 0; +end + +if (~isempty(outdir_data)) + % add slash to output directory + if (outdir_data(end) ~= '/') + outdir_data = [ outdir_data '/' ]; + end + + % create output directory + [mkdir_stat,mkdir_message] = mkdir(outdir_data); + if (~mkdir_stat) + error('invalid directory %s: %s',outdir_data,mkdir_message); + end + if ((mkdir_stat) && (isempty(mkdir_message))) + fprintf('The output directory %s has been created.\n',outdir_data); + else + fprintf('The output directory is %s.\n',outdir_data); + end +else + fprintf('data are not saved\n'); +end + +% load integration masks from this file +% this loads: +% center_xy, no_of_segments, integ_masks +fprintf('loading integration masks from %s\n',filename_integ_masks); +load(filename_integ_masks); +if ((~exist('center_xy','var')) && (exist('center_x','var'))) + center_xy(1) = center_x; + center_xy(2) = center_y; + if (~exist('integ_masks','var')) + integ_masks.radius = r; + integ_masks.indices = masks_r; + integ_masks.norm_sum = mask_r_sum; + end +end +fprintf('center at (x, y) = (%.1f, %.1f)\n',center_xy(1),center_xy(2)); + +% limit radial range +if (r_max_forced > 0) + ind = find( integ_masks.radius < r_max_forced ); + if (length(ind) < 1) + fprintf('No radii below rMaxForced = %d found\n',r_max_forced); + return; + end + integ_masks.radius = integ_masks.radius(1:ind(end)); + integ_masks.norm_sum = integ_masks.norm_sum(1:ind(end), :); +end +if isfield(integ_masks,'radius') +fprintf('radii from %d to %d\n',... + integ_masks.radius(1),integ_masks.radius(end)); +else + fprintf('radii from %d to %d\n',... + integ_masks.q(1),integ_masks.q(end)); +end +% ease handling by ensuring that filename_masks is a cell array +if (~iscell(filename_masks)) + filename_masks = { filename_masks }; +end + + +% initialize parallel processing if this is enabled and not yet done +if (parallel_tasks_max > 1) + %matlabpool_size = matlabpool('size'); + %if (matlabpool_size < 1) + if isempty(gcp('nocreate')) %MGS2015 If there is no current pool + % create a scheduler object using the default configuration, which is a + % local scheduler if nothing else has been installed + % scheduler = findResource('scheduler','type', defaultParallelConfig); + scheduler = parcluster; %MGS2015 + + % adapt maximum number of tasks/workers, if necessary + %cluster_size = get(scheduler,'ClusterSize'); + cluster_size = scheduler.NumWorkers; %MGS2015 + if (parallel_tasks_max > cluster_size) + fprintf('Adapting the maximum number of tasks from %d to %d.\n',... + parallel_tasks_max, cluster_size); + parallel_tasks_max = cluster_size; + end + + % open a Matlab pool for simple parallel processing + if (parallel_tasks_max > 1) + %matlabpool('open',parallel_tasks_max);%MGS2015 + pool = parpool(parallel_tasks_max); + fprintf('Using parallel processing with %d tasks.\n', ... + parallel_tasks_max); + end + else + pool = gcp;%MGS2015 + if ( pool.NumWorkers < parallel_tasks_max ) + fprintf('%s: usage of up to %d CPUs in parallel has been specified but an already open matlabpool with %d workers has been found and will be used instead\n', ... + mfilename, parallel_tasks_max, pool.NumWorkers); + parallel_tasks_max = pool.NumWorkers; + end + end +end + +pool.IdleTimeout = Inf; + +if ((parallel_tasks_max > 1) && (fig_no > 0)) + fprintf('%s: Online plotting is disabled since parallel processing is enabled.\n', ... + mfilename); +end + +% loop over all filename masks +ind_mask_max = length(filename_masks); + +% Initialize variables for saving +no_of_segments = size(integ_masks.indices,2); +if isfield(integ_masks,'radius') + radius = integ_masks.radius; + ind_r_max = length(radius); +else + radius = []; + q = integ_masks.q; + ind_r_max = length(q); +end +if isfield(integ_masks,'q') + q = integ_masks.q; +else + q = []; +end + +for (ind_mask = 1:ind_mask_max) %#ok<*NO4LP> + filename_mask = filename_masks{ind_mask}; + fprintf('%s:\n',filename_mask); + [data_dir,fnames] = find_files( filename_mask, 'UseFind',use_find ); + + if (length(fnames) < 1) + fprintf('No matching files found for %s.\n',filename_mask); + continue; + end + + % collect recursively all matching file names + [ filenames_all ] = ... + collect_radial_integ_filenames(data_dir, fnames, ... + recursive, ... + vararg); + + % prepare for integration of the so far identified files + file_ind_max = length(filenames_all); + for file_ind=1:file_ind_max + filenames_all{file_ind}=abspath(filenames_all{file_ind}); + end + + % get the number of frames per file by loading the first file (not very + % elegant) + [frame] = image_read(filenames_all{1}, vararg); + no_of_frames = size(frame.data,3); + I_all = zeros(ind_r_max, no_of_segments, no_of_frames, file_ind_max); + I_std = zeros(ind_r_max, no_of_segments, no_of_frames, file_ind_max); + + if (parallel_tasks_max > 1) + % integration using parallel processing + parfor (file_ind = 1:file_ind_max) + + % read the raw data frame and integrate it + [frame_I, frame_std] = ... + perform_radial_integ_parallel(file_ind, file_ind_max, ... + filenames_all{file_ind}, ... + integ_masks, ind_r_max, no_of_segments, ... + vararg); + + % no_of_frames = size(frame_I,3); + % if (no_of_frames ~= size(I_all,3)) + % error('number of frames per file changes from %d to %d',size(I_all,3),no_of_frames); + % end + + I_all(:,:,:,file_ind) = frame_I; + I_std(:,:,:,file_ind) = frame_std; + end + else + % read the raw data + if c_reader + try + [~, ~, ext] = fileparts(filenames_all{1}); + arg.data_path = filenames_all'; + arg.nthreads = min(round(feature('numcores')*0.8),14); + arg.precision = 'single'; + arg.extension = ext(2:end); + if strcmpi(ext(1:end), 'h5') && ~isempty(find(strcmp(varargin, 'H5Location'))) + arg.data_location = varargin{find(strcmp(varargin, 'H5Location'))+1}; + end + frameStorage.data = io.read_measurement(arg); + frameStorage.data = permute(frameStorage.data,[2 1 3]); + frameStorage.data = flip(flip(frameStorage.data,1),2); + catch ME + fprintf('Failed to load data. If the problem persists, set c_reader=false.\n'); + rethrow(ME); + end + else + [frameStorage] = image_read(filename_masks, vararg); + end + if ~useStack + for (file_ind = 1:file_ind_max) + + % read the raw data frame and integrate it + [frame_I, frame_std] = ... + perform_radial_integ(file_ind, file_ind_max, ... + frameStorage.data(:,:,file_ind), ... + integ_masks, ind_r_max, no_of_segments, use_mex, ... + vararg); + + I_all(:,:,:,file_ind) = frame_I; + I_std(:,:,:,file_ind) = frame_std; + + % plot integrated intensities as feedback + if (fig_no > 0) + if isfield(integ_masks,'radius') + d.radius = radius; + else + d.radius= q; + end + d.I_all = frame_I; + d.I_std = frame_std; + plot_radial_integ(d,'FigNo',fig_no); + drawnow; + end + end + else + % integrate it + try + [frame_I, frame_std] = radial_integ_mex(int32(ind_r_max),int32(no_of_segments), integ_masks.norm_sum, integ_masks.indices, double(frameStorage.data)); + catch + tmpPath = fileparts(mfilename('fullpath')); + fprintf('Recompiling mex function...\n'); + + % Fall back to single thread if the OpenMP fail. + + eval(['mex ' fullfile(tmpPath, 'private', 'radial_integ_mex.cpp') ' -outdir ' fullfile(tmpPath, 'private')]); + try + [frame_I, frame_std] = radial_integ_mex(int32(ind_r_max),int32(no_of_segments), integ_masks.norm_sum, integ_masks.indices, double(frameStorage.data)); + catch ME + fprintf('radial_integ_mex failed. If the problem persists, consider setting use_mex=false.\n'); + rethrow(ME); + end + end + I_all(:,:,1,:) = frame_I; + I_std(:,:,1,:) = frame_std; + end + end + + % reshuffle the data to get rid off the frame-within-file dimension, + % dimension 3. + % This would be easier with linear indexing in case the frame and file + % dimensions would be 1 and 2. + I_all_org = I_all; + I_std_org = I_std; + I_all = zeros(size(I_all_org,1), size(I_all_org,2), size(I_all_org,3) * size(I_all_org,4)); + I_std = zeros(size(I_all)); + for (ind_frame = 1:size(I_all_org,3)) + for (ind_file = 1:size(I_all_org,4)) + I_all(:,:,(ind_file-1)*size(I_all_org,3)+ind_frame) = I_all_org(:,:,ind_frame,ind_file); + I_std(:,:,(ind_file-1)*size(I_std_org,3)+ind_frame) = I_std_org(:,:,ind_frame,ind_file); + end + end + + % save data, if this option is enabled + if (~isempty(outdir_data)) + if (save_combined_I) + % save all integrated frames as single Matlab file + + if (exist('I_all','var')) + % use first file as file-name base + [~, name] = fileparts(filenames_all{1}); + % name = name(1:end-12); + fname_out = fullfile(outdir_data, [ name '_integ.mat' ]); + fprintf('saving %s\n',fname_out); + % remove directory information before storing the filenames + for (file_ind = 1:file_ind_max) + [~, name, extension] = fileparts(filenames_all{file_ind}); + filenames_all{file_ind} = [ name extension ]; + end + norm_sum = integ_masks.norm_sum; + save(fname_out,'I_all','I_std', 'norm_sum', 'filenames_all','radius','q','angular_segments','phi_det', save_format); + else + fprintf('No data to save for directory %s\n',data_dir); + end + else + + % save the integrated data for each frame as separate ASCII + % file + savedat = zeros(ind_r_max, no_of_segments +1); + if isfield(integ_masks,'radius') + savedat(:,1) = radius; + else + savedat(:,1)= q; + end + for (file_ind = 1:file_ind_max) + % save integrated data for this image in the output arrays + savedat(:,2:end) = I_all(:,:,file_ind); + + [pathstr, name] = fileparts(filenames_all{file_ind}); + fname_out = fullfile(pathstr, [ name '_integ.txt' ]); + fprintf('saving %s\n',fname_out); + save([outdir_data fname_out],'savedat','-ascii'); + end + + fprintf('\nOutput data format:\n'); + fprintf('- first column with radius of circle in pixel\n'); + fprintf('- further columns with average intensity in circle segment\n'); + end + end + + % compile return value + I(ind_mask).I_all = I_all; + I(ind_mask).I_std = I_std; + if isfield(integ_masks,'radius') + I(ind_mask).radius = integ_masks.radius; + end + I(ind_mask).norm_sum = integ_masks.norm_sum; + I(ind_mask).filenames_all = filenames_all; + if isfield(integ_masks,'q') + I(ind_mask).q = integ_masks.q; + end +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [ filenames_all ] = ... + collect_radial_integ_filenames(data_dir, fnames, ... + recursive, ... + vararg) +import beamline.prep_integ_masks +import io.image_read +import plotting.plot_radial_integ +import utils.default_parameter_value +import utils.find_files +% add slashes to directories +if ((~isempty(data_dir)) && (data_dir(end) ~= '/')) + data_dir = [ data_dir '/' ]; +end + +% define some variables which depend on the input arguments +file_ind_max = length(fnames); + +% initialize variables used in the loop +filenames_all_max = 0; +filenames_all = cell(file_ind_max,1); + +% loop over all matching files +for (file_ind=1:file_ind_max) + % % skip single frames created using the spec macro ct + % if (length(fnames(file_ind).name) > 7) + % fprintf(''); + % if (strcmp(fnames(file_ind).name((end-6):(end-3)),'_ct.')) + % fprintf('skipping %s\n',fnames(file_ind).name); + % continue + % end + % end + % directory: recursion + if ((fnames(file_ind).isdir) && (recursive)) + % ignore . and .. directories + if ((strcmp(fnames(file_ind).name,'.')) || ... + (strcmp(fnames(file_ind).name,'..'))) + fprintf('skipping %s\n',fnames(file_ind).name); + continue + end + data_dir_sub = [ data_dir fnames(file_ind).name '/' ]; + fnames_sub = dir( data_dir_sub ); + fprintf('recursion for %s\n',fnames(file_ind).name); + [ filenames_all_rec,vararg_remain ] = ... + collect_radial_integ_filenames(data_dir_sub, ... + fnames_sub, ... + integ_masks, ... + fig_no, save_combined_I, recursive, ... + vararg); + % store result of this recursion + if (~isempty(filenames_all_rec)) + filenames_all_ind = (filenames_all_max+1):(filenames_all_max+length(filenames_all_rec)); + filenames_all(filenames_all_ind) = filenames_all_rec; + filenames_all_max = filenames_all_ind(end); + end + continue; + end + if ((length(fnames(file_ind).name) <= 4) || ... + (strcmp(fnames(file_ind).name(end-3:end),'.tmp')) || ... + (strcmp(fnames(file_ind).name(end-3:end),'.log'))) + fprintf('skipping %s\n',fnames(file_ind).name); + continue + end + + + + % store matching filenames in one array + filenames_all_max = filenames_all_max +1; + filenames_all{filenames_all_max} = [ data_dir fnames(file_ind).name ]; + + +end + + +if (~exist('filenames_all','var')) + filenames_all = []; +end + + +if (length(filenames_all) > filenames_all_max) + filenames_all = filenames_all{1:filenames_all_max}; +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [frame_I,frame_std] = ... + perform_radial_integ(file_ind, file_ind_max, ... + frame, ... + integ_masks, ind_r_max, no_of_segments, use_mex, ... + vararg) +import beamline.prep_integ_masks +import io.image_read +import plotting.plot_radial_integ +import utils.default_parameter_value +import utils.find_files +% read the raw data frame +% fprintf('%6d /%6d: ',file_ind,file_ind_max); +% [frame] = image_read(filename, vararg); +if (isempty(frame)) + error('could not load frame %u',file_ind); +end + +% get the number of frames in case of multi-frame data files like HDF5 +no_of_frames = size(frame,3); + +% initialize result variables +frame_I = zeros(ind_r_max,no_of_segments,no_of_frames); +frame_std = zeros(ind_r_max,no_of_segments,no_of_frames); + +if use_mex + for (ind_frame = 1:no_of_frames) + % get the current frame + frame_data = double(frame(:,:,ind_frame)); + try + [frame_I(:,:,ind_frame), frame_std(:,:,ind_frame)] = radial_integ_mex(int32(ind_r_max),int32(no_of_segments), integ_masks.norm_sum, integ_masks.indices, frame_data); + catch + tmpPath = fileparts(mfilename('fullpath')); + fprintf('Recompiling mex function...\n'); + + % Fall back to single thread if the OpenMP fail. + + eval(['mex ' fullfile(tmpPath, 'private', 'radial_integ_mex.cpp') ' -outdir ' fullfile(tmpPath, 'private')]); + try + [frame_I(:,:,ind_frame), frame_std(:,:,ind_frame)] = radial_integ_mex(int32(ind_r_max),int32(no_of_segments), integ_masks.norm_sum, integ_masks.indices, frame_data); + catch ME + fprintf('radial_integ_mex failed. If the problem persists, consider setting use_mex=false.\n'); + rethrow(ME); + end + end + end +else + for (ind_frame = 1:no_of_frames) + + % get the current frame + frame_data = double(frame(:,:,ind_frame)); + % initialize output variables for current data + frame_I_one_frame = zeros(ind_r_max,no_of_segments); + frame_std_one_frame = zeros(ind_r_max,no_of_segments); + for (ind_r = 1:ind_r_max) + for (ind_seg = 1:no_of_segments) + if (integ_masks.norm_sum(ind_r,ind_seg) > 0) + frame_I_one_frame(ind_r,ind_seg) = ... + mean(frame_data(integ_masks.indices{ind_r,ind_seg})); + frame_std_one_frame(ind_r,ind_seg) = ... + std(frame_data(integ_masks.indices{ind_r,ind_seg})); + else + % mark unknown intensities + frame_I_one_frame(ind_r,ind_seg) = -1; + frame_std_one_frame(ind_r,ind_seg) = -1; + end + end + end + frame_I(:,:,ind_frame) = frame_I_one_frame; + frame_std(:,:,ind_frame) = frame_std_one_frame; + end +end + + +%%%%%%%%%% +function [frame_I,frame_std] = ... + perform_radial_integ_parallel(file_ind, file_ind_max, ... + filename, ... + integ_masks, ind_r_max, no_of_segments, ... + vararg) +import beamline.prep_integ_masks +import io.image_read +import plotting.plot_radial_integ +import utils.default_parameter_value +import utils.find_files +% read the raw data frame +fprintf('%6d /%6d: ',file_ind,file_ind_max); +[frame] = image_read(filename, vararg); +if (isempty(frame.data)) + error('could not load %s',filename); +end + +% get the number of frames in case of multi-frame data files like HDF5 +no_of_frames = size(frame.data,3); + +% initialize result variables +frame_I = zeros(ind_r_max,no_of_segments,no_of_frames); +frame_std = zeros(ind_r_max,no_of_segments,no_of_frames); + + +parfor (ind_frame = 1:no_of_frames) + % get the current frame + frame_data = double(frame.data(:,:,ind_frame)); + % initialize output variables for current data + frame_I_one_frame = zeros(ind_r_max,no_of_segments); + frame_std_one_frame = zeros(ind_r_max,no_of_segments); + + for (ind_r = 1:ind_r_max) + for (ind_seg = 1:no_of_segments) + if (integ_masks.norm_sum(ind_r,ind_seg) > 0) + frame_I_one_frame(ind_r,ind_seg) = ... + mean(frame_data(integ_masks.indices{ind_r,ind_seg})); + frame_std_one_frame(ind_r,ind_seg) = ... + std(frame_data(integ_masks.indices{ind_r,ind_seg})); + else + % mark unknown intensities + frame_I_one_frame(ind_r,ind_seg) = -1; + frame_std_one_frame(ind_r,ind_seg) = -1; + end + end + end + + frame_I(:,:,ind_frame) = frame_I_one_frame; + frame_std(:,:,ind_frame) = frame_std_one_frame; +end diff --git a/+beamline/radial_integ_wrapper.m b/+beamline/radial_integ_wrapper.m new file mode 100644 index 0000000..93a33a8 --- /dev/null +++ b/+beamline/radial_integ_wrapper.m @@ -0,0 +1,194 @@ +% (beamline.)radial_integ_wrapper() +% Reads the radial integration filequeue when the filequeue is enabled +% by _filequeue_on in SPEC, and calls the radial integration script with +% parameters generated by radial_integration_SAXS_and_WAXS from scan of +% standards. +% This function is called without arguments to run on multiple nodes in +% parallel. +% Make sure your current matlab directory is Data10/matlab/. +% To change default settings modify the starting lines in function body. +% +% see also: beamline.radial_integ + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function radial_integ_wrapper() + +import utils.verbose + +p=struct(); +p.queue_path = utils.abspath('../specES1/radial_integ_queue'); +p.det_todo = [1 2]; +p.recon_latest_first = 0; % =0 from first; =1 from last; =2 random. + + +% ----- Modify until here ----- + +finishup = utils.onCleanup(@(x) radial_integ_exit(x), p); +for i=1:numel(p.det_todo) + mat_todo=[utils.abspath('../analysis/radial_integration_todo') sprintf('/vargin_det%d.mat',p.det_todo(i))]; + try + m=load(mat_todo); + catch err + error(sprintf('Did not find vargin file for detector %d, check current folder is in matlab/, and the radial integration standards are finished.\n',p.det_todo(i))); + end + args_todo{i}=m.args; +end + + +while 1==1 + + finishup.update(p); + + if ~exist(fullfile(p.queue_path,'in_progress'),'dir') + mkdir(fullfile(p.queue_path,'in_progress')); + end + + if ~exist(fullfile(p.queue_path,'failed'),'dir') + mkdir(fullfile(p.queue_path,'failed')); + end + + if ~exist(fullfile(p.queue_path,'done'),'dir') + mkdir(fullfile(p.queue_path,'done')); + end + + fext = 'dat'; + + status_ok = true; + verbose(1,['Touching folder and looking for files in the queue in ' p.queue_path]); + system(sprintf('touch %s',p.queue_path)); + files_recons = dir(fullfile(p.queue_path,'scan*.dat')); + + % Found one file to reconstruct + if ~isempty(files_recons) + if p.recon_latest_first==0 + p.file_this_recons = files_recons(1).name; + elseif p.recon_latest_first==1 + p.file_this_recons = files_recons(end).name; + else + p.file_this_recons = files_recons(randi([1 numel(files_recons)])).name; + end + finishup.update(p); + verbose(1,['Found file in queue ' fullfile(p.queue_path,p.file_this_recons)]); + % now move it quickly before someone else will take it + try + io.movefile_fast(fullfile(p.queue_path,p.file_this_recons),fullfile(p.queue_path,'in_progress')) + verbose(1,['Moving file to ' fullfile(p.queue_path,'in_progress')]); + catch + verbose(1,['Failed moving file to ' fullfile(p.queue_path,'in_progress')]); + pause(1); + status_ok = false; + end + + if status_ok + % parse the file + + fid = fopen(fullfile(p.queue_path,'in_progress',p.file_this_recons),'r'); + + tline = fgetl(fid); + while ischar(tline) + str_parts = strsplit(tline, ' '); + if numel(str_parts)>1 + fname = strtrim(str_parts{1}); + if strcmpi(fname(1:2), 'p.') + % found p entry + val = []; + for ii=2:numel(str_parts) + if ~isempty(strtrim(str_parts{ii})) + if ~isnan(str2double(str_parts{ii})) + % found number + val = [val, str2double(str_parts{ii})]; + else + % found char + val = [val, strtrim(str_parts{ii})]; + end + end + end + p.(fname(3:end)) = val; + + + elseif strcmpi(str_parts{1}, 'samplename') + p.samplename = strjoin(strtrim(str_parts(2:end)), '_'); + end + end + tline = fgetl(fid); + end + + fclose(fid); + finishup.update(p); + try + for i=1:numel(p.det_todo) + beamline.integrate_range(p.scan_number,p.scan_number,1,args_todo{i}); + end + verbose(1,['Radial integration of scan ' num2str(p.scan_number) ' finished, moving queue file to ' fullfile(p.queue_path,'done')]); + file_move_from = fullfile(p.queue_path,'in_progress',p.file_this_recons); + file_move_to = fullfile(p.queue_path,'done',p.file_this_recons); + io.movefile_fast(file_move_from,file_move_to); + catch err + try + verbose(1,['Error encountered at scan ' num2str(p.scan_number) ', moving queue file to ' fullfile(p.queue_path,'failed')]); + file_move_from = fullfile(p.queue_path,'in_progress',p.file_this_recons); + file_move_to = fullfile(p.queue_path,'failed',p.file_this_recons); + io.movefile_fast(file_move_from,file_move_to); + disp(err); + catch err + verbose(1,['Error with file system delays, skipping.']); + disp(err); + end + end + end + + else + verbose(1,'Did not find enough files in queue, pausing 10 seconds.'); + pause(10); + end +end +end + +function radial_integ_exit(p) + import utils.verbose; + verbose(1,'Radial integration interrupted'); + if isfile(fullfile(p.queue_path,'in_progress',p.file_this_recons)) + try + verbose(1,['Moving current queue file back to ' p.queue_path]); + file_move_from = fullfile(p.queue_path,'in_progress',p.file_this_recons); + file_move_to = fullfile(p.queue_path,p.file_this_recons); + io.movefile_fast(file_move_from,file_move_to); + catch + disp(err); + verbose(1,'File system error, aborting.'); + end + end +end \ No newline at end of file diff --git a/+beamline/radial_integration_SAXS_and_WAXS.m b/+beamline/radial_integration_SAXS_and_WAXS.m new file mode 100644 index 0000000..1016296 --- /dev/null +++ b/+beamline/radial_integration_SAXS_and_WAXS.m @@ -0,0 +1,548 @@ +% radial_integration_SAXS_and_WAXS.m +% Template for radial integration made around 2015 +% Changes: +% 2016-08-22: define mask files at the beginning, allowing for a flag in case it needs to be repeated +% add the save fast and v6 + +% License at the end of script + +clear all +close all + +%% step 0: add the path for the matlab-scripts (fill in userID,detno and specdatfile) +addpath .. +%e-account followed by underline +userID = [beamline.identify_eaccount '_']; +% detector number: 1 for SAXS Pilatus 2M, 2 for WAXS Pilatus 300k, 3 for +% SAXS Eiger 500 k +detno = 1; +% which data format to save? '-v6' is the standard. +save_format = '-v6'; +% flag for filenames for valid pixel mask, beamstop mask coordinates and integration mask. +% Example: '_2M_at_two_meters' +% Leave empty '' for default folder and filenames. +file_flag=''; + +% change here for offline analysis +homedir = sprintf('~/Data10/'); +%homedir = '/mnt/das-gpfs/work/p16268/'; + +%CHANGE: spec dat file +SpecDatFile = '~/Data10'; + +if (detno == 1 )||(detno == 2) + datadir = fullfile(sprintf('%s',homedir),sprintf('pilatus_%d/',detno)); +elseif detno == 3 + datadir = fullfile(sprintf('%s',homedir),sprintf('eiger')); +end +if detno == 2 + integdir = sprintf('%sanalysis/radial_integration_waxs%s/',homedir,file_flag); +elseif detno == 1 + integdir = sprintf('%sanalysis/radial_integration%s/',homedir,file_flag); +elseif detno == 3 + integdir = sprintf('%sanalysis/radial_integration_eiger%s/',homedir,file_flag); +end +if detno == 2 + outdir = sprintf('%sanalysis/data_waxs%s/',homedir,file_flag); +elseif detno == 1 + outdir = sprintf('%sanalysis/data/%s',homedir,file_flag); +elseif detno == 3 + outdir = sprintf('%sanalysis/data_eiger%s/',homedir,file_flag); +end +addpath(sprintf('%smatlab/',homedir)); +if (detno == 1 )||(detno == 2) + maskfilename = sprintf('%spilatus_%d_valid_mask%s.mat', outdir,detno,file_flag); + integmaskfilename=sprintf('%spilatus_%d_integration_masks%s.mat',outdir,detno,file_flag); +elseif detno == 3 + maskfilename = sprintf('%seiger_%d_valid_mask%s.mat', outdir,detno,file_flag); + integmaskfilename=sprintf('%seiger_%d_integration_masks%s.mat',outdir,detno,file_flag); +end +maskcoordfilename=sprintf('%smask_coordinates_%d%s.mat',outdir,detno, file_flag); + +dirs = whos('-regexp','.*dir$'); +for ii=1:numel(dirs) + dir_to_do = eval(dirs(ii).name); + if ~exist(dir_to_do,'dir') + fprintf('creating directory %s\n', dir_to_do); + system(sprintf('mkdir -p %s',dir_to_do)); + end +end +%% enter scan numbers of standards +%glassy carbon, glassy carbon moved detector to side, air scattering, first +%one is glassy carbon used to remove beamstop later + scannr = [14 15 14]; + %AgBE (for SAXS and WAXS), LaB6 (for WAXS), Si (for WAXS) + todo = [12 13 14]; + legendstr = {'AgBE';'LaB6';'Si'}; + +%% step 1: prepare the valid pixel mask +redo = 1; + +if (redo) + fprintf('preparing the valid pixel mask\n'); + +% calculating the union of several valid pixel masks +% starting with a rather dark file to discriminate hot pixels + system(sprintf('rm -f %s', maskfilename)); + +if (detno == 1 )||(detno == 2) + prepvalidmask_args = {}; + compilex12sa_args = {'DetectorNumber',detno,'FileExtension','cbf'}; + integrate_range_args = {'PilatusDetNo',detno,'FileExtension','cbf'}; +elseif detno == 3 + prepvalidmask_args = {'H5Location','/eh5/images/','FilenameMask','*'}; + compilex12sa_args = {'FileExtension','h5'}; +end + + for ii=scannr + beamline.prep_valid_mask(utils.compile_x12sa_filename(ii,-1, ... + 'BasePath',datadir,'BaseName',userID,compilex12sa_args{:}), ... + 'ThresholdDark',1, ... + 'ThresholdHot',20, ... + 'Extend','or', ... + 'FilenameValidMask',maskfilename,prepvalidmask_args{:}); + % 'FigNo',ii==scannr(end)); + end +end +%% step 2: cut out beam stop and shadows manually (for WAXS only necessary if there is a shadow) + +redo = 1; +if (redo) + scannr = scannr(1); + if (detno == 1)||(detno == 2) + compilex12sa_args = {'DetectorNumber',detno,'FileExtension','cbf'}; + imageshow_args = {}; + elseif (detno == 3) + compilex12sa_args = {'FileExtension','h5'}; + imageshow_args = {'H5Location','/eh5/images/'}; + end + % include the beamstop in the valid pixel mask - follow instructions in + % popup box + beamline.choose_beamstop_mask(utils.compile_x12sa_filename(scannr(1),0, 'BasePath',datadir,'BaseName',userID, compilex12sa_args{:}),... + 'ReadCoord',0,'SaveCoord',1, 'SaveData',1,'FilenameValidMask',maskfilename,'FilenameCoord',maskcoordfilename, 'ImageShowArgs', imageshow_args) + +end +%% show silver behenate scattering to find the radius of the first ring (only SAXS) +if (detno==1) +plotting.image_show(utils.compile_x12sa_filename(todo(1),0, ... + 'PointWildcard', 1, ... + 'SubExpWildcard', 1, ... + 'DetectorNumber',detno, ... + 'BasePath',datadir,'BaseName',userID), ... + 'IsFmask', true); +elseif (detno == 3) + filepath = utils.compile_x12sa_dirname(todo(1)); + D = dir(fullfile(datadir,filepath,'*.h5')); + plotting.image_show(fullfile(D(1).folder,D(1).name), ... + 'H5Location','/eh5/images/'); +end +%% here you have to give some manual inputs to run step 3 +% for SAXS you have to put y pixel value of the the silver behenate ring above the beamstop, and the order of the peak that you chose +if (detno==1)||(detno == 3) + order_AgBE = 1; + y_from = 509; + y_to = 514; + cen_guess = []; %[y,x] ; leave empty, i.e. cen_guess=[], for automatic guess; + %and choose how many sectors you want to do the integration (16 for + %anisotropic scattering, 1 for isotropic scattering + num_segments=16; +elseif (detno==2) + %for WAXS you can run with the default values to start with and adjust in + %case an error appears or the fit (shown in figure 4) is bad + + open('+beamline/WAXS_standards.fig'); + %give the order of the first silver behenate ring appearing + %(compare with WAXS_standards.fig) + order_AgBe=7; + + %parameter used in finding the x-position, default 5, if in figure 20 the + %blue curve is all zeros, lower this value (necessary for low intensity of + %silver behenate measurement + d = 5; + + %threshold to find WAXS peak of standards, default is 50, might be lowered + %for lower intensities + threshold=[2 50 100]; + %if wrong peaks are found tune finding the right peaks with the window + %where peaks are being searched here, default is min=0 and max=1500, + %(see WAXS_standards.fig) + min_AgBE=0; + max_AgBE=1500; + min_Si=0; + max_Si=1500; + min_LaB6=0; + max_LaB6=1500; + +end +% step 3: prepare integration mask +% For the WAXS mask this is still a bit clunky. You can adjust above the +% min and max values where it will look for a peak and the threshold. Also +% in the fit for the horizonal position make sure there is both red and +% blue peaks for the fitting, if not you can adjust the d parameter above. +% Decreasing it helps when the silver behenate scattering is low. + +if (detno==1) + scannr = todo(1); +else + %here enter the scannumbers of the standards + % todo = [211,208,212]; + % legendstr = {'AgBE';'LaB6';'Si'}; + scannr = todo(1); + S = io.spec_read(SpecDatFile,'ScanNr',todo(1)); +end + +if (detno==1)||(detno==2) + I = plotting.image_show(utils.compile_x12sa_filename(scannr,0, ... + 'PointWildcard', 1, ... + 'SubExpWildcard', 1, ... + 'DetectorNumber',detno, ... + 'BasePath',datadir,'BaseName',userID), ... + 'IsFmask', true); +elseif (detno == 3) + filepath = utils.compile_x12sa_dirname(scannr); + D = dir(fullfile(datadir,filepath,'*.h5')); + I = plotting.image_show(fullfile(D(1).folder,D(1).name), ... + 'H5Location','/eh5/images/'); +end + + +mask = getfield(load(maskfilename),'valid_mask'); +mask.frame = zeros(mask.framesize); +mask.frame(mask.indices) = 1; + +I = mean(I.data,3).*mask.frame; +if (detno==1)||(detno == 3) + J = ifftn(fftn(I,size(I)*2-[1 1]).^2); + if isempty(cen_guess) + cen_guess = math.peakfit2d(J)/2; %[y,x] + end + + if (detno == 1) + filename_center = utils.compile_x12sa_filename(scannr(1),0, 'BasePath',datadir,'BaseName',userID); + imageshow_args = {}; + elseif (detno == 3) + filename_center = fullfile(D(1).folder,D(1).name); + imageshow_args = {'H5Location','/eh5/images/'}; + end + + [cen]=utils.get_beam_center(filename_center,'GuessX',cen_guess(2),'GuessY',cen_guess(1), ... + 'RadiusFrom',y_from-cen_guess(1),'RadiusTo',y_to-cen_guess(1), ... + 'TestX',4,'TestY',4,'FilenameValidMask',maskfilename, imageshow_args{:}); + +else + % this isn't nice yet + % i) it depends on the chosen orientation on how to read + % detector-2 images + % ii) it merely finds maximum values instead of fitting, possibly + % with sub-pixel precision + % iii) as a consequence, figuring out which values are trustworthy + % is done rather crudly + %d = 3; %5 seams not to work if intensity of silver behenate is too low?? + if (detno == 2) + imageshow_args = {}; + end + dx = 30; + [s1,s2] = size(I); + + J = ifft(fft(I,s1*2-1,1).^2,[],1); + [~,n] = max(J); + w = std(I,1,1)./sqrt(mean(I,1)); + o = 1:numel(n); + + o = o(w>d); + n = n(w>d)/2; + + o = o(abs(n-s1/2)0), ... + log(I(round(cen1),mask.frame(round(cen1),:)>0))/ ... + max(log(I(round(cen1),mask.frame(round(cen1),:)>0)))*s1, ... + 'k') + hold off + + figure(30) + WAXS = zeros(s2,numel(todo)); + WAXS(:,1) = I(round(cen1),:); + for ii=2:numel(todo) + I = io.image_read(utils.compile_x12sa_filename(todo(ii),0, ... + 'PointWildcard', 1, ... + 'SubExpWildcard', 1, ... + 'DetectorNumber',detno, ... + 'BasePath',datadir,'BaseName',userID), ... + 'IsFmask', 1); + WAXS(:,ii) = mean(I.data(round(cen1),:,:),3); + end + + h = semilogy(WAXS); + legend(legendstr) + + % finding peaks "automatically" + x_coord = []; + q_coord = []; + hold on + peaks = cell(1,size(WAXS,2)); + for ii=1:size(WAXS,2) + %the treshhold value, default set to 50, might be adjusted + peaks{ii} = utils.peakfinder((WAXS(:,ii)),threshold(ii)); + %peaks{ii} = peakfinder((WAXS(:,ii)),50); + if strcmp(legendstr{ii},'AgBE') + tmp = peaks{ii}; + tmp = tmp(tmp>=min_AgBE); + peaks{ii} = tmp(tmp<=max_AgBE); + + end + if strcmp(legendstr{ii},'Si') + tmp = peaks{ii}; + tmp = tmp(tmp>=min_Si); + peaks{ii} = tmp(tmp<=max_Si); + end + if strcmp(legendstr{ii},'LaB6') + tmp = peaks{ii}; + tmp = tmp(tmp<=max_LaB6); + peaks{ii} = tmp(tmp>=min_LaB6); + + end + + + x_coord = vertcat(x_coord,peaks{ii}); + if strcmp(legendstr{ii},'AgBE') + q0 = 2*pi/58.38; + q_coord = horzcat(q_coord,q0*(order_AgBe+(0:numel(peaks{ii})-1))); + elseif strcmp(legendstr{ii},'LaB6') + q0 = 2*pi/4.1549; + q_coord = horzcat(q_coord,q0*sqrt((1:numel(peaks{ii})))); + elseif strcmp(legendstr{ii},'Si') + q0 = 2*pi/5.4308; + q_coord = horzcat(q_coord,q0*sqrt(3)); + end + semilogy(peaks{ii},WAXS(peaks{ii},ii),'.', ... + 'Color',get(h(ii),'Color'), ... + 'MarkerSize',24) + end + hold off + + figure(40); clf + if (numel(x_coord)>3) + % fprintf('%f\t%f\n',[x_coord';q_coord]) + % % a + % % b + % % c + s = fitoptions('Method','NonlinearLeastSquares',... + 'Lower' ,[-Inf,-Inf, 0],... + 'Upper' ,[ Inf, 0,1e3],... + 'Startpoint',[s2/2, 200,550]); + f = fittype('4*pi/l*sin((atan((a-b)*p/c)+atan((x-a)*p/c))/2)', ... + 'problem',{'p','l'},'options',s); + [c,~] = fit(x_coord,q_coord',f,'problem',{.172,12.398/S.mokev}); + subplot(2,1,1) + plot(x_coord,q_coord,'x'); + hold on + drawnow; + tmp = axis; + x = linspace(c.b,tmp(2)); + plot(x,feval(c,x),'r'); + subplot(2,1,2) + bar(x_coord,feval(c,x_coord)-q_coord'); + xlim(tmp(1:2)); + dc = confint(c); + dc = (dc(2,:)-dc(1,:))/2; + fprintf(['detector distance:\t%.1fmm, \t%.1fmm\n', ... + 'center of rings: \t%.1fpixels,\t%.1fpixels\n', ... + 'angle of detector:\t%.1fdeg, \t%.1fdeg.\n'], ... + c.c,dc(3), ... + c.b,dc(2), ... + atan((c.a-c.b)*c.p/c.c)/pi*180, ... + 180/pi*c.p/c.c*sqrt(dc(1)^2+dc(2)^2 + ((c.a-c.b)/c.c*dc(3))^2)); + end +end + + +tic +if (detno==1)||(detno==3) + S = io.spec_read(SpecDatFile,'ScanNr',todo(1)); + fprintf('preparing the integration mask(s)\n'); + beamline.prep_integ_masks(utils.compile_x12sa_filename(todo(1),0, ... + 'BasePath',datadir,'BaseName',userID, compilex12sa_args{:}), ... + cen, ... + 'DetNo',detno, ... + 'NoOfSegments',num_segments, ... + 'FilenameValidMask',maskfilename, ... + 'FilenameIntegMasks',integmaskfilename, imageshow_args{:}); + + beamline.integrate_range(todo(1),todo(1),1, ... % change for not re-running on already integrated files + 'OutdirData',integdir, ... + 'BasePath',datadir,'BaseName',userID, ... + 'FilenameIntegMasks',integmaskfilename, ... + compilex12sa_args{:},imageshow_args{:}); + +elseif (detno==2) + S = io.spec_read(SpecDatFile,'ScanNr',todo(1)); + fprintf('preparing the integration mask(s)\n'); + beamline.prep_integ_masks(utils.compile_x12sa_filename(todo(1),0, ... + 'DetectorNumber',detno, ... + 'BasePath',datadir,'BaseName',userID), ... + [c.b cen1], ... + 'DetNo',detno, ... + 'Wavelength_nm', 12.398/S.mokev, ... + 'NormalXY', [c.a cen1], ... + 'DetDist_mm', c.c, ... + 'PixelSize_mm', .172, ... + 'NoOfSegments',1, ... + 'FilenameValidMask',maskfilename, ... + 'FilenameIntegMasks',integmaskfilename, ... + 'DisplayValidMask',0); +end +toc + + +%% calculate detector distance (SAXS only) check in Figure 100 if the peak_agbe really is the 1st order AgBE +if (detno==1)||(detno==3) + [x,y] = plotting.plot_radial_integ(sprintf('%s%s%d_%05d_00000_00000_integ.mat',integdir,userID,1,todo(1))); + %%the 1st order silver behenate is at ... pixels + %peakfinder(log(y(10:end)),1); + peaks2 = utils.peakfinder(log(y(10:end)),1); + peak_agbe = x(peaks2(order_AgBE+1))+9 %normally the 1st order AgBE, check! + wavelength = 12.398/S.mokev; + detector_distance = peak_agbe*.172/tan(2*asin(wavelength*order_AgBE/(2*58.38))) +end +%% redo SAXS integration mask now it will take the detector distance into account and also save the q-value +if (detno==1)||(detno==3) + if (detno == 1) + detector_pixelsize = 0.172; + elseif (detno == 3) + detector_pixelsize = 0.075; + end + S = io.spec_read(SpecDatFile,'ScanNr',todo(1)); + fprintf('preparing the integration mask(s)\n'); + beamline.prep_integ_masks(utils.compile_x12sa_filename(todo(1),0, ... + 'BasePath',datadir,'BaseName',userID,compilex12sa_args{:}), ... + cen, ... + 'DetNo',detno, ... + 'NoOfSegments',num_segments, ... + 'Wavelength_nm', 12.398/S.mokev, ... + 'DetDist_mm', detector_distance, ... + 'PixelSize_mm', detector_pixelsize, ... + 'FilenameValidMask',maskfilename, ... + 'FilenameIntegMasks',integmaskfilename, imageshow_args{:}); +end +%% step 5: radial integration & averaging of files -- +%start here again if you merely want to integreat +%for fast measurements (i.e. scanning SAXS) start on several cn parallel +%adjust therefor integrate_range(scan_no_from,scan_no_to,scan_no_step) +%and rund only step 0 and step 5 +save_format = '-v6'; + +close all +% beamline.integrate_range(107,1e8,3, ... % change for not re-running on already integrated files +% 'PilatusDetNo',detno, ... +% 'OutdirData',integdir, ... +% 'BasePath',datadir,'BaseName',userID, ... +% 'FilenameIntegMasks',integmaskfilename, 'SaveFormat', save_format); + +beamline.integrate_range(136,137,1, ... % change for not re-running on already integrated files + 'OutdirData',integdir, ... + 'BasePath',datadir,'BaseName',userID, ... + 'FilenameIntegMasks',integmaskfilename, 'SaveFormat', save_format, ... + integrate_range_args{:},imageshow_args{:}); + + +%% or alternatively when computers node are ready and matlab is open +save_format = '-v6'; +fprintf('beamline.integrate_range(107,1e8,4,''OutdirData'',''%s'',''BasePath'',''%s'',''BaseName'',''%s'',''FilenameIntegMasks'',''%s'',''SaveFormat'', ''%s''',integdir,datadir,userID,integmaskfilename,save_format) +args={'OutdirData', integdir,'BasePath',datadir ,'BaseName',userID ,'FilenameIntegMasks',integmaskfilename ,'SaveFormat',save_format }; + +for ii = 1:2:numel(integrate_range_args) + if ischar(integrate_range_args{ii+1}) + straux = '''%s'''; + elseif isnumeric(integrate_range_args{ii+1}) + straux = '%d'; + end + fprintf( [',''%s'',' straux ' '] ,integrate_range_args{ii},integrate_range_args{ii+1}); + args=[args,integrate_range_args{ii},integrate_range_args{ii+1}]; +end +for ii = 1:2:numel(imageshow_args) + if ischar(imageshow_args{ii+1}) + straux = '''%s'''; + elseif isnumeric(imageshow_args{ii+1}) + straux = '%d'; + end + fprintf([',''%s'',' straux ' '],imageshow_args{ii},imageshow_args{ii+1}); + args=[args,imageshow_args{ii},imageshow_args{ii+1}]; +end + +% if detno==2 % Disable CReader for WAXS detector since currently it's not supported. +% fprintf([',''CReader'',0 ']); +% args=[args,'CReader',0]; +% end + +fprintf(');\n') + +folder_todo=utils.abspath('~/Data10/analysis/radial_integration_todo/'); +if ~exist(folder_todo) + mkdir(folder_todo); +end + +save(sprintf([folder_todo 'vargin_det%d.mat'],detno),'args'); +fprintf(['Parameters saved to' folder_todo 'vargin_det%d.mat\n'],detno); +%% +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. diff --git a/+beamline/read_omny_angles.m b/+beamline/read_omny_angles.m new file mode 100644 index 0000000..56d2e7a --- /dev/null +++ b/+beamline/read_omny_angles.m @@ -0,0 +1,130 @@ +% read_omny_angles( OMNY_angles_file, scannums, tomo_id ) +% OMNY_angles_file - File with Scan number, angle target, angle readout +% scannums - Array of scan numbers +% tomo_id - integer or list of integers, only if the scannums is empty +% +% out - Contains fields with scan, target_angle, readout_angle +% errorflag - = 1 if at least one scan was not found + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ out errorflag ] = read_omny_angles( OMNY_angles_file, scannums, tomo_id ) + +if ~exist(OMNY_angles_file, 'file') + error('Missing OMNY file: %s', OMNY_angles_file) +end + +if ~exist('tomo_id') + tomo_id = []; +end + +if (~isempty(scannums))&&(~isempty(tomo_id)) + error('You have provided both scannums and tomo_id, please provide just either scannums OR tomo_id. One of them should be empty ( =[] ).') +end +if (isempty(scannums))&&(isempty(tomo_id)) + error('You have not provided scannums or tomo_id, please provide either scannums OR tomo_id. One of them should be empty ( =[] ).') +end +fid = fopen(OMNY_angles_file); + +% check omny file type +ln = fgetl(fid); +switch numel(strsplit(ln, ' ')) + case {3,6} + outmat = textscan(fid,'%f %f %f %f %f %s'); + fclose(fid); + out = []; + errorflag = 0; + counter = 1; + + for ii = 1:numel(scannums) + ind = find(outmat{1}==scannums(ii),1,'last'); + if isempty(ind) + fprintf('Did not find Scan %d in %s\n',scannums(ii),OMNY_angles_file); + errorflag = 1; + else + out.scan(counter) = outmat{1}(ind); + out.target_angle(counter) = outmat{2}(ind); + out.readout_angle(counter) = outmat{3}(ind); + out.subtomo_num(counter) = outmat{4}(ind); + out.detpos_num(counter) = outmat{5}(ind); + out.sample_name(counter) = outmat{6}(ind); + counter = counter+1; + end + end + case 7 + outmat = textscan(fid,'%f %f %f %f %f %f %s'); + fclose(fid); + out = []; + errorflag = 0; + counter = 1; + if ~isempty(scannums) + for ii = 1:numel(scannums) + ind = find(outmat{1}==scannums(ii),1,'last'); + if isempty(ind) + fprintf('Did not find Scan %d in %s\n',scannums(ii),OMNY_angles_file); + errorflag = 1; + else + out.scan(counter) = outmat{1}(ind); + out.target_angle(counter) = outmat{2}(ind); + out.readout_angle(counter) = outmat{3}(ind); + out.tomo_id(counter) = outmat{4}(ind); + out.subtomo_num(counter) = outmat{5}(ind); + out.detpos_num(counter) = outmat{6}(ind); + out.sample_name(counter) = outmat{7}(ind); + counter = counter+1; + end + end + elseif ~isempty(tomo_id) + ind = find(ismember(outmat{4},tomo_id)); + if isempty(ind) + fprintf(['Did not find tomo_id ',repmat('%i ',1,length(tomo_id)),' in %s\n'],tomo_id,OMNY_angles_file); + errorflag = 1; + end + out.scan = outmat{1}(ind); + out.target_angle = outmat{2}(ind); + out.readout_angle = outmat{3}(ind); + out.tomo_id = outmat{4}(ind); + out.subtomo_num = outmat{5}(ind); + out.detpos_num = outmat{6}(ind); + out.sample_name = outmat{7}(ind); + end + otherwise + error('Unknown OMNY file format.') +end + + +return +end + diff --git a/+beamline/read_omny_pos.m b/+beamline/read_omny_pos.m new file mode 100644 index 0000000..2208ebf --- /dev/null +++ b/+beamline/read_omny_pos.m @@ -0,0 +1,52 @@ +% Read interferometer positions written by Orchestra +% Input is the filename with path +% Output is a structure containing fields: +% The two values of the one line header originally 'Scan' and 'Samroy' +% Values for each point of 10 expected columns of numbers +% 12 June 2013 +% June6 2015 - Changed in order to accept an arbitrary number +% of values in order to be compatible with 10 columns for flOMNI and 19 for +% OMNY +% This function should be replaced by beamline.read_position_file in the +% ptycho codes and deprecated. + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function struct_out = read_omny_pos( omnyposfile ) +%disp(omnyposfile) +struct_out = beamline.read_position_file( omnyposfile ); +%disp(size(struct_out.TotalPoints)) +end + diff --git a/+beamline/read_position_file.m b/+beamline/read_position_file.m new file mode 100644 index 0000000..9b13730 --- /dev/null +++ b/+beamline/read_position_file.m @@ -0,0 +1,72 @@ +% READ_POSITION_FILE Read positions from a file, the format and header are +% compatible with multiple interferometer positions and standard deviations +% as written by Orchestra and the sgalil spec macro. +% +% struct_out = read_position_file( posfile ) +% Inputs: +% **posfile filename with path +% *returns* +% ++struct_out is a structure containing fields including the values +% for header and for each scanning point + +% 12 June 2013 +% June6 2015 - Changed in order to accept an arbitrary number +% of values in order to be compatible with 10 columns for flOMNI and 19 for +% OMNY +% 15 Apr 2019 - Changed name and generalized description beyond OMNY and +% Orchestra + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function struct_out = read_position_file( posfile ) + +assert(exist(posfile, 'file')>0, ['Position file ', posfile, ' not found']) + +f = fopen(posfile,'r'); +header = textscan(f,'%s %d, %s %f',1); +struct_out.(header{1}{1}) = header{2}; +struct_out.(header{3}{1}) = header{4}; +names = textscan(f,'%s',1,'Delimiter','\r'); +names = strsplit(char(names{1})); +reading_string = ['%f', repmat(' %f',1,numel(names)-1)]; +values = textscan(f,reading_string); +fclose(f); + +for ii = 1:numel(names) + struct_out.(char(names(ii))) = values{ii}; +end + +end + diff --git a/+beamline/stxm_online.m b/+beamline/stxm_online.m new file mode 100644 index 0000000..bf14f32 --- /dev/null +++ b/+beamline/stxm_online.m @@ -0,0 +1,803 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: stxm_online.m,v $ +% +% $Revision: 1.16 $ $Date: 2011/04/04 17:03:48 $ +% $Author: $ +% $Tag: $ +% +% Description: +% plot a STXM scan +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read +% +% history: +% +% April 4th 2011: +% do not normalize the dark field since this is problematic for SAXS with a +% beam stop +% +% September 29th 2010: +% include changes by Martin Dierolf and Joan Vila in the standard version +% of stxm_online +% +% December 10th 2008: +% add bug-fixes and suggestions from Martin Dierolf: +% DirPerLine parameter could not be set via the command line, +% BurstMode flag was always active, is now coupled to dir_per_line, +% new Parameter ZeroOrderR +% +% September 5th 2008: +% use compile_x12sa_filename, +% plot as 2x2 sub figures +% +% June 14th 2008: 1st documented version based on work + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [varargout] = stxm_online(first_scan_number, Ny, varargin) +import beamline.pilatus_valid_pixel_roi +import io.image_read +import plotting.image_show +import utils.compile_x12sa_filename +import utils.find_files + +% set default values +% Pilatus 2M +detector_number = 1; +% single directory or directory per line format +dir_per_line = 1; +% figure number for display +fig_no = 2; +% number of points along a scan line, 0 for automatic determination from +% the first line +Nx = 0; +% size of the regio of interest +roi_dim = 128; +% automatic determination of the center position +cen_x = 0; +cen_y = 0; +% dark field integration starting radius +dark_field_r = 20; +% radius of excluded area around center +zero_order_r = 0; +% calculate the first moment rather than a Fourier transform to get the +% differential phase contrast +first_moment = 1; +% use additionally differentiation of the integrated phase +integrated_phase = 1; +% do not update the plot every line to save some time +update_interval = 3; +% save resulting figure +figure_dir = '~/Data10/analysis/online/stxm/figures/'; +% save the resulting data +data_dir = '~/Data10/analysis/online/stxm/data/'; +% valid pixel mask +filename_valid_mask = '~/Data10/analysis/data/pilatus_valid_mask.mat'; + +phase = []; +gx = []; +gy = []; + +full_screen_position_integrated_phase = [ 5 525 1201 420]; +print_a4_position_integrated_phase = [ 5 525 743 420 ]; +full_screen_position_standard = [ 5 109 1201 836]; +print_a4_position_standard = [ 5 109 743 836 ]; + +% check minimum number of input arguments +if (nargin < 2) + fprintf('Usage:\n') + fprintf('[trans,dpcx,dpcy,df]=%s(<(first) scan number>, [[,,] ...]);\n',... + mfilename); + fprintf('The optional , pairs are:\n'); + fprintf('''DetectorNumber'',<1-Pilatus 2M, 2-Pilatus 300k, 3-Pilatus 100k>\n'); + fprintf('''Nx'', default is %d (0 means automatic determination from first scan line)\n',Nx); + fprintf('''ROIdim'', region of interest used for data analysis, default is %d\n',roi_dim); + fprintf('''CenX'', 0 means automatic determination, default is %d\n',cen_x); + fprintf('''CenY'', 0 means automatic determination, default is %d\n',cen_y); + fprintf('''DarkFieldR'', dark field integration starts at this radius, default is %.0f\n',dark_field_r); + fprintf('''FigNo'', figure number for data display, default is %d\n',fig_no); + fprintf('''DirPerLine'',<0-no,1-yes> separate directory for each scan line, default is %d\n',dir_per_line); + fprintf('''ZeroOrderR'', pixel values inside this radius are set to zero, default is %d\n', zero_order_r); + fprintf('''FirstMoment'',<0-no,1-yes> calculate the first moment rather than a Fourier transform to get the differential phase contrast, default is %d\n',first_moment); + fprintf('''IntegratedPhase'',<0-no,1-yes> differentiate additionally the sum signal and re-differentiate it, default is %d\n',integrated_phase); + fprintf('''UpdateInterval'', update the plot each Nth line, default is %d\n',update_interval); + fprintf('''FigureDir'',''directory'' save the resulting plot in eps, jpeg and Matlab fig format, '''' for no saving, default is %s\n',figure_dir); + fprintf('''DataDir'',''directory'' save the resulting data as Matlab file, '''' for no saving, default is %s\n',data_dir); + fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices ind_valid, [] for no valid pixel mask,\n'); + fprintf(' default is %s\n',filename_valid_mask); + fprintf('Additional , pairs recognized by compile_x12sa_filename and by image_read can be specified. Please call them for an overview\n'); + fprintf('\n'); + error('At least the (first) scan number and the number of scan lines have to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'DetectorNumber' + detector_number = value; + case 'Nx' + Nx = value; + case 'ROIdim' + roi_dim = value; + case 'CenX' + cen_x = value; + case 'CenY' + cen_y = value; + case 'DarkFieldR' + dark_field_r = value; + case 'FigNo' + fig_no = value; + case 'DirPerLine' + dir_per_line = value; + case 'ZeroOrderR' + zero_order_r = value; + case 'FirstMoment' + first_moment = value; + case 'IntegratedPhase' + integrated_phase = value; + case 'UpdateInterval' + update_interval = value; + case 'FilenameValidMask' + filename_valid_mask = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + +% pass some parameters to image_show +vararg(11:(end+10)) = vararg; +vararg{ 1} = 'RetryReadSleep'; +vararg{ 2} = 5.0; +vararg{ 3} = 'RetryReadMax'; +vararg{ 4} = 5; +vararg{ 5} = 'ErrorIfNotFound'; +vararg{ 6} = 0; +% vararg{ 7} = 'BurstMode'; +% if (dir_per_line) +% vararg{ 8} = 1; +% else +% vararg{ 8} = 0; +% end +vararg{7} = 'UnhandledParError'; +vararg{8} = 0; +vararg{9} = 'DetectorNumber'; +vararg{10} = detector_number; + +% region of interest index in each dimension +roi_rel_ind = -round(0.5*roi_dim):(round(0.5*roi_dim)-1); + +% load the indices of valid pixels +if ((~isempty(filename_valid_mask)) && (exist(filename_valid_mask,'file'))) + fprintf('loading the valid pixel mask %s\n',filename_valid_mask); + load(filename_valid_mask); +end + +% wait for the data to be available +scan_no_check = first_scan_number; +if ((dir_per_line) && (Ny > 1)) + scan_no_check = scan_no_check +1; +end +filename_mask = compile_x12sa_filename(scan_no_check,0,'DetectorNumber',detector_number); +[~, fnames] = find_files(filename_mask); +data_available = (~isempty(fnames)); +if (~data_available) + fprintf('Waiting for %s to become available.\n',filename_mask); + while (~data_available); + pause(1); + [~, fnames] = find_files(filename_mask); + data_available = (~isempty(fnames)); + end +end + +% check that number of points per line determination will be possible + +% determine number of points per line +if (Nx <= 0) + if (~dir_per_line) + error('The number of points per line can only automatically be determined if separate scan directories are used for each line.'); + end + vararg_remain = vararg; + vararg_remain(3:(end+2)) = vararg_remain; + vararg_remain{1} = 'SubExpWildcard'; + vararg_remain{2} = 1; + [fmask,vararg_remain] = ... + compile_x12sa_filename(first_scan_number,0,vararg_remain); %#ok + Nx = length(dir(fmask)); + if (Nx < 1) + error('No matching files found for %s',fmask); + end +end +fprintf('%d lines with %d points per line in\n',Ny,Nx); + + +if (integrated_phase) + figure(fig_no +1); + hold off; + clf; + % print as layed out on the screen, i.e., preserve aspect ratio + set(gcf,'PaperPositionMode','auto'); + % paper size + set(gcf,'PaperType','A4'); + % background color + set(gcf,'Color','white'); + % resize and position + set(gcf,'Position',full_screen_position_integrated_phase); + + colormap(bone(256)); +end + + +figure(fig_no); +hold off; +clf; +% print as layed out on the screen, i.e., preserve aspect ratio +set(gcf,'PaperPositionMode','auto'); +% paper size +set(gcf,'PaperType','A4'); +% background color +set(gcf,'Color','white'); +% resize and position +set(gcf,'Position',full_screen_position_standard); + +colormap(bone(256)); + + +% STXM display loop +point_no = 0; +scan_number = first_scan_number; + +frame = []; +for ii=Ny:-1:1 + sub_exp_no = 0; + for jj=Nx:-1:1 + if (dir_per_line) + vararg_remain = vararg; + vararg_remain(3:(end+2)) = vararg_remain; + vararg_remain{1} = 'SubExpNo'; + vararg_remain{2} = sub_exp_no; + [filename,vararg_remain] = ... + compile_x12sa_filename(scan_number,0,vararg_remain); + else + [filename,vararg_remain] = ... + compile_x12sa_filename(scan_number,point_no,vararg); + end + last_frame = frame; + [frame,vararg_remain] = image_read(filename,vararg_remain); + if (isempty(frame.data)) + fprintf('%s not found, repeating the previous frame\n',filename); + frame = last_frame; + end + if (~isempty(vararg_remain)) + vararg_remain + error('There are unhandled parameters.'); + end + + if (point_no == 0) + trans = zeros(Ny,Nx); + dpcx = trans; + dpcy = trans; + df = trans; + + if ((cen_x <= 0) || (cen_y <= 0)) + [cx, cy] = find_center(frame.data); + fprintf('Beam cemter guess (x,y) = (%d,%d)\n',cx,cy); + if (cen_x <= 0) + cen_x = cx; + end + if (cen_y <= 0) + cen_y = cy; + end + end + + roi_x_ind = cen_x + roi_rel_ind; + if ((roi_x_ind(1) < 1) || (roi_x_ind(end) > size(frame.data,2))) + error('Region of interest out of range in x\n'); + end + roi_y_ind = cen_y + roi_rel_ind; + if ((roi_y_ind(1) < 1) || (roi_y_ind(end) > size(frame.data,1))) + error('Region of interest out of range in y\n'); + end + + [yy,xx] = meshgrid(roi_rel_ind,roi_rel_ind); + [~, rho] = cart2pol(xx,yy); + + ind_df = find((rho > dark_field_r) & (rho < roi_rel_ind(end))); + + if (~isempty(filename_valid_mask)) + % in case of less than full detector readout cut out the right part of + % the valid pixel mask + valid_mask = pilatus_valid_pixel_roi(valid_mask,'RoiSize',size(frame.data)); + else + % if the valid pixel mask is not used specify all pixels to + % be valid + valid_mask.indices = 1:(size(frame.data,1)*size(frame.data,2)); + end + + % calculate the indices of the valid and invalid pixels within + % the region of interest + frame_valid = zeros(size(frame.data)); + frame_valid(valid_mask.indices) = 1; + frame_valid = frame_valid(roi_y_ind,roi_x_ind); + ind_invalid = find(frame_valid == 0); +% ind_valid = find(frame_valid ~= 0); + ind_df = setdiff(ind_df,ind_invalid); + end + + % cut out the region of interest + frame_roi = frame.data(roi_y_ind,roi_x_ind); + frame_roi(ind_invalid) = 0; + + % set central part of detector frame to zero, if specified + if (zero_order_r> 0) + frame_roi(rho 1) && (Ny > 1) + if ((ii == Ny) || (rem(ii,update_interval) == 1) || (ii == 1)) + if(gcf ~= fig_no) + figure(fig_no); + end + iv = 2; + ih = 2; + colormap(bone(256)); + + subplot(iv,ih,1); + imagesc(trans); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(min(trans(trans ~= 0))); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(max(trans(trans ~= 0))); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title_str = [ 'transmission #' num2str(first_scan_number,'%d') ]; + if (dir_per_line) + title_str = [ title_str '-' num2str(first_scan_number+Ny-1,'%d') ]; %#ok + end + title_str = sprintf('%s (detector %d)',title_str,detector_number); + title(title_str); + + subplot(iv,ih,2); + imagesc(df); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(min(df(df ~= 0))); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(max(df(df~=0))); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title('dark field'); + + subplot(iv,ih,3); + imagesc(dpcx); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(min(dpcx(dpcx ~= 0))); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(max(dpcx(dpcx~=0))); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title('DPC x'); + + subplot(iv,ih,4); + imagesc(dpcy); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(min(dpcy(dpcy ~= 0))); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(max(dpcy(dpcy~=0))); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title('DPC y'); + + drawnow; + end + end + + if (dir_per_line) + scan_number = scan_number +1; + end +end + +% store return arguments +if nargout > 0 + varargout{1} = trans; +end +if nargout > 1 + varargout{2} = dpcx; +end +if nargout > 2 + varargout{3} = dpcy; +end +if nargout > 3 + varargout{4} = df; +end + +if nargout > 4 + varargout{5} = phase; +end + +if nargout > 5 + varargout{6} = gx; +end + +if nargout > 6 + varargout{7} = gy; +end + +if (integrated_phase) + % calculate the integrated phase from the differential phase contrast + % in horizontal and vertical direction + phase = phase_from_dpc(dpcx,dpcy, 'fourier'); + + % calculate the 1D differential phase contrast from the integrated + % phase + [gx, gy] = gradient(phase); + + figure(fig_no +1); + iv = 1; + ih = 3; + colormap(bone(256)); + + subplot(iv,ih,1); + imagesc(phase); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(phase(phase ~= 0)); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(phase(phase~=0)); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title_str = [ 'integrated phase #' num2str(first_scan_number,'%d') ]; + if (dir_per_line) + title_str = [ title_str '-' num2str(first_scan_number+Ny-1,'%d') ]; + end + title_str = sprintf('%s (detector %d)',title_str,detector_number); + title(title_str); + + subplot(iv,ih,2); + imagesc(gx); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(gx(gx ~= 0)); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(gx(gx ~= 0)); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title('DPC x from integrated phase'); + + subplot(iv,ih,3); + imagesc(gy); + axis xy; axis equal; axis tight; + colorbar; + axis_min = min(gy(gy ~= 0)); + if (isnan(axis_min)) + axis_min = 0; + end + axis_max = max(gy(gy ~= 0)); + if (isnan(axis_max)) + axis_max = 0; + end + caxis([(axis_min-.0001) (axis_max+.0001)]); + title('DPC y from integrated phase'); + + drawnow; + +end + + +% file name for saving +filename = sprintf('stxm_scans_%d_%05d-%05d',detector_number,... + first_scan_number,first_scan_number+Ny-1); + + +% save figures +if (~isempty(figure_dir)) + figure(fig_no); + + % create output directories and write the plot in different formats + if (~exist(figure_dir,'dir')) + mkdir(figure_dir) + end + if ((figure_dir(end) ~= '/') && (figure_dir(end) ~= '\')) + figure_dir = [ figure_dir '/' ]; + end + fprintf('output directory for figures is %s\n',figure_dir); + + % resize to a smaller width as print layout + set(gcf,'Position',print_a4_position_standard); + + subdir = [ figure_dir 'jpg/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.jpg\n',filename); + print('-djpeg','-r300',[subdir filename '.jpg'] ); + + subdir = [ figure_dir 'eps/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.eps\n',filename); + print('-depsc','-r1200',[subdir filename '.eps'] ); + + % resize to full screen + set(gcf,'Position',full_screen_position_standard); + + subdir = [ figure_dir 'fig/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.fig\n',filename); + hgsave([subdir filename '.fig']); + + if (integrated_phase) + figure(fig_no +1); + + subdir = [ figure_dir 'jpg/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + + % resize to a smaller width as print layout + set(gcf,'Position',print_a4_position_integrated_phase); + + fprintf('saving %s_integrated_phase.jpg\n',filename); + print('-djpeg','-r300',[subdir filename '_integrated_phase.jpg'] ); + + subdir = [ figure_dir 'eps/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s_integrated_phase.eps\n',filename); + print('-depsc','-r1200',[subdir filename '_integrated_phase.eps'] ); + + % resize to a smaller width as print layout + set(gcf,'Position',full_screen_position_integrated_phase); + + subdir = [ figure_dir 'fig/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s_integrated_phase.fig\n',filename); + hgsave([subdir filename '_integrated_phase.fig']); + end +end + + +% save resulting data +if (~isempty(data_dir)) + if ((data_dir(end) ~= '/') && (data_dir(end) ~= '\')) + data_dir = [ data_dir '/' ]; + end + + % create output directory + if (~exist(data_dir,'dir')) + mkdir(data_dir) + end + + % save data + fprintf('saving %s.mat\n',[data_dir filename]); + if (integrated_phase) + save([data_dir filename],'trans','dpcx','dpcy','df', 'phase', 'gx', 'gy'); + else + save([data_dir filename],'trans','dpcx','dpcy','df'); + end +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [cx, cy] = find_center(f) + +f = medfilt2(f,[5 5]); + +[~, cx] = max(sum(f,1)); +[~, cy] = max(sum(f,2)); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [tr, px, py] = stxm_pt(a) + + persistent c1 c2 s1 s2 sz + + if (isempty(sz)) || (any(sz ~= size(a))) + sz = size(a); + c1 = -cos(2*pi*(0:sz(1)-1)/sz(1)); + s1 = sin(2*pi*(0:sz(1)-1)/sz(1)); + c2 = -cos(2*pi*(0:sz(2)-1)/sz(2)); + s2 = sin(2*pi*(0:sz(2)-1)/sz(2)); + end + + a1 = sum(a,1); + a2 = sum(a,2)'; + + tr = sum(a1); + px = atan2(sum(a1.*c1), sum(a1.*s1)); + py = atan2(sum(a2.*c2), sum(a2.*s2)); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [tr, px, py ] = stxm_pt2(a) + + persistent x y sz + + if (isempty(sz)) || (any(sz ~= size(a))) + sz = size(a); + % masking out the invalid pixels is done by setting the + % corresponding intensities to zero before calling this function + [y,x] = ndgrid((0:sz(1)-1)-sz(1)/2, (0:sz(1)-1)-sz(1)/2); +% x2 = x.^2; + end + + tr = sum(sum(a)); + px = sum(sum(a.*x))/tr; + py = sum(sum(a.*y))/tr; +% p2 = (sum(a1.*x2)/tr + sum(a2.*x2)/tr - px^2 - py^2); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function p = phase_from_dpc(dpcx,dpcy,varargin) +% +% Integrates the phase from a combination of x and y gradients. +% phase_from_dpc(dpcx,dpcy,'fourier') uses the Fourier method (default), +% phase_from_dpc(dpcx,dpcy,'finitdiff') uses a finite difference method. + +if nargin > 2 + method = varargin{1}; +else + %method = 'fourier'; + method = 'finitediff'; +end + +px = -dpcy; +py = -dpcx; + +sz = size(px); + +switch lower(method) + case 'fourier' + f = zeros(2*sz); + f(1:sz(1),1:sz(2)) = px + 1i*py; + f(1:sz(1),sz(2)+1:end) = fliplr(px + 1i*py); + f(sz(1)+1:end,1:sz(2)) = flipud(px + 1i*py); + f(sz(1)+1:end,sz(2)+1:end) = rot90(px + 1i*py,2); + [x1,x2] = ndgrid(-sz(1):(sz(1)-1),-sz(2):(sz(2)-1)); + q1 = pi*fftshift(x1)/sz(1); + q2 = pi*fftshift(x2)/sz(2); + qc = q2 - 1i*q1; + inv_qc = 1./qc; + inv_qc(1,1) = 0; + nf = ifftn(fftn(f).*inv_qc); + p = real(nf(1:sz(1),1:sz(2))); + case 'finitediff' + ggx = pgradient(dpcx); + [~, ggy] = pgradient(dpcy); + f = .25*(ggx + ggy); + ta = zeros(sz); + for i = 1:10000 + ta = ta + (pdel2(ta) - f); + + % Zero boundary conditions + %ta(1,:) = 0; + %ta(:,1) = 0; + %ta(end,:) = 0; + %ta(:,end) = 0; + + % Zero normal gradient boundary condition + ta(1,:) = ta(2,:); + ta(:,1) = ta(:,2); + ta(end,:) = ta(end-1,:); + ta(:,end) = ta(:,end-1); + + if mod(i,1000)==0 + figure(1); imagesc(real(ta)); colormap(bone(256)); colorbar; drawnow; + end + + p = ta; + end +end + diff --git a/+beamline/template_stxm_online_cont_dmesh.m b/+beamline/template_stxm_online_cont_dmesh.m new file mode 100644 index 0000000..6695d74 --- /dev/null +++ b/+beamline/template_stxm_online_cont_dmesh.m @@ -0,0 +1,44 @@ +import beamline.stxm_online + +first_scan_nr = 15; +nr_lines = 21; +centerx = 224; +centery = 98; +dark_field_radius = 20; +roi = 128; + +[trans,dpcx,dpcy,df]=stxm_online(first_scan_nr, nr_lines , 'ROIdim',roi,'CenX', centerx, 'CenY', centery, 'DarkFieldR', dark_field_radius,'FilenameValidMask',[]); + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. \ No newline at end of file diff --git a/+beamline/template_stxm_online_mesh.m b/+beamline/template_stxm_online_mesh.m new file mode 100644 index 0000000..3543647 --- /dev/null +++ b/+beamline/template_stxm_online_mesh.m @@ -0,0 +1,44 @@ + +first_scan_nr = 2175; +Nx=21; +nr_lines = 19; +centerx = 224; +centery = 98; +dark_field_radius = 20; +roi = 128; + +[trans,dpcx,dpcy,df]=stxm_online(first_scan_nr, nr_lines , 'Nx',Nx,'ROIdim',roi,'CenX', centerx, 'CenY', centery, 'DarkFieldR', dark_field_radius,'FilenameValidMask',[],'DirPerLine',0); + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. \ No newline at end of file diff --git a/+beamline/tune_valid_mask.m b/+beamline/tune_valid_mask.m new file mode 100644 index 0000000..3b45622 --- /dev/null +++ b/+beamline/tune_valid_mask.m @@ -0,0 +1,414 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: tune_valid_mask.m,v $ +% +% $Revision: 1.5 $ $Date: 2012/09/02 15:13:40 $ +% $Author: bunk $ +% $Tag: $ +% +% Description: +% remove outlyers of intensity that deviates from the azimuthal integration +% from the valid pixel mask +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read +% +% history: +% +% May 21st 2010, Oliver Bunk: +% 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [valid_mask] = tune_valid_mask(data_dir, varargin) +import beamline.radial_integ +import io.image_read +import plotting.display_valid_mask +import utils.find_files + + +% set default values for the variable input arguments: +% directory with the integrated data files +indir_integ_data = '~/Data10/analysis/radial_integration/'; +% filename of the integrated data, empty to determine it from the raw data +% file name +filename_integ_data = []; +% use all cbf files +filename_mask = '*.cbf'; +% filename for loading and saving the valid pixel mask +filename_valid_mask = '~/Data10/analysis/data/pilatus_valid_mask.mat'; +% integration masks +filename_integ_masks = '~/Data10/analysis/data/pilatus_integration_masks.mat'; + +% size of the median filter that is use to smooth the data for identifying +% outlyers +median_size = 11; +% first pixel to start at +radius_from = 20; +% last pixel to check +radius_to = 0; +% only intensities above this threshold are considered for being hot +threshold_hot = 5; +% this value times the standard deviation of the intensity is used as hot pixel +% threshold +threshold_median = 3.0; +% save the updated mask +save_data = 0; +% display result in this figure +fig_no = 201; +% matching files to use +point_range = []; + +% check minimum number of input arguments +if (nargin < 1) + fprintf('\nUsage:\n'); + fprintf('[valid_mask]=%s(data_dir [[,,]...]);\n',mfilename); + fprintf('Remove outlyers from the valid pixel mask by comparing azimuthally integrated data\n'); + fprintf('against the same data median filtered and rejecting pixels with a deviation\n'); + fprintf('in intensity specified in multiples of the standard deviation.\n'); + fprintf('\n'); + fprintf('The optional , pairs are:\n'); + fprintf('''FilenameMask'', specify the files to be used from the data directory, empty string for all, default is ''%s''\n',... + filename_mask); + fprintf('''PointRange'', matching files to use, default is [] for all files\n'); + fprintf('''IndirIntegData'', directory with the azimuthally integrated data, default is %s\n',... + indir_integ_data); + fprintf('''FilenameIntegData'', filename for the azimuthally integrated data, empty to determine the name\n'); + fprintf(' from the first raw data file name, default is ''%s''\n',... + filename_integ_data); + fprintf('''FilenameIntegMasks'', Matlab file containing the integration masks, default is ''%s''\n',filename_integ_masks); + fprintf('''RadiusFrom'', no. of the pixel to start with, default is %.0f\n',radius_from); + fprintf('''RadiusTo'', no. of the last pixel to check, default is %.0f\n',radius_to); + fprintf('''MedianSize'', size of the median filter in pixels, default is %.0f\n',... + median_size); + fprintf('''ThresholdHot'', pixels above this value are considered for being hot, default is %d\n',... + threshold_hot); + fprintf('''ThresholdMedian'', pixels outside the range (I+/-threshold_median*sqrt(I))\n'); + fprintf(' of the median filtered data are considered to be hot,\n'); + fprintf(' default is %.1f\n',... + threshold_median); + fprintf('''SaveData'',<0-no,1-yes> save the valid pixel mask, default is %d\n',save_data); + fprintf('''FilenameValidMask'', Matlab file with the valid pixel indices,\n'); + fprintf(' default is %s\n',filename_valid_mask); + fprintf('''FigNo'', number of the figure in which the result is displayed, default is %d\n',... + fig_no); + fprintf('\n'); + fprintf('Examples:\n'); + fprintf('[valid_mask]=%s(''~/Data10/pilatus/S05000-05999/S05715/e12612_1_05715_00000_00000.cbf'');\n',... + mfilename); + fprintf('[valid_mask]=%s(''~/Data10/pilatus/S05000-05999/S05715/*.cbf'');\n',... + mfilename); + + error('At least the filename of the raw data has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = no_of_in_arg -1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments: +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'FilenameIntegMasks' + filename_integ_masks = value; + case 'FilenameMask' + filename_mask = value; + case 'PointRange' + point_range = value; + case 'IndirIntegData' + indir_integ_data = value; + case 'FilenameIntegData' + filename_integ_data = value; + case 'RadiusFrom', + radius_from = round(value); + case 'RadiusTo', + radius_to = round(value); + case 'MedianSize' + median_size = round(value); + case 'ThresholdMedian' + threshold_median = value; + case 'ThresholdHot' + threshold_hot = value; + case 'FilenameValidMask' + filename_valid_mask = value; + case 'SaveData' + save_data = value; + case 'FigNo' + fig_no = value; + otherwise + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + end +end + +vararg_remain{end+1} = 'UnhandledParError'; +vararg_remain{end+1} = 0; +vararg_remain{end+1} = 'DisplayFilename'; +vararg_remain{end+1} = 0; + +% set some default values for the plot window +set(0, 'DefaultAxesfontsize', 12); +set(0, 'DefaultAxeslinewidth', 1, 'DefaultAxesfontsize', 12); +set(0, 'DefaultLinelinewidth', 1); + +% get all matching filenames +if (data_dir(end) ~= '/') + data_dir(end+1) = '/'; +end +[data_dir,fnames,vararg_remain] = ... + find_files( [ data_dir filename_mask ], vararg_remain ); + +if (length(fnames) < 1) + error('No matching files found for %s%s.\n',data_dir,filename_mask); +end + +% load the current valid pixel mask in variable valid_mask +fprintf('loading the existing valid mask %s\n',filename_valid_mask); +load(filename_valid_mask); +framesize = valid_mask.framesize(1) * valid_mask.framesize(2); + +% load the integration masks in variable integ_masks +fprintf('Loading the integration masks from %s\n',filename_integ_masks); +load(filename_integ_masks); +no_of_radii = length(integ_masks.radius); + +if ((radius_to < radius_from) || (radius_to > no_of_radii)) + radius_to = no_of_radii; +end + +% process the frames +ind_hot = []; +ind_dark = []; +integ_data = []; +fprintf('data directory is %s\n',data_dir); +if (isempty(point_range)) + point_range = 1:length(fnames); +else + ind = find(point_range <= length(fnames)); + if (length(point_range) ~= length(ind)) + fprintf('Warning, %d value(s) from the specified point range are out of the range [1,%.0f] and not used.\n',... + length(point_range)-length(ind),length(fnames)); + point_range = point_range(ind); + end +end + +for (point_ind=1:length(point_range)) + f_ind = point_range(point_ind); + % read the raw data + fprintf('%3d/%3d: reading %s%s\n',f_ind,length(point_range),... + data_dir,fnames(f_ind).name); + filename_raw = [data_dir fnames(f_ind).name ]; + [frame] = image_read(filename_raw,vararg_remain); + + % check that the files have identical dimensions + if ((size(frame.data,1) ~= valid_mask.framesize(1)) || ... + (size(frame.data,2) ~= valid_mask.framesize(2))) + error('The valid pixel mask has %d x %d pixels, this frame has %d x %d pixels',... + valid_mask.framesize(1),valid_mask.framesize(2),... + size(frame.data,1),size(frame.data,2)); + end + + % read the radially integrated data + if (isempty(integ_data)) + % determine filename for the integrated data from the first raw + % data filename + if (isempty(filename_integ_data)) + [pathstr, filename_integ_data] = fileparts(fnames(f_ind).name); + filename_integ_data = [ filename_integ_data '_integ.mat' ]; %#ok + end + filename_integ_data = fullfile(indir_integ_data,filename_integ_data); + fprintf('Loading the integrated intensities from %s\n',... + filename_integ_data); + integ_data = load(filename_integ_data); + + % take the median of all segments with positive intensities, i.e., + % skip negative intensities + I_all_prev = integ_data.I_all; + no_of_segments = size(I_all_prev,2); + no_of_points = size(I_all_prev,3); + I_all = zeros(no_of_radii,no_of_points); + I_std = zeros(no_of_radii,no_of_points); + if (no_of_segments > 1) + fprintf('Using the median of %d segments.\n',no_of_segments); + end + for (ind1=1:no_of_radii) + for (ind3=1:no_of_points) + no_of_el = 0; + I_use = zeros(1,no_of_segments); + ind_I_use = zeros(1,no_of_segments); + for (ind2=1:no_of_segments) + if (I_all_prev(ind1,ind2,ind3) >= 0) + no_of_el = no_of_el +1; + I_use(no_of_el) = I_all_prev(ind1,ind2,ind3); + ind_I_use(no_of_el) = ind2; + end + end + if (no_of_el > 1) + [I_sorted,ind_sorted] = sort(I_use(1:no_of_el)); + ind_median = round(0.5*no_of_el); + I_all(ind1,ind3) = I_sorted(ind_median); + % get the standard deviation of this intensity + I_std(ind1,ind3) = integ_data.I_std(ind1,ind_I_use(ind_sorted(ind_median)),ind3); + end + end + end + + % print this information once rather than for each file + fprintf('Checking radii from %d to %d.\n',radius_from,radius_to); + end + + % get the index to the integrated data + ind = 1; + ind_max = length(integ_data.filenames_all); + while ((ind <= ind_max) && ... + (isempty(strfind(integ_data.filenames_all{ind},fnames(f_ind).name)))) + ind = ind +1; + end + if (ind > ind_max) + error('Could not find integrated data for raw data file %s in %s.',... + filename_raw,filename_integ_data); + end + data_integ = squeeze(I_all(:,ind)); + data_integ_std = squeeze(I_std(:,ind)); + + % median filtered data for comparison + data_integ_med = medfilt1(data_integ,median_size,size(data_integ,1),1); + +% figure(fig_no+2); +% hold off; +% clf; +% semilogy(data_integ); +% hold all; +% semilogy(data_integ_med); +% semilogy(data_integ_med+data_integ_std*threshold_median); +% semilogy(data_integ_med-data_integ_std*threshold_median); + + frame_cmp = ones(valid_mask.framesize) -2; + frame_cmp_std = zeros(valid_mask.framesize); + for (ind_r = radius_from:radius_to) + for (ind_seg = 1:no_of_segments) + if (integ_masks.norm_sum(ind_r,ind_seg) > 0) + frame_cmp(integ_masks.indices{ind_r,ind_seg}) = ... + data_integ_med(ind_r); + frame_cmp_std(integ_masks.indices{ind_r,ind_seg}) = ... + data_integ_std(ind_r); + end + end + end + + % + ind_dark = union(ind_dark, ... + find((frame.data >= 0) & ... + (frame_cmp >= 0) & ... + (frame.data < frame_cmp - threshold_median*frame_cmp_std))); + % only consider pixels of sufficient intensity for being hot + ind_hot = union(ind_hot, ... + find((frame.data > threshold_hot) & ... + (frame_cmp >= 0) & ... + (frame.data > frame_cmp + threshold_median*frame_cmp_std))); +end + + +% calculate the complementary masks of the valid pixels +valid_mask.indices = intersect(valid_mask.indices,... + setdiff(1:framesize,union(ind_dark,ind_hot))); + +fprintf('In total %d dark and %d hot pixels found.\n',... + length(ind_dark),length(ind_hot)); +fprintf('%d valid pixels remain.\n',length(valid_mask.indices)); + + +if (save_data) + % create a backup of the mask + if (exist(filename_valid_mask,'file')) + filename_mask_backup = [ filename_valid_mask '.bak' ]; + fprintf('Copying the current mask %s to %s\n',filename_valid_mask,... + filename_mask_backup); + copyfile(filename_valid_mask,filename_mask_backup); + end + + % save the masks + fprintf('Saving valid_mask to %s\n',filename_valid_mask); + save(filename_valid_mask,'valid_mask'); + % plot new valid pixel mask + display_valid_mask('FilenameValidMask',filename_valid_mask,... + 'NoHelp',1,'FigNo',fig_no); +else + fprintf('The updated valid pixel mask is NOT saved.\n'); +end + +% plot the additional invalid pixels +figure(fig_no+1); + +% mark the valid pixels as 1, leave the invalid at 0 +frame = zeros(valid_mask.framesize); +frame(valid_mask.indices) = 1; +frame(ind_dark) = -10; +frame(ind_hot) = 10; +imagesc(frame); +caxis([-10 10]); +axis xy; +axis equal; +axis tight; +colorbar; +title_str = ['valid pixels, ' ... + num2str(length(ind_dark)+length(ind_hot),'%d') ... + ' update(s) marked with intensity -10/10']; +title(title_str); +set(gcf,'Name','valid pixels, updates marked'); diff --git a/+beamline/update_mask.m b/+beamline/update_mask.m new file mode 100644 index 0000000..e9a8fee --- /dev/null +++ b/+beamline/update_mask.m @@ -0,0 +1,95 @@ +%% UDPATE_MASK +% This small script guides you to update an alread existing mask for +% ptychography. The main tool for creating a new mask is +% beamline.create_mask, a GUI that lets you select bad/hot pixels. +% UPDATE_MASK loads the data, specified by file_path, plots it and starts +% the GUI. Although you can create a 3D mask, i.e. a mask which varies from +% frame to frame, a 2D mask is sufficient for most datasets. + +% You can load an already existing mask within the GUI. + +close all + + +file_path = '~/Data10/eiger_4/S00000-00999/S00089/run_00089_000000000000.h5'; +single_file = true; % if you have multiple files use * in file_path +H5Location = '/entry/data/eiger_4/'; % check the location within the h5 file in ptycho/+detector +orientation = [1 0 0]; % check the detector orientation in ptycho/+detector + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% load the data + +image_read_args = []; + +image_read_args{1} = 'Orientation'; +image_read_args{2} = orientation; +image_read_args{3} = 'OrientByExtension'; +image_read_args{4} = false; + +if ~single_file + image_read_args{end+1} = 'IsFmask'; + image_read_args{end+1} = 1; +end + +if ~isempty(H5Location) + image_read_args{end+1} = 'H5Location'; + image_read_args{end+1} = H5Location; +end + + +data = io.image_read(file_path, image_read_args(:)); + +%% plot the data +figure(1), +plotting.imagesc3D(abs(log10(double(data.data)+1))); +colorbar +axis xy equal tight +colorbar +title('Detector raw data') +colormap jet + +%% iterative step for a mask update (add dead pixels to the current mask) +mask = beamline.create_mask; + +%% check it again +figure (2), +imagesc(mask); axis equal tight xy +title('Final mask') + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2018 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + diff --git a/+io/+CBF/cbf_uncompress.c b/+io/+CBF/cbf_uncompress.c new file mode 100644 index 0000000..6d0241b --- /dev/null +++ b/+io/+CBF/cbf_uncompress.c @@ -0,0 +1,161 @@ +/* cbf_uncompress.c: + + Compilation from Matlab: + mex cbf_uncompress.c + maybe a tiny bit faster code is generated by + mex -O COPTIMFLAGS='-O2' LDOPTIMFLAGS='-O2' cbf_uncompress.c + + Usage from Matlab: + [frame] = ... + cbf_uncompress(dat_in,dim1,dim2,no_of_in_bytes,compression_type); + + history: + April 24th 2008: + 1st version based on code snippet from Eric Eikenberry + +*-----------------------------------------------------------------------* +|                                                                       | +|  Except where otherwise noted, this work is licensed under a          | +|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +|  International (CC BY-NC-SA 4.0) license.                             | +|                                                                       | +|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +|                                                                       | +|      Author: CXS group, PSI  | +*-----------------------------------------------------------------------* + You may use this code with the following provisions: + + If the code is fully or partially redistributed, or rewritten in another + computing language this notice should be included in the redistribution. + + If this code, or subfunctions or parts of it, is used for research in a + publication or if it is fully or partially rewritten for another + computing language the authors and institution should be acknowledged + in written form in the publication: “Data processing was carried out + using the “cSAXS matlab package” developed by the CXS group, + Paul Scherrer Institut, Switzerland.” + Variations on the latter text can be incorporated upon discussion with + the CXS group if needed to more specifically reflect the use of the package + for the published work. + + A publication that focuses on describing features, or parameters, that + are already existing in the code should be first discussed with the + authors. + + This code and subroutines are part of a continuous development, they + are provided “as they are” without guarantees or liability on part + of PSI or the authors. It is the user responsibility to ensure its + proper use and the correctness of the results. +*/ + +#include "mex.h" + +#include +#include + +void mexFunction(int nlhs, mxArray *plhs[], + int nrhs, const mxArray *prhs[]) +{ + const mxArray *curr_arg; + union { + unsigned char *uint8; + char *int8; + unsigned short *uint16; + short *int16; + unsigned int *uint32; + int *int32; + } data_in,data_in_start; + + int dim1, dim2, no_of_in_bytes, compression_type; + int diff, val_curr; + double *frame_out, *frame_out_start; + + /* initialize return argument*/ + plhs[0] = NULL; + + /* Check for proper number of arguments. */ + if (nrhs != 5) + mexErrMsgTxt("Five input arguments required: dat_in,dim1,dim2,no_of_in_bytes,compression_type."); + else if (nlhs != 1) + mexErrMsgTxt("One output argument has to be specified."); + + { + int ind; + for (ind = 0; ind < nrhs; ind++) { + if(mxGetNumberOfDimensions(prhs[ind]) != 2) { + printf("The %d. input argument must have two dimensions.",ind+1); + mexErrMsgTxt("wrong number of dimensions"); + } + } + } + + /* check 1st input argument: input data */ + curr_arg = prhs[0]; + if (mxIsUint8(curr_arg) != 1) + mexErrMsgTxt("Input 1 (input data) must be of type uint8."); + data_in_start.uint8 = data_in.uint8 = (char *) mxGetPr(curr_arg); + + /* check 2nd input argument: dim1 */ + curr_arg = prhs[1]; + if (mxIsDouble(curr_arg) != 1) + mexErrMsgTxt("Input 2 (dimension 1) must be of type double."); + dim1 = mxGetScalar(curr_arg); + if (dim1 < 1) { + mexErrMsgTxt("Input 2 (dimension 1) must be at least 1."); + } + + /* check 3rd input argument: dim2 */ + curr_arg = prhs[2]; + if (mxIsDouble(curr_arg) != 1) + mexErrMsgTxt("Input 3 (dimension 2) must be of type double."); + dim2 = mxGetScalar(curr_arg); + if (dim2 < 1) { + mexErrMsgTxt("Input 3 (dimension 2) must be at least 1."); + } + + /* check 4th input argument: no_of_in_bytes */ + curr_arg = prhs[3]; + if (mxIsDouble(curr_arg) != 1) + mexErrMsgTxt("Input 4 (no. of input bytes) must be of type double."); + no_of_in_bytes = mxGetScalar(curr_arg); + + /* check 5th input argument: compression_type */ + curr_arg = prhs[4]; + if (mxIsDouble(curr_arg) != 1) + mexErrMsgTxt("Input 5 (compression type) must be of type double."); + compression_type = mxGetScalar(curr_arg); + if (compression_type != 1) { + mexErrMsgTxt("currently only compression type 1, byte-offset compression, is supported"); + } + + /* allocate memory for the output data */ + plhs[0] = mxCreateNumericMatrix(dim1, dim2, mxDOUBLE_CLASS, mxREAL); + if (plhs[0] == NULL) + mexErrMsgTxt("Could not allocate memory for return data."); + frame_out_start = frame_out = mxGetPr(plhs[0]); + + val_curr = 0; + while (data_in.uint8-data_in_start.uint8 < no_of_in_bytes) { + if (*data_in.uint8 != 0x80) { + diff = (int) *data_in.int8++; + } else { + data_in.uint8++; + if (*data_in.uint16 != 0x8000) { + diff = (int) *data_in.int16++; + } else { + data_in.uint16++; + diff = (int) *data_in.int32++; + } + } + val_curr += diff; + *frame_out++ = (double) val_curr; + } + + if (frame_out-frame_out_start != dim1*dim2) { + printf("%ld bytes after uncompression, %ld bytes expected", + frame_out-frame_out_start, dim1*dim2); + mexErrMsgTxt("mismatch in number of extracted data bytes"); + } + + return; +} diff --git a/+io/+CBF/cbfread.m b/+io/+CBF/cbfread.m new file mode 100644 index 0000000..33f46ef --- /dev/null +++ b/+io/+CBF/cbfread.m @@ -0,0 +1,339 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: cbfread.m,v $ +% +% $Revision: 1.2 $ $Date: 2011/05/09 13:03:07 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading Crystallographic Binary File (CBF) files written by the +% Pilatus detector control program camserver. +% +% Note: +% Compile the C-program cbf_uncompress using mex (see header of +% cbf_uncompress.c) to use it for uncompression instead of the slower +% Matlab code. +% Currently this routine supports only the subset of CBF features needed to +% read the Pilatus detector data. +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read_set_default +% - fopen_until_exists +% - get_hdr_val +% - compiling cbf_uncompress.c increases speed but is not mandatory +% +% +% history: +% +% May 9th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = cbfread(filename,varargin) +import io.* +import io.CBF.* +import utils.char_to_cellstr +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'cbf'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',value pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + +% expected maximum length for the text header +max_header_length = 4096; + +% end of header signature +eoh_signature = char([ 12 26 4 213 ]); + +% CBF file signature +cbf_signature = '###CBF: VERSION'; + +% Calling an external C routine for uncompressing the data did save about +% 30% time on a specific machine. +% The C-routine is used if a compiled version of it exists. +% See the header of cbf_uncompress.c for information on how to compile the +% C file using mex in Matlab. + +c_routine = exist('+io/+CBF/cbf_uncompress.mexa64', 'file') || ... + ~isempty(which('cbf_uncompress')) ; % faster than which('cbf_uncompress') + + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end + +% read all data at once +[fdat,fcount] = fread(fid,'uint8=>uint8'); + +% close input data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes read\n',fcount); +end + +% search for end of header signature within the expected maximum length of +% a header +end_of_header_pos = ... + strfind( fdat(1:min(max_header_length,length(fdat)))',... + eoh_signature ); +if (length(end_of_header_pos) < 1) + cbf_error(filename,'no header end signature found'); + return; +end +if (debug_level >= 2) + fprintf('Header length is %d bytes.\n',end_of_header_pos -1); +end + +% return the complete header as lines of a cell array +frame.header = char_to_cellstr( char(fdat(1:(end_of_header_pos-1))') ); + +% check for CBF signature +if (~strncmp(cbf_signature,frame.header{1},length(cbf_signature))) + cbf_error(filename,[ 'CBF signature ''' cbf_signature ... + ''' not found in first line ''' frame.header{1} '''' ]); +end + +% extract the mandatory information for decompression from the header +no_of_bin_bytes = get_hdr_val(frame.header,'X-Binary-Size:','%f',1); +dim1 = get_hdr_val(frame.header,'X-Binary-Size-Fastest-Dimension:','%f',1); +dim2 = get_hdr_val(frame.header,'X-Binary-Size-Second-Dimension:','%f',1); +el_type = get_hdr_val(frame.header,'X-Binary-Element-Type: "','%[^"]',1); +switch (el_type) + case 'signed 32-bit integer' + bytes_per_pixel = 4; + otherwise + cbf_error(filename,[ 'unknown element type ' el_type ]); +end +compr_type = get_hdr_val(frame.header,'conversions="','%[^"]',1); +switch (compr_type) + case 'x-CBF_BYTE_OFFSET' + compression_type = 1; + case 'x-CBF_NONE' + compression_type = 2; + otherwise + cbf_error(filename,[ 'unknown compression type ' compr_type ]); +end +if (debug_level >= 2) + fprintf('Frame dimensions are %d x %d.\n',dim2,dim1); +end + +% uncompress the binary data +[frame.data] = ... + extract_frame(fdat((end_of_header_pos+length(eoh_signature)):end),... + dim1,dim2,no_of_bin_bytes,compression_type,... + filename,... + c_routine,debug_level); + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [] = cbf_error(filename,text) + +fprintf('cbfread of %s:\n %s\n',filename,text); +return; + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [frame] = ... + extract_frame(dat_in,... + dim1,dim2,no_of_in_bytes,compression_type,... + filename,... + c_routine,debug_level) + +import io.* +import io.CBF.* +import utils.char_to_cellstr +import utils.fopen_until_exists +import utils.get_hdr_val + + +% uncompressed data are copied directly +if (compression_type == 2) + % initialize return array + frame = zeros(dim1,dim2); + % copy uncompressed data + for (ind_out = 1:(dim1*dim2)) + ind_in = ind_out *4 -3; + frame(ind_out) = double(dat_in(ind_in)) + ... + 256 * double(dat_in(ind_in+1)) + ... + 65536 * double(dat_in(ind_in+2)) + ... + 16777216 * double(dat_in(ind_in+3)); + end + return; +end + + +if (c_routine) + if (debug_level >= 2) + fprintf('C routine called.\n'); + end + [frame] = ... + cbf_uncompress(dat_in,dim1,dim2,no_of_in_bytes,compression_type); + return; +end + +if (debug_level >= 2) + fprintf('Matlab routine called.\n'); +end + + +% initialize return array +frame = zeros(dim1,dim2); + +% only byte-offset compression is supported +if (compression_type ~= 1) + cbf_error(filename,... + ['extract_frame does not support compression type no. ' ... + num2str(compression_type)]); +end + + +% In byte-offset compression the difference to the previous pixel value is +% stored as a byte, 16-bit integer or 32-bit integer, depending on its +% size. +% The sizes above one byte are indicated by the escape sequence -1 in the +% previous data format, i.e, a 32-bit integer is preceded by the sequence % +% 0x80 (too large for a byte) +% 0x8000 (too large for a 16-bit integer). +ind_out = 1; +ind_in = 1; +val_curr = 0; +val_diff = 0; +while (ind_in <= no_of_in_bytes) + val_diff = double(dat_in(ind_in)); + ind_in = ind_in +1; + if (val_diff ~= 128) + % if not escaped as -128 (0x80=128) use the current byte as + % difference, with manual complement to emulate the sign + if (val_diff >= 128) + val_diff = val_diff - 256; + end + else + % otherwise check for 16-bit integer value + if ((dat_in(ind_in) ~= 0) || (dat_in(ind_in+1) ~= 128)) + % if not escaped as -32768 (0x8000) use the current 16-bit integer + % as difference + val_diff = double(dat_in(ind_in)) + ... + 256 * double(dat_in(ind_in+1)); + % manual complement to emulate the sign + if (val_diff >= 32768) + val_diff = val_diff - 65536; + end + ind_in = ind_in +2; + else + ind_in = ind_in +2; + % if everything else failed use the current 32-bit value as + % difference + val_diff = double(dat_in(ind_in)) + ... + 256 * double(dat_in(ind_in+1)) + ... + 65536 * double(dat_in(ind_in+2)) + ... + 16777216 * double(dat_in(ind_in+3)); + % manual complement to emulate the sign + if (val_diff >= 2147483648) + val_diff = val_diff - 4294967296; + end + ind_in = ind_in +4; + end + end + val_curr = val_curr + val_diff; + frame(ind_out) = val_curr; + ind_out = ind_out +1; +end + +if (ind_out-1 ~= dim1*dim2) + cbf_error(filename,[ 'mismatch between ' num2str(ind_out-1) ... + ' bytes after decompression with ' num2str(dim1*dim2) ... + ' expected ones' ]); +end + + +% if (~original_orientation) +% frame.data = frame.data(end:-1:1,end:-1:1)'; +% end diff --git a/+io/+CBF/cbfwrite.m b/+io/+CBF/cbfwrite.m new file mode 100644 index 0000000..6b60456 --- /dev/null +++ b/+io/+CBF/cbfwrite.m @@ -0,0 +1,258 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: cbfwrite.m,v $ +% +% $Revision: 1.2 $ $Date: 2014/04/17 16:49:22 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for writing data to a Crystallographic Binary File (CBF) file. +% Assumes that the data have been read from such a file beforehand, i.e., +% that the header exists already. +% +% +% history: +% +% April 10th 2014: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [fcount_total] = cbfwrite(filename,frame,varargin) +import io.CBF.* +import io.* +import utils.default_parameter_value +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% determine default orientation based on the file name extension +orient_by_extension = default_parameter_value('image_read','OrientByExtension'); + +fcount_total = -1; + +% check minimum number of input arguments +if (nargin < 2) + fprintf('\nUsage:\n'); + fprintf('[bytes_written]=%s( filename, frame-structure [[,,]...]);\n',mfilename); + fprintf('Write a CBF file from data that have been read via cbfread.m before.\n'); + fprintf('Conventions are likely to be specific for the PILATUS detector.\n') + fprintf('\n'); + fprintf('The optional , pairs are:\n'); + image_orient_help(mfilename,'ParametersOnly',1); + fprintf('\n'); + fprintf('The file name should be the name of a single file without wildcards.\n'); + + error('At least the filename and the data-structure have to be specified as input parameter.'); +end + +% check frame structure +if (~isstruct(frame)) + error('The 2nd parameter needs to be a structure as read by cbfread.m'); +end +if (~isfield(frame,'header')) + error('The 2nd parameter needs to be a structure with a field ''header'', as read by cbfread.m'); +end +if (~isfield(frame,'data')) + error('The 2nd parameter needs to be a structure with a field ''data'', as read by cbfread.m'); +end +if (size(frame.header,2) ~= 1) + error('The header has the wrong dimensions. Only single-frame structures are suppoerted.\n'); +end + +% check minimum number of input arguments +if (nargin < 2) + error('At least the filename and the frame have to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',value pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg_remain = cell(4,1); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'OrientByExtension' + orient_by_extension = value; + otherwise + % pass further arguments on to fopen_until_exists + vararg_remain{end+1} = name; %#ok + vararg_remain{end+1} = value; %#ok + end +end +vararg_remain{1} = 'OrientByExtension'; +vararg_remain{2} = orient_by_extension; +vararg_remain{3} = 'InvertOrientation'; +vararg_remain{4} = 1; + +% get the length of the zero-padding at the end of the file +padding_length = get_hdr_val(frame.header{1},'X-Binary-Size-Padding:','%d',1); + +% end of header signature +eoh_signature = char([ 12 26 4 213 ]); + +% orient image +[frame,vararg_remain] = image_orient(frame,vararg_remain); + + +% open file for write-access, overwrite in case of an existing file +[fid] = fopen(filename,'w'); +if (fid < 0) + return; +end + + +% In byte-offset compression the difference to the previous pixel value is +% stored as a byte, 16-bit integer or 32-bit integer, depending on its +% size. +% The sizes above one byte are indicated by the escape sequence -1 in the +% previous data format, i.e, a 32-bit integer is preceded by the sequence % +% 0x80 (too large for a byte) +% 0x8000 (too large for a 16-bit integer). +ind_out = 1; +no_of_pixels = size(frame.data,1) * size(frame.data,2); + +% initialize output array at maximum size +frame_out = zeros(no_of_pixels*4 + padding_length,1,'uint8'); +val_prev = 0; +for ind_in = 1:no_of_pixels + val_diff = frame.data(ind_in) - val_prev; + if (abs(val_diff) < 128) + % write differences in the range from -127 to 127 directly as 8bit + % signed integer: + % manual complement to emulate the sign + if (val_diff < 0) + val_diff = 256 + val_diff; + end + frame_out(ind_out) = val_diff; + ind_out = ind_out +1; + else + % escape with 0x80 + frame_out(ind_out) = 128; + % check for 16-bit integer value + if (abs(val_diff) < 32768) + % write signed 16bit integer value: + % manual complement to emulate the sign + if (val_diff < 0) + val_diff = 65536 + val_diff; + end + frame_out(ind_out+1) = bitand(val_diff,255); + frame_out(ind_out+2) = bitand(val_diff,65280)/256; + ind_out = ind_out +3; + else + % escape with 0x8000 + frame_out(ind_out+1) = 0; + frame_out(ind_out+2) = 128; + % write signed 32bit integer value: + % manual complement to emulate the sign + if (val_diff < 0) + val_diff = 4294967296 + val_diff; + end + frame_out(ind_out+3) = bitand(val_diff,255); + frame_out(ind_out+4) = bitand(val_diff,65280)/256; + frame_out(ind_out+5) = bitand(val_diff,16711680)/65536; + frame_out(ind_out+6) = bitand(val_diff,4278190080)/16777216; + ind_out = ind_out +7; + end + end + % the current intensity value becomes the previous value + val_prev = frame.data(ind_in); +end + +% calculate length of binary data including zero-padding at the end +binary_length = ind_out -1 + padding_length; + +% update this value in the file-header +[~, line_no] = get_hdr_val(frame.header{1},'X-Binary-Size:','%f',1); +frame.header{1}{line_no} = sprintf('X-Binary-Size: %d',binary_length-padding_length); + +% write header +[fcount_total] = fprintf(fid,'%s\r\n',frame.header{1}{:}); +if (fcount_total == 0) + error('Could not write any header data.\n'); +end +% write end-of-header signature +[fcount] = fwrite(fid,eoh_signature,'uint8'); +fcount_total = fcount_total + fcount; +if (fcount == 0) + error('Could not write end-of-header signature.\n'); +end + +% write binary data +[fcount] = fwrite(fid,frame_out(1:(binary_length))); +fcount_total = fcount_total + fcount; +if (fcount ~= binary_length) + error('Could not write CBF image-data.'); +end + +% write binary-end signature +[fcount] = fprintf(fid,'\r\n--CIF-BINARY-FORMAT-SECTION----\r\n;\r\n\r\n'); +fcount_total = fcount_total + fcount; +if (fcount < 1) + error('Could not write CBF binary-end signature'); +end + + +% close output data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes written\n',fcount_total); +end + +return; diff --git a/+io/+HDF/add_content.m b/+io/+HDF/add_content.m new file mode 100644 index 0000000..5e0776b --- /dev/null +++ b/+io/+HDF/add_content.m @@ -0,0 +1,82 @@ +%ADD_CONTENT write matlab structure to H5 file + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function add_content(data, gid, plist, comp, overwrite) +import io.HDF.* + +fn = fieldnames(data); +for jj=1:length(fn) + if isstruct(data.(fn{jj})) + fn_sub = fieldnames(data.(fn{jj})); + if length(fn_sub) <= 2 && isfield(data.(fn{jj}), 'Value') + % found dataset + if isfield(data.(fn{jj}), 'Attributes') + Attributes.MATLAB_class = class(data.(fn{jj}).Value); + write_dataset(data.(fn{jj}).Value, gid, fn{jj}, plist, comp, overwrite, data.(fn{jj}).Attributes); + else + Attributes.MATLAB_class = class(data.(fn{jj}).Value); + write_dataset(data.(fn{jj}).Value, gid, fn{jj}, plist, comp, overwrite, Attributes); + end + elseif strcmpi(fn{jj}, 'Attributes') + % add attributes to group + fn_attr = fieldnames(data.Attributes); + for ii=1:length(fn_attr) + write_attribute(gid, data.Attributes.(fn_attr{ii}), fn_attr{ii}); + end + else + % found group + gid_new = add_groups(gid, fn{jj}, plist, true); + if length(data.(fn{jj})) == 1 + add_content(data.(fn{jj}), gid_new, plist, comp, overwrite) + else + fn_names = cell(1,length(data.(fn{jj}))); + for ii=1:length(data.(fn{jj})) + fn_names{ii} = sprintf([fn{jj} '_%d'],ii-1); + gid_new_sub = add_groups(gid_new, fn_names{ii}, plist, true); + add_content(data.(fn{jj})(ii), gid_new_sub, plist, comp, overwrite); + end + write_attribute(gid_new, 'structure array', 'MATLAB_class'); + + end + end + else + % append dataset + Attributes.MATLAB_class = class(data.(fn{jj})); + write_dataset(data.(fn{jj}), gid, fn{jj}, plist, comp, overwrite, Attributes); + end +end + +end diff --git a/+io/+HDF/add_groups.m b/+io/+HDF/add_groups.m new file mode 100644 index 0000000..861fe02 --- /dev/null +++ b/+io/+HDF/add_groups.m @@ -0,0 +1,65 @@ +%ADD_GROUPS add groups or open them if they exist + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function gid = add_groups(fileID, gpath, plist, varargin) + +if nargin > 3 + single_data = varargin{1}; +else + single_data = true; +end + +if ~single_data + gpath_depth = length(gpath); + gid{1} = fileID; + for ii=1:gpath_depth + try + gid{end+1} = H5G.open(gid{ii}, gpath{ii}, plist); + catch + gid{end+1} = H5G.create(gid{ii},gpath{ii},plist,plist,plist); + end + end + +else + try + gid = H5G.open(fileID, gpath, plist); + catch + gid = H5G.create(fileID,gpath,plist,plist,plist); + end +end + +end + diff --git a/+io/+HDF/get_datatype.m b/+io/+HDF/get_datatype.m new file mode 100644 index 0000000..54129de --- /dev/null +++ b/+io/+HDF/get_datatype.m @@ -0,0 +1,84 @@ +% Determine datatype for HDF files + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [datatype_h5, data] = get_datatype(data) + +if isempty(data) + data = ''; +end + +switch class(data) + case 'uint32' + datatype_h5 = 'H5T_STD_U32LE'; + case 'int32' + datatype_h5 = 'H5T_STD_I32LE'; + case 'int64' + datatype_h5 = 'H5T_STD_I64LE'; + case 'uint64' + datatype_h5 = 'H5T_STD_U64LE'; + case 'double' + if isreal(data) + datatype_h5 = 'H5T_NATIVE_DOUBLE'; + else + datatype_h5 = 'complex'; + end + case 'single' + if isreal(data) + datatype_h5 = 'H5T_NATIVE_FLOAT'; + else + datatype_h5 = 'complex'; + end + case 'logical' + data = uint32(data); + datatype_h5 = 'H5T_STD_U32LE'; + case 'char' + if size(data,1)>1 + data = cellstr(data); + datatype_h5 = 'char_array'; + else + datatype_h5 = 'H5T_UNIX_D32BE'; %'H5T_UNIX_D32LE'; + end + case 'cell' + datatype_h5 = 'H5T_C_S1'; + case 'struct' + datatype_h5 = 'struct'; + + otherwise + error('Unknown data type %s', class(data)) +end + +end + diff --git a/+io/+HDF/hdf5_append_attr.m b/+io/+HDF/hdf5_append_attr.m new file mode 100644 index 0000000..4174fe7 --- /dev/null +++ b/+io/+HDF/hdf5_append_attr.m @@ -0,0 +1,69 @@ +%HDF5_APPEND_ATTR Append an attribute to a dataset +% +% file... HDF filename +% attr... structure of attributes +% loc... location of the dataset that needs to be removed +% +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function hdf5_append_attr( file, attr, loc) +import io.HDF.* +plist = 'H5P_DEFAULT'; + +fileID = H5F.open(file,'H5F_ACC_RDWR',plist); + +gpath = strsplit(rm_delimiter(loc), '/'); + +gpath_depth = length(gpath); +gid{1} = fileID; +for ii=1:gpath_depth + try + gid{end+1} = H5G.open(gid{ii}, gpath{ii}, plist); + catch + gid{end+1} = H5D.open(gid{ii}, gpath{ii}); + end +end + +fn = fieldnames(attr); +for ii=1:length(fn) + write_attribute(gid{end}, attr.(fn{ii}), fn{ii}, true); +end + +H5F.close(fileID); + + +end diff --git a/+io/+HDF/hdf5_attr_exists.m b/+io/+HDF/hdf5_attr_exists.m new file mode 100644 index 0000000..2e2abd4 --- /dev/null +++ b/+io/+HDF/hdf5_attr_exists.m @@ -0,0 +1,65 @@ +%HDF5_ATTR_EXISTS check if attribute exists in given file +% file... h5 file path +% attr... dataset name +% +% *optional* +% gpath... path within the h5 file; default root (/) +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [ out ] = hdf5_attr_exists(file, name, varargin) + +out = false; + +% load info +if nargin > 2 + h = h5info(file, varargin{1}); +else + h = h5info(file); +end + +% loop through datasets and check if name exists +for ii=1:numel(h.Attributes) + if strcmpi(h.Attributes(ii).Name, name) + out = true; + break + end +end + + +end + + diff --git a/+io/+HDF/hdf5_cp_file.m b/+io/+HDF/hdf5_cp_file.m new file mode 100644 index 0000000..9bd26f8 --- /dev/null +++ b/+io/+HDF/hdf5_cp_file.m @@ -0,0 +1,132 @@ +%HDF5_CP_FILE copy HDF files +% orig_filename... source file +% duplicate_filename... target file +% +% *optional* given as name/value pair +% groups... groups to copy; either string or cell of +% strings; default: everything in root +% copy_type... 'deep', 'normal' or 'shallow' copy; +% 'shallow' creates external links in target file; +% 'normal' is similar to linux 'cp' command; +% 'deep' dereferences all internal and external links; +% default: 'shallow' +% +% EXAMPLES: +% hdf5_cp_file('./test.h5', './test_new.h5') +% hdf5_cp_file('./test.h5', './test_new.h5', 'copy_type', 'deep'); +% +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function hdf5_cp_file(orig_filename, duplicate_filename, varargin) +import io.HDF.* +% take care of input arguments +groups = []; +copy_type = 'shallow'; + +% parse the variable input arguments vararg = cell(0,0); +if ~isempty(varargin) + for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch lower(name) + case 'groups' + groups = value; + case 'copy_type' + copy_type = value; + end + end +end + +switch copy_type + case 'shallow' + if isempty(groups) + % if no groups are specified, use h5info to get all datasets and groups + % from root + h = h5info(orig_filename, '/'); + lng = length(h.Groups); + lnd = length(h.Datasets); + lna = length(h.Attributes); + + groups = cell([1 lng+lnd]); + attributes = []; + + for ii=1:lng + groups{ii} = h.Groups(ii).Name; + end + for ii=1:lnd + groups{ii+lng} = h.Datasets(ii).Name; + end + for ii=1:lna + attributes.(h.Attributes(ii).Name) = h.Attributes(ii).Value; + if iscell(h.Attributes(ii).Value) + attributes.(h.Attributes(ii).Name) = attributes.(h.Attributes(ii).Name){1}; + end + end + else + attributes = []; + end + + + s = []; + if iscell(groups) + for ii=1:length(groups) + subgrps = strsplit(rm_delimiter(groups{ii}), '/'); + s = setfield(s, subgrps{:}, ['ext:' orig_filename ':' groups{ii}]); + end + else + s.groups = ['ext:' orig_filename ':' groups]; + end + + % append attributes + if ~isempty(attributes) + s.Attributes = attributes; + end + + save2hdf5(duplicate_filename, s, 'overwrite', true, 'iscopy', true); + + case 'deep' + s = io.HDF.hdf5_load(orig_filename, '-ca'); + save2hdf5(duplicate_filename, s, 'overwrite', true, 'iscopy', true); + + case 'normal' + copyfile(orig_filename, duplicate_filename) + + otherwise + error('Unknown copy type!') +end + +end diff --git a/+io/+HDF/hdf5_dset_exists.m b/+io/+HDF/hdf5_dset_exists.m new file mode 100644 index 0000000..fa040c0 --- /dev/null +++ b/+io/+HDF/hdf5_dset_exists.m @@ -0,0 +1,99 @@ +%HDF5_DSET_EXISTS check if dataset exists in given file +% file... h5 file path +% dset... dataset name +% +% *optional* +% gpath... path within the h5 file; default root (/) +% check_links... include links; default true +% +% EXAMPLES: +% out = io.HDF.hdf5_dset_exists('./recons.h5', +% 'object_phase_unwrapped', '/reconstruction', true); +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [out] = hdf5_dset_exists(file, dset, varargin) + +out = false; + +% load info +if nargin > 2 + h = h5info(file, varargin{1}); +else + h = h5info(file); +end + +if nargin > 3 + check_links = varargin{2}; +else + check_links = true; +end + +if nargin > 4 + check_groups = varargin{3}; +else + check_groups = true; +end + +% loop through datasets and check if name exists +for ii=1:numel(h.Datasets) + if strcmpi(h.Datasets(ii).Name, dset) + out = true; + break + end +end + +if check_links + for ii=1:numel(h.Links) + if strcmpi(h.Links(ii).Name, dset) + out = true; + break + end + end +end + +if check_groups + for ii=1:numel(h.Groups) + [~, gname] = fileparts(h.Groups(ii).Name); + if strcmpi(gname, dset) + out = true; + break + end + end +end + +end + diff --git a/+io/+HDF/hdf5_load.m b/+io/+HDF/hdf5_load.m new file mode 100644 index 0000000..87d4c78 --- /dev/null +++ b/+io/+HDF/hdf5_load.m @@ -0,0 +1,642 @@ +% HDF5_LOAD Load an hdf5 file +% +% DATA = HDF5_LOAD(filename) reads a complete file hierarchy recursively, with +% file name/path being specified by the 'filename' argument +% +% DATA = HDF5_LOAD(filename, '-a') reads a complete file hierarchy +% recursively, including attributes +% +% DATA = HDF5_LOAD(filename, location) reads a particular group, link, or a single dataset +% specified by the 'location' argument +% +% ATT = HDF5_LOAD(filename, location, '-a') reads all datasets and attributes associated +% with a particular location in the file (group, link or dataset) +% +% ATT = HDF5_LOAD(filename, location, '-ca') reads all datasets and attributes associated +% with a particular location in the file (group, link or dataset) and +% converts datasets to a specific matlab class based on attribute 'MATLAB_class' +% +% SLICE = HDF5_LOAD(filename, location, {rowRange, colRange, frameRange, ...}) reads a +% portion of a dataset along specified dimentions, where slicing ranges can be defined in +% the following ways (negative indexes count from the end of the corresponding dimensions): +% range = scalar_index - reads a particular row/col/frame/... (indentical to +% 'range = [scalar_index, scalar_index]') +% range = [start_index, end_index] - reads all data between start and end +% indexes +% range = [start_index, Inf] - reads all data from start_index to the last +% existing element in the file +% range = [], or range is omitted at the end - reads the full range of values for that +% dimention (indentical to 'range = [1, Inf]') +% +% Examples: +% hdf5_load('scan_003.hdf5') +% hdf5_load('scan_003.hdf5', '/entry/sample/description') +% hdf5_load('scan_003.hdf5', '/entry/collection/data/spec', '-a') +% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {5}) +% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {[-100, Inf]}) +% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {5, [500, Inf], [1, 100]}) +% hdf5_load('scan_003.hdf5', '/entry/instrument/Pilatus_2M/data', {[], [], [1, 100]}) + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group +% and the Science IT group, Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function data = hdf5_load(filename, varargin) +import io.HDF.* + +load_attr = false; +convert2matlab = false; + +narginchk(1, 3); +if nargin == 1 + % Read the complete file hierarchy recursively + try + info = h5info(filename); + info.Name = ''; % a special case of the root group + + catch ME + if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr') + ME = MException('hdf5_load:h5info', ... + strjoin({'File', filename, 'does not exist'})); + end + + throwAsCaller(ME); + end + + [data, links] = hdf5_loadGroup(filename, info); + data = assign_links(data, info, links); + +elseif nargin == 2 + if any(strcmp(varargin{1}, {'-a', '-ca', '-c'})) + % second argument is an attribute flag + try + info = h5info(filename); + info.Name = ''; % a special case of the root group + + catch ME + if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr') + ME = MException('hdf5_load:h5info', ... + strjoin({'File', filename, 'does not exist or is not valid h5 file'})); + end + + throwAsCaller(ME); + end + + if any(strcmp(varargin{1}, {'-a', '-ca'})) + load_attr = true; + end + + if any(strcmp(varargin{1}, {'-ca', '-c'})) + convert2matlab = true; + end + + [data, links] = hdf5_loadGroup(filename, info, convert2matlab, load_attr); + data = assign_links(data, info, links, varargin{1}); + + else + % Read a group or a single dataset + location = varargin{1}; + try + info = h5info(filename, location); + if strcmp(info.Name, '/') % a special case of the root group + info.Name = ''; + end + + catch ME + if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr') + ME = MException('hdf5_load:h5info', ... + strjoin({'File', filename, 'does not exist'})); + + elseif strcmp(ME.identifier, 'MATLAB:imagesci:h5info:libraryError') + ME = MException('hdf5_load:h5info', ... + strjoin({'H5Location', location, 'was not found in', filename, 'file'})); + end + + throwAsCaller(ME); + end + + if isfield(info, 'Groups') + % Read a group with its internal hierarchy + [data, links] = hdf5_loadGroup(filename, info); + data = assign_links(data, info, links); + + elseif isfield(info, 'Datatype') + % Read a data set + type = info.Datatype.Class; + data = hdf5_loadDataset(filename, location, type); + + elseif isfield(info, 'Type') + % Read a link + data = hdf5_loadLink(info); + + else + error('hdf5_load:parse_argument', ... + 'The 2-nd argument must be a name of a group, dataset, or link'); + end + end + +elseif nargin == 3 + % Read attributes of a group or a data set, or slices of a data set + location = varargin{1}; + try + info = h5info(filename, location); + if strcmp(info.Name, '/') % a special case of the root group + info.Name = ''; + end + + catch ME + if strcmp(ME.identifier, 'MATLAB:imagesci:h5info:fileOpenErr') + ME = MException('hdf5_load:h5info', ... + strjoin({'File', filename, 'does not exist or is not HDF5 format'})); + + elseif strcmp(ME.identifier, 'MATLAB:imagesci:h5info:libraryError') + ME = MException('hdf5_load:h5info', ... + strjoin({'H5Location', location, 'was not found in', filename, 'file'})); + end + + throwAsCaller(ME); + end + + if iscell(varargin{2}) + % Read slices of a data set + slices = varargin{2}; + + % Check if the specified location is a data set + if ~isfield(info, 'Dataspace') + error('hdf5_load:invalid_location', ... + 'Slicing ranges are not applicable, the location is not a data set'); + end + + data_size = info.Dataspace.Size; + if length(slices) > length(data_size) + error('hdf5_load:invalid_slicing', ... + 'A number of slicing ranges is larger than a dimention of a data set') + end + + % Parse ranges + startIndex = ones(1, length(data_size)); + nElements = Inf(1, length(data_size)); + for i = 1:length(slices) + [startIndex(i), nElements(i)] = parse_range(slices{i}, data_size(i)); + end + + % Read data + data = h5read(filename, location, startIndex, nElements); + + elseif any(strcmp(varargin{2}, {'-a', '-ca', '-c'})) + % Read attributes and/or convert to matlab structures + if any(strcmp(varargin{2}, {'-a', '-ca'})) + load_attr = true; + end + if any(strcmp(varargin{2}, {'-ca', '-c'})) + convert2matlab = true; + end + + if isfield(info, 'Groups') + % Read a group with its internal hierarchy + [data, links] = hdf5_loadGroup(filename, info, convert2matlab, load_attr); + data = assign_links(data, info, links, varargin{2}); + + elseif isfield(info, 'Datatype') + % Read a data set + type = info.Datatype.Class; + dset_val = hdf5_loadDataset(filename, location, type); + if load_attr || convert2matlab + [dset_attr, ml_class_dset] = hdf5_loadAttributes(info, convert2matlab, load_attr); + else + ml_class_dset = []; + end + + if ~isempty(ml_class_dset) + switch ml_class_dset + case 'complex' + dset_val = dset_val.r + 1i*dset_val.i; + + case 'cell' + if ~iscell(dset_val) + dset_val = {dset_val}; + end + + case 'char_array' + dset_val = char(dset_val); + + + otherwise + conv2ml = str2func(ml_class_dset); + dset_val = conv2ml(dset_val); + end + end + + if load_attr + data.Attributes = dset_attr; + data.Value = dset_val; + + else + data = dset_val; + end + + elseif isfield(info, 'Type') + % Read a link + data = hdf5_loadLink(info, convert2matlab, load_attr); + + end + + else + error('hdf5_load:parse_argument', ... + 'Incorrect 3-rd argument'); + end +end + +function [data, links] = hdf5_loadGroup(filename, info, varargin) +import io.HDF.* + +if nargin > 2 + convert2matlab = varargin{1}; + load_attr = varargin{2}; +else + convert2matlab = false; + load_attr = false; +end + +data = []; + +% Collect links +links = info.Links.'; % transform to a row for easier indexing +if ~isempty(links) + for link_ind = 1:length(links) + links(link_ind).Name = [info.Name, '/', links(link_ind).Name]; + end +end + +% Load the datasets +for dataset_ind = 1:length(info.Datasets) + dset_info = info.Datasets(dataset_ind); + dset_name = dset_info.Name; + location = [info.Name, '/', dset_name]; + type = dset_info.Datatype.Class; + + dset_val = hdf5_loadDataset(filename, location, type); + + % Load attributes of a dataset + if load_attr || convert2matlab + [dset_attr, ml_class_dset] = hdf5_loadAttributes(dset_info, convert2matlab, load_attr); + else + ml_class_dset = []; + end + + if ~isempty(ml_class_dset) + switch ml_class_dset + case 'complex' + dset_val = dset_val.r + 1i*dset_val.i; + + case 'cell' + if ~iscell(dset_val) + dset_val = {dset_val}; + end + + case 'char_array' + dset_val = char(dset_val); + + otherwise + conv2ml = str2func(ml_class_dset); + dset_val = conv2ml(dset_val); + end + end + + if load_attr + data.(dset_name).Attributes = dset_attr; + data.(dset_name).Value = dset_val; + + else + data.(dset_name) = dset_val; + end +end + +% Load attributes of a group +if load_attr || convert2matlab + [group_attr, ml_class_group] = hdf5_loadAttributes(info, convert2matlab, load_attr); + if load_attr + data.Attributes = group_attr; + end +else + ml_class_group = []; +end + +% Load the internal groups recursively +for group_ind = 1:length(info.Groups) + [group_data, child_links] = hdf5_loadGroup(filename, info.Groups(group_ind), convert2matlab, load_attr); + + [~, group_name] = fileparts(info.Groups(group_ind).Name); + data.(group_name) = group_data; + + % Aggregate links + links = [links, child_links]; %#ok There shouldn't be too many links present +end + +if ~isempty(ml_class_group) + % convert the groups + data_temp = data; + data = []; + if isfield(data_temp, 'Attributes') + data.Attributes = data_temp.Attributes; + data_temp = rmfield(data_temp, 'Attributes'); + fn = fieldnames(data_temp); + for group_ind = 1:length(fn) + switch ml_class_group + case 'cell' + data.Value{group_ind} = data_temp.(fn{group_ind}); + + case 'structure array' + data.Value(group_ind) = data_temp.(fn{group_ind}); + + otherwise + keyboard + end + end + else + fn = fieldnames(data_temp); + for group_ind = 1:length(fn) + switch ml_class_group + case 'cell' + data{group_ind} = data_temp.(fn{group_ind}); + + case 'structure array' + data(group_ind) = data_temp.(fn{group_ind}); + + otherwise + keyboard + end + end + end + +end + +function [data, ml_class] = hdf5_loadAttributes(info, convert2matlab, load_attr) +data = []; +ml_class = []; +if isfield(info, 'Attributes') % info structure may not contain Attributes field + attr_info = info.Attributes; + for attr_ind = 1:length(attr_info) + attr = attr_info(attr_ind); + attr_name = attr.Name; + if ~isvarname(attr_name) + if ~any(strcmpi({attr_info.Name}, ['MATLAB' attr_name])) && ~strcmpi(attr_name, '_class') + warning('Invalid attribute name! Added "MATLAB" prefix to %s.', attr_name) + attr_name = ['MATLAB' attr_name]; + else + error('Invalid attribute name.') + end + end + if convert2matlab && strcmpi(attr_name, 'MATLAB_class') + ml_class = attr.Value{1}; + + elseif load_attr + if iscell(attr.Value) + data.(attr_name) = attr.Value{1}; + else + data.(attr_name) = attr.Value; + end + end + end +end + +function data = hdf5_loadDataset(filename, location, type) +if strcmp(type, 'H5T_ENUM') + % Workaround for a bug in h5postprocessenums (part of h5read) function + data = read_enum(filename, location); + +else + data = h5read(filename, location); + if iscell(data) && numel(data) == 1 && ischar(data{1}) + data = data{1}; % utility string unwrapping from a single cell + end +end + +function data = hdf5_loadLink(link, varargin) + +if nargin > 2 + convert2matlab = varargin{1}; + load_attr = varargin{2}; +else + convert2matlab = false; + load_attr = false; +end + +switch link.Type + case {'hard link', 'soft link'} + filename = link.Filename; + location = link.Value{1}; + + case 'external link' + filename = absolute_path(link.Value{1}, link.Filename); + location = link.Value{2}; + + otherwise + error('hdf5_load:hdf5_loadLink', ... + strjoin({'Unknown link type at', link.Name})); +end + +link_info = h5info(filename, location); +if strcmp(link_info.Name, '/') % a special case of the root group + link_info.Name = ''; +end + +if isfield(link_info, 'Groups') + [data, links] = hdf5_loadGroup(filename, link_info, convert2matlab, load_attr); + data = assign_links(data, link_info, links); + +elseif isfield(link_info, 'Datatype') + type = link_info.Datatype.Class; + data = hdf5_loadDataset(filename, location, type); + +elseif isfield(link_info, 'Type') + data = hdf5_loadLink(link_info, convert2matlab, load_attr); + +else + error('hdf5_load:hdf5_loadLink', ... + strjoin({'A link at', link_info.Name, 'must be a name of a group, dataset, or link'})); +end + +function data = assign_links(data, info, links, varargin) +import io.HDF.* + +if ~isempty(varargin) + flag = varargin{1}; +else + flag = []; +end +if ~isempty(links) + cut_start = length(info.Name) + 1; + + while true + resolved_links = false(size(links)); + + for ind = 1:length(links) + link = links(ind); + place = strrep(link.Name(cut_start:end), '/', '.'); + target = []; + target_struc = []; + if ~isempty(flag) && contains(flag, 'a') && contains(flag, 'c') + target_struc = ['.Value']; + end + switch link.Type + case {'hard link', 'soft link'} + try + parent = strsplit(link.Value{1}, '/'); + parent = strjoin(parent(1:end-1), '/'); + parent_info = h5info(info.Filename, parent); + if isfield(parent_info, 'Attributes') && ~isempty(parent_info.Attributes) + for ii=1:numel(parent_info.Attributes) + if strcmp(parent_info.Attributes(ii).Name, 'MATLAB_class') && ~isempty(flag) && contains(flag, 'c') + % get pointer index + pnt_indx = strsplit(link.Value{1}, '_'); + pnt_indx = str2double(pnt_indx(end)); + target_add = []; + switch parent_info.Attributes(ii).Value{1} + case 'cell' + target_add = sprintf('{%d}', pnt_indx+1); + + case 'structure array' + target_add = sprintf('(%d)', pnt_indx+1); + + otherwise + keyboard + end + target = [strrep(parent, '/', '.') target_struc target_add]; + break + end + end + end + if isempty(target) + target = [strrep(link.Value{1}(cut_start:end), '/', '.') target_struc]; + end + + evalc(['data', place, ' = data', target]); + + catch + continue % postpone this link resolution + end + + case 'external link' + ext_link = absolute_path(link.Value{1}, info.Filename); + + % make sure to reference the same variable in evalc! + if ~isempty(flag) + target_data = hdf5_load(ext_link, link.Value{2}, flag); %#ok + else + target_data = hdf5_load(ext_link, link.Value{2}); + end + evalc(['data', place, ' = target_data']); + + otherwise + error('hdf5_load:assign_links', ... + strjoin({'Unknown link type at', place})); + end + + resolved_links(ind) = true; + end + + if all(resolved_links) + % all links have been assigned + return + end + + if ~any(resolved_links) + % none of the links has been assigned in this iteration + error('hdf5_load:assign_links', ... + strjoin({'Cannot assign link(s) at', ''})); + end + + links = links(~resolved_links); + end +end + +function filepath = absolute_path(filepath, current_filepath) +if ~startsWith(filepath, '/') + path = fileparts(current_filepath); + filepath = fullfile(path, filepath); +end + +function [startVal, nVals] = parse_range(valRange, maxVal) +if isempty(valRange) % empty + startVal = 1; + nVals = Inf; + +elseif isscalar(valRange) % single value + if valRange <= -1 + valRange = maxVal + valRange + 1; + end + startVal = valRange; + nVals = 1; + +elseif isvector(valRange) && numel(valRange) == 2 % vector with two values + if valRange(1) <= -1 + if isinf(valRange(1)) + valRange(1) = 1; % = -Inf + else + valRange(1) = maxVal + valRange(1) + 1; + end + end + startVal = valRange(1); + + if valRange(2) <= -1 + if isinf(valRange(2)) + valRange(2) = 1; % = -Inf + else + valRange(2) = maxVal + valRange(2) + 1; + end + end + nVals = valRange(2) - startVal + 1; + +else + error('hdf5_load:parse_range', ... + 'A range should be specified with <= 2 parameters'); +end + +if startVal < 1 || startVal > maxVal || nVals < 1 || (nVals > maxVal && ~isinf(nVals)) + error('hdf5_load:parse_range', ... + 'The resulting range is out of data borders'); +end + +function data = read_enum(filename, location) +file_id = H5F.open(filename); +dset_id = H5D.open(file_id, location); +type_id = H5D.get_type(dset_id); + +data = H5D.read(dset_id); % numerical member of enumeration +data = H5T.enum_nameof(type_id, data); % associated symbol name + +H5T.close(type_id); +H5D.close(dset_id); +H5F.close(file_id); + diff --git a/+io/+HDF/hdf5_mv_data.m b/+io/+HDF/hdf5_mv_data.m new file mode 100644 index 0000000..78b64f2 --- /dev/null +++ b/+io/+HDF/hdf5_mv_data.m @@ -0,0 +1,80 @@ +%HDF5_MV_DATA Move data within an HDF5 file +% hdf5_mv_data creates a new (UNIX-like) hard link at loc_dest to the dataset at +% loc_origin and deletes the hard link to the dataset at loc_origin. +% +% file... HDF filename +% loc_origin... location of the data that needs to be moved +% loc_dest... destination and name of the new data +% +% EXAMPLE: +% % move dataset probe from root to group measurements +% hdf5_mv_data('./awesome_file.h5', 'probe', 'measurements/probe') +% +% Please notice that all groups and datasets have to exist before running +% the script! + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. +function hdf5_mv_data( file, loc_origin, loc_dest) +import io.HDF.* +plist = 'H5P_DEFAULT'; + +fileID = H5F.open(file,'H5F_ACC_RDWR',plist); + +gpath1 = strsplit(rm_delimiter(loc_origin), '/'); +gid1 = add_groups(fileID, gpath1(1:end-1), plist, false); + +gpath2 = strsplit(rm_delimiter(loc_dest), '/'); +gid2 = add_groups(fileID, gpath2(1:end-1), plist, false); + +try + datasetID = H5D.open(gid1{end}, gpath1{end}); + dataset = true; +catch + gid1 = add_groups(fileID,gpath1,plist, false); + datasetID = gid1{end}; + dataset = false; +end + +H5O.link(datasetID,gid2{end},gpath2{end},plist,plist); +if dataset + H5L.delete(gid1{end}, gpath1{end}, plist); +else + H5L.delete(gid1{end-1}, gpath1{end}, plist); +end + +H5F.close(fileID); + + +end diff --git a/+io/+HDF/hdf5_rm_attr.m b/+io/+HDF/hdf5_rm_attr.m new file mode 100644 index 0000000..6b7bd5c --- /dev/null +++ b/+io/+HDF/hdf5_rm_attr.m @@ -0,0 +1,65 @@ +%HDF5_RM_ATTR Delete attribute(s) from HDF file +% +% file... HDF filename +% loc... location within the HDF file +% attr_name... string or cell of strings containing the names of the +% obsolete attributes +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function hdf5_rm_attr( file, loc, attr_name) +import io.HDF.* + +plist = 'H5P_DEFAULT'; +% open file +fileID = H5F.open(file,'H5F_ACC_RDWR',plist); + +% get group ID +gpath = strsplit(rm_delimiter(loc), '/'); +gid = add_groups(fileID, gpath(1:end-1), plist, false); + +% delete attributes +if iscell(attr_name) + for ii=1:length(attr_name) + H5A.delete(gid{end}, attr_name{ii}) + end +else + H5A.delete(gid{end}, attr_name) +end + + +end + diff --git a/+io/+HDF/hdf5_rm_data.m b/+io/+HDF/hdf5_rm_data.m new file mode 100644 index 0000000..ab5553a --- /dev/null +++ b/+io/+HDF/hdf5_rm_data.m @@ -0,0 +1,57 @@ +%HDF5_RM_DATA Delete a dataset within an HDF5 file +% +% file... HDF filename +% loc... location of the dataset that needs to be removed +% +% Please notice that HDF5 does not free the space after removing datasets! +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function hdf5_rm_data( file, loc) +import io.HDF.* +plist = 'H5P_DEFAULT'; + +fileID = H5F.open(file,'H5F_ACC_RDWR',plist); + +gpath1 = strsplit(rm_delimiter(loc), '/'); +gid1 = add_groups(fileID, gpath1(1:end-1), plist, false); + +H5L.delete(gid1{end}, gpath1{end}, plist); + +H5F.close(fileID); + + +end diff --git a/+io/+HDF/hdf5read_main.m b/+io/+HDF/hdf5read_main.m new file mode 100644 index 0000000..6383d16 --- /dev/null +++ b/+io/+HDF/hdf5read_main.m @@ -0,0 +1,143 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: hdf5read_main.m,v $ +% +% $Revision: 1.1 $ $Date: 2010/10/02 07:58:50 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading HDF5 files written for example by the EIGER server +% program cbd_server +% +% Note: +% So far this is mainly a place holder for a thorough implementation. +% +% Dependencies: +% - image_read_set_default +% - fopen_until_exists +% - get_hdr_val +% +% +% history: +% +% September 30th 2010: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = hdf5read_main(filename,varargin) +import io.HDF.* +import io.image_read +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'h5'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',value pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end +% close input data file +fclose(fid); + +% get file header +hdr = hdf5info(filename); + +% store part of the file header in the return argument +frame.header = {}; +frame.header{end+1} = 'Exposure_time 1.0'; +% add the file modification date to the header +dir_entry = dir(filename); +frame.header{end+1} = [ 'DateTime ' dir_entry.date ]; + +% read all data of first data set at once +frame.data = hdf5read(hdr.GroupHierarchy(1).Groups(1).Datasets(1)); + +if (debug_level >= 2) + fprintf('%dx%dx%dx%s data bytes read\n',... + size(fdat,1),size(fdat,2),size(fdat,3),size(fdat,4)); +end diff --git a/+io/+HDF/private/rm_delimiter.m b/+io/+HDF/private/rm_delimiter.m new file mode 100644 index 0000000..126d240 --- /dev/null +++ b/+io/+HDF/private/rm_delimiter.m @@ -0,0 +1,43 @@ +%RM_DELIMITER makes sure that the path does not start with / + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function path = rm_delimiter(path) +% make sure that the path does not start with / +if strcmp(path(1), '/') + path = path(2:end); +end +end + diff --git a/+io/+HDF/save2hdf5.m b/+io/+HDF/save2hdf5.m new file mode 100644 index 0000000..acb822e --- /dev/null +++ b/+io/+HDF/save2hdf5.m @@ -0,0 +1,242 @@ +%SAVE2HDF5 saves matlab data to a Hierarchical Data Format file (hdf5) +% +% filename... full path to file, including file extension +% data... matlab structure or array or link +% data_name... needed if input data is not a matlab structure, needs +% to be given as name/value pair +% +% *optional* +% overwrite... replace existing file if it exists +% gpath... specify the group to which you want to append the data +% (only if data is an array); default root ('/') +% Attributes... structure of attributes; will be appended to current +% gpath +% comp... compression level; default 0 (no compression) +% creator... attribute in root; default 'ptycho_recons' +% +% +% If you want to save a structure, everything declared within an 'Attributes' +% fieldname will be treated as an attribute to the current group. +% If you want to add attributes to a dataset, you have to define your +% data within .Value and your attributes within .Attributes. +% +% A simple structure could look like: +% h5_struc = []; +% h5_struc.probe_mask = ones(256,256); +% h5_struc.Attributes.probe_id = 1; +% h5_struc.measurement.n0.diff = fmag(:,:,1); +% h5_struc.measurement.n0.Attributes.detector = 0; +% h5_struc.measurement.n1.diff.Value = fmag(:,:,2); +% h5_struc.measurement.n1.diff.Attributes.slice = 2; +% +% fmag(:,:,1) will be written to dataset 'diff' in group '/measurement/n0' +% fmag(:,:,2) with attribute 'slice' will be written to dataset 'diff' in +% group '/measurement/n1' +% +% +% EXAMPLES: +% -) if data is a matlab structure: +% save2hdf5('./awesome_file.h5', data); +% save2hdf5('./awesome_file.h5', data, 'overwrite', true); +% +% +% -) if data is a matlab array: +% save2hdf5('./awesome_file.h5', data, 'data_name', data_name); +% save2hdf5('./awesome_file.h5', data, 'data_name', 'my_dataset',... +% 'gpath', 'group1/group2', 'Attributes', attr_struc); +% +% -) if data is a link: +% currently, only external links ('ext') and internal soft links +% ('int_soft') are supported +% +% external links have to be specified by a single string with +% 3 sections: '::' +% +% e.g.: 'ext:./awesome_file2.h5:/data' +% save2hdf5('./awesome_file.h5',... +% 'ext:./awesome_file2.h5:/data', 'data_name', data_name) +% +% will create a link called $data_name to dataset (or group) '/data' +% in './awesome_file2.h5' +% +% internal links have to be specified by a single string with +% 2 sections: ':' +% +% e.g.: 'int_soft:/data' +% save2hdf5('./awesome_file.h5',... +% 'int_soft:/data', 'data_name', data_name, 'gpath', 'g1/g2') +% +% will create a link called $data_name to dataset (or group) '/data' +% in '/g1/g2' +% +% +% Please notice that structures are not supported as attributes, i.e. +% h5_struc = []; +% h5_struc.attr.probe.probe_id = 1; +% +% save2hdf5('./awesome_file.h5', h5_struc) +% +% will crash! +% +% + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function save2hdf5( filename, data, varargin) +import io.HDF.* + +% take care of input arguments +overwrite = false; +gpath_full = ''; +attr = []; +data_name = ''; +comp = 0; +creator = 'ptycho_recons'; +iscopy = false; +extend_dim = 0; +extendable = false; +extend_offset = 0; +extend_maxdims = 0; + +vararg = cell(0,0); +% parse the variable input arguments vararg = cell(0,0); +if ~isempty(varargin) + for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch lower(name) + case 'data_name' + data_name = value; + case 'overwrite' + overwrite = value; + case 'gpath' + gpath_full = value; + case 'attr' + attr = value; + case 'comp' + comp = value; + case 'creator' + creator = value; + case 'iscopy' + iscopy = value; + case 'extend_dim' + extend_dim = value; + case 'extendable' + extendable = value; + case 'extend_offset' + extend_offset = value; + case 'extend_maxdims' + extend_maxdims = value; + + otherwise + vararg{end+1} = name; + vararg{end+1} = value; + end + end +end + +if ~isstruct(data) + full_data = false; +else + full_data = true; +end + +if ~isstruct(data) && isempty(data_name) + data_name = inputname(2); + if isempty(data_name) + error('Please specify the data_name.') + end +end + +if extendable && extend_dim + error('Extending the dimension of an unlimited dataset is currently not supported.'); +end + +plist = 'H5P_DEFAULT'; + +%%% create file if it does not exist +if exist(filename, 'file')&&~overwrite + fileID = H5F.open(filename,'H5F_ACC_RDWR',plist); +else + fileID = H5F.create(filename,'H5F_ACC_TRUNC','H5P_DEFAULT','H5P_DEFAULT'); + if ~iscopy + write_attribute(fileID, filename, 'filename'); + write_attribute(fileID, creator,'creator'); + write_attribute(fileID, datestr(now),'file_time'); + end +end + +if full_data + + %%%%%%%%%%%%%%%%%%%%%%%%% + %%% data as structure %%% + %%%%%%%%%%%%%%%%%%%%%%%%% + + add_content(data, fileID, plist, comp, overwrite) + + +else + + %%%%%%%%%%%%%%%%%%%%% + %%% data as array %%% + %%%%%%%%%%%%%%%%%%%%% + + % prepare group handles + if ~isempty(gpath_full) + gpath = strsplit(rm_delimiter(gpath_full), '/'); + gid = add_groups(fileID, gpath, plist, false); + else + gid{1} = fileID; + end + + % write data to file + write_dataset(data, gid{end}, data_name, plist, comp, overwrite, [], extend_dim, extendable, extend_offset, extend_maxdims); + + % append attributes + if ~isempty(attr) + attr_fn = fieldnames(attr); + for ii=1:length(attr_fn) + write_attribute(gid{end}, attr.(attr_fn{ii}), attr_fn{ii}, true); + end + end + +end + +% close handles +H5F.close(fileID); + +end + + diff --git a/+io/+HDF/write_attribute.m b/+io/+HDF/write_attribute.m new file mode 100644 index 0000000..acbff84 --- /dev/null +++ b/+io/+HDF/write_attribute.m @@ -0,0 +1,119 @@ +%WRITE_ATTRIBUTE write attribute data_name with value data to ID gid + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function write_attribute(gid, data, data_name, varargin) +import io.HDF.* + +if nargin > 3 + safe = varargin{1}; +else + safe = false; +end + +[datatypeID, data] = get_datatype(data); +if ischar(data) + % The ptycho C++ code expects strings as H5S_SCALAR, so we have to + % convert it + data = {data}; + filetype = H5T.copy ('H5T_FORTRAN_S1'); + H5T.set_size (filetype,'H5T_VARIABLE'); + memtype = H5T.copy ('H5T_C_S1'); + H5T.set_size (memtype, 'H5T_VARIABLE'); + space = H5S.create ('H5S_SCALAR'); + if safe + try + attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT'); + catch + H5A.delete(gid, data_name); + attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT'); + end + else + attr = H5A.create (gid, data_name, filetype, space, 'H5P_DEFAULT'); + end + + H5A.write (attr, memtype, data); + +elseif iscell(data) + % If it is a cell, save it as 1D dataset + H5T.set_size(datatypeID,'H5T_VARIABLE'); + agcv = H5ML.get_constant_value('H5S_UNLIMITED'); + dspace = H5S.create_simple(1,numel(data),agcv); + + plist = H5P.create('H5P_ATTRIBUTE_CREATE'); + if safe + try + attr = H5A.create(gid,data_name,datatypeID,dspace,plist); + catch + H5A.delete(gid, data_name); + attr = H5A.create(gid,data_name,datatypeID,dspace,plist); + end + else + attr = H5A.create(gid,data_name,datatypeID,dspace,plist); + end + H5A.write(attr,'H5ML_DEFAULT',data); + +else + + acpl = H5P.create('H5P_ATTRIBUTE_CREATE'); + dims = size(data); + if length(dims)>1 && dims(2)~=1 + if dims(1) == 1 + space_id = H5S.create_simple(dims(1), dims(2), []); + else + space_id = H5S.create_simple(dims(1), dims, []); + end + else + space_id = H5S.create('H5S_SCALAR'); + end + if safe + try + attr = H5A.create(gid,data_name,datatypeID,space_id,acpl); + catch + H5A.delete(gid, data_name); + attr = H5A.create(gid,data_name,datatypeID,space_id,acpl); + end + else + attr = H5A.create(gid,data_name,datatypeID,space_id,acpl); + end + + + H5A.write(attr,'H5ML_DEFAULT',data) + + +end +H5A.close(attr); +end + diff --git a/+io/+HDF/write_dataset.m b/+io/+HDF/write_dataset.m new file mode 100644 index 0000000..06e323d --- /dev/null +++ b/+io/+HDF/write_dataset.m @@ -0,0 +1,370 @@ +%WRITE_DATASET write dataset data_name, containing data to ID gid + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function write_dataset(data, gid, data_name, plist, varargin) +import io.HDF.* + +extend_data = false; +link = false; +link_type = ''; +cellstrdata = false; +write_data = true; + +if ~isempty(varargin) + comp = varargin{1}; +else + comp = true; +end +if nargin > 5 + overwrite = varargin{2}; +else + overwrite = true; +end + +if nargin > 6 + data_attr = varargin{3}; +else + data_attr = []; +end + +if nargin > 7 + extend_dim = varargin{4}; +else + extend_dim = 0; +end + +if nargin > 8 + extendable = varargin{5}; +else + extendable = false; +end + +if nargin > 9 + extend_offset = varargin{6}; +else + extend_offset = 0; +end + +if nargin > 10 + extend_maxdims = varargin{7}; +else + extend_maxdims = 0; +end + +[datatype, data] = get_datatype(data); +filespaceID = []; + + function create_dataspace() + + if extendable + unlimited = H5ML.get_constant_value('H5S_UNLIMITED'); + dims_max = repmat(unlimited, 1, numel(dims)); + else + dims_max = dims; + end + + if ~extend_dim + if ~extendable + dataspaceID = H5S.create_simple(length(dims), fliplr(dims), fliplr(dims_max)); + else + try + datasetID = H5D.open(gid, data_name); + filespaceID = H5D.get_space(datasetID); + [~, spaceDims] = H5S.get_simple_extent_dims(filespaceID); +% spaceDims = fliplr(spaceDims); + + start = ones(1,numel(dims))-1; + count = dims; + + stride = ones(1, numel(start)); + boundsEnd = start + (count).*stride; + new_dims = fliplr(boundsEnd); + H5S.close(filespaceID); + H5D.set_extent(datasetID,new_dims); + filespaceID = H5D.get_space(datasetID); + H5S.select_hyperslab(filespaceID, 'H5S_SELECT_SET', fliplr(start), fliplr(stride), ... + fliplr(count), ones(1,length(start))); + + dataspaceID = H5S.create_simple(numel(count),fliplr(count),[]); + + extend_data = true; + + catch + dataspaceID = H5S.create_simple(length(dims), fliplr(dims), fliplr(dims_max)); + end + end + + + else + try + datasetID = H5D.open(gid, data_name); + filespaceID = H5D.get_space(datasetID); + [~, spaceDims] = H5S.get_simple_extent_dims(filespaceID); + spaceDims = fliplr(spaceDims); + if extend_offset + start = [ones(1,extend_dim-1) extend_offset+1]-1; + else + start = [ones(1,extend_dim-1) spaceDims(end)+1]-1; + end + + if numel(spaceDims) > numel(dims) + count = [dims 1]; + else + count = dims; + end + stride = ones(1, numel(start)); + boundsEnd = start + (count-1).*stride; + if extend_maxdims + boundsStart = spaceDims; + boundsStart(end) = extend_maxdims; + else + boundsStart = spaceDims; + end + new_dims = fliplr(max(boundsStart,boundsEnd+1)); + H5S.close(filespaceID); + H5D.set_extent(datasetID,new_dims); + filespaceID = H5D.get_space(datasetID); + H5S.select_hyperslab(filespaceID, 'H5S_SELECT_SET', fliplr(start), fliplr(stride), ... + fliplr(count), ones(1,length(start))); + + dataspaceID = H5S.create_simple(numel(count),fliplr(count),[]); + + extend_data = true; + + + catch + unlimited = H5ML.get_constant_value('H5S_UNLIMITED'); + maxdims = [dims(1:extend_dim-1) unlimited]; +% maxdims = repmat(-1, 1, extend_dim); + if numel(maxdims) > numel(dims) + dims = [dims 1]; + end + dataspaceID = H5S.create_simple(length(dims), [fliplr(dims)], fliplr(maxdims)); + end + end + end + +if strcmp(datatype, 'complex') + + %%% prepare compound dataset for complex input data + dims = size(data); + + data_temp = data; + data = []; + data.r = real(data_temp); + data.i = imag(data_temp); + + create_dataspace(); + + % Create the required data types + complexType = H5T.copy(get_datatype(data.r)); + sz = H5T.get_size(complexType); + + % Create the compound datatype for memory. + datatypeID = H5T.create ('H5T_COMPOUND', 2*sz); + H5T.insert (datatypeID, 'r',0, complexType); + H5T.insert (datatypeID, 'i',sz, complexType); + memtype = datatypeID; + + data_attr.MATLAB_class = 'complex'; + + +elseif ischar(data) + % check if char is a link + ch_entrs = strsplit(data, ':'); + if length(ch_entrs) >= 2 + link = true; + if strcmp(ch_entrs{1}, 'ext') + % prepare external link + link_type = 'ext'; + elseif strcmp(ch_entrs{1}, 'int_soft') + % prepare internal soft link + link_type = 'int_soft'; + elseif strcmp(ch_entrs{1}, 'int_hard') + % prepare internal hard link + link_type = 'int_hard'; + end + else + data = {data}; + datatypeID = H5T.copy ('H5T_FORTRAN_S1'); + H5T.set_size (datatypeID,'H5T_VARIABLE'); + memtype = H5T.copy ('H5T_C_S1'); + H5T.set_size (memtype, 'H5T_VARIABLE'); + dataspaceID = H5S.create ('H5S_SCALAR'); + + end + +elseif iscell(data) || strcmp(datatype, 'char_array') + + + if iscellstr(data) + cellstrdata = true; + datatypeID = H5T.copy ('H5T_C_S1'); + H5T.set_size (datatypeID, 'H5T_VARIABLE'); + + dgcv = H5ML.get_constant_value('H5S_UNLIMITED'); + dataspaceID = H5S.create_simple(1,numel(data),dgcv); + memtype = datatypeID; + plist_cr = H5P.create('H5P_DATASET_CREATE'); + H5P.set_chunk(plist_cr,1); + if strcmp(datatype, 'char_array') + data_attr.MATLAB_class = 'char_array'; + end + else + write_data = false; + fn_names = cell(1,length(data)); + cell_gid = add_groups(gid, data_name, plist, true); + for ii=1:length(data) + fn_names{ii} = sprintf([data_name '_%d'],ii-1); + write_dataset(data{ii}, cell_gid, fn_names{ii}, plist, comp, overwrite); + end + write_attribute(cell_gid, 'cell', 'MATLAB_class'); + + + end + +elseif isstruct(data) + + write_data = false; + struct_gid = add_groups(gid, data_name, plist, true); + add_content(data, struct_gid, plist, comp, overwrite); + +else + datatypeID = H5T.copy(datatype); + dims = size(data); + if isfield(data_attr, 'save2hdf5DataShape') + dims = data_attr.save2hdf5DataShape; + end + + % prepare dataspace + create_dataspace(); + + memtype = 'H5ML_DEFAULT'; +end + +%%% create groups and write data +if comp && ~iscell(data) && ~ischar(data) && write_data || extend_dim || extendable + % define compression and chunk size + plist_ch = H5P.create('H5P_DATASET_CREATE'); + if length(dims)>=3 + chunk_dims = [dims(1) dims(2) ones(1, numel(dims)-2)]; + else + chunk_dims = dims; + end + + h5_chunk_dims = fliplr(chunk_dims); + H5P.set_chunk(plist_ch,h5_chunk_dims); + H5P.set_shuffle(plist_ch); + if comp + H5P.set_deflate(plist_ch,comp); + end + + % Try to create a new dataset. If it exists, try to open it. + try + if ~extend_data + if cellstrdata + datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_cr); + else + datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_ch); +% create_dataspace(); + end + end + catch + if ~overwrite + try + datasetID = H5D.open(gid, data_name); + catch + error('Could not create dataset %s! Try a different name or overwrite the already existing file.', data_name); + end + else + keyboard + error('Dataset %s already exists! Try a different name or overwrite the already existing file.', data_name); + end + end + +elseif ~link && write_data + % Same as above but without compression: + % Try to create a new dataset. If it exists, try to open it. + try + if cellstrdata + datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist_cr); + else + datasetID = H5D.create(gid,data_name,datatypeID,dataspaceID,plist); + end + catch + if ~overwrite + try + datasetID = H5D.open(gid, data_name); + catch + error('Could not open dataset %s! Try a different name or overwrite the already existing file.', data_name); + end + else + error('Dataset %s already exists! Try a different name or overwrite the already existing file.', data_name); + end + end +end + +if write_data + % write data to disk or link data + + if ~link && ~extend_data + H5D.write(datasetID,memtype,'H5S_ALL','H5S_ALL',plist ,data); + % append attributes if needed + if ~isempty(data_attr) + fn = fieldnames(data_attr); + for ii=1:length(fn) + write_attribute(datasetID, data_attr.(fn{ii}), fn{ii}, true); + end + end + H5D.close(datasetID); + elseif extend_data + H5D.write(datasetID,memtype,dataspaceID, filespaceID, plist, data) + elseif strcmp(link_type, 'ext') + H5L.create_external(ch_entrs{2},ch_entrs{3},gid,data_name,plist,plist); + elseif strcmp(link_type, 'int_hard') + error('Currently not supported, sorry!') + % H5L.create_hard(ch_entrs{2},'g3',gid1,'g4',plist,plist); + elseif strcmp(link_type, 'int_soft') + H5L.create_soft(ch_entrs{2},gid,data_name,plist,plist); + end + + + + +end + +end + diff --git a/+io/common_header_value.m b/+io/common_header_value.m new file mode 100644 index 0000000..9cb469f --- /dev/null +++ b/+io/common_header_value.m @@ -0,0 +1,201 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: common_header_value.m,v $ +% +% $Revision: 1.7 $ $Date: 2013/01/25 10:22:26 $ +% $Author: $ +% $Tag: $ +% +% Description: +% return header information which are common to most file formats used at +% the cSAXS beamline +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - get_hdr_val +% +% +% history: +% +% September 30th 2010: +% add HDF5 +% +% October 3rd 2008: +% update FLI date-field since version 1.20 provides a time stamp string +% +% August 28th 2008: +% correct error display for unknown extensions, +% add extension .dat +% +% July 17th 2008: add mar extension +% +% May 7th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [value] = common_header_value(header,extension,signature) +import io.image_read +import utils.get_hdr_val + +% initialize return value +value = []; + +% check number of input arguments +if (nargin ~= 3) + common_header_value_help(); + error('invalid number of input arguments'); +end + +switch extension + case 'cbf' + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'Exposure_time',' %f',1); + case 'Date' + % The date is available in the Pilatus comments, without + % any signature. Searching for the 20 string will fail + % beyond the year 2099 + value = [ '20' get_hdr_val(header,'# 20',' %[^\r]',1) ]; + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case {'h5', 'hdf5'} + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'Exposure_time',' %f',1); + case 'Date' + value = get_hdr_val(header,'DateTime',' %[^\n]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case 'dat' + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'Exposure_time',' %f',1); + case 'Date' + value = get_hdr_val(header,'DateTime',' %[^\n]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case 'edf' + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'count_time',' = %f',1); + case 'Date' + value = get_hdr_val(header,'Date',' = %[^;]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case {'mar','mccd'} + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'ExposureTime_ms',' %f',1)/1000; + case 'Date' + value = get_hdr_val(header,'DateTime',' %[^\n]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case {'mat'} + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'Exposure_time',' %f',1); + case 'Date' + value = get_hdr_val(header,'DateTime',' %[^\n]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case 'raw' + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'exptimesec',' %f',1); + case 'Date' + value = get_hdr_val(header,'version',' %[^\n]',1); + if (version < 1.20) + value = get_hdr_val(header,'FileTimestamp',' %[^\n]',1); + else + value = get_hdr_val(header,'timestamp_string',' %[^\n]',1); + end + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case 'spe' + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'exposure',' %f',1); + case 'Date' + value = [ get_hdr_val(header,'date',' %[^\n]',1)'; ' '; + get_hdr_val(header,'ExperimentTimeLocal',' %[^\n]',1)' ]'; + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + case {'tif', 'tiff'} + switch signature + case 'ExposureTime' + value = get_hdr_val(header,'Exposure_time',' %f',1); + case 'Date' + value = get_hdr_val(header,'DateTime',' %[^\n]',1); + otherwise + error('unknown signature %s for extension %s',... + signature,extension); + end + otherwise + common_header_value_help(); + error('unknown extension ''%s''',extension); +end + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [] = common_header_value_help() + +fprintf('Usage:\n'); +fprintf('[value]=%s(header,extension,signature);',mfilename); +fprintf('header and extension are returned by image_read\n'); +fprintf('The following signatures are recognized:\n'); +fprintf('ExposureTime exposure time in seconds\n'); +fprintf('Date date string in detector specific format\n'); +fprintf('Example:\n'); +fprintf('exp_time_sec=common_header_value(frame.header{1},frame.extension{1},''ExposureTime'');\n'); diff --git a/+io/convert_radial_2_dat.m b/+io/convert_radial_2_dat.m new file mode 100644 index 0000000..f3f431c --- /dev/null +++ b/+io/convert_radial_2_dat.m @@ -0,0 +1,86 @@ +% convert_radial_2_dat converts all radial integration mat files in +% readpahtmask into dat files, pauses 10 minutes and repeats +% +% Inputs: +% **readpathmask A cell containing the input file string masks +% **outpathmask A cel containint the corresponding output +% directories +% +% Example: +% readpathmask{1} = '~/Data10/analysis/radial_integration/*.mat'; +% outpathmask{1} = '~/Data10/analysis/radial_integration_dat/'; +% readpathmask{2} = '~/Data10/analysis/radial_integration_waxs/*.mat'; +% outpathmask{2} = '~/Data10/analysis/radial_integration_waxs_dat/'; +% convert_radial_2_dat(readpathmask, outpathmask) + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2019 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + +% clear + +% readpathmask{1} = '~/Data10/analysis/radial_integration/*.mat'; +% outpathmask{1} = '~/Data10/analysis/radial_integration_dat/'; +% +% readpathmask{2} = '~/Data10/analysis/radial_integration_waxs/*.mat'; +% outpathmask{2} = '~/Data10/analysis/radial_integration_waxs_dat/'; + +function convert_radial_2_dat(readpathmask, outpathmask) + +while 1==1 +for ii = 1:numel(readpathmask) + if ~exist(outpathmask{ii},'dir') + mkdir(outpathmask{ii}) + end + files = dir(readpathmask{ii}); + for jj = 1:numel(files) + currentradial = fullfile(files(jj).folder,files(jj).name); + [auxpath, auxname, auxext] = fileparts(currentradial); + outputradial = fullfile(outpathmask{ii},[auxname '.dat']); + s = load(currentradial); + + save_data = [s.q.', ... + reshape(s.I_all , [size(s.I_all,1) size(s.I_all,2)*size(s.I_all,3) ]) , ... + reshape(s.I_std , [size(s.I_std,1) size(s.I_std,2)*size(s.I_std,3) ]) , ... + reshape(s.norm_sum , [size(s.norm_sum,1) size(s.norm_sum,2)*size(s.norm_sum,3) ]) , ... + ]; + + fprintf('Saving %s\n',outputradial); + save( outputradial , 'save_data', '-ascii','-double'); + end + clear files +end +fprintf('Pausing 10 minutes\n') +pause(60*10) +end diff --git a/+io/convert_to_rgb.m b/+io/convert_to_rgb.m new file mode 100644 index 0000000..91de678 --- /dev/null +++ b/+io/convert_to_rgb.m @@ -0,0 +1,32 @@ +function [rgb_data] = convert_to_rgb(data) +%Convert complex data into rgb image showing both magnitude and phase +% Detailed explanation goes here + +import math.sp_quantile + +[W,H] = size(data); +adata = abs(data); + +alpha = 1e-3; +tmp= sort(adata(:)); +MAX = tmp(ceil(end*(1-alpha))); +ind = adata > MAX; +data(ind) = MAX * data(ind) ./ abs(data(ind)); +adata = abs(data); +range = sp_quantile(adata(:), [1e-2, 1-1e-2],10); +adata = (adata - range(1) ) ./ ( range(2) - range(1) ); + +ang_data = angle(data); +hue = mod(ang_data+2.5*pi, 2*pi)/(2*pi); +hsv_data = [ hue(:) , ones(W*H,1), adata(:) ]; + +hsv_data = min(max(0, hsv_data),1); + + +rgb_data = hsv2rgb(hsv_data); + +rgb_data = reshape(rgb_data, W,H,3); +rgb_data = min(1,rgb_data); + +end + diff --git a/+io/datread.m b/+io/datread.m new file mode 100644 index 0000000..3793bbf --- /dev/null +++ b/+io/datread.m @@ -0,0 +1,201 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: datread.m,v $ +% +% $Revision: 1.2 $ $Date: 2009/02/20 19:33:01 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading .dat files in self-defined data formats +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read_set_default +% - fopen_until_exists +% - get_hdr_val +% - compiling cbf_uncompress.c increases speed but is not mandatory +% +% +% history: +% +% February 18th 2009: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = datread(filename,varargin) +import io.* +import utils.fopen_until_exists +import utils.get_hdr_val +import utils.char_to_cellstr + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'cbf'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',value pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + +% expected maximum length for the text header +max_header_length = 4096; + +% end-of-header signature +eoh_signature = [ '# end-of-header' char(10) ]; + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end + +% read all data at once +[fdat,fcount] = fread(fid,'uint8=>uint8'); + +% close input data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes read\n',fcount); +end + +% files with header start with a has sign, otherwise just a series of +% numbers is expected +if (~strcmp(fdat(1),'#')) + % convert the array to cell strings + cell_values = char_to_cellstr( char(fdat)' ); + % convert the strings to double precision values + frame.data = zeros(length(cell_values),1); + for (ind = 1:length(cell_values)) + frame.data(ind) = str2double(cell_values{ind}); + end + + % No header information are available. + % Fake exposure time information to avoid problems in other + % macros. + frame.header{end+1} = 'Exposure_time 1.0'; + % add the file modification date to the header + dir_entry = dir(filename); + frame.header{end+1} = [ 'DateTime ' dir_entry.date ]; +end + + +% search for end of header signature within the expected maximum length of +% a header +end_of_header_pos = ... + strfind( fdat(1:min(max_header_length,length(fdat)))',... + eoh_signature ); +if (length(end_of_header_pos) < 1) + error( [ filename,': no header end signature found' ] ); + return; +end +if (debug_level >= 2) + fprintf('Header length is %d bytes.\n',end_of_header_pos -1); +end + +% return the complete header as lines of a cell array +frame.header = char_to_cellstr( char(fdat(1:(end_of_header_pos-1))') ); + +% increase the index to the first data byte +end_of_header_pos = end_of_header_pos + length(eoh_signature); + +% check for information on the various dimensions in ascending speed order +dim1 = get_hdr_val(frame.header,'dim2','%d',1); +dim2 = get_hdr_val(frame.header,'dim1','%d',1); +dim3 = get_hdr_val(frame.header,'number-of-exposures','%d',1); +dim4 = get_hdr_val(frame.header,'channels','%d',1); + +if (debug_level >= 2) + fprintf('Frame dimensions are %d x %d % %d x %d.\n', ... + dim4,dim3,dim2,dim1); +end + +% store the numbers in the array, fastest axis first +frame.data = zeros(dim4,dim3,dim2,dim1); + +% convert the strings to double precision values +data_1d = sscanf(char(fdat(end_of_header_pos:end))','%f'); +frame.no_of_el_read = length(data_1d); +if (length(data_1d) > numel(frame.data)) + frame.data = zeros(dim4,dim3,dim2,ceil(length(data_1d)/(dim4*dim3*dim2))); +end +frame.data(1:frame.no_of_el_read) = data_1d; + diff --git a/+io/edfread.m b/+io/edfread.m new file mode 100644 index 0000000..0576e16 --- /dev/null +++ b/+io/edfread.m @@ -0,0 +1,234 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: edfread.m,v $ +% +% $Revision: 1.1 $ $Date: 2008/06/10 17:05:14 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading ESRF Data Format (EDF) files written by the +% Pilatus detector control program camserver. +% +% Note: +% Currently this routine supports only the subset of EDF features needed to +% read the Pilatus detector data. +% Call without arguments for a brief help text. +% +% Dependencies: +% - fopen_until_exists +% - get_hdr_val +% - image_read_set_default +% +% +% history: +% +% May 9th 2008: 1st version after redesign + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = edfread(filename,varargin) +import io.* +import utils.char_to_cellstr +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'edf'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + +% expected maximum length for the text header +max_header_length = 4096; + + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end + +% read all data at once +[fdat,fcount] = fread(fid,'uint8=>uint8'); + +% close input data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes read\n',fcount); +end + +% search for end of header signature within the expected maximum length of +% a header +end_of_header_pos = 1024; +max_pos = min( max_header_length,length(fdat) ); + +while ((end_of_header_pos < max_pos) && (fdat(end_of_header_pos-1) ~= '}')) + end_of_header_pos = end_of_header_pos +1024; +end +if (end_of_header_pos >= max_pos) + error('no header end signature found'); +end +if (debug_level >= 2) + fprintf('Header length is %d bytes.\n',end_of_header_pos); +end +data_length = fcount - end_of_header_pos; + +% convert the header to lines of a cell array +frame.header = char_to_cellstr( char(fdat(1:(end_of_header_pos-1))') ); + +% check for opening parenthesis +if (frame.header{1} ~= '{') + error([filename ': EDF start ,''{'' not found in first line ''' ... + frame.header{1} '''' ]); +end + + +% extract the mandatory information for data extraction from the header: +byte_order = get_hdr_val(frame.header,'ByteOrder',' = %s',1); +dim1 = get_hdr_val(frame.header,'Dim_1',' = %d',1); +dim2 = get_hdr_val(frame.header,'Dim_2',' = %d',1); +data_type = get_hdr_val(frame.header,'DataType',' = %s',1); +if (debug_level >= 2) + fprintf('Byte order is %s\n',byte_order); + fprintf('Frame dimensions are %d x %d.\n',dim2,dim1); + fprintf('Data type is %s\n',data_type); +end + +% determine number of bytes per pixel +switch data_type + case 'UnsignedByte', + bytes_per_pixel = 1; + data_class = 'uint8'; + case 'UnsignedShort', + bytes_per_pixel = 2; + data_class = 'uint16'; + case {'SignedInteger','UnsignedInteger','UnsignedInt','UnsignedLong'} + bytes_per_pixel = 4; + data_class = 'uint32'; + case {'Float','FloatValue','Real'} + bytes_per_pixel = 4; + data_class = 'single'; + case 'DoubleValue' + bytes_per_pixel = 8; + data_class = 'double'; + otherwise + error('unsupported data type %s',data_type); +end +no_of_bytes = bytes_per_pixel * dim1 * dim2; +if (debug_level >= 2) + fprintf('%d bytes per pixel, %d in total expected, %d available\n',... + bytes_per_pixel,no_of_bytes,data_length); +end + +% check length of available data +if (no_of_bytes > data_length) + error('%d data bytes expected, %d are available',... + no_of_bytes,data_length); +end + +% compare file with machine byte order, swap if necessary +[str,maxsize,endian] = computer; +if (((strcmp(byte_order,'HighByteFirst')) && (endian == 'L')) || ... + ((strcmp(byte_order,'LowByteFirst')) && (endian == 'H'))) + if (debug_level >= 2) + fprintf('Machine byte order is %s: swapping data bytes\n',... + endian,bytes_per_pixel); + end + dat = fdat(end_of_header_pos+1:end_of_header_pos+no_of_bytes); + dat = reshape(dat,bytes_per_pixel,[]); + dat = flipud(dat); + fdat(end_of_header_pos+1:end_of_header_pos+no_of_bytes) = dat(:); +end + +% extract the frame from the binary data +[frame.data] = ... + double(reshape(typecast(fdat(end_of_header_pos+1:end_of_header_pos+no_of_bytes),... + data_class),... + dim1,dim2)); + + +% conversion to standard view on Pilatus 2M data at the SLS/cSAXS beamline +% if (~original_orientation) +% % this is slow, even slower is fliplr(flipud(frame.')) +% frame.data = frame.data(end:-1:1,end:-1:1)'; +% end diff --git a/+io/export_for_SASfit.m b/+io/export_for_SASfit.m new file mode 100644 index 0000000..322ff4e --- /dev/null +++ b/+io/export_for_SASfit.m @@ -0,0 +1,298 @@ +% This script is to plot, correct and export solution SAXS data to SASfit +% accounts for transmission, time and thickness correction +% scales the data to a calibration factor +% background correction, removal of bad pixels +% not suitable for anisotropic data +% saves the output to be used in SASfit + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% EDIT HERE +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% give the calibration factor for absolute intensity, calculated previously +cal_factor_SAXS = 2.91e-4; +cal_factor_WAXS = 2.01e-5 ; + +% where the data is saved (Data10, afs, p-account) +base_dir = '~/Data10/';%'/sls/X12SA/Data20/e16598/'; +save_dir = '~/Data10/';%'/mnt/das-gpfs/work/p16598/'; +eaccount = beamline.identify_eaccount; % 'e16598'; + +% samples and thicknesses +Air = 15; % scan used for transmission calculation +sample_scan = [34:38]; % should be given +sample_thickness = 0.15; % in cm: important for absolute scattering +back_scan = []; % used as background, leave it empty [] for no subtraction !!NOT TESTED!! +back_thickness = 0.01; % in cm: important for absolute scattering + +% export data for SASfit? +export_sasfit = 1; + +%plot curves? +plot_curves = 0; + +% save figures? +save_fig = 0; + +% average the scan points? 1 = yes, 0 = no +average_scan = 1; +% scale also the waxs data? yes = 1; no = 0; +use_waxs = 0; + +% which bad pixels should be removed +bad_pixel = []; %given as a vector [811, 825] + +% use all data measurement points or skip some (faster) +skip_measurements = [100]; % use 1 to show all + +% used to reduce noise at the beginning and end of scattering curve +skip_first_points = 55; +skip_last_points = 150; +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% load the diode value for the air +S_air = io.spec_read(base_dir,'ScanNr',Air); +%scale in case the exposure times are different +exp_time = S_air.sec(1,1); +scale_air = 1/exp_time; +Air_data = load(sprintf('%s/analysis/radial_integration/%s_1_%05d_00000_00000_integ.mat', base_dir, eaccount, Air)); +I_air = mean(Air_data.I_all, 3); +% average over the segments when needed +if size(I_air, 2) > 1 + I_air = (I_air .* Air_data.norm_sum)./sum(Air_data.norm_sum, 2); + I_air = sum(I_air, 2); +end + +if use_waxs + Air_waxs = load(sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat', base_dir, eaccount, Air)); + I_air_waxs = mean(squeeze(Air_waxs.I_all), 2); +end +%% background correction +if ~isempty(back_scan) + for b = 1:length(back_scan) + bgr = load(sprintf('%s/analysis/radial_integration/%s_1_%05d_00000_00000_integ.mat', base_dir, eaccount, back_scan(b))); + S_back = spec_read(base_dir,'ScanNr',back_scan(b)); + % in case the burst scan takes place, the transmission is + % calculated differently + if ~isempty(findstr(S_back.S, 'burst_scan')) + delimiter = ' '; + formatSpec = '%*s%*s%s%[^\n\r]'; + fileID = fopen(sprintf('%smcs/S00000-00999/S%05d/%s_%05d.dat', base_dir,back_scan(b), eaccount, back_scan(b)), 'r'); + dataArray = textscan(fileID, formatSpec, 'Delimiter', delimiter, 'MultipleDelimsAsOne', true, 'ReturnOnError', false); + exp_time = dataArray{1,1}{7,1}; + scale_back = 1/str2num(exp_time); + diode = mean(str2num(dataArray{1,1}{9,end})); + transm_back = ((diode*scale_back)/(mean(S_air.diode)*scale_air)); + fclose(fileID); + else + %scale in case the exposure times are different + exp_time = S_back.sec(1,1); + scale_back = 1/exp_time; + transm_back = (mean(S_back.diode)/mean(S_back.bpm4i))/(mean(S_air.diode)/mean(S_air.bpm4i)); + end + %average background + I_bgr = mean(bgr.I_all, 3); + if size(I_bgr, 2) > 1 + I_bgr = (I_bgr .* bgr.norm_sum)./sum(bgr.norm_sum, 2); + I_bgr = sum(I_bgr, 2); + end + I_bgr = ((((I_bgr*scale_back)*1/transm_back)-(I_air*scale_air))*1/back_thickness); + I_bgr = I_bgr * cal_factor_SAXS; + if use_waxs + %average background_WAXS + bgr_waxs = importdata(sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat', base_dir, eaccount, back_scan(b))); + I_bgr_waxs = mean(squeeze(bgr_waxs.I_all), 2); + I_bgr_waxs = ((((I_bgr_waxs*scale_back)*1/transm_back)-(I_air_waxs*scale_air))*1/back_thickness); + I_bgr_waxs = I_bgr_waxs * cal_factor_WAXS; + end + end +else + I_bgr_waxs = 0; + I_bgr = 0; +end + +%% load and correct the sample +for s = 1:length(sample_scan) + sample_filename=sprintf('%s/analysis/radial_integration/%s_1_%05d_00000_00000_integ.mat', base_dir, eaccount, sample_scan(s)); + if exist(sample_filename) == 2 + display(['reading file ',sample_filename]) + sample = load(sample_filename); + else + continue + end + S_s = io.spec_read(base_dir,'ScanNr',sample_scan(s)); + if ~isempty(findstr(S_s.S, 'burst_scan')) + delimiter = ' '; + formatSpec = '%*s%*s%s%[^\n\r]'; + fileID = fopen(sprintf('%smcs/S00000-00999/S%05d/%s_%05d.dat', base_dir,sample_scan(s), eaccount,sample_scan(s)), 'r'); + dataArray = textscan(fileID, formatSpec, 'Delimiter', delimiter, 'MultipleDelimsAsOne', true, 'ReturnOnError', false); + exp_time = dataArray{1,1}{7,1}; + scale_s = 1/str2num(exp_time); + diode = mean(str2num(dataArray{1,1}{9,end})); + transm_sample = ((diode*scale_s)/(mean(S_air.diode)*scale_air)); + fclose(fileID); + else + %scale in case the exposure times are different + exp_time = S_s.sec(1,1); + scale_s = 1/exp_time; + transm_sample = (mean(S_s.diode)/mean(S_s.bpm4i))/(mean(S_air.diode)/mean(S_air.bpm4i)); + end + %load the sample + I_sample = squeeze(sample.I_all); + q_sample = sample.q'; + + if use_waxs + %average background_WAXS + sample_waxs = load(sprintf('%s/analysis/radial_integration_waxs/%s_2_%05d_00000_00000_integ.mat', base_dir, eaccount, sample_scan(s))); + I_sample_waxs = (sample_waxs.I_all); + q_sample_waxs = sample_waxs.q'; + else + q_sample_waxs = []; + I_sample_waxs = []; + end + if average_scan + I_sample = mean(I_sample, 3); + I_std = mean(sample.I_std, 3); + if size(I_sample, 2) > 1 + I_sample = (I_sample .* sample.norm_sum)./sum(sample.norm_sum, 2); + I_std = (I_std .* sample.norm_sum)./sum(sample.norm_sum, 2); + I_std = sum(I_std, 2).*cal_factor_SAXS; + I_sample = sum(I_sample, 2); + end + I_std = I_std(skip_first_points:end-skip_last_points,:); + I_sample = ((((I_sample*scale_s)*1/transm_sample)-(I_air*scale_air))*1/sample_thickness); + if ~isempty(bad_pixel) + I_sample(bad_pixel,1) = (I_sample(bad_pixel-1,1)+I_sample(bad_pixel+1,1))/2; + end + I_sample = I_sample * cal_factor_SAXS; + I_cor = (I_sample-I_bgr); + I_cor = I_cor(skip_first_points:end-skip_last_points,:); + + if use_waxs + hold on + I_sample_waxs = median(I_sample_waxs,3); + I_sample_waxs = ((((I_sample_waxs.*scale_s).*1/transm_sample)-(I_air_waxs.*scale_air)).*1/sample_thickness); + I_sample_waxs = I_sample_waxs * cal_factor_WAXS; + I_cor_waxs = (I_sample_waxs-I_bgr_waxs); + + else + I_cor_waxs = []; + end + I_total = [I_cor; I_cor_waxs]; + + q_total = [q_sample(skip_first_points: end-skip_last_points,:); q_sample_waxs]; + [q_total, index] = sort(q_total); + I_total = I_total(index); + + if plot_curves + figure + plot(q_total*10, I_total); + set(gca,'XScale','log', 'YScale','log'); + grid on; + box on; + xlabel('scattering vector q (nm^{-1})'); + ylabel('differential scattering cross-section (cm^{-1})'); + hold on + end + if export_sasfit + save_data = [q_total*10, I_total, I_std]; + filename = sprintf('scan_%05d_avg', sample_scan); + save(sprintf('%sanalysis/dat_files/%s.dat', save_dir , filename) , 'save_data', '-ascii'); + end + else + if plot_curves + figure + hold on + end + for i = 1:skip_measurements:size(sample.I_all, 3) + I_point = sample.I_all(:,:,i); + I_point_std = sample.I_std(:,:, i); + if size(I_point, 2) > 1 + I_point = (I_point .* sample.norm_sum)./sum(sample.norm_sum, 2); + I_point = sum(I_point, 2); + I_point_std = (I_point_std .* sample.norm_sum)./sum(sample.norm_sum, 2); + I_point_std = sum(I_point_std, 2).*cal_factor_SAXS; + end + I_point_std = I_point_std(skip_first_points:end-skip_last_points,:); + if ~isempty(bad_pixel) + I_point(bad_pixel,1) = (I_point(bad_pixel-1,1) + I_point(bad_pixel+1,1))/2; + end + I_point = ((((I_point*scale_s)*1/transm_sample)-(I_air*scale_air))*1/sample_thickness); + I_point = I_point * cal_factor_SAXS; + I_cor = (I_point-I_bgr); + I_cor = I_cor(skip_first_points: end-skip_last_points,:); + + if use_waxs + I_point_waxs = I_sample_waxs(:,i); + I_point_waxs = ((((I_point_waxs*scale_s)*1/transm_sample)-(I_air_waxs*scale_air))*1/sample_thickness); + I_point_waxs = I_point_waxs * cal_factor_WAXS; + I_cor_waxs = (I_point_waxs-I_bgr_waxs); + I_point_std_WAXS = I_sample_waxs(:,:, i); + I_point_std_WAXS = I_point_std_WAXS.*cal_factor_WAXS; + I_point_std_WAXS = sum(I_point_std_WAXS, 2).*cal_factor_SAXS; + else + I_cor_waxs = []; + end + I_total = [I_cor; I_cor_waxs]; + q_total = [q_sample(skip_first_points: end-skip_last_points,:); q_sample_waxs]; + [q_total, index] = sort(q_total); + I_total = I_total(index); + I_point_std_total= [I_point_std; I_point_std_WAXS]; + if plot_curves + plot(q_total*10, I_total); + grid on; + box on; + set(gca,'XScale','log', 'YScale','log'); + xlabel('scattering vector q (nm^{-1})'); + ylabel('differential scattering cross-section (cm^{-1})'); + axis tight + hold on + drawnow + end + if export_sasfit + save_data = [q_total*10, I_total, I_point_std_total]; + filename = sprintf('scan_%05d_pt_%05d', sample_scan(s), i); + save(sprintf('%sanalysis/dat-files/%s.dat', save_dir , filename) , 'save_data', '-ascii'); + end + end + + end + +end +if save_fig + %save the results + saveas(gcf, sprintf('%sanalysis/scanNr_%05d.jpg', save_dir , sample_scan)) +end + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. diff --git a/+io/falcon_read.m b/+io/falcon_read.m new file mode 100644 index 0000000..12d2445 --- /dev/null +++ b/+io/falcon_read.m @@ -0,0 +1,67 @@ + +% Read data from Falcon readout electronics +% Input is the filename with path +% Output is a structure containing fields: +% Data contains one spectrum per measurement point +% Metadata contains some other information like the number of points per +% data transfer and the total number of points +% 12 March 2019 +% +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function dataout = falcon_read(filename) + +data1 = io.HDF.hdf5_load(filename); + +spectra_per_transfer = data1.entry.instrument.FalconX1.PixelsPerBuffer(end);%meta.spectra_per_transfer; % aka pixels per buffer in the MEDM +numberofpositions = data1.entry.instrument.FalconX1.CurrentPixel(end);%data1.entry.instrument.NDAttributes.CurrentPixel(end);%meta.numberofpositions; +data = data1.entry.data.data; + +data = data(1+256:end,1,:); + +N = size(data); +if size(data,3) > 1 + data = reshape(data,N(1)/spectra_per_transfer,spectra_per_transfer*N(3)); +else + data = reshape(data,N(1)/spectra_per_transfer,spectra_per_transfer); +end + +dataout.data = data(1:2:end,1:numberofpositions); + +end + + + + diff --git a/+io/find_base_package.m b/+io/find_base_package.m new file mode 100644 index 0000000..a53b771 --- /dev/null +++ b/+io/find_base_package.m @@ -0,0 +1,69 @@ +%FIND_BASE_PACKAGE +% finds the path to the cSAXS base package by looking for a specific file (+math) +% the code goes up to 3 levels down in the folder structure and it tries to find any folder matching ./*/+math/ +% +% returns: +% ++ base_package_path path to the cSAXS base package +% +% Example how to use it: addpath(find_base_package()) + + +%*-----------------------------------------------------------------------* +%| | +%| Except where otherwise noted, this work is licensed under a | +%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 | +%| International (CC BY-NC-SA 4.0) license. | +%| | +%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) | +%| | +%| Author: CXS group, PSI | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + + + +function base_package_path = find_base_package() + maxdepth = 3; + test_path = '+math'; % one file to find them all + + lvl = 1; + cpath = ''; + ret = ''; + while isempty(ret) && ~contains(strtrim(ret), test_path) + [~, ret] = system(sprintf('find -L %s -maxdepth 2 -type d -name "%s"', cpath, test_path)); + if lvl > maxdepth + break + end + lvl = lvl + 1; + cpath = [cpath '../']; + end + ret = split(ret); + base_package_path = strtrim(ret{1}); + base_package_path = base_package_path(1:end-length(test_path)); + + if isempty(base_package_path) + error('cSAXS base package was not found') + end +end diff --git a/+io/fliread.m b/+io/fliread.m new file mode 100644 index 0000000..eb76037 --- /dev/null +++ b/+io/fliread.m @@ -0,0 +1,202 @@ +% Call function without arguments for a detailed explanation of its use + +% Filename: $RCSfile: fliread.m,v $ +% +% $Revision: 1.4 $ $Date: 2008/10/03 13:50:04 $ +% $Author: $ +% $Tag: $ +% +% Description: +% read a data file in the format stored by the program ccdfli.c +% +% Note: +% The image files have the extension raw and the file format is home +% defined. +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read_set_default +% - fopen_until_exists +% - get_hdr_val +% +% +% history: +% +% Pctober 3rd 2008: +% add new end-of-header signature search for version 1.20 files +% +% October 1st 2008: Exchange width and height in reshape command +% +% May 9th 2008: adapt to call from image_read +% +% November 3, 2005: include new fields of data format 1.1: +% exposure time Spec, exposure time measured, monitor counts +% +% October 2005: include optional from-to line reading +% +% March 2005: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = fliread(filename, varargin) +import io.* +import utils.char_to_cellstr +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'raw'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + +% expected maximum length for the text header +max_header_length = 1024; + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end + +% read all data at once +[fdat,fcount] = fread(fid,'uint8=>uint8'); + +% close input data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes read\n',fcount); +end + +% return the complete header as lines of a cell array +frame.header = char_to_cellstr( char(fdat(1:max_header_length)'),1 ); + +version_no = get_hdr_val(frame.header,'% version','%f',1); +if ((version_no ~= 1.00) && (version_no ~= 1.10) && (version_no ~= 1.20)) + fprintf('%s: File version number %.2f may not be supported\n',... + mfilename,version_no); +end + +[frameHeight,line_number] = get_hdr_val(frame.header,'% rows','%d',1); +[frameWidth,line_number] = get_hdr_val(frame.header,'% columns','%d',1); + +if (version_no < 1.20) + if (version_no == 1.10) + [monCounts,line_number] = get_hdr_val(frame.header,'% monitorcounts','%d',1); + end + + % cut off non-header lines + frame.header = frame.header(1:line_number); + + % find start of data + eol_ind = regexp(char(fdat(1:max_header_length)'),'\n'); + data_start = eol_ind(line_number) +1; +else + eoh_signature = sprintf('%% EOH%c%c',10,26); + end_of_header_pos = ... + strfind( fdat(1:min(max_header_length,length(fdat)))',... + eoh_signature ); + data_start = end_of_header_pos + length(eoh_signature); +end + + +% calculate end of data (should be end of file) +data_end = data_start + frameWidth * frameHeight *2 -1; +if (data_end > fcount) + error('%d bytes read but %d are needed',fcount,data_end); +end +if (data_end ~= fcount) + fprintf('%s warning: %d bytes read vs. %d needed\n',mfilename,... + fcount,data_end); +end +% cut out frame data +frame.data = double( reshape(typecast(fdat(data_start:data_end),'uint16'), ... + frameWidth,frameHeight) ); + +% conversion to standard view on FLI-CCD data at the SLS/cSAXS beamline +% (to be determined) +% if (~original_orientation) +% % frame = flipud(frame'); +% frame.data = frame.data'; +% end + +% add the file modification date to the header +dir_entry = dir(filename); +frame.header{end+1} = [ 'FileTimestamp ' dir_entry.date ]; diff --git a/+io/get_host_name.m b/+io/get_host_name.m new file mode 100644 index 0000000..808ac97 --- /dev/null +++ b/+io/get_host_name.m @@ -0,0 +1,11 @@ +function [name] = get_host_name() +%Return host name +% Written by YJ for I/O +if isunix() + name = getenv('HOSTNAME'); +else + name = getenv('hostname'); +end + +end + diff --git a/+io/get_user_name.m b/+io/get_user_name.m new file mode 100644 index 0000000..a7bc54f --- /dev/null +++ b/+io/get_user_name.m @@ -0,0 +1,11 @@ +function [name] = get_user_name() +%Return account username +% Written by YJ for I/O +if isunix() + name = getenv('USER'); +else + name = getenv('username'); +end + +end + diff --git a/+io/imExportTiff.m b/+io/imExportTiff.m new file mode 100644 index 0000000..4566eb1 --- /dev/null +++ b/+io/imExportTiff.m @@ -0,0 +1,78 @@ +function [ ] = imExportTiff( matrix_input,name,section,varargin) +%Wirte a image stack as a 'Tiff' file +%imExportTiff( matrix,name,section ) +% matrix: 3D matrix(y,x,z) +% name: output file name +% section: choose corss sections +% 'XY' matrix(:,:,i) +% 'YZ' matrix(:,i,:) +% 'ZX' matrix(i,:,:) +% + +matrix = matrix_input; +matrix = matrix-min(min(min(matrix))); +s = size(matrix); +m = double(max(max(max(matrix)))); + +if isempty(varargin) %gray + switch section + case 'XY' + imwrite(double(matrix(:,:,1))./m, name,'tiff') + for i=2:s(3) + imwrite(double(matrix(:,:,i))./m, name,'tiff', 'WriteMode','append') + end + case 'ZY' + imwrite(double(squeeze(matrix(:,1,:))./m), name,'tiff') + for i=2:s(2) + imwrite(double(squeeze(matrix(:,i,:))./m), name,'tiff', 'WriteMode','append') + end + case 'ZYs' + imwrite(mat2gray(double(squeeze(matrix(:,1,:)))), name,'tiff') + for i=2:s(2) + imwrite(mat2gray(double(squeeze(matrix(:,i,:)))), name,'tiff', 'WriteMode','append') + end + case 'ZX' + imwrite(double(squeeze(matrix(1,:,:)))./m, name,'tiff') + for i=2:s(1) + imwrite(double(squeeze(matrix(i,:,:)))./m, name,'tiff', 'WriteMode','append') + end + case 'XYs' + imwrite(mat2gray(matrix(:,:,1)), name,'tiff') + for i=2:s(3) + imwrite(mat2gray(matrix(:,:,i)), name,'tiff', 'WriteMode','append') + end + case 'ZXs' + imwrite(squeeze(matrix(1,:,:))./m, name,'tiff') + for i=2:s(1) + imwrite(mat2gray(squeeze(matrix(i,:,:))), name,'tiff', 'WriteMode','append') + end + end +else + a = varargin{1}; %color + switch section + case 'XY' + imwrite(double(matrix(:,:,1))*a*64/m,jet, name,'tiff') + for i=2:s(3) + imwrite(double(matrix(:,:,i))*a*64/m,jet, name,'tiff', 'WriteMode','append') + end + case 'ZY' + imwrite(squeeze(matrix(:,1,:))*a*64/m,jet, name,'tiff') + for i=2:s(2) + imwrite(squeeze(matrix(:,i,:))*a*64/m,jet, name,'tiff', 'WriteMode','append') + end + + case 'ZX' + imwrite(squeeze(matrix(1,:,:))*a*64/m,jet, name,'tiff') + for i=2:s(1) + imwrite(squeeze(matrix(i,:,:))*a*64/m,jet, name,'tiff', 'WriteMode','append') + end + case 'XYs' + imwrite(mat2gray(matrix(:,:,1))*a*64,parula, name,'tiff') + for i=2:s(3) + imwrite(mat2gray(matrix(:,:,i))*a*64,parula, name,'tiff', 'WriteMode','append') + end + end +end + +end + diff --git a/+io/image_default_orientation.m b/+io/image_default_orientation.m new file mode 100644 index 0000000..0cf1878 --- /dev/null +++ b/+io/image_default_orientation.m @@ -0,0 +1,169 @@ +% [orient_vec] = image_default_orientation(header, extension, varargin) +% Determine default orientation for an image_orient.m call based on the +% file extension + +% Filename: $RCSfile: image_default_orientation.m,v $ +% +% $Revision: 1.11 $ $Date: 2013/03/23 15:01:09 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Determine default orientation for an image_orient.m call based on the +% file extension +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% --- +% +% history: +% +% September 30th 2010: +% add orientation for HDF5 files +% +% November 19th 2008: +% add .mat files +% +% August 28th 2008: +% add orientation for extension .dat +% +% July 17th 2008: +% add mar extension, raw default orientation changed before +% +% June 19th 2008: +% add header to call parameters +% +% June 10th 2008: +% 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [orient_vec] = ... + image_default_orientation(header, extension, varargin) +import io.* + +% check minimum number of input arguments +if (nargin < 1) + fprintf('Usage:\n') + fprintf('[orientation_vector]=%s(extension);\n',... + m_file_name); + fprintf('The vector contains three values which can be 0 or 1 for transpose, flip-left-right, flip-up-down\n'); + error('At least one input parameter has to be specified.'); +end + + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + + +% parse the variable input arguments +% vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; +% value = varargin{ind+1}; + switch name + otherwise + error('Do not know how to handle parameter %s\n',name); +% vararg_remain{end+1} = name; +% vararg_remain{end+1} = value; + end +end + + +% image_read calls this function prior to converting the single frame +% to a cell array of frames +if (~iscell(header)) + fprintf('Warning (%s): header is not a cell array\n',mfilename); + fprintf('If this is an image_spec call then please report to Oliver:\n') + whos header + header +else + if ((~isempty(header)) && (iscell(header{1}))) + header = header{1}; + end +end + + +% set the default orientation as a function of the filename extension +switch extension + case 'dat' + orient_vec = [ 0 0 0 ]; + case 'edf' + orient_vec = [ 1 1 1 ]; + case 'cbf' + orient_vec = [ 1 1 1 ]; + case {'h5', 'hdf5', 'nxs', 'cxs'} + orient_vec = [ 0 0 1 ]; + case {'tif', 'tiff'} + if (strcmp(header{1}(1:5),'Andor')) + orient_vec = [ 1 0 0 ]; + else + orient_vec = [ 0 0 0 ]; + end + case {'mar','mccd'} + orient_vec = [ 1 0 1 ]; + case 'mat' + orient_vec = [ 0 0 0 ]; + case 'raw' + % FLI CCD at ICON +% orient_vec = [ 0 1 0 ]; + % FLI CCD at laser setup + orient_vec = [ 1 0 1 ]; + case 'spe' + orient_vec = [ 0 0 0 ]; + otherwise + error([ 'unknown extension ''' extension '''' ]); +end diff --git a/+io/image_info.m b/+io/image_info.m new file mode 100644 index 0000000..2e91f82 --- /dev/null +++ b/+io/image_info.m @@ -0,0 +1,214 @@ +% [im_info,vararg_remain] = image_info(filenames,varargin) +% Get information like the dimensions of the data stored in an image file + +% Filename: $RCSfile: image_info.m,v $ +% +% $Revision: 1.3 $ $Date: 2013/01/25 10:23:07 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Get information like the dimensions of the data stored in an image file +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% +% history: +% +% November 11th 2010: +% 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [im_info,vararg_remain] = image_info(filenames,varargin) +import io.* +import utils.default_parameter_value +import utils.find_files +import utils.fopen_until_exists + +% initialize return arguments +im_info = struct('no_of_frames',[]); + +% check minimum number of input arguments +if (nargin < 1) +% image_read_help('ext',mfilename); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% % set default values for the variable input arguments: +% % default data type for the returned frames +% data_type = default_parameter_value(mfilename,'DataType'); +% % recognize file type by file name extension +% force_file_type = default_parameter_value(mfilename,'ForceFileType'); +% % determine default orientation based on the file name extension +% orient_by_extension = default_parameter_value(mfilename,'OrientByExtension'); +% % filename is actually a mask that may include wildcards +% filename_is_fmask = default_parameter_value(mfilename,'IsFmask'); +% % display file name of the file to be loaded +% display_filename = default_parameter_value(mfilename,'DisplayFilename'); + +% exit with an error message if unhandled named parameters are left at the +% end of this macro +unhandled_par_error = 1; +filename_is_fmask = 0; +force_file_type = []; +display_filename = 0; + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'ForceFileType' + force_file_type = lower(value); + case 'OrientByExtension' + orient_by_extension = value; + case 'UnhandledParError' + unhandled_par_error = value; + case 'IsFmask' + filename_is_fmask = value; + case 'DisplayFilename' + display_filename = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + + +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); + +% convert the filename to a cell array to use the same loop for single and +% multiple file names +if (~iscell(filenames)) + filenames = { filenames }; +end + +% loop over all specified file names +file_ind_max = length(filenames); +for (file_ind=1:file_ind_max) + filename = filenames{file_ind}; + vararg_remain = vararg; + + % in case of file name mask get a list of all matching file names + data_dir = ''; + if (filename_is_fmask) + % sub macros must not complain about unknown arguments + vararg_remain{end+1} = 'UnhandledParError'; + vararg_remain{end+1} = 0; + + [data_dir, fnames, vararg_remain] = ... + find_files( filename, vararg_remain ); + else + fnames = struct('name',filename); + end + + for (sub_file_ind = 1:length(fnames)) + % pick out the current filename + filename = [ data_dir fnames(sub_file_ind).name ]; + + % check for minimum filename length + if (length(filename) < 5) + error([ mfilename ': invalid filename ' filename ]); + end + + if (isempty(force_file_type)) + % get the extension from the last three to four characters + extension = lower(filename((end-4):end)); + pos = strfind(extension,'.'); + if (length(pos) < 1) + error([ mfilename ': invalid extension in ' filename ]); + end + extension = extension(pos(end)+1:end); + else + % the file name extension is ignored since the file type is + % forced to a specific one + extension = force_file_type; + end + + if (display_filename) + fprintf('file information on %s\n',filename); + end + + if ((strcmp(extension,'dat')) || ... + (strcmp(extension,'tif')) || (strcmp(extension,'tiff')) || ... + (strcmp(extension,'mat'))) + % open the file to support functionality like + % wait-until-exists + [fid,vararg_remain] = fopen_until_exists(filename,vararg); + if (fid >= 0) + fclose(fid); + end + end + + % interprete file in the format indicated by the filename extension + switch extension + case {'h5', 'hdf5'} + fi = hdf5info(filename); + im_info.no_of_frames = fi.GroupHierarchy.Groups.Datasets.Dims(3); + case {'cbf', 'dat', 'edf', 'mar', 'mccd', 'mat', 'raw', 'spe', 'tif', 'tiff'} + [frame,vararg_remain] = image_read(filename,vararg_remain); + im_info.no_of_frames = size(frame.data,3); + otherwise + error([ 'unknown extension of ' filename ]); + end + end +end + diff --git a/+io/image_orient.m b/+io/image_orient.m new file mode 100644 index 0000000..578c79b --- /dev/null +++ b/+io/image_orient.m @@ -0,0 +1,176 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: image_orient.m,v $ +% +% $Revision: 1.5 $ $Date: 2014/04/11 12:47:09 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for mirroring or rotating images or stacks of images. +% +% Note: +% Call without arguments for a brief help text. +% In case of image stacks the first two dimensions are treated as the image +% dimensions. +% +% Dependencies: +% --- +% +% history: +% +% January 16th 2009: +% return the complete structure rather than just the data array +% +% June 19th 2008: +% change call to complete frame rather than data only +% +% May 27th 2008: +% 1st version based on orientm.m by Tilman Donath + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame_out,vararg_remain] = image_orient(frame, varargin) +import io.* +import utils.default_parameter_value + +% check minimum number of input arguments +if (nargin < 1) + image_orient_help(mfilename); + error('At least one input parameter has to be specified.'); +end + +if (ndims(frame.data) < 2) + error('The input data array must have at least two dimensions.'); +end + + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% set default values for the variable input arguments: +orient_extension = frame.extension{1}; +do_transpose = default_parameter_value(mfilename,'Transpose'); +do_fliplr = default_parameter_value(mfilename,'FlipLR'); +do_flipud = default_parameter_value(mfilename,'FlipUD'); +invert_orientation = 0; + +% parse the variable input arguments +vararg_remain = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'Transpose' + do_transpose = value; + case 'FlipLR' + do_fliplr = value; + case 'FlipUD' + do_flipud = value; + case 'Orientation' + if (length(value) ~= 3) + error('Invalid Orientation parameter of length %d',length(value)); + end + do_transpose = value(1); + do_fliplr = value(2); + do_flipud = value(3); + case 'OrientExtension' + orient_extension = value; + case 'OrientByExtension' + if (value) + orient_vec = image_default_orientation(frame.header,orient_extension); + do_transpose = orient_vec(1); + do_fliplr = orient_vec(2); + do_flipud = orient_vec(3); + end + case 'InvertOrientation' + invert_orientation = value; + otherwise + vararg_remain{end+1} = name; + vararg_remain{end+1} = value; + end +end + + +% initialize return arguments +frame_out = frame; + +if (invert_orientation) + % apply orientation modifications in inverse order, e.g., to revert the + % original orientation prior to writing a file + if (do_flipud) + frame_out.data = flip(frame_out.data,1); + end + + if (do_fliplr) + frame_out.data = flip(frame_out.data,2); + end +end + +if (do_transpose) + dim_order = 1:ndims(frame_out.data); + dim_order(1) = 2; + dim_order(2) = 1; + frame_out.data = permute(frame_out.data,dim_order); +end + +if (~invert_orientation) + if (do_fliplr) + frame_out.data = flip(frame_out.data,2); + end + + if (do_flipud) + frame_out.data = flip(frame_out.data,1); + end +end diff --git a/+io/image_read.m b/+io/image_read.m new file mode 100644 index 0000000..0fc516e --- /dev/null +++ b/+io/image_read.m @@ -0,0 +1,728 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: image_read.m,v $ +% +% $Revision: 1.17 $ $Date: 2013/01/25 10:23:23 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading image data formats used at the SLS / cSAXS beamline. +% The data are returned in double precision floating point format. +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - edfread +% - cbfread +% - hdf5read +% - fliread +% - speread +% - char_to_cellstr +% +% history: +% +% September 30th 2010: +% add a call to hdf5read_main +% +% June 5th 2009: +% disable UhandledParError before calling sub-macros +% +% January 16th 2009: +% adapt to image_orient returning the complete structure rather than just +% the data array +% +% November 19th 2008: +% add reading of Matlab files +% +% September 5th 2008: +% skip further processing for a frame if it was not possible to read it +% +% September 4th 2008: +% add the rowcol-from field to the frames structure as origin information +% +% June 19th 2008: adapt call to image_orient +% +% May 16th 2008: send variable arguments through find files +% +% May 9th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group +% and Computing Department, Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frames,vararg_remain] = image_read(filenames,varargin) +import io.* +import io.HDF.* +import io.CBF.* +import plotting.* +import utils.char_to_cellstr +import utils.default_parameter_value +import utils.find_files +import utils.fopen_until_exists + +% initialize return arguments +frames = struct('data',[], ... + 'img_full_size',[], 'rowcol_from',[], ... + 'no_of_el_read', [], ... + 'header',[], 'filename',[], 'extension', []); + +% check minimum number of input arguments +if (nargin < 1) + image_read_help('ext',mfilename); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% convert the filename to a cell array to use the same loop for single and +% multiple file names +if (~iscell(filenames)) + filenames = { filenames }; +end + +% hdf5 files are read via a separate sub-routine +% if length(filenames) == 1 % accept only one file name + filename = filenames{1}; + [~, ~, ext] = fileparts(filename); + + if any(strcmp(ext, {'.h5', '.hdf5', '.nxs', '.cxs'})) + if length(filenames) == 1 + frames = hdf5read(filename, varargin); + else + frames = hdf5read(filenames, varargin); + end + vararg_remain = []; + + return % image_read ends here for hdf5 image files + end +% end + +% set default values for the variable input arguments: +% default data type for the returned frames +data_type = default_parameter_value(mfilename,'DataType'); +% recognize file type by file name extension +force_file_type = default_parameter_value(mfilename,'ForceFileType'); +% from/to row 0 means all rows +row_from = default_parameter_value(mfilename,'RowFrom'); +row_to = default_parameter_value(mfilename,'RowTo'); +% from/to column 0 means all lines +column_from = default_parameter_value(mfilename,'ColumnFrom'); +column_to = default_parameter_value(mfilename,'ColumnTo'); +% determine default orientation based on the file name extension +orient_by_extension = default_parameter_value(mfilename,'OrientByExtension'); +% filename is actually a mask that may include wildcards +filename_is_fmask = default_parameter_value(mfilename,'IsFmask'); +% display file name of the file to be loaded +display_filename = default_parameter_value(mfilename,'DisplayFilename'); +% variable to load from Matlab files +matlab_var = default_parameter_value(mfilename,'MatlabVar'); + +% exit with an error message if unhandled named parameters are left at the +% end of this macro +unhandled_par_error = 1; + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'DataType' + if (~ischar(value)) + error('The DataType must be string defining a valid Matlab data type.'); + end + data_type = value; + case 'ForceFileType' + force_file_type = lower(value); + case 'MatlabVar' + matlab_var = value; + case 'RowFrom' + row_from = value; + case 'ROI' + if (length(value) ~= 4) + error('The ROI parameter needs a vector of length four as argument.'); + end + column_from = value(1); + row_from = value(2); + column_to = value(3); + row_to = value(4); + case 'RowTo' + row_to = value; + case 'ColumnFrom' + column_from = value; + case 'ColumnTo' + column_to = value; + case 'OrientByExtension' + orient_by_extension = value; + case 'UnhandledParError' + unhandled_par_error = value; + case 'IsFmask' + filename_is_fmask = value; + case 'DisplayFilename' + display_filename = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + + +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); + +% loop over all specified file names +file_ind_max = length(filenames); +store_ind = 1; +for (file_ind=1:file_ind_max) + filename = filenames{file_ind}; + vararg_remain = vararg; + + % in case of file name mask get a list of all matching file names + data_dir = ''; + if (filename_is_fmask) + % sub macros must not complain about unknown arguments + vararg_remain{end+1} = 'UnhandledParError'; + vararg_remain{end+1} = 0; + + [data_dir, fnames, vararg_remain] = ... + find_files( filename, vararg_remain ); + else + fnames = struct('name',filename); + end + + for (sub_file_ind = 1:length(fnames)) + % pick out the current filename + filename = [ data_dir fnames(sub_file_ind).name ]; + + % check for minimum filename length + if (length(filename) < 5) + error([ mfilename ': invalid filename ' filename ]); + end + + if (isempty(force_file_type)) + % get the extension from the last three to four characters + extension = lower(filename((end-4):end)); + pos = strfind(extension,'.'); + if (length(pos) < 1) + error([ mfilename ': invalid extension in ' filename ]); + end + extension = extension(pos(end)+1:end); + else + % the file name extension is ignored since the file type is + % forced to a specific one + extension = force_file_type; + end + + if (display_filename) + fprintf('loading %s\n',filename); + end + + if ((strcmp(extension,'dat')) || ... + (strcmp(extension,'tif')) || (strcmp(extension,'tiff')) || ... + (strcmp(extension,'mat'))) + % open the file to support functionality like + % wait-until-exists + [fid,vararg_remain] = fopen_until_exists(filename,vararg); + if (fid >= 0) + fclose(fid); + end + end + + % interprete file in the format indicated by the filename extension + switch extension + case 'cbf' + [frame,vararg_remain] = cbfread(filename,vararg_remain); + case {'hdf5', 'h5', 'nxs', 'cxs'} + [frame,vararg_remain] = hdf5read_main(filename,vararg_remain); + case 'dat' + [frame,vararg_remain] = datread(filename,vararg_remain); + case 'edf' + [frame,vararg_remain] = edfread(filename,vararg_remain); + case {'mar', 'mccd'} + [frame,vararg_remain] = marread(filename,vararg_remain); + case 'mat' + tmp_data = load(filename); + frame.data = tmp_data.(matlab_var); + frame.header = {}; + % No header information are available. + % Fake exposure time information to avoid problems in other + % macros. + frame.header{end+1} = 'Exposure_time 1.0'; + % add the file modification date to the header + dir_entry = dir(filename); + frame.header{end+1} = [ 'DateTime ' dir_entry.date ]; + case 'raw' + [frame,vararg_remain] = fliread(filename,vararg_remain); + case 'spe' + [frame,vararg_remain] = speread(filename,vararg_remain); + case {'tif', 'tiff'} + % reading higher bit depths than 16bit needs a sufficiently + % up-to-date Matlab version + frame.data = imread(filename,'tif'); + hdr = imfinfo(filename); + frame.header = {}; + if (isfield(hdr,'ImageDescription')) + frame.header = char_to_cellstr(hdr.ImageDescription); + end + if ((isfield(hdr,'Model')) && ... + (strcmp(hdr.Model(1:7),'PILATUS'))) + frame.header{end+1} = [ 'DateTime ' hdr.DateTime ]; + frame.header{end+1} = [ 'Software ' hdr.Software ]; + frame.header{end+1} = [ 'Model ' hdr.Model ]; + else + % the exposure time is not available + if (isfield(hdr,'exptimesec')) + frame.header{end+1} = [ 'Exposure_time' hdr.exptimesec ]; + else + frame.header{end+1} = 'Exposure_time 1.0'; + end + if (isfield(hdr,'DateTime')) + frame.header{end+1} = [ 'DateTime ' hdr.DateTime ]; + else + if (isfield(hdr,'FileModDate')) + frame.header{end+1} = [ 'DateTime ' hdr.FileModDate ]; + else + % add the file modification date to the header + dir_entry = dir(filename); + frame.header{end+1} = ... + [ 'DateTime ' dir_entry.date ]; + end + end + end + otherwise + error([ 'unknown extension of ' filename ]); + end + + % the remaining code is not needed if no file was read + if (isempty(frame.data)) + continue; + end + % determine the orientation from the filename extension + vararg_remain_prev = vararg_remain; + vararg_remain = cell(1,length(vararg_remain)+2); + vararg_remain(3:end) = vararg_remain_prev; + vararg_remain{1} = 'OrientByExtension'; + vararg_remain{2} = orient_by_extension; + % set the extension since image_orient is called prior + % to defining the return variables frames + frame.extension = cell(1,1); + frame.extension{1} = extension; + + % orient image + [frame,vararg_remain] = image_orient(frame,vararg_remain); + + % cut out region of interest + full_size = size(frame.data); + if ((row_from > 0) || (row_to > 0) ||... + (column_from > 0) || (column_to > 0)) + if (row_from <= 0) + row_from = 1; + end + if (row_from > size(frame.data,1)) + error('The RowFrom specification is beyond the maximum value of %d',... + size(frame.data,1)); + end + if (row_to <= row_from) + row_to = full_size(1); + end + if (row_to > full_size(1)) + error('The RowTo specification is beyond the maximum value of %d',... + full_size(1)); + end + + if (column_from <= 0) + column_from = 1; + end + if (column_from > full_size(2)) + error('The ColumnFrom specification is beyond the maximum value of %d',... + full_size(2)); + end + if (column_to <= column_from) + column_to = full_size(2); + end + if (column_to > size(frame.data,2)) + error('The ColumnTo specification is beyond the maximum value of %d',... + full_size(2)); + end + frame.data = frame.data(row_from:row_to,column_from:column_to,:); + end + + % initialize the return array with the now known dimensions + if (store_ind == 1) + % in case of file name masks or multiple images in one data + % file the final array dimensions can only be estimated + init_guess = file_ind_max -1 + length(fnames); + frames.data = zeros( [ size(frame.data) init_guess ], data_type ); + frames.rowcol_from = cell(1,init_guess); + frames.no_of_el_read = cell(1,init_guess); + frames.img_full_size = cell(1,init_guess); + frames.filename = cell(1,init_guess); + frames.extension = cell(1,init_guess); + frames.header = cell(1,init_guess); + end + + + % store the frame(s) + if ((size(frame.data,1) ~= size(frames.data,1)) || ... + (size(frame.data,2) ~= size(frames.data,2))) + error('Expected frame dimension is %d x %d, frame read has %d x %d',... + size(frames.data,2),size(frames.data,1),... + size(frame.data,2),size(frame.data,1)); + end + + % in some cases multiple frames are stored in a single file + if (ndims(frame.data) == 4) + store_ind_to = (store_ind+size(frame.data,4)-1); + frames.data(:,:,:,store_ind:store_ind_to) = cast(frame.data,data_type); + else + store_ind_to = (store_ind+size(frame.data,3)-1); + frames.data(:,:,store_ind:store_ind_to) = cast(frame.data,data_type); + end + + % store the filename, extension and the header in the return argument + for (ind=store_ind:store_ind_to) + frames.img_full_size{ind} = full_size; + frames.rowcol_from{ind} = [ row_from column_from ]; + if (isfield(frame,'no_of_el_read')) + frames.no_of_el_read{ind} = frame.no_of_el_read; + else + frames.no_of_el_read{ind} = size(frame.data,3); + end + % zero means no ROI, i.e., starting at point (1,1) + if (frames.rowcol_from{ind}(1) < 1) + frames.rowcol_from{ind}(1) = 1; + end + if (frames.rowcol_from{ind}(2) < 1) + frames.rowcol_from{ind}(2) = 1; + end + frames.filename{ind} = filename; + frames.extension{ind} = extension; + frames.header{ind} = frame.header; + end + + % update index to free space in the output arrays + store_ind = store_ind_to + 1; + + % exit in case of unhandled named parameters, if this has not been switched + % off + if ((unhandled_par_error) && (~isempty(vararg_remain))) + vararg_remain + error('Not all named parameters have been handled.'); + end + % restore parameters for next iteration + vararg_remain = vararg_remain_prev; + end +end + +% resize the output arrays in case the initial size is too large +store_ind = store_ind -1; +if (size(frames.data,3) > store_ind) + frames.data = frames.data(:,:,1:store_ind); + frames.img_full_size = frames.img_full_size(1:store_ind); + frames.rowcol_from = frames.rowcol_from(1:store_ind); + frames.filename = frames.filename(1:store_ind); + frames.extension = frames.extension(1:store_ind); + frames.header = frames.header(1:store_ind); +end + +function frames = hdf5read(filename, params) +import utils.fopen_until_exists +import io.image_default_orientation +import io.HDF.hdf5_load +import io.image_orient +import utils.find_files + +frames = struct('data', [], 'img_full_size', [], 'rowcol_from', [], ... + 'no_of_el_read', [], 'header',[], 'filename',[], 'extension', []); + + + +p = inputParser; +p.KeepUnmatched = true; +p.FunctionName = 'image_read'; + +addParameter(p, 'H5Location', '/'); +addParameter(p, 'ReadAttr', false); + +addParameter(p, 'FrameRange', [1, Inf], @(x) isvector(x) && numel(x) <= 2 && isnumeric(x)); +addParameter(p, 'RowFrom', 1, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'RowTo', Inf, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'ColumnFrom', 1, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'ColumnTo', Inf, @(x) isscalar(x) && isnumeric(x)); + +addParameter(p, 'OrientByExtension', 1, @(x) isscalar(x) && (isnumeric(x) || islogical(x))); +addParameter(p, 'Orientation', [0, 0, 0], @(x) isvector(x) && numel(x) == 3 && isnumeric(x)); +addParameter(p, 'InvertOrientation', 0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'Transpose', 0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'FlipLR', 0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'FlipUD', 0, @(x) isscalar(x) && isnumeric(x)); +addParameter(p, 'CatDim', -1, @(x) isscalar(x) && isnumeric(x)); + +addParameter(p, 'DisplayFilename', 1, @(x) isscalar(x) && isnumeric(x)); + +% No support for filename wildcards, i.e. ('IsMask', 1) +addParameter(p, 'IsFmask', true, ... + @islogical);%@(x) assert(~x, 'Filename wildcards, i.e. (''IsFmask'', 1), are not supported for hdf5 files')); + +parse(p, params{:}); +r = p.Results; +vararg_remain = [fieldnames(p.Unmatched)'; struct2cell(p.Unmatched)']; + +if (r.IsFmask) + [data_dir, fnames, vararg_remain] = ... + find_files( filename, vararg_remain ); + filename = []; + for ii=1:length(fnames) + filename{ii} = fullfile(data_dir, fnames(ii).name); + end +end + + +if iscell(filename) + dir_entry = dir(filename{1}); + frames.header{1} = {['DateTime ' dir_entry.date], 'Exposure_time 0.0'}; + [~, ~, frames.extension{1}] = fileparts(filename{1}); + frames.extension{1} = frames.extension{1}(2:end); % truncate a leading dot + frames.filename{1} = '*multiple_frames*'; +else + dir_entry = dir(filename); + frames.header{1} = {['DateTime ' dir_entry.date], 'Exposure_time 0.0'}; + [~, frames.filename{1}, frames.extension{1}] = fileparts(filename); + frames.extension{1} = frames.extension{1}(2:end); % truncate a leading dot +end + + +inputs_orient = {'OrientByExtension', r.OrientByExtension, 'InvertOrientation', r.InvertOrientation}; + +if r.OrientByExtension + orient_vec = image_default_orientation(frames.header{1}, frames.extension{1}); + do_transpose = orient_vec(1); + do_fliplr = orient_vec(2); + do_flipud = orient_vec(3); + + % Warn user if they also provided either of 'Orientation', 'Transpose', 'FlipLR' or 'FlipUD' + if ~all(ismember({'Orientation', 'Transpose', 'FlipLR', 'FlipUD'}, p.UsingDefaults)) + warning(['Default hdf5 image orientation is potentially modified by either ' ... + '''Orientation'', or any of ''Transpose'', ''FlipLR'', or ''FlipUD'' parameters. ' ... + 'To supress this warning set ''OrientByExtension'' to 0.']); + end + +else + do_transpose = 0; + do_fliplr = 0; + do_flipud = 0; +end + +if ~ismember({'Orientation'}, p.UsingDefaults) + do_transpose = r.Orientation(1); + do_fliplr = r.Orientation(2); + do_flipud = r.Orientation(3); + + inputs_orient = [inputs_orient, {'Orientation', r.Orientation}]; + + % Warn user if they also provided either of 'Transpose', 'FlipLR' or 'FlipUD' + if ~all(ismember({'Transpose', 'FlipLR', 'FlipUD'}, p.UsingDefaults)) + warning(['Image orientation specified via ''Orientation'' parameter is potentially ' ... + 'modified by either ''Transpose'', ''FlipLR'', and/or ''FlipUD''. ' ... + 'To supress this warning use either ''Orientation'' or a combination of ' ... + '''Transpose'', ''FlipLR'', and/or ''FlipUD'' parameters.']); + end +end + +if r.Transpose || r.FlipLR || r.FlipUD + do_transpose = r.Transpose; + do_fliplr = r.FlipLR; + do_flipud = r.FlipUD; + inputs_orient = [inputs_orient, {'Transpose', r.Transpose}]; + inputs_orient = [inputs_orient, {'FlipLR', r.FlipLR}]; + inputs_orient = [inputs_orient, {'FlipUD', r.FlipUD}]; +end + +inputs = {}; +if ~isempty(r.H5Location) && ischar(r.H5Location) + inputs{end+1} = r.H5Location; +end + +if r.ReadAttr + inputs{end+1} = '-sa'; +end + +% Add slicing indexes if a user specified any of them +if ~all(ismember({'FrameRange', 'RowFrom', 'RowTo', 'ColumnFrom', 'ColumnTo'}, ... + p.UsingDefaults)) + + % Support 0's as start/end index -> full left/right range + if numel(r.FrameRange) == 1 + r.FrameRange(2) = r.FrameRange(1); + end + if r.FrameRange(1) == 0; r.FrameRange(1) = 1; end + if r.FrameRange(2) == 0; r.FrameRange(2) = Inf; end + if r.RowFrom == 0; r.RowFrom = 1; end + if r.RowTo == 0; r.RowTo = Inf; end + if r.ColumnFrom == 0; r.ColumnFrom = 1; end + if r.ColumnTo == 0; r.ColumnTo = Inf; end + + % Adjust range values according to the consequent image orientation procedure + if r.InvertOrientation % Transpose -> FlipLR/FlipUD + if do_transpose; [r.ColumnTo, r.ColumnFrom, r.RowTo, r.RowFrom] = ... + deal(r.RowTo, r.RowFrom, r.ColumnTo, r.ColumnFrom); end + if do_fliplr; [r.ColumnTo, r.ColumnFrom] = deal(-r.ColumnFrom, -r.ColumnTo); end + if do_flipud; [r.RowTo, r.RowFrom] = deal(-r.RowFrom, -r.RowTo); end + + else % FlipLR/FlipUD -> Transpose + if do_fliplr; [r.ColumnTo, r.ColumnFrom] = deal(-r.ColumnFrom, -r.ColumnTo); end + if do_flipud; [r.RowTo, r.RowFrom] = deal(-r.RowFrom, -r.RowTo); end + if do_transpose; [r.ColumnTo, r.ColumnFrom, r.RowTo, r.RowFrom] = ... + deal(r.RowTo, r.RowFrom, r.ColumnTo, r.ColumnFrom); end + end + + % Form the input + inputs{end+1} = {[r.RowFrom, r.RowTo],[r.ColumnFrom, r.ColumnTo], r.FrameRange}; +end +if iscell(filename) + + if (r.DisplayFilename) + fprintf('loading %s\n', filename{1}); + end + tmp = frames; + tmp.data = hdf5_load(filename{1}, inputs{:}); + % Orient image frame(s) + [tmp, vararg_remain] = image_orient(tmp, [inputs_orient, vararg_remain]); + + % let's handle the 2D (or 3d with singleton) case first + if isnumeric(tmp.data) && ndims(tmp.data==3) && size(tmp.data,3)==1 + frames.data = zeros([size(tmp.data(:,:,1)) length(filename)*size(tmp.data,3)]); + frames.data(:,:,1:size(tmp.data,3)) = tmp.data; + if length(filename)>1 + for frame=2:length(filename) + if (r.DisplayFilename) + fprintf('loading %s\n', filename{frame}); + end + tmp.data = hdf5_load(filename{frame}, inputs{:}); + + % Orient image frame(s) + [tmp, vararg_remain] = image_orient(tmp, [inputs_orient, vararg_remain]); + frames.data(:,:,frame) = tmp.data; + end + end + else + % in case of more than 2 dimensions, concatenate along the + % specified dimension, or return a cell array + if length(filename)>1 + frames.data{1} = tmp.data; + framedim = ndims(frames.data{1}); + for frameID=2:length(filename) + if (r.DisplayFilename) + fprintf('loading %s\n', filename{frameID}); + end + frames.data{frameID} = hdf5_load(filename{frameID}, inputs{:}); + if ndims(frames.data{frameID})~=framedim + framedim = -1; + end + % Orient image frame(s) + [frames, vararg_remain] = image_orient(frames, [inputs_orient, vararg_remain]); + end + if framedim > 0 + try + if r.CatDim == -1 + frames.data = cat(ndims(frames.data{1}),frames.data{:}); + else + frames.data = cat(r.CatDim, frames.data{:}); + end + catch + warning('Failed to concatenate frames.') + end + end + + else + frames.data = tmp.data; + end + end +else + % Support wait-until-exist functionality + [fid, vararg_remain] = fopen_until_exists(filename, vararg_remain(:)); + if fid >= 0 + fclose(fid); + else + % Silently exit if a file was not found and ('ErrorIfNotFound', 0) + return; + end + + + if (r.DisplayFilename) + fprintf('loading %s\n', filename); + end + frames.data = hdf5_load(filename, inputs{:}); + + % Orient image frame(s) + [frames, vararg_remain] = image_orient(frames, [inputs_orient, vararg_remain]); + +end + +% If its a dataset then fill these fields for further showing with +% image_show.m or image_spec.m +if isnumeric(frames.data) + frames.img_full_size = {[size(frames.data, 1), size(frames.data, 2)]}; + frames.rowcol_from = {[r.RowFrom, r.ColumnFrom]}; + frames.no_of_el_read = {size(frames.data, 3)}; +elseif (do_fliplr||do_flipud||do_transpose) + warning(['H5Location points to a group, not a dataset. Orientation/OrientByExtension/Transpose/FlipUD/FlipLR will be ignored. \n '... + 'To remove this warning, set ''OrientByExtension'' to 0 and ''Orientation'' to [0 0 0]']) +end + +% Show a warning message for unsupported input parameters +unmatched = vararg_remain(1:2:end); +if ~isempty(unmatched) + warning('These input parameters are not supported for hdf5 files and will be ignored: %s', ... + strjoin(unmatched, ', ')); +end diff --git a/+io/image_read_help.m b/+io/image_read_help.m new file mode 100644 index 0000000..ee55553 --- /dev/null +++ b/+io/image_read_help.m @@ -0,0 +1,161 @@ +% image_read_help(extension,m_file_name,varargin) +% parameter help for image_read + +% Filename: $RCSfile: image_read_help.m,v $ +% +% $Revision: 1.3 $ $Date: 2013/01/25 10:23:47 $ +% $Author: $ +% $Tag: $ +% +% Description: +% parameter help for image_read +% +% Note: +% none +% +% Dependencies: +% none +% +% +% history: +% +% July 17th 2008: +% add ForceFileType parameter and support for MAR CCD TIFF +% +% May 9th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [] = image_read_help(extension,m_file_name,varargin) +import io.* + +% check minimum number of input arguments +if (nargin < 2) + error('At least the extension and m-file name have to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% parse the variable input arguments +examples = 1; +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'Examples' + examples = value; + otherwise + % pass unknown parameters to image_read_sub_help + vararg{end+1} = name; + vararg{end+1} = value; + end +end + +% do not display examples from image_read_sub_help +vararg{end+1} = 'Examples'; +vararg{end+1} = 0; + +image_read_sub_help(m_file_name,extension,vararg) +fprintf('''DataType'', default is ''double'', other possibilities are ''single'', ''uint16'', ''int16'', ''uint32'', etc.\n'); +fprintf(' The conversion is done using ''cast'', i.e, out-of-range values are mapped to the minimum or maximum value\n'); +fprintf('''ForceFileType'',<''extension''> force the file types to be recognized by the here specified extension,\n'); +fprintf(' useful in case of no or other types of extensions, used by default as OrientExtension as well.\n'); +fprintf(' The extension ''mar'' and ''mccd'' can be used to read MAR CCD TIFF data.\n'); +fprintf('''RowFrom'',<0-max> region of interest definition, 0 or 1 for full frame\n'); +fprintf('''RowTo'',<0-max> region of interest definition, 0 for full frame\n'); +fprintf('''ColumnFrom'',<0-max> region of interest definition, 0 or 1 for full frame\n'); +fprintf('''ColumnTo'',<0-max> region of interest definition, 0 for full frame\n'); +image_orient_help(m_file_name,'ParametersOnly',1); +fprintf('''IsFmask'',<0-no,1-yes> interprete the filename(s) as search mask that may include wildcards, default true\n'); +fprintf('''DisplayFilename'',<0-no,1-yes> display filename of a file before loading it, default yes\n'); +fprintf('''UnhandledParError'',<0-no,1-yes> exit in case not all named parameters are used/known, default is yes\n'); +fprintf('\n'); +fprintf('HDF5, H5 or NeXus specifics These files contain data and metadata hierarchically organized in groups and datasets,\n'); +fprintf(' each group or dataset can also have attributes. Such files are thus here treated in a special way.\n'); +fprintf(' If you provide only filename then the file contents, including links but excluding attributes,\n'); +fprintf(' will be recursively read and returned as a Matlab structure. See also hdf5_load.m\n'); +fprintf('''H5Location'', If is a group then it will be read recursively and returned as a Matlab structure.\n'); +fprintf(' If is a dataset, the dataset will be read and returned within the field ''data'',\n'); +fprintf(' this is done in an effort to be compatible with the output of image_read for other file extensions. \n'); +fprintf(' Only in this case the data region options will be used, e.g ''RowFrom'', ''RowTo'', etc. \n'); +fprintf('''FrameRange'',<[first_fr last_fr]> Read only a subset of the frames available in the HDF5 file dataset specifed with ''H5Location''\n'); +fprintf(' This will only have an effect if ''H5Location'' is a dataset and not a group \n'); +fprintf('''ReadAttr'',<0-no,1-yes> Read the attributes of a dataset or group (default 0). The Name and Value of the attributes are \n'); +fprintf(' returned in a structure. Note with this option only the attributes (and not the dataset) are read\n'); + +if (examples) + fprintf('\n'); + fprintf('\n'); + fprintf('Examples:\n'); + fprintf('[frame]=%s(''~/Data10/pilatus/image_1_ct.cbf'');\n',... + m_file_name); + fprintf('[frame]=%s({''~/Data10/pilatus/image_1_ct1.cbf'',''~/Data10/pilatus/image_1_ct2.cbf''});\n',... + m_file_name); + fprintf('[frame]=%s(''~/Data10/pilatus/S00010/*.cbf'',''IsFmask'',1);\n',... + m_file_name); + fprintf('[frame]=%s(''~/Data10/pilatus/image_1_ct.cbf'',''RowFrom'',500,''RowTo'',600);\n',... + m_file_name); + fprintf('\n'); + fprintf('The returned structure has the fields data, header, filename and extension.\n'); + fprintf('\n'); + fprintf('\n'); + fprintf('Examples for HDF5:\n'); + fprintf('[data] = image_read(''scan_00300.hdf5'') Read all data in the file.\n') + fprintf('[data] = image_read(''scan_00300.hdf5'',''H5Location'',''/entry/instrument'') Reads NeXus instrument group.\n') + fprintf('[data] = image_read(''scan_00300.hdf5'',''H5Location'',''/entry/collection/data/spec'') Reads spec data which includes counters and motors that change during a scan.\n') + fprintf('[data] = image_read(''scan_00300.hdf5'',''H5Location'',''/entry/collection/data/spec'',''ReadAttr'',1) Reads spec data that did not change during the scan, e.g. static motors.\n') + fprintf('[data] = image_read(''scan_00300.hdf5'',''H5Location'',''/entry/instrument/Pilatus_2M/data'') Reads all Pilatus frames from the scan.\n') + fprintf('[data] = image_read(''scan_00300.hdf5'',''H5Location'',''/entry/instrument/Pilatus_2M/data'',''FrameRange'',[5 10], ''RowFrom'',500,''RowTo'',Inf,''ColumnFrom'',200,''ColumnTo'',800 )\n') + fprintf(' Reads the specified frame range and region of interest of the pilatus frames.\n') +end diff --git a/+io/json2mat.m b/+io/json2mat.m new file mode 100644 index 0000000..f882bf8 --- /dev/null +++ b/+io/json2mat.m @@ -0,0 +1,159 @@ +function M=json2mat(J) +import io.* +%JSON2MAT converts a javscript data object (JSON) into a Matlab structure +% using s recursive approach. J can also be a file name. +% +%Example: lala=json2mat('{lele:2,lili:4,lolo:[1,2,{lulu:5,bubu:[[1,2],[3,4],[5,6]]}]}') +% notice lala.lolo{3}.bubu is read as a 2D matrix. +% +% Jonas Almeida, March 2010 + +% Copyright (c) 2010, Jonas Almeida +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + +if exist(J)==2 % if J is a filename + fid=fopen(J,'r'); + J=''; + while ~feof(fid) + J=[J,fgetl(fid)]; + end + fclose(fid); + M=json2mat(J); +else + J1=regexprep(J(1:min([5,length(J)])),'\s',''); %despaced start of J string + if J1(1)=='{' %extract structures + JJ=regexp(J,'\{(.*)\}','tokens'); + M=extract_struct(JJ{1}{1}); + elseif J1(1)=='[' %extract cells + JJ=regexp(J,'\[(.*)\]','tokens'); + M=extract_cell(JJ{1}{1}); + elseif J1(1)=='"' %literal string + JJ=regexp(J,'\"(.*)\"','tokens'); + M=JJ{1}{1}; + else %numeric value + M=str2num(J); % is number + end +end + +function y=extract_struct(x) +import io.* + +%detag arrays first +indOC=extract_embed(x,'[',']'); +n=size(indOC,1); +for i=n:-1:1 + tag{i}=json2mat(x(indOC(i,1):indOC(i,2))); + x=[x(1:indOC(i,1)-1),'tag{',num2str(i),'}',x(indOC(i,2)+1:end)]; +end + + +a=regexp(x,'[^:,]+:[^,]+'); +n=length(a); +a=[a,length(x)+2]; +for i=1:n + s=x(a(i):a(i+1)-2); + t=regexp(s,'([^:]+):(.+)','tokens'); + %t{1}{1}(t{1}{1}==32)=[]; % remove blanks, maybe later do something fancier like replace with underscores + t{1}{1}=strrep(t{1}{1},' ','_'); + t{1}{1}=strrep(t{1}{1},'"',''); + if t{1}{1}(1)=='_' %JSON allows for fieldnames starting with "_" + t{1}{1}(1)=''; % this line will cause hard to track problems if the same object has 2 attributes with the same name but one of them starting with "_" + end + if regexp(t{1}{2},'tag{\d+}') + y.(t{1}{1})=eval(t{1}{2}); + else + y.(t{1}{1})=json2mat(t{1}{2}); + end + %y.(t{1}{1})=json2mat(t{1}{2}); +end + +function y=extract_cell(x) +import io.* + +indOC=extract_embed(x,'{','}'); +n=size(indOC,1); +for i=n:-1:1 + tag{i}=json2mat(x(indOC(i,1):indOC(i,2))); + x=[x(1:indOC(i,1)-1),'tag~<',num2str(i),'>~',x(indOC(i,2)+1:end)]; +end +indOC=extract_embed(x,'[',']'); +m=size(indOC,1); +for j=m:-1:1 + i=n+j; + tag{i}=json2mat(x(indOC(i,1):indOC(i,2))); + try;tag{i}=cell2mat(tag{i});end + x=[x(1:indOC(i,1)-1),'tag{',num2str(i),'}',x(indOC(i,2)+1:end)]; +end +x=strrep(x,'~<','{'); +x=strrep(x,'>~','}'); +if exist('tag') %catching numeric content + if isnumeric([tag{:}]) + try + y=eval(['[',strrep(x,'},','};'),']']); + end + end +end + +if exist('y')~=1 + y=eval(['{',strrep(x,'"',''''),'}']); +end + +%look for embeded objects and arrays + +function y=extract_embed(x,tagOpen,tagClose) +import io.* + +%EXTRACT_EMBED identifies embeded tagged segments +%Example y=extract_embed(str,'[',']') + +indOpen=strfind(x,tagOpen)'; +indOpen=[indOpen,ones(length(indOpen),1)]; +indClose=strfind(x,tagClose)'; +indClose=[indClose,-ones(length(indClose),1)]; +indOpenClose=[indOpen;indClose]; +[~,Ind]=sort(indOpenClose(:,1)); +indOpenClose=indOpenClose(Ind,:); +n=size(indOpenClose,1); +for i=2:n % add one for open, take one for close + indOpenClose(i,2)=indOpenClose(i-1,2)+indOpenClose(i,2); +end +i=0; +op=0; %open +while iscanindexrange(2,det+1) + scanindexrange(2,det+1) = ii; + end + + fmag(:,:,cid) = transpose(h5_data.measurements.(cf).diff_pat.Value); + if isfield(h5_data.measurements.(cf), 'bad_pixels') + bp_temp = h5_data.measurements.(cf).bad_pixels.Value; + else + bp_temp = []; + end + pos(cid, :) = h5_data.measurements.(cf).Attributes.position; + fmask(:,:,cid) = fmaskdet{det+1}; + if ~isempty(bp_temp) + for kk=1:size(bp_temp,2) + fmask(bp_temp(1,kk)+1,bp_temp(2,kk)+1,cid) = 0; + end + end + cid = cid + 1; + end + + end + fmag = fmag(:,:,1:cid-1); + fmask = fmask(:,:,1:cid-1); + pos = pos(1:cid-1,:); +% attr = hdf5_load(filename, '/measurements/','-sa'); + max_power = h5_data.measurements.Attributes.max_power; + fmask = logical(fmask); + if return_intensity + renorm = sqrt(1/max_power); + fmag = (fmag/renorm).^2; + end + + scanindexrange = scanindexrange'; + for ii=2:length(enum) + scanindexrange(ii,1) = scanindexrange(ii-1,2)+1; + end + + + + + + + case 'LibDetXR' + + %% get data dims + fmag_dim(1) = 0; + fmag_dim(2) = 1; + if isempty(enum) + enum = 1:length(fieldnames(h5_data.measurement))-1; + end + + for ii=enum + fmag_temp{ii} = h5_data.measurement.(['n' num2str(ii-1)]).data.Value; + fmag_dim(ii+2) = size(fmag_temp{ii},3); + end + + asize = size(fmag_temp{enum(1)}); + if asize(1) ~= asize(2) + error('Loading of asymmetric prepated datasets not supported, use p.force_preparation_data=true') + end + + fmag = zeros(asize(1), asize(2), sum(fmag_dim)-1, 'single'); + fmask = ones(asize(1), asize(2), sum(fmag_dim)-1, 'logical'); + pos = zeros(sum(fmag_dim)-1, 2); + max_sum = zeros(length(enum), 1); + + %% load modules to prepare fmask and load everything into containers + if isempty(enum) + enum = 1:length(fieldnames(h5_data.detector)); + end + + for ii=enum + % get modules for mask + fmaskdet{ii} = zeros(asize(1),asize(2), 'logical'); + modules = transpose(h5_data.detector.(['n' num2str(ii-1)]).modules.Value); + + numrows = modules(:,1); + numcols = modules(:,2); + indbeginmody = modules(:,3)+1; + indbeginmodx = modules(:,4)+1; + indendmody = numrows + indbeginmody -1; + indendmodx = numcols + indbeginmodx -1; + nummody = length(indbeginmody); + nummodx = length(indbeginmodx); + + for kk = 1:nummody + for jj = 1:nummodx + fmaskdet{ii}(indbeginmody(kk):indendmody(kk),indbeginmodx(jj):indendmodx(jj))=1; + end + end + temp_range = sum(fmag_dim(1:ii+1)):sum(fmag_dim(1:ii+2))-1; + fmask(:,:,temp_range) = repmat(fmaskdet{ii},[1,1,fmag_dim(ii+2)]); + + % get bad pixels + if isfield(h5_data.detector.(['n' num2str(ii-1)]), 'bad_pixels') + bp = h5_data.detector.(['n' num2str(ii-1)]).bad_pixels.Value; + for kk=1:size(bp,2) + fmask(bp(1,kk)+1,bp(2,kk)+1,temp_range) = 0; + end + else + if isfield(h5_data.measurement.(['n' num2str(ii-1)]), 'bad_pixels') + bp = h5_data.measurement.(['n' num2str(ii-1)]).bad_pixels.Value; + bpi = h5_data.measurement.(['n' num2str(ii-1)]).bad_pixels_index.Value; + assert(length(bpi)==length(temp_range), 'Number of frames does not match the number of bad pixel datasets.') + offset = 1; + for kk=1:length(bpi) + for jj=offset:bpi(kk) + fmask(bp(1,jj)+1, bp(2,jj)+1, temp_range(kk)) = 0; + end + offset = bpi(kk); + end + end + end + fmag(:,:,temp_range) = permute(fmag_temp{ii}, [2 1 3]); + pos_temp = h5_data.measurement.(['n' num2str(ii-1)]).positions.Value; + pos(temp_range,1) = pos_temp(1,:); + pos(temp_range,2) = pos_temp(2,:); + + max_sum(enum) = h5_data.measurement.(['n' num2str(ii-1)]).Attributes.max_sum; + + + end + + + % normalize to provide similar data as in h5-mex + max_power = max(sum(sum(fmag,1),2),[],3) / numel(fmag(:,:,1)); + renorm = sqrt(1/max_power); + if ~return_intensity + fmag = sqrt(fmag)*renorm; + end + fmask = logical(fmask); + + scanindexrange = zeros(numel(enum),2); + scanindexrange(1,:) = fmag_dim(2:3); + for ii=2:numel(enum) + scanindexrange(ii,1) = scanindexrange(ii-1,2)+1; + scanindexrange(ii,2) = scanindexrange(ii-1,2)+fmag_dim(ii+2); + end + + otherwise + error('Unknown HDF5 data structure!') +end + +if ~return_fftshifted + % return data as seen by detector, + fmag = math.ifftshift_2D(fmag); + fmask = math.ifftshift_2D(fmask); +end + + +end + diff --git a/+io/load_ptycho_recons.m b/+io/load_ptycho_recons.m new file mode 100644 index 0000000..6fcf226 --- /dev/null +++ b/+io/load_ptycho_recons.m @@ -0,0 +1,270 @@ +%LOAD_PTYCHO_RECONS Load data from cxs/h5 or mat file and return it as +% structure, single dataset or directly into the workspace. +% An additional argument can be passed to select subsections of the data. +% Loading single datasets is only supported for at least 2 output +% arguments. +% +% file... path to cxs/h5 or mat file +% +% *optional* +% section... 'full', 'probe', 'object', 'recon' or 'p' to select +% subsections of the data; default: 'full' +% +% EXAMPLES: +% %% recommended usage %% +% % load into a structure +% S = load_ptycho_recons('./recon.h5'); +% +% % load a subset +% S = load_ptycho_recons('./recon.h5', 'probe'); +% +% % load into single datasets +% [object, probe, p] = load_ptycho_recons('./recon.h5'); +% +% %% not recommended, only works in 'base' workspace %% +% % load directly into workspace +% load_ptycho_recons('./recon.h5'); +% +% +% full = object, probe (current scan) and p +% recon = object and probe (current scan) +% probe = probe (current scan) +% object = object (current scan) +% + +function varargout = load_ptycho_recons( filename_with_path, varargin ) + +import io.HDF.hdf5_load + +varargout = {}; + +if ~ischar(filename_with_path) + error('First argument has to be string') +end + +filename_with_path = utils.abspath(filename_with_path); + +if ~exist(filename_with_path, 'file') + error('Could not find reconstruction file %s', filename_with_path) +end + +if nargin > 1 + switch varargin{1} + case {'pr'; 'probe'; 'probes'} + section = 'probe'; + case {'ob'; 'obj'; 'objects'} + section = 'object'; + otherwise + section = varargin{1}; + end +else + section = 'full'; +end + +if ~nargout + output = 0; +elseif nargout >=2 + output = 2; +else + output = 1; +end + + function assign_struct(val, val_name) + switch output + case 1 + varargout{1}.(val_name) = val; + case 2 + varargout{end+1} = val; + otherwise + assignin('base', val_name, val); + end + end + + function assign_val(struc) + switch output + case 1 + varargout{1} = struc; + + case 2 + if isfield(struc, 'object') + varargout{end+1} = struc.object; + end + if isfield(struc, 'probe') + varargout{end+1} = struc.probe; + end + if isfield(struc, 'p') + varargout{end+1} = struc.p; + end + + otherwise + fn = fieldnames(struc); + for ii=1:length(fn) + assignin('base', fn{ii}, struc.(fn{ii})) + end + end + end + + +% check if it is a .mat file or a .cxs file +[~, ~, ext] = fileparts(filename_with_path); +switch ext + case '.mat' + switch section + case 'recon' + S = load(filename_with_path, 'object', 'probe'); + assign_val(S); + + case 'full' + S = load(filename_with_path); + assign_val(S); + + case 'object' + S = load(filename_with_path, 'object'); + assign_val(S); + + case 'probe' + S = load(filename_with_path, 'probe'); + assign_val(S); + + case 'p' + S = load(filename_with_path, 'p'); + assign_val(S); + + otherwise + error('Unknown data section %s', section); + end + + case {'.cxs','.h5'} + if io.HDF.hdf5_dset_exists(filename_with_path, 'object', '/reconstruction', true) + h5_path = '/reconstruction'; + else + h5_path = ''; + end + + % reconstruction + switch section + case 'recon' + % load object + h = hdf5_load(filename_with_path, [h5_path '/object']); + assign_struct(load_data_cell(h), 'object'); + % load probe + h = hdf5_load(filename_with_path, [h5_path '/probes']); + assign_struct(load_data_cell(h), 'probe'); + case 'full' + % load object + h = hdf5_load(filename_with_path, [h5_path '/object']); + assign_struct(load_data_cell(h), 'object'); + % load probe + h = hdf5_load(filename_with_path, [h5_path '/probes']); + assign_struct(load_data_cell(h), 'probe'); + % load p + p = convert2p(hdf5_load(filename_with_path, '/reconstruction/p', '-c')); + if io.HDF.hdf5_dset_exists(filename_with_path, 'meta_all', '/measurement', true) + p.meta = hdf5_load(filename_with_path, '/measurement/meta_all', '-c'); + elseif io.HDF.hdf5_dset_exists(filename_with_path, 'spec_all', '/measurement', true) + p.meta = hdf5_load(filename_with_path, '/measurement/spec_all', '-c'); + end + assign_struct(p, 'p'); + case 'object' + % load object + h = hdf5_load(filename_with_path, [h5_path '/object']); + assign_struct(load_data_cell(h), 'object'); + case 'probe' + % load probe + h = hdf5_load(filename_with_path, [h5_path '/probes']); + assign_struct(load_data_cell(h), 'probe'); + case 'p' + % load p + p = convert2p(hdf5_load(filename_with_path, '/reconstruction/p', '-c')); + if io.HDF.hdf5_dset_exists(filename_with_path, 'meta_all', '/measurement', true) + p.meta = hdf5_load(filename_with_path, '/measurement/meta_all', '-c'); + elseif io.HDF.hdf5_dset_exists(filename_with_path, 'spec_all', '/measurement', true) + p.meta = hdf5_load(filename_with_path, '/measurement/spec_all', '-c'); + end + assign_struct(p, 'p'); + otherwise + error('Unknown data section %s', section); + end + + + + otherwise + error('Unknown ptycho datatype %s.', ext) +end + + + +end + +function tmp = load_data_cell(h) + + fn = fieldnames(h); + num_end = str2double(subsref(strsplit(fn{1}, '_'), struct('type', '{}', 'subs',{{length(strsplit(fn{1},'_'))}}))); + if length(fn)==2 && (strcmpi(fn{1}, 'i') || strcmpi(fn{1}, 'r')) + tmp = permute(h.r + 1i*h.i, [2,1,3,4]); + elseif isnumeric(num_end) && ~isnan(num_end) + for ii=1:length(fn) + if isstruct(h.(fn{ii})) + tmp{ii} = load_data_cell(h.(fn{ii})); + else + if isnumeric(h.(fn{ii})) + tmp{ii} = double(h.(fn{ii})); + else + tmp{ii} = h.(fn{ii}); + end + end + end +% tmp = h; + else + for ii=1:length(fn) + if isstruct(h.(fn{ii})) + tmp.(fn{ii}) = load_data_cell(h.(fn{ii})); + else + if isnumeric(h.(fn{ii})) + tmp.(fn{ii}) = double(h.(fn{ii})); + else + tmp.(fn{ii}) = h.(fn{ii}); + end + end + end + end + +end + +function tmp = convert2p(h) + + fn = fieldnames(h); + for ii=1:length(fn) + if isstruct(h.(fn{ii})) + h.(fn{ii}) = load_data_cell(h.(fn{ii})); + elseif isnumeric(h.(fn{ii})) + h.(fn{ii}) = double(h.(fn{ii})); + else + continue; + end + end + tmp = h; + + % object + for ii=1:length(h.objects) + tmp.object{ii} = permute(h.objects{ii}, [2 1 3 4]); + end + tmp = rmfield(tmp, 'objects'); + + % probes + pr = tmp.probes; + tmp.probes = []; + for ii=1:length(pr) + tmp.probes(:,:,ii,:) = permute(pr{ii}, [2 1 3 4]); + end + + % positions + tmp.positions = transpose(tmp.positions); + tmp.positions_real = transpose(tmp.positions_real); + tmp.positions_orig = transpose(tmp.positions_orig); + + % ctr + tmp.ctr = transpose(tmp.ctr); + +end + diff --git a/+io/marread.m b/+io/marread.m new file mode 100644 index 0000000..c3a882a --- /dev/null +++ b/+io/marread.m @@ -0,0 +1,194 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: marread.m,v $ +% +% $Revision: 1.1 $ $Date: 2008/07/17 16:55:40 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading TIFF files written by a MAR CCD +% +% Note: +% MAR data are TIFF and can be read by forcing the type to tif. +% The advantage of forcing the type to mar is, that additional header +% fields like the exposure time are read. +% This follows the MarCCD header documentaion by Blum and Doyle +% marccd v0.17.1 +% Call without arguments for a brief help text. +% +% Dependencies: +% - fopen_until_exists +% - get_hdr_val +% - compiling cbf_uncompress.c increases speed but is not mandatory +% +% +% history: +% +% July 17th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [frame,vararg_remain] = marread(filename,varargin) +import io.* +import utils.fopen_until_exists +import utils.get_hdr_val + +% 0: no debug information +% 1: some feedback +% 2: a lot of information +debug_level = 0; + +% initialize return argument +frame = struct('header',[], 'data',[]); + + +% check minimum number of input arguments +if (nargin < 1) + image_read_sub_help(mfilename,'mar'); + error('At least the filename has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',value pairs'); +end + +% set default values for the variable input arguments and parse the named +% parameters: +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + otherwise + % pass further arguments on to fopen_until_exists + vararg{end+1} = name; + vararg{end+1} = value; + end +end + + +% try to open the data file +if (debug_level >= 1) + fprintf('Opening %s.\n',filename); +end +[fid,vararg_remain] = fopen_until_exists(filename,vararg); +if (fid < 0) + return; +end + +% read all data at once +[fdat,fcount] = fread(fid,'uint8=>uint8'); + +% close input data file +fclose(fid); +if (debug_level >= 2) + fprintf('%d data bytes read\n',fcount); +end + +% the MAR header has a fixed length of 1024 bytes for the TIFF header plus +% 3072 bytes for the MAR specific part +end_of_header_pos = 4096; + +if (length(fdat) < end_of_header_pos) + error([ num2str(length(fdat)) ' bytes read, which is less than the constant header length' ]); +end + +% check little/big endian, also to recognize MAR files +if (typecast(fdat(1025+32:1025+35),'uint32') ~= 1234) + error([ filename ' is not a MAR file or has big endian byte order' ]); +end + +% get image dimensions +nfast = typecast(fdat(1025+80:1025+83),'uint32'); +nslow = typecast(fdat(1025+84:1025+87),'uint32'); +bytes_per_pixel = typecast(fdat(1025+88:1025+91),'uint32'); + +if ((bytes_per_pixel ~= 2) && (bytes_per_pixel ~= 4)) + error( [ 'unforseen no. of bytes per pixel of ' num2str(bytes_per_pixel) ] ); +end +bytes_expected = end_of_header_pos + nfast*nslow*bytes_per_pixel; +if (bytes_expected ~= length(fdat)) + error([ num2str(bytes_expected) ' bytes expected, ' num2str(length(fdat)) ' read' ]); +end + +% return some selected header fields as lines of a cell array +frame.header = cell(7,1); +frame.header{1} = sprintf('IntegrationTime_ms %d',... + typecast(fdat(1025+640+12:1025+640+15),'uint32')); +frame.header{2} = sprintf('ExposureTime_ms %d',... + typecast(fdat(1025+640+16:1025+640+19),'uint32')); +frame.header{3} = sprintf('ReadoutTime_ms %d',... + typecast(fdat(1025+640+20:1025+640+23),'uint32')); +frame.header{4} = sprintf('nReads %d',... + typecast(fdat(1025+640+24:1025+640+27),'uint32')); +frame.header{5} = sprintf('DateTime %s %s %s:%s%s %s',... + char(fdat(2369:2370)'),... + char(fdat(2371:2372)'),... + char(fdat(2373:2374)'),... + char(fdat(2375:2376)'),... + char(fdat(2381:2383)'),... + char(fdat(2377:2380)')); +frame.header{6} = sprintf('PixelSizeX_nm %d',... + typecast(fdat(1025+768+4:1025+768+7),'uint32')); +frame.header{7} = sprintf('PixelSizeY_nm %d',... + typecast(fdat(1025+768+8:1025+768+11),'uint32')); + +% store data +switch bytes_per_pixel + case 2 + frame.data = typecast(fdat(4097:end),'uint16'); + case 4 + frame.data = typecast(fdat(4097:end),'uint32'); + otherwise + error( [ 'unforseen no. of bytes per pixel of ' num2str(bytes_per_pixel) ] ); +end +frame.data = reshape(frame.data,nfast,nslow); diff --git a/+io/mat2json.m b/+io/mat2json.m new file mode 100644 index 0000000..da50b36 --- /dev/null +++ b/+io/mat2json.m @@ -0,0 +1,90 @@ +function J=mat2json(M,F) +import io.* +%JSON2MAT converts a Matlab structure into a javscript data object (JSON). +% M can also be a file name. In teh spirit of fast prototyping +% this function takes a very loose approach to data types and +% dimensionality - neither is explicitly retained. +% +% The second input argument is optional and when used it indicates +% the name of teh file where J is to be stored. +% +%Example: mat2json(json2mat('{lala:2,lele:4,lili:[1,2,{bubu:5}]}')) +% +% Jonas Almeida, March 2010 + +% Copyright (c) 2010, Jonas Almeida +% All rights reserved. +% +% Redistribution and use in source and binary forms, with or without +% modification, are permitted provided that the following conditions are +% met: +% +% * Redistributions of source code must retain the above copyright +% notice, this list of conditions and the following disclaimer. +% * Redistributions in binary form must reproduce the above copyright +% notice, this list of conditions and the following disclaimer in +% the documentation and/or other materials provided with the distribution +% +% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +% POSSIBILITY OF SUCH DAMAGE. + + +switch class(M) + case 'struct' + J='{'; + f=fieldnames(M); + for i=1:length(f) + fld=f{i}; + id=regexp(fld,'^id(\d*)$','tokens'); + if ~isempty(id) + fld=id{1}{1}; + end + J=[J,'"',fld,'":',mat2json(M.(f{i})),',']; + end + J(end)='}'; + + case 'cell' + J='['; + for i=1:length(M) + J=[J,mat2json(M{i}),',']; + end + J(end)=']'; + otherwise + if isnumeric(M) % notice looseness in not converting single numbers into arrays + if length(M(:))==1 + J=num2str(M); + else + s=size(M); + if (length(s)==2)&(s(1)<2) % horizontal or null vector + J=['[',num2str(M),']']; % and of destroying dimensionality + J=regexprep(J,'\s+',','); + elseif length(s)==2 %2D solution + J='['; + for i=1:s(1) + J=[J,mat2json(M(i,:)),',']; + end + J(end)=']'; + elseif length(s)>2 % for now treat higher dimensions as linear vectors + J=['[',num2str(M(:)'),']']; % and of destroying dimensionality + J=regexprep(J,'\s+',','); + end + end + else + J=['"',M,'"']; % otherwise it is treated as a string + end +end + +if nargin>1 %save JSON result in file + fid=fopen(F,'w'); + fprintf(fid,'%s',J); + fclose(fid); +end \ No newline at end of file diff --git a/+io/mcs_mesh.m b/+io/mcs_mesh.m new file mode 100644 index 0000000..a89bc49 --- /dev/null +++ b/+io/mcs_mesh.m @@ -0,0 +1,429 @@ +% Call function without arguments for instructions on how to use it + +% Filename: $RCSfile: mcs_mesh.m,v $ +% +% $Revision: 1.7 $ $Date: 2016/08/03 08:38:32 $ +% $Author: $ +% $Tag: $ +% +% Description: +% Macro for reading .dat files in self-defined data formats +% +% Note: +% Call without arguments for a brief help text. +% +% Dependencies: +% - image_read_set_default +% - fopen_until_exists +% - get_hdr_val +% - compiling cbf_uncompress.c increases speed but is not mandatory +% +% +% history: +% +% February 18th 2009: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [mcs_data, data_adjusted, pos_data] = mcs_mesh(first_scan_no,no_of_intervals,varargin) +import io.* +import utils.adjust_projection +import utils.fopen_until_exists +import utils.get_hdr_val + +% initialize return arguments +mcs_data = []; +% legacy_sgalil = false; % A flag that keeps track of different commands needed if the older legacy file of reading positions is used - 2019.04 + +% set default parameter +% plot this MCS channel +ch_to_plot = 4; +snake_scan=0; +fast_axis_x = 1; +% create the plot in this figure +fig_no = 123; +% exit with an error message if unhandled named parameters are left at the +% end of this macro +unhandled_par_error = 1; +% file name base +fname_base = ''; +% first part of directory path +dir_base = '~/Data10/mcs/'; +% scaling factors for the axes +x_scale = 1.0; +y_scale = 1.0; +% +axis_minmax = []; +% save resulting figure +figure_dir = '~/Data10/analysis/online/stxm/figures/'; +% save the resulting data +data_dir = '~/Data10/analysis/online/stxm/data/'; +pos_file = '~/Data10/sgalil/S%05d.dat'; +positions_only = false; + +% check minimum number of input arguments +if (nargin < 2) + fprintf('[mcs_data data_adjusted pos_data]=%s(, [[,,] ...]);\n',... + mfilename); + fprintf('The optional , pairs are:\n'); + fprintf('''ChToPlot'', if greater than zero than this channel is plotted, default is %d\n',ch_to_plot); + fprintf('''SnakeScan'',<0-no, 1-yes> scan mode is a snake pattern, default is 0\n'); + fprintf('''FastAxisX'',<0-no, 1-yes> fast scan axis is along x, default is 1\n'); + fprintf('''FigNo'',
plot the data in this figure, 0 for no figure, default is %d\n',fig_no); + fprintf('''XScale'', scale the x-axis with this factor\n'); + fprintf('''YScale'', scale the y-axis with this factor\n'); + fprintf('''AxisMinMax'',<[ min max]> specify both min and max value\n'); + fprintf('''DirBase'', including an ending slash, default is ''%s''\n',dir_base); + fprintf('''FnameBase'', in case of an empty string the current Unix user name is used followed by an underscore, default if ''%s''\n',fname_base); + fprintf('''FigureDir'',''directory'' save the resulting plot in eps, jpeg and Matlab fig format, '''' for no saving, default is %s\n',figure_dir); + fprintf('''DataDir'',''directory'' save the resulting data as Matlab file, '''' for no saving, default is %s\n',data_dir); + fprintf('''Pos_file'',''path and filename structure'' checks the flipping of images using the positions found in these files. It gives a warning if it detects the wrong fast axis and it flips the lines for snake scans.\n'); + fprintf(' If the filename structure includes an %% it tries to read positions assuming one dat file for each scan. If it does not include an %% then it uses spec_read.\n'); + fprintf(' Default is %s, set to empty =[ ] to avoid warnings \n',pos_file); + fprintf('''Positions_only'',<0-no, 1-yes> if you only use the routine to get and check positions of a scan but you don''t have a MCS scalar measurement, default is %d.\n', positions_only); + fprintf('The data are in the format ''fast to slow axis'', i.e., the first dimension is the MCS channel,\n'); + fprintf('the second dimension is the exposure index, the third dimension is the fast axis of a mesh scan\n'); + fprintf('and the fourth dimension is the slow axis of a mesh scan.\n'); + fprintf('An optional second output ''data_adjusted'' can be obtained. This is a structure that has been adjusted and flipped according to the scan parameters in order to reflect\n'); + fprintf(' the sample physical orientation. One of the fields is ''transm'' which is the sample transmissivity. Other fields are positions_out, scan_num, and scan_point.\n'); + error('At least the number of the first scan and the number of line intervals need to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + +% parse the variable input arguments +vararg = cell(0,0); +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'ChToPlot' + ch_to_plot = value; + case 'SnakeScan' + snake_scan = value; + case 'FastAxisX' + fast_axis_x = value; + case 'FigNo' + fig_no = value; + case 'XScale' + x_scale = value; + case 'YScale' + y_scale = value; + case 'AxisMinMax' + axis_minmax = value; + case 'UnhandledParError' + unhandled_par_error = value; + case 'FnameBase' + fname_base = value; + case 'DirBase' + dir_base = value; + case 'DataDir' + data_dir = value; + case 'FigureDir' + figure_dir = value; + case 'Pos_file' + pos_file = value; + case 'Positions_only' + positions_only = value; + otherwise + vararg{end+1} = name; %#ok + vararg{end+1} = value; %#ok + end +end + + +% initialize the list of unhandled parameters +vararg_remain = cell(0,0); + + +% get the current user name + + if (length(fname_base) < 1) + [stat,usr]=unix('echo $USER'); + fname_base = [ sscanf(usr,'%s') '_' ]; + end + + +% initialize the output figure +if ~positions_only + figure(fig_no); + hold off; + clf; +end + +last_scan_no = first_scan_no + no_of_intervals; +store_ind = 1; +last_draw_time = clock; + +% over all the scan lines +for scan_no = first_scan_no:last_scan_no + dir = [ dir_base 'S' num2str(floor(scan_no/1000)*1000,'%05d') '-' ... + num2str(floor(scan_no/1000)*1000+999,'%05d') '/S' num2str(scan_no,'%05d') '/' ]; + filename = [ fname_base num2str(scan_no,'%05d') '.dat' ]; + + % read the frame until all data are available + ind_rep = 0; + ind_max = 3; + last_no_of_el_read = 0; + if ~positions_only + while (ind_rep < ind_max) + frame = image_read([dir filename ], 'RetryReadSleep',10, ... + 'RetryReadMax',1, 'RetrySleepWhenFound',10); + if ~isempty(frame.data)&&(frame.no_of_el_read{1} >= numel(frame.data)) + % the complete data set has been read + break; + end + if ~isempty(frame.data)&&(frame.no_of_el_read{1} <= last_no_of_el_read) + % no progress, increase timeout counter + ind_rep = ind_rep +1; + else + ind_rep = 0; + end + if ~isempty(frame.no_of_el_read) + last_no_of_el_read = frame.no_of_el_read{1}; + end + if ~isempty(frame.header) + exp_time = get_hdr_val(frame.header{1},'Exposure_time','%f',1); + end + wait_time = numel(frame.no_of_el_read)*exp_time; + if (wait_time > 2.0) + fprintf('%d/%d: frame incomplete (%d/%d), waiting %.1fs and retrying\n',... + ind_rep+1,ind_max,frame.no_of_el_read{1},numel(frame.data)); + end + pause(wait_time); + end + end + if ~isempty(pos_file) + if contains(pos_file,'%') + positions_from_spec = false; + filepos = sprintf(pos_file,scan_no); + try + positions = beamline.read_position_file(filepos); + positions.data(1,:) = positions.Avg_x; + positions.data(2,:) = positions.Avg_y; + numpts = numel(positions.Avg_x); + catch + warning('The reading of positions for sgalil did not work, now trying legacy mode in older sgalil position format') + positions = image_read(filepos,'RetryReadSleep',10,'RetryReadMax',0); + numpts = size(positions.data,2); +% legacy_sgalil = true; + end + else % If it does not contain % delimiter then we assume is a spec file + positions_from_spec = true; + if scan_no == last_scan_no % Only read spec positions once all scans are done, otherwise is too slow to read each time + try + fprintf('Reading positions from spec file %s \n',pos_file) + positions = io.read_scan_positions_spec(pos_file,first_scan_no:last_scan_no,{'samx','samy'}); + catch + warning('io.read_scan_positions_spec failed, pausing 5 seconds and retrying') + pause(5); + positions = io.read_scan_positions_spec(pos_file,first_scan_no:last_scan_no,{'samx','samy'}); + end + else + positions.data = []; + end + end + end + % initialize return array + if (scan_no == first_scan_no) + if ~positions_only + mcs_data = zeros(size(frame.data,1),size(frame.data,2),... + size(frame.data,3) + 1 ,no_of_intervals+1); + numpts = size(frame.data,3) + 1; + else + mcs_data = []; + numpts = size(positions.data,2); + end + + [scan_num, scan_point] = meshgrid(first_scan_no:last_scan_no,0:numpts-1); + % Check positions of stage for flipping + if ~isempty(pos_file) + pos_data = zeros(numpts, no_of_intervals+1, 2); + else + pos_data = []; + end + end + + if ~positions_only + store_ind_to = store_ind + size(frame.data,4) - 1; + else + store_ind_to = store_ind; + end + %%% Special about MCS is that it does not take the first image because + %%% it is triggered in a strange way, so here to match other + %%% detectors we make the matrix one element larger and we replicate + %%% the first value + if ~positions_only + mcs_data(:,:,2:end,store_ind:(store_ind+size(frame.data,4)-1)) = frame.data; + mcs_data(:,:,1,store_ind:(store_ind+size(frame.data,4)-1)) = frame.data(:,1,2,:); + end + + if ~isempty(pos_file) + if ~positions_from_spec + pos_data(:,store_ind,:) = positions.data.'; + else % if positions are from spec they are read only at the end and arranged in a structure so they need special handling + if scan_no == last_scan_no + pos_data = zeros(size(positions(1).data,1),no_of_intervals+1,2); + for ii = numel(positions) + pos_data(:,ii,:) = positions(ii).data; + end + end + end + end + store_ind = store_ind_to +1; + + if ((scan_no == first_scan_no) || (scan_no == last_scan_no) || ... + (etime(clock,last_draw_time) > 10)) + if (size(mcs_data,2) == 1) + data_plot = squeeze(mcs_data(ch_to_plot,1,:,:)); + elseif ~isempty(mcs_data) + data_plot = squeeze(mcs_data(ch_to_plot,:,:,1)); + else + data_plot = []; + end + if (~positions_only)&&((size(data_plot,1) > 1) && (size(data_plot,2) > 1)) + %CHANGE + [data_plot, positions_out] = adjust_projection(data_plot, snake_scan, fast_axis_x, pos_data); + % 2D plot + x_values = (1:size(data_plot,2)) * x_scale; + y_values = (1:size(data_plot,1)) * y_scale; + + if (~isempty(axis_minmax)) + caxis(axis_minmax); + end + %plot the image + figure(fig_no) + imagesc(data_plot); + axis xy; + axis equal; + axis tight; + colormap gray; + colorbar; + title( [ fname_base ': #' num2str(first_scan_no) ' -' num2str(scan_no)] ); + drawnow; + elseif (~positions_only) + % 1D plot if only one line has been read + x_values = (1:length(data_plot)) * x_scale; + plot(x_values,data_plot); + else + [data_plot, positions_out] = adjust_projection(data_plot, snake_scan, fast_axis_x, pos_data); + end + last_draw_time = clock; + end +end + +[scan_num] = adjust_projection(scan_num, snake_scan, fast_axis_x, pos_data); +[scan_point] = adjust_projection(scan_point, snake_scan, fast_axis_x, pos_data); + +data_adjusted.transm = data_plot; +data_adjusted.positions_out = positions_out; +data_adjusted.scan_num = scan_num; +data_adjusted.scan_point = scan_point; + +% file name for saving +filename = sprintf('stxm_scans_%05d-%05d_mcs',first_scan_no,... + last_scan_no); + +% save figures +if (~isempty(figure_dir))&&(~positions_only) + figure(fig_no); + + % create output directories and write the plot in different formats + if (~exist(figure_dir,'dir')) + mkdir(figure_dir) + end + if ((figure_dir(end) ~= '/') && (figure_dir(end) ~= '\')) + figure_dir = [ figure_dir '/' ]; + end + fprintf('output directory for figures is %s\n',figure_dir); + + subdir = [ figure_dir 'jpg/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.jpg\n',filename); + print('-djpeg','-r300',[subdir filename '.jpg'] ); + + subdir = [ figure_dir 'eps/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.eps\n',filename); + print('-depsc','-r1200',[subdir filename '.eps'] ); + + subdir = [ figure_dir 'fig/' ]; + if (~exist(subdir,'dir')) + mkdir(subdir); + end + fprintf('saving %s.fig\n',filename); + hgsave([subdir filename '.fig']); +end + +% save resulting data +if (~isempty(data_dir)) + if ((data_dir(end) ~= '/') && (data_dir(end) ~= '\')) + data_dir = [ data_dir '/' ]; + end + + % create output directory + if (~exist(data_dir,'dir')) + mkdir(data_dir) + end + + % save data + fprintf('saving %s.mat\n',[data_dir filename]); + save([data_dir filename],'mcs_data','first_scan_no','last_scan_no','pos_data','data_adjusted'); +end + +return + diff --git a/+io/movefile_fast.m b/+io/movefile_fast.m new file mode 100644 index 0000000..a9a1bed --- /dev/null +++ b/+io/movefile_fast.m @@ -0,0 +1,58 @@ +% MOVEFILE_FAST faster alternative to the matlab movefile function +% +% movefile_fast(source, destination) +% + + + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function movefile_fast(source, destination) + + Sd = java.io.File(destination); + if Sd.isDirectory + % if provided path to move is a directory, create full file path from the source name + [~, filename, ext] = fileparts(source); + destination = fullfile(destination, [filename, ext]); + Sd = java.io.File(destination); + end + Ss = java.io.File(source); + assert(Ss.canRead, sprintf('File %s does not exist or is not readable', source)) + % move the file using java + Ss.renameTo(Sd); + assert(Sd.canWrite, sprintf('Moving file %s to %s failed', source, destination)) +end + + diff --git a/+io/multiple_mcs_headers.m b/+io/multiple_mcs_headers.m new file mode 100644 index 0000000..a35695c --- /dev/null +++ b/+io/multiple_mcs_headers.m @@ -0,0 +1,75 @@ +% [] = multiple_mcs_headers(scan_no_from,scan_no_to) + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [] = multiple_mcs_headers(scan_no_from,scan_no_to) +import utils.compile_x12sa_filename + +if (nargin ~= 2) + fprintf('Usage: %s \n',mfilename); + fprintf('Searches for multiple MCS headers, renames the matching files and\n'); + fprintf('writes a new file with only the last header and its data.\n'); + fprintf('This is just a workaround for the current implementation of the MCS\n'); + fprintf('where spec stores the MCS data several times in case a cont_line is repeated.\n'); +end + +for (scan_no = scan_no_from:scan_no_to) + dirname = compile_x12sa_filename(scan_no, -1, 'BasePath','~/Data10/mcs/'); + dirinfo = dir([ dirname '*.dat' ]); + if (length(dirinfo) < 1) + fprintf('%s not found\n',filename); + else + for (ind = 1:length(dirinfo)) + filename = [dirname dirinfo(ind).name]; + fid = fopen(filename,'r'); + % read all data at once + [fdat,~] = fread(fid,'uint8=>uint8'); + fclose(fid); + % check for multiple fileheaders + header_start = strfind(fdat','# MCS file version'); + if (length(header_start) > 1) + fprintf('%s has %d headers\n',filename, length(header_start)); + % rename the file + movefile(filename, [filename '_org']); + % use the last file part + fdat = fdat(header_start(end):end); + % store the truncated data + fid = fopen(filename,'w'); + fwrite(fid,fdat); + fclose(fid); + end + end + end +end diff --git a/+io/private/image_orient_help.m b/+io/private/image_orient_help.m new file mode 100644 index 0000000..3e07f6f --- /dev/null +++ b/+io/private/image_orient_help.m @@ -0,0 +1,119 @@ +% [] = image_orient_help(m_file_name,varargin) +% parameter help for image_orient and calling functions + +% Filename: $RCSfile: image_orient_help.m,v $ +% +% $Revision: 1.3 $ $Date: 2009/01/16 15:30:29 $ +% $Author: $ +% $Tag: $ +% +% Description: +% parameter help for image_orient and calling functions +% +% Note: +% none +% +% Dependencies: +% none +% +% +% history: +% +% May 27th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [] = image_orient_help(m_file_name,varargin) +import io.* + +% check minimum number of input arguments +if (nargin < 1) + error('At least the m-file name has to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 2) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 1 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 1) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +parameters_only = 0; +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'ParametersOnly' + parameters_only = value; + otherwise + error('unknown parameter %s',name); + end +end + +if (~parameters_only) + fprintf('Usage:\n') + fprintf('[data_out]=image_orient( [[,,] ...]);\n'); + fprintf('The optional , pairs are:\n'); +end +fprintf('''OrientExtension'',<''extension''> file name extension to determine the orientation from\n'); +fprintf('''OrientByExtension'',<0-no,1-yes> use the default orientation for this file type, default yes,\n'); +fprintf(' superseeded by following orientation parameters (i.e., parameter order matters)\n'); +fprintf('''Transpose'',<0-no,1-yes> mirror at the diagonal\n'); +fprintf('''FlipUD'',<0-no,1-yes> mirror about the horizontal axis\n'); +fprintf('''FlipLR'',<0-no,1-yes> mirror about the vertical axis\n'); +fprintf('''Orientation'',<[ ]>\n'); +fprintf(' specify for each of the three parameters 0 (no) or 1 (yes)\n'); +if (~parameters_only) + fprintf('\n'); + fprintf('Examples:\n'); + fprintf('[data_out]=%s(data, ''Orientation'',[1 1 0]);\n',mfilename); + fprintf('[data_out]=%s(data, ''Transpose'',1, ''FlipLR'',1);\n',mfilename); + fprintf('These two calls are equivalent.\n'); +end diff --git a/+io/private/image_read_sub_help.m b/+io/private/image_read_sub_help.m new file mode 100644 index 0000000..42d496a --- /dev/null +++ b/+io/private/image_read_sub_help.m @@ -0,0 +1,124 @@ +% [] = image_read_sub_help(m_file_name,extension,varargin) +% parameter help for sub-routines of image_read like cbfread, edfread, +% speread and fliread + +% Filename: $RCSfile: image_read_sub_help.m,v $ +% +% $Revision: 1.1 $ $Date: 2008/06/10 17:05:14 $ +% $Author: $ +% $Tag: $ +% +% Description: +% parameter help for sub-routines of image_read like cbfread, edfread, +% speread and fliread +% +% Note: +% none +% +% Dependencies: +% none +% +% +% history: +% +% May 9th 2008: 1st version + +%*-----------------------------------------------------------------------* +%|                                                                       | +%|  Except where otherwise noted, this work is licensed under a          | +%|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            | +%|  International (CC BY-NC-SA 4.0) license.                             | +%|                                                                       | +%|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    | +%|                                                                       | +%|      Author: CXS group, PSI  | +%*-----------------------------------------------------------------------* +% You may use this code with the following provisions: +% +% If the code is fully or partially redistributed, or rewritten in another +% computing language this notice should be included in the redistribution. +% +% If this code, or subfunctions or parts of it, is used for research in a +% publication or if it is fully or partially rewritten for another +% computing language the authors and institution should be acknowledged +% in written form in the publication: “Data processing was carried out +% using the “cSAXS matlab package” developed by the CXS group, +% Paul Scherrer Institut, Switzerland.” +% Variations on the latter text can be incorporated upon discussion with +% the CXS group if needed to more specifically reflect the use of the package +% for the published work. +% +% A publication that focuses on describing features, or parameters, that +% are already existing in the code should be first discussed with the +% authors. +% +% This code and subroutines are part of a continuous development, they +% are provided “as they are” without guarantees or liability on part +% of PSI or the authors. It is the user responsibility to ensure its +% proper use and the correctness of the results. + +function [] = image_read_sub_help(m_file_name,extension,varargin) +import io.* +% check minimum number of input arguments +if (nargin < 2) + error('At least the m-file name and the extension have to be specified as input parameter.'); +end + +% accept cell array with name/value pairs as well +no_of_in_arg = nargin; +if (nargin == 3) + if (isempty(varargin)) + % ignore empty cell array + no_of_in_arg = no_of_in_arg -1; + else + if (iscell(varargin{1})) + % use a filled one given as first and only variable parameter + varargin = varargin{1}; + no_of_in_arg = 2 + length(varargin); + end + end +end + +% check number of input arguments +if (rem(no_of_in_arg,2) ~= 0) + error('The optional parameters have to be specified as ''name'',''value'' pairs'); +end + + +% parse the variable input arguments +examples = 1; +extension_returned = 0; +for ind = 1:2:length(varargin) + name = varargin{ind}; + value = varargin{ind+1}; + switch name + case 'Examples' + examples = value; + case 'ExtensionReturned' + extension_returned = 1; + otherwise + error('unknown parameter %s',name); + end +end + +fprintf('Usage:\n') +if (extension_returned) + fprintf('[frame]=%s( [[,,] ...]);\n',... + m_file_name); +else + fprintf('[frame]=%s( [[,,] ...]);\n',... + m_file_name); +end +fprintf('The optional , pairs are:\n'); +fprintf('''RetryReadSleep'', if greater than zero retry opening after this time (default: 0.0)\n'); +fprintf('''RetryReadMax'',<0-...> maximum no. of retries, 0 for infinity (default: 0)\n'); +fprintf('''MessageIfNotFound'',<0-no,1-yes> display a mesage if not found, 1-yes is default\n'); +fprintf('''ErrorIfNotFound'',<0-no,1-yes> exit with an error if not found, default is 1-yes\n'); +if (examples) + fprintf('\n'); + fprintf('Examples:\n'); + fprintf('[frame]=%s(''~/Data10/roper/image.%s'');\n',... + m_file_name,extension); + fprintf('\n'); + fprintf('The returned structure has the fields data, header and extension.\n'); +end diff --git a/+io/private/ptycho_reader/Makefile b/+io/private/ptycho_reader/Makefile new file mode 100644 index 0000000..a67c14b --- /dev/null +++ b/+io/private/ptycho_reader/Makefile @@ -0,0 +1,63 @@ +ifndef MATLAB_HOME + $(error "MATLAB_HOME is undefined, load matlab/2018a module!") +endif + +ifndef HDF5_SERIAL_HOME + $(error "HDF5_SERIAL_HOME is undefined, load gcc/6.3.0 and hdf5_serial/1.8.18 modules!") +endif + +ifndef TIFF_INCLUDE_DIR + ifndef TIFF_LIBRARY_DIR + $(error "TIFF_INCLUDE_DIR or TIFF_LIBRARY_DIR undefined, load tiff/4.0.9 module!") + endif +endif + +MEXOPTIONS += -R2018a + +IO_DIR := ../.. +RDOBJ_TARGET_BASE := $(IO_DIR)/ptycho_read +RDMSR_TARGET_BASE := $(IO_DIR)/read_measurement +TARGET_SUFFIX := mexa64 +RDOBJ_TARGET_FILE := $(RDOBJ_TARGET_BASE).$(TARGET_SUFFIX) +RDMSR_TARGET_FILE := $(RDMSR_TARGET_BASE).$(TARGET_SUFFIX) + +all: $(RDOBJ_TARGET_FILE) $(RDMSR_TARGET_FILE) + +ptycho_read: $(RDOBJ_TARGET_FILE) + +read_measurement: $(RDMSR_TARGET_FILE) + +debug_helper.o : debug_helper.cc debug_helper.h + mex $(MEXOPTIONS) -c $< + +hdf5_helper.o: hdf5_helper.cc hdf5_helper.h + mex $(MEXOPTIONS) -c $< + +read_object_data.o: read_object_data.cc read_object_data.h precision.h hdf5_helper.h debug_helper.h env_helper.h multi_processing.h + mex $(MEXOPTIONS) -c $< + +read_eiger_data.o: read_eiger_data.cc read_eiger_data.h precision.h hdf5_helper.h debug_helper.h env_helper.h multi_processing.h + mex $(MEXOPTIONS) -c $< + +read_data_threaded.o: read_data_threaded.cc read_data_threaded.h precision.h debug_helper.h env_helper.h ${TIFF_INCLUDE_DIR}/tiffio.h + mex $(MEXOPTIONS) -I$(TIFF_INCLUDE_DIR) -c $< + +# Put the resultinf mex file into +io/+HDF folder +$(RDOBJ_TARGET_FILE): readObjectData.cc debug_helper.o hdf5_helper.o read_object_data.o mex_helper.h env_helper.h precision.h + mex $(MEXOPTIONS) $< read_object_data.o hdf5_helper.o debug_helper.o $(HDF5_SERIAL_HOME)/lib/libhdf5.a -lz -ldl -output $(RDOBJ_TARGET_BASE) + +$(RDMSR_TARGET_FILE): readMeasurementData.cc read_eiger_data.o hdf5_helper.o read_data_threaded.o debug_helper.o env_helper.h mex_helper.h precision.h read_eiger_data.h read_data_threaded.h $(TIFF_LIBRARY_DIR)/libtiff.a + mex $(MEXOPTIONS) $< read_eiger_data.o hdf5_helper.o read_data_threaded.o debug_helper.o $(HDF5_SERIAL_HOME)/lib/libhdf5.a $(TIFF_LIBRARY_DIR)/libtiff.a -lz -ldl -output $(RDMSR_TARGET_BASE) + +doc: + test -r doxygen.conf && rm -rf doc/html + test -d doc || mkdir doc + doxygen doxygen.conf + +clean: + rm -f *~ *.o + +proper: clean + rm -f $(RDOBJ_TARGET_FILE) $(RDMSR_TARGET_FILE) + +.PHONY: all clean proper ptycho_read read_measurement doc diff --git a/+io/private/ptycho_reader/Readme.md b/+io/private/ptycho_reader/Readme.md new file mode 100644 index 0000000..14cb4a6 --- /dev/null +++ b/+io/private/ptycho_reader/Readme.md @@ -0,0 +1,149 @@ +This directory contains code for reading ptychographic reconstruction results and measurement data. + +ptycho_read +=========== + +This is a MEX function for reading ptychographically constructed object datasets from HDF5 files. + +The parameters to the function are +1. number of reader processes +2. precision of the returned array: either 'single' or 'double' +3. desired dimension of the returned objects + This is an optional parameter. If absent, the dimension of the object in the first file is assumed to be the desired object dimension. + Objects that have different dimension will be adapted (either some pixels at the borders left away, or zero pixels added) +4. path to the object dataset within the HDF5 file (e.g. '/reconstruction/object') +5. cell array with the file paths (e.g. {'the/path/to/example.h5'}) + If there is only one file, the parameter can also be a character array + +From MATLAB call it in one of the following ways: + + [A, ind] = io.ptycho_read(1, 'single', [21,22], '/reconstruction/object', {'test1_20x20_c.h5', 'test2_20x20_c.h5'}); + A = io.ptycho_read(1, 'single', [21,22], '/reconstruction/object', {'test1_20x20_c.h5', 'test2_20x20_c.h5'}); + +The array *A* will contain the object datasets as a multidimensional array (Ncols, Nrows, Nmodes, Nslices, Nobjects). Nslices and Nmodes dimensions will be dropped if they are of size one. *ind* will contain a list of file indices that could not be read, if the first form is used. In the second form you'll get an error message if a file cannot be read. The function will print out some information about the read speed, if the environment variable *PTYCHO_READ_VERBOSE* is set to one of the values '1', 'yes', 'true'. + + setenv('PTYCHO_READ_VERBOSE', 'yes') + +The above command accomplishes this inside MATLAB. + +read_measurement +================ + +This is a MEX function for reading data from Eiger detector files in HDF5 format. The data must be located at /eh5/images within the file. + +The parameters to the function are +1. parameter structure + +The parameter structure contains some general fields: +* 'asize' with the result image dimensions (Ncols, Nrows) (optional, derived from data by default) +* 'ctr' with the regioin of interest center [col, row] relative to the data (optional, derived from data by default) +* 'precision' with the desired precision - 'single' or 'double' + +For the **Eiger** detector, the parameter structure must contain the following fields: +* 'extension' with value 'h5' +* 'data_path' cell array (size 1 or N) with with the paths to the HDF5 files conaining the Eiger data +* 'data_location' cell array (size 1 or N) with the dataset location(s) within the file(s) +* 'nthreads' with the desired number of parallel read processes + +Data is expected to be present in the following form: + + GROUP "/" { + GROUP "eh5" { + DATASET "images" { + DATATYPE H5T_STD_U32LE + DATASPACE SIMPLE { ( 464, 514, 1030 ) / ( 464, 514, 1030 ) } + } + } + } + +Here, there are 464 images with 514x1030 pixels. The file must be readable with the standard HDF5 library version 1.10.2 + +For the **Pilatus** detector +* 'extension' with value 'cbf' +* 'data_path' cell array with the paths of the CBF data files +* 'nthreads' with the desired number of parallel reader threads +Every data file must contain lines like + + conversions="x-CBF_BYTE_OFFSET" + X-Binary-Size-Fastest-Dimension: 1475 + X-Binary-Size-Second-Dimension: 1679 + +where 1475x1679 are the image dimensions and the image data should be byte offset encoded. + +For the **Moench** detector +* 'extension' with value 'tiff' +* 'data_path' cell array with the paths of the TIFF data files +* 'nthreads' with the desired number of parallel reader threads +The files are expected to contain raw single precision floating point data in one image plane, stored in IMAGELENGTH chunks of size IMAGEWIDTH. These tags must also give the image dimensions. + +From MATLAB call it in the following way: + + arg = struct() + arg.extension = 'h5' + arg.precision = 'single' + arg.nthreads = 1 + arg.data_path = { 'test.h5' } + arg.data_location = { '/eiger/images' } + A = io.read_measurement(arg); + +The array *A* will contain the measurement datasets as a multidimensional array [Ncols, Nrows, Nimages]. The function will print out some information about the arguments, if the environment variable *PTYCHO_READ_VERBOSE* is set to one of the values '1', 'yes', 'true'. + + setenv('PTYCHO_READ_VERBOSE', 'yes') + +The above command accomplishes this inside MATLAB. + +Debug output +------------ + +If debug output about the inner workings of the functions is desired, the environment variable *PTYCHO_READ_DEBUG* should be set to the filename where debug output will end up. + + setenv('PTYCHO_READ_DEBUG', '/tmp/debug_output.txt') + +The above command accomplishes this inside MATLAB. + +Compilation +----------- + +The code works only with MATLAB versions at or above 2018a, because the code requires the new interleaved complex array format. You need to load the matlab/2018a or a later MATLAB environment module in order to compile the code. Aditionally the gcc version and an apropriate HDF5 serial and TIFF environment module need to be loaded. This can be done by sourcing the *setup-environment.sh* script. After these steps module list shoud approximately like this: + + [stadler_h@ra-l-002 ~]$ module list + Currently Loaded Modulefiles: + 1) gcc/6.3.0 2) hdf5_serial/1.8.18 3) matlab/2018a 4) tiff/4.0.9 + +Now you should be ready to compile the code: + + [stadler_h@ra-l-002 ptycho_reader]$ make + mex -R2018a -c debug_helper.cc + Building with 'g++'. + MEX completed successfully. + ... + mex -R2018a readObjectData.cc read_object_data.o hdf5_helper.o debug_helper.o /opt/psi/Compiler/hdf5_serial/1.8.18/gcc/6.3.0/lib/libhdf5.a -lz -ldl -output ../../ptycho_read + Building with 'g++'. + MEX completed successfully. + ... + mex -R2018a readMeasurementData.cc read_eiger_data.o hdf5_helper.o read_data_threaded.o debug_helper.o /opt/psi/Compiler/hdf5_serial/1.8.18/gcc/6.3.0/lib/libhdf5.a -lz -ldl -output ../../read_measurement + Building with 'g++'. + MEX completed successfully. + +The make process will procude the ptycho_read and the read_measurement MEX file in the io package: *io.ptycho_read* and *io.read_measurement* + +Intermediate files of the make process can be cleaned using the command + + [stadler_h@ra-l-002 ptycho_reader]$ make clean + rm -f *~ *.o + +If you also want to delete the ptycho_read MEX file, use the command + + [stadler_h@ra-l-002 ptycho_reader]$ make proper + rm -f *~ *.o + rm -f ../../ptycho_read.mexa64 ../../read_measurement.mexa64 + +Code documentation in HTML format can be produced using doxygen (tested with version 1.8.13) + + [stadler_h@ra-l-002 ptycho_reader]$ make doc + test -r doxygen.conf && rm -rf doc/html + test -d doc || mkdir doc + doxygen doxygen.conf + ... + +Hope everything works as expected! diff --git a/+io/private/ptycho_reader/debug_helper.cc b/+io/private/ptycho_reader/debug_helper.cc new file mode 100644 index 0000000..5be9b6e --- /dev/null +++ b/+io/private/ptycho_reader/debug_helper.cc @@ -0,0 +1,6 @@ +#include +#include + +namespace debug { + std::unique_ptr out; //!< pointer to debug log file +} diff --git a/+io/private/ptycho_reader/debug_helper.h b/+io/private/ptycho_reader/debug_helper.h new file mode 100644 index 0000000..a568ff8 --- /dev/null +++ b/+io/private/ptycho_reader/debug_helper.h @@ -0,0 +1,60 @@ +/*! + * \file + * Helper code for debug handling + */ + +#ifndef DEBUG_HELPER +#define DEBUG_HELPER + +/*! + * \brief Debug functionality + * + * The environment variable PTYCHO_READ_DEBUG gives the name of the debug log file + */ +namespace debug { + extern std::unique_ptr out; //!< pointer to debug file + + /*! + * \brief Check debug output stream + * \return true if stream is operational, otherwise false + */ + inline bool check_out() + { + if (out.get()) + return out.get()->good(); + return false; + } + + /*! + * \brief Initialize debug output stream + */ + inline void debug_init() + { + out.reset(nullptr); + const char *fname = std::getenv("PTYCHO_READ_DEBUG"); + if (fname) { + out.reset(new std::ofstream(fname, std::ios::app)); + if (! out.get()->good()) + out.reset(nullptr); + } + } +} + +/*! + * \brief Initialize debug stream + */ +#define DEBUG_INIT debug::debug_init() + +/*! + * \brief Start debug code block + * Only access the debug stream inside such a block + */ +#define DEBUG if (debug::check_out()) + +/*! + * \brief Access debug output stream + * \return debug output stream reference + */ +#define OUT (*debug::out.get()) + +#endif diff --git a/+io/private/ptycho_reader/doxygen.conf b/+io/private/ptycho_reader/doxygen.conf new file mode 100644 index 0000000..0bb3d51 --- /dev/null +++ b/+io/private/ptycho_reader/doxygen.conf @@ -0,0 +1,2384 @@ +# Doxyfile 1.8.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Ptycho Reader" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = NO + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = . + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.cc \ + *.h + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = YES + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = ch.psi.csaxs.ptycho + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = YES + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /