igor-public/pearl/pearl-scienta-preprocess.ipf
matthias muntwiler 9a65d26984 updates: scaling of pshell data, matrix preview, elog panel
- elog panel supports multiple attachments
- matrix (omicron STM) data file preview in data explorer
- various improvements for the scaling of pshell data
2017-02-02 15:31:13 +01:00

802 lines
24 KiB
Igor

#pragma rtGlobals=3 // Use modern global access method and strict wave access.
#pragma IgorVersion = 6.1
#pragma ModuleName = PearlScientaPreprocess
#pragma version = 1.02
// $Id$
// author: matthias.muntwiler@psi.ch
// Copyright (c) 2013-14 Paul Scherrer Institut
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
/// @file
/// @brief preprocessing functions for Scienta detector images.
/// @ingroup ArpesPackage
///
/// this procedure contains functions for data reduction
/// and instrument-specific normalization.
///
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
///
/// @copyright 2013-15 Paul Scherrer Institut @n
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
/// you may not use this file except in compliance with the License. @n
/// You may obtain a copy of the License at
/// http://www.apache.org/licenses/LICENSE-2.0
/// @namespace PearlScientaPreprocess
/// @brief preprocessing functions for Scienta detector images.
///
/// PearlScientaPreprocess is declared in @ref pearl-scienta-preprocess.ipf.
function prompt_int_linbg_reduction(param)
string &param
variable Lcrop = NumberByKey("Lcrop", param, "=", ";")
variable Lsize = NumberByKey("Lsize", param, "=", ";")
variable Hcrop = NumberByKey("Hcrop", param, "=", ";")
variable Hsize = NumberByKey("Hsize", param, "=", ";")
variable Cpos = NumberByKey("Cpos", param, "=", ";")
variable Csize = NumberByKey("Csize", param, "=", ";")
prompt Lcrop, "Lower cropping region"
prompt Hcrop, "Upper cropping region"
prompt Lsize, "Lower background region"
prompt Hsize, "Upper background region"
prompt Cpos, "Center position"
prompt Csize, "Center integration region"
doprompt "int_linbg_reduction Parameters", lcrop, hcrop, lsize, hsize, cpos, csize
if (v_flag == 0)
param = ReplaceNumberByKey("Lcrop", param, Lcrop, "=", ";")
param = ReplaceNumberByKey("Lsize", param, Lsize, "=", ";")
param = ReplaceNumberByKey("Hcrop", param, Hcrop, "=", ";")
param = ReplaceNumberByKey("Hsize", param, Hsize, "=", ";")
param = ReplaceNumberByKey("Cpos", param, Cpos, "=", ";")
param = ReplaceNumberByKey("Csize", param, Csize, "=", ";")
endif
return v_flag
end
function /s capture_int_linbg_cursors()
// this function is for testing only, until we implement a proper interface
string param = csr_int_linbg_reduction("")
svar /z global_params = root:packages:pearl_explorer:s_reduction_params
if (svar_exists(global_params))
global_params = param
endif
return param
end
function /s csr_int_linbg_reduction(win)
// PRELIMINARY - function arguments may change
// sets reduction parameters from cursors in a graph.
// an even number of cursors (2 or more) must be set on the image.
// cursor names and order do not matter,
// except that the alphabetically first cursor which is attached to an image selects the image.
// the cursors mark the following positions, from innermost to outermost pair:
// 1) low and high limits of peak region.
// 2) peak-side boundary of lower and upper background region.
// 3) lower and upper cropping region.
string win
// read all cursor positions
variable ic
string sc
variable nc = 10
make /n=(nc) /free positions
variable np = 0
wave /z image = $""
string imagename = ""
string tracename = ""
string info
for (ic = 0; ic < nc; ic += 1)
sc = num2char(char2num("A") + ic)
wave /z wc = CsrWaveRef($sc, win)
info = CsrInfo($sc, win)
tracename = StringByKey("TNAME", info, ":", ";")
if (waveexists(wc) && (wavedims(wc) == 2))
if (!waveexists(image))
wave image = wc
imagename = tracename
endif
if (cmpstr(tracename, imagename) == 0)
positions[np] = pcsr($sc, win)
np += 1
endif
endif
endfor
np = floor(np / 2) * 2 // ignore odd cursor
redimension /n=(np) positions
sort positions, positions
// shift upper positions by one so that the rightmost pixel becomes 1.0
positions = p >= np / 2 ? positions + 1 : positions
positions = positions / dimsize(image, 0)
// map innermost cursor pair to peak center and size
variable ip2 = np / 2
variable ip1 = ip2 - 1
variable Cpos = (positions[ip1] + positions[ip2]) / 2
variable Csize = positions[ip2] - positions[ip1]
if (ip1 >= 0)
Cpos = (positions[ip1] + positions[ip2]) / 2
Csize = positions[ip2] - positions[ip1]
else
// default: a small region in the center
Cpos = 0.5
Csize = 0.2
endif
// background region
ip1 -= 1
ip2 += 1
variable Lsize
variable Hsize
if (ip1 >= 0)
Lsize = positions[ip1]
Hsize = 1 - positions[ip2]
else
// default: everything outside the peak region
Lsize = Cpos - Csize / 2
Hsize = 1 - (Cpos + Csize / 2)
endif
// crop region
ip1 -= 1
ip2 += 1
variable Lcrop
variable Hcrop
if (ip1 >= 0)
Lcrop = positions[ip1]
Hcrop = 1 - positions[ip2]
else
// default: dead corners of the EW4000 at PEARL
Lcrop = 0.11
Hcrop = 0.11
endif
Lsize = max(Lsize - Lcrop, 0)
Hsize = max(Hsize - Hcrop, 0)
string param = ""
param = ReplaceNumberByKey("Lcrop", param, Lcrop, "=", ";")
param = ReplaceNumberByKey("Lsize", param, Lsize, "=", ";")
param = ReplaceNumberByKey("Hcrop", param, Hcrop, "=", ";")
param = ReplaceNumberByKey("Hsize", param, Hsize, "=", ";")
param = ReplaceNumberByKey("Cpos", param, Cpos, "=", ";")
param = ReplaceNumberByKey("Csize", param, Csize, "=", ";")
return param
end
function test_int_linbg(image)
// useful for testing or manual processing
// since the int_linbg_reduction function cannot be called from the command line directly.
wave image
string param = ""
prompt_int_linbg_reduction(param)
string data1_name = "test_data1"
string data2_name = "test_data2"
duplicate /o image, $data1_name, $data2_name
wave w_data1 = $data1_name
wave w_data2 = $data2_name
int_linbg_reduction(image, w_data1, w_data2, param)
end
threadsafe function int_linbg_reduction(source, dest1, dest2, param)
// data reduction function for adh5_load_reduced_detector
// calculates the average pixel value of each angular slice
// in one center and two background intervals.
// a background value is calculated at the center position
// by linear interpolation from the two background values.
// returns the center minus linear background in dest1.
// returns the Poisson one-sigma error in dest2.
wave source // source wave
// Scienta detector image, energy axis along X, angle axis along Y
wave dest1, dest2 // destination waves
// each wave is a one-dimensional intensity distribution
// the function may redimension these waves to one of the image dimensions
// (it must be clear to the user which dimension this is).
// the meaning of dest1 and dest2 is up to the particular function,
// e.g. dest1 could hold the mean value and dest2 the one-sigma error,
// or dest1 could hold the X-profile, and dest2 the Y-profile.
string &param // parameters in a key1=value1;key2=value2;... list
// all region parameters are relative to the image size (0...1)
// Lcrop = size of the lower cropping region
// Hcrop = size of the upper cropping region
// Lsize = size of the lower background integration region
// Hsize = size of the upper background integration region
// Cpos = center position of the of the peak integration region
// Csize = size of the peak integration region
// typical values (peak centered on detector, FWHM ~ 20 % of image)
// Lcrop=0.11;Hcrop=0.11;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.2
variable nx = dimsize(source, 0)
variable ny = dimsize(source, 1)
// read parameters
variable lcrop = NumberByKey("Lcrop", param, "=", ";")
variable lsize = NumberByKey("Lsize", param, "=", ";")
variable hcrop = NumberByKey("Hcrop", param, "=", ";")
variable hsize = NumberByKey("Hsize", param, "=", ";")
variable cpos = NumberByKey("Cpos", param, "=", ";")
variable csize = NumberByKey("Csize", param, "=", ";")
// validate parameters
// background parameters are optional, center parameter is required.
if (numtype(lcrop) != 0)
lcrop = 0
endif
if (numtype(lsize) != 0)
lsize = 0
endif
if (numtype(hcrop) != 0)
hcrop = 0
endif
if (numtype(hsize) != 0)
hsize = 0
endif
if (numtype(Cpos) != 0)
return 1 // Cpos parameter missing
endif
if (numtype(Csize) != 0)
return 2 // Csize parameter missing
endif
variable lpos = lcrop + lsize / 2
variable hpos = 1 - (hcrop + hsize / 2)
variable p0
variable p1
adh5_setup_profile(source, dest1, 1)
adh5_setup_profile(source, dest2, 1)
duplicate /free dest1, lbg, hbg
if (lsize > 0)
p0 = round(lcrop * nx)
p1 = round((lcrop + lsize) * nx)
ad_profile_y_w(source, p0, p1, lbg)
else
lbg = 0
endif
if (hsize > 0)
p0 = round((1 - hcrop - hsize) * nx)
p1 = round((1 - hcrop) * nx)
ad_profile_y_w(source, p0, p1, hbg)
else
hbg = 0
endif
if (csize > 0)
p0 = round((cpos - csize/2) * nx)
p1 = round((cpos + csize/2) * nx)
ad_profile_y_w(source, p0, p1, dest1)
else
dest1 = 0
endif
variable scale = (cpos - lpos) / (hpos - lpos)
dest2 = dest1
dest1 -= scale * (hbg - lbg) + lbg
dest2 = sqrt(dest2 + scale^2 * (hbg + lbg))
return 0 // return zero if successful, non-zero if an error occurs
end
function test_shockley_anglefit(image, branch)
// apply the Shockley_anglefit function to a single image
// useful for testing or manual processing
// since the Shockley_anglefit function cannot be called from the command line directly.
wave image
variable branch // +1 or -1
string param = ""
param = ReplaceStringByKey("branch", param, num2str(branch), "=", ";")
string s_branch
if (branch >= 0)
s_branch = "p"
else
s_branch = "n"
endif
string pkpos_name = "saf_pkpos_" + s_branch
string pkwid_name = "saf_pkwid_" + s_branch
duplicate /o image, $pkpos_name, $pkwid_name
wave w_pkpos = $pkpos_name
wave w_pkwid = $pkwid_name
shockley_anglefit(image, w_pkpos, w_pkwid, param)
end
function prompt_Shockley_anglefit(param)
string &param
variable branch = NumberByKey("branch", param, "=", ";")
prompt branch, "Branch (-1 or +1)"
doprompt "Shockley_anglefit_reduction Parameters", branch
if (v_flag == 0)
param = ReplaceNumberByKey("branch", param, branch, "=", ";")
endif
return v_flag
end
threadsafe function Shockley_anglefit(source, dest1, dest2, param)
// data reduction function for adh5_load_reduced_detector
// specialized for analysing the Cu(111) Shockley surface state
// do curve fitting of one branch of the surface state
// the result is peak position versus energy
// TODO: this function contains hard-coded parameters. please generalize as necessary.
wave source // source wave
// Scienta detector image, energy axis along X, angle axis along Y
// the apex of the surface state must be at angle 0
wave dest1, dest2 // destination waves
// dest1: peak position
// dest2: peak width (sigma)
string &param // parameters in a key1=value1;key2=value2;... list
// branch=-1 or +1: select negative (positive) angles for the fit interval, respectively
variable nx = dimsize(source, 0)
variable ny = dimsize(source, 1)
// read parameters
variable branch = NumberByKey("branch", param, "=", ";")
// validate parameters
if (numtype(branch) != 0)
branch = +1
endif
// prepare output
adh5_setup_profile(source, dest1, 0)
adh5_setup_profile(source, dest2, 0)
dest1 = nan
dest2 = nan
// select angle range
// hard-coded for a particular measurement series
variable y0
variable y1
if (branch < 0)
y0 = -5
y1 = 0
else
y0 = 0
y1 = 5
endif
// select energy range
// start at the point of highest intensity and go up 0.45 eV
variable p0
variable p1
variable q0
variable q1
duplicate /free dest1, center
q0 = round((y0 - dimoffset(source, 1)) / dimdelta(source, 1))
q1 = round((y1 - dimoffset(source, 1)) / dimdelta(source, 1))
ad_profile_x_w(source, q0, q1, center)
wavestats /q/m=1 center
p0 = round((v_maxloc - dimoffset(source, 0)) / dimdelta(source, 0))
p1 = round((v_maxloc + 0.4 - dimoffset(source, 0)) / dimdelta(source, 0))
// prepare intermediate data buffer
make /n=(ny)/d/free profile
setscale /p x dimoffset(source,1), dimdelta(source,1), waveunits(source,1), profile
variable pp
for (pp = p0; pp <= p1; pp += 1)
profile = source[pp][p]
curvefit /Q /NTHR=1 /W=2 gauss profile(y0,y1)
wave w_coef
dest1[pp] = w_coef[2]
dest2[pp] = w_coef[3]
endfor
return 0 // return zero if successful, non-zero if an error occurs
end
function prompt_int_quadbg_reduction(param)
string &param
variable Lcrop = NumberByKey("Lcrop", param, "=", ";")
variable Lsize = NumberByKey("Lsize", param, "=", ";")
variable Hcrop = NumberByKey("Hcrop", param, "=", ";")
variable Hsize = NumberByKey("Hsize", param, "=", ";")
variable Cpos = NumberByKey("Cpos", param, "=", ";")
variable Csize = NumberByKey("Csize", param, "=", ";")
prompt Lcrop, "Lower cropping region"
prompt Hcrop, "Upper cropping region"
prompt Lsize, "Lower background region"
prompt Hsize, "Upper background region"
prompt Cpos, "Center position"
prompt Csize, "Center integration region"
doprompt "int_quadbg_reduction Parameters", lcrop, hcrop, lsize, hsize, cpos, csize
if (v_flag == 0)
param = ReplaceNumberByKey("Lcrop", param, Lcrop, "=", ";")
param = ReplaceNumberByKey("Lsize", param, Lsize, "=", ";")
param = ReplaceNumberByKey("Hcrop", param, Hcrop, "=", ";")
param = ReplaceNumberByKey("Hsize", param, Hsize, "=", ";")
param = ReplaceNumberByKey("Cpos", param, Cpos, "=", ";")
param = ReplaceNumberByKey("Csize", param, Csize, "=", ";")
endif
return v_flag
end
function test_int_quadbg(image)
// useful for testing or manual processing
// since the int_quadbg_reduction function cannot be called from the command line directly.
wave image
string param = ""
prompt_int_quadbg_reduction(param)
string data1_name = "test_data1"
string data2_name = "test_data2"
duplicate /o image, $data1_name, $data2_name
wave w_data1 = $data1_name
wave w_data2 = $data2_name
int_quadbg_reduction(image, w_data1, w_data2, param)
end
threadsafe function int_quadbg_reduction(source, dest1, dest2, param)
// data reduction function for adh5_load_reduced_detector
// integrates peak area minus a quadratic backgrouind
wave source // source wave
// Scienta detector image, energy axis along X, angle axis along Y
wave dest1, dest2 // destination waves
string &param // parameters in a key1=value1;key2=value2;... list
// all region parameters are relative to the image size (0...1)
// Lcrop = size of the lower cropping region
// Hcrop = size of the upper cropping region
// Lsize = size of the lower background integration region
// Hsize = size of the upper background integration region
// Cpos = center position of the of the peak integration region
// Csize = size of the peak integration region
// typical values (peak centered on detector, FWHM ~ 20 % of image)
// Lcrop=0.11;Hcrop=0.11;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.2
variable nx = dimsize(source, 0)
variable ny = dimsize(source, 1)
// read parameters
variable lcrop = NumberByKey("Lcrop", param, "=", ";")
variable lsize = NumberByKey("Lsize", param, "=", ";")
variable hcrop = NumberByKey("Hcrop", param, "=", ";")
variable hsize = NumberByKey("Hsize", param, "=", ";")
variable cpos = NumberByKey("Cpos", param, "=", ";")
variable csize = NumberByKey("Csize", param, "=", ";")
// validate parameters
// background parameters are optional, center parameter is required.
if (numtype(lcrop) != 0)
lcrop = 0
endif
if (numtype(lsize) != 0)
lsize = 0
endif
if (numtype(hcrop) != 0)
hcrop = 0
endif
if (numtype(hsize) != 0)
hsize = 0
endif
if (numtype(Cpos) != 0)
return 1 // Cpos parameter missing
endif
if (numtype(Csize) != 0)
return 2 // Csize parameter missing
endif
// crop boundaries
variable pcl = round(lcrop * nx)
variable pch = round((1 - hcrop) * nx)
// fit boundaries
variable pfl = round((lcrop + lsize) * nx)
variable pfh = round((1 - hcrop - hsize) * nx)
// integration boundaries
variable pil = round((cpos - csize/2) * nx)
variable pih = round((cpos + csize/2) * nx)
adh5_setup_profile(source, dest1, 0)
adh5_setup_profile(source, dest2, 0)
// prepare intermediate data buffer
make /n=(nx) /d /free profile, mask, fit
setscale /p x dimoffset(source,0), dimdelta(source,0), waveunits(source,0), profile, mask, fit
mask = ((p >= pcl) && (p < pfl)) || ((p >= pfh) && (p < pch))
variable qq
variable sp, sf
variable xil = x2pnt(profile, pil)
variable xih = x2pnt(profile, pih)
make /n=3 /free /d w_coef
for (qq = 0; qq < ny; qq += 1)
profile = source[p][qq]
curvefit /Q /NTHR=1 /W=2 poly 3, kwCWave=w_coef, profile /M=mask
fit = poly(w_coef, x)
sp = sum(profile, xil, xih)
sf = sum(fit, xil, xih)
dest1[qq] = sp - sf
dest2[qq] = sqrt(sp)
endfor
return 0 // return zero if successful, non-zero if an error occurs
end
function scienta_norm(w, x): fitfunc
wave w
variable x
return w[0] * (x^2 - w[1]^2)
end
function /wave fit_scienta_ang_transm(data, params)
wave data // measured angular distribution (1D)
wave /z params
if (!waveexists(params))
make /n=12 /o params
endif
redimension /n=12/d params
variable h = wavemax(data) - wavemin(data)
params[0] = h / 2
params[1] = 0
params[2] = 7
params[3] = h / 4
params[4] = -23
params[5] = 4
params[6] = h / 4
params[7] = +23
params[8] = 4
params[9] = h / 2
params[10] = 0
params[11] = -0.001
FuncFit /NTHR=0 /q scienta_ang_transm params data
return params
end
threadsafe function scienta_ang_transm(w, x): fitfunc
// parameterized angular transmission function of the analyser
wave w // coefficients
// w[0] = amplitude gauss 1
// w[1] = position gauss 1
// w[2] = width gauss 1
// w[3] = amplitude gauss 2
// w[4] = position gauss 2
// w[5] = width gauss 2
// w[6] = amplitude gauss 3
// w[7] = position gauss 3
// w[8] = width gauss 3
// w[9] = constant background
// w[10] = linear background
// w[11] = quadratic background
variable x
make /free /n=4 /d w_int
w_int[0] = 0
w_int[1,] = w[p - 1]
variable pk1 = gauss1d(w_int, x)
w_int[1,] = w[p + 2]
variable pk2 = gauss1d(w_int, x)
w_int[1,] = w[p + 5]
variable pk3 = gauss1d(w_int, x)
w_int[0,2] = w[9 + p]
w_int[3] = 0
variable bg = poly(w_int, x)
return bg + pk1 + pk2 + pk3
end
function /wave fit_scienta_poly_bg(data, params, bgterms)
wave data // measured distribution (2D)
wave /z params // wave, will be redimensioned for the correct size
variable bgterms // number of terms in the polynomial background: 2, 3, or 4
if (!waveexists(params))
make /n=15 /o params
endif
redimension /n=15 /d params
variable wmax = wavemax(data)
variable wmin = wavemin(data)
params[0] = 0
params[1] = 7
params[2] = 1 / 2
params[3] = -23
params[4] = 4
params[5] = 1 / 2
params[6] = +23
params[7] = 4
params[8] = 1
params[9] = 0
params[10] = -0.001
params[11] = wmin
params[12] = (wmax - wmin) / dimdelta(data,1) / dimsize(data,1)
params[13] = 0
params[14] = 0
string h = "0000000000000"
if (bgterms < 3)
h = h + "1"
else
h = h + "0"
endif
if (bgterms < 4)
h = h + "1"
else
h = h + "0"
endif
FuncFitMD /NTHR=1 /q /h=h scienta_poly_bg params data
return params
end
function scienta_poly_bg(w, e, a): fitfunc
// polynomial background with
// parameterized angular transmission function of the analyser
wave w // coefficients
// angular transmission, varies with a
// amplitude of gauss 1 = 1 constant
// other peak amplitudes and linear terms are relative to gauss 1
// w[0] = position gauss 1
// w[1] = width gauss 1
// w[2] = amplitude gauss 2, relative to gauss 1
// w[3] = position gauss 2
// w[4] = width gauss 2
// w[5] = amplitude gauss 3, relative to gauss 1
// w[6] = position gauss 3
// w[7] = width gauss 3
// w[8] = constant term
// w[9] = linear term
// w[10] = quadratic term
// spectral background, varies with e
// w[11] = constant term
// w[12] = linear term
// w[13] = quadratic term
// w[14] = cubic term
variable a // detection angle
variable e // electron energy
make /free /n=4 /d w_int
variable p0 = 0
w_int[0] = 0
w_int[1] = 1
w_int[2,] = w[p0 + p - 2]
variable pk1 = gauss1d(w_int, a)
p0 += 2
w_int[1,] = w[p0 + p - 1]
variable pk2 = gauss1d(w_int, a)
p0 += 3
w_int[1,] = w[p0 + p - 1]
variable pk3 = gauss1d(w_int, a)
p0 += 3
w_int[0,2] = w[p0 + p]
w_int[3] = 0
variable base = poly(w_int, a)
p0 += 3
w_int[0,3] = w[p0 + p]
variable bg = poly(w_int, e)
return bg * (base + pk1 + pk2 + pk3)
end
/// parameter dialog for the redim_linbg_reduction() function
///
/// @param param parameter string in a key1=value1;key2=value2;... list.
/// the parameter string is passed by reference.
/// see redim_linbg_reduction() for a description of parameters.
///
/// @return zero if the user clicked OK, non-zero if the user clicked Cancel.
///
function prompt_redim_linbg_reduction(param)
string &param
variable Lcrop = NumberByKey("Lcrop", param, "=", ";")
variable Lsize = NumberByKey("Lsize", param, "=", ";")
variable Hcrop = NumberByKey("Hcrop", param, "=", ";")
variable Hsize = NumberByKey("Hsize", param, "=", ";")
variable Cpos = NumberByKey("Cpos", param, "=", ";")
variable Csize = NumberByKey("Csize", param, "=", ";")
prompt Lcrop, "Lower cropping region"
prompt Hcrop, "Upper cropping region"
prompt Lsize, "Lower background region"
prompt Hsize, "Upper background region"
prompt Cpos, "Center position"
prompt Csize, "Center integration region"
doprompt "redim_linbg_reduction Parameters", lcrop, hcrop, lsize, hsize, cpos, csize
if (v_flag == 0)
param = ReplaceNumberByKey("Lcrop", param, Lcrop, "=", ";")
param = ReplaceNumberByKey("Lsize", param, Lsize, "=", ";")
param = ReplaceNumberByKey("Hcrop", param, Hcrop, "=", ";")
param = ReplaceNumberByKey("Hsize", param, Hsize, "=", ";")
param = ReplaceNumberByKey("Cpos", param, Cpos, "=", ";")
param = ReplaceNumberByKey("Csize", param, Csize, "=", ";")
endif
return v_flag
end
/// linear background reduction function for incorrectly dimensioned scienta image
///
/// if the energy step size does not divide the energy range to an integer number,
/// the scienta image is exported with the wrong array size.
/// this can be fixed by redimensioning the array.
///
/// the current implementation works in the case where dimension 0 needs to be incremented.
/// the function may be generalized to dimension 1 and/or decrementing by additional parameters.
/// it is not known yet whether a generalization is needed or whether it can cover all cases.
///
/// background subtraction and peak integration is the same as by the int_linbg_reduction() function.
///
/// @param source source wave
/// Scienta detector image, energy axis along X, angle axis along Y
///
/// @param dest1 destination wave 1
///
/// @param dest2 destination wave 2
/// each wave is a one-dimensional intensity distribution
/// the function may redimension these waves to one of the image dimensions
/// (it must be clear to the user which dimension this is).
/// the meaning of dest1 and dest2 is up to the particular function,
/// e.g. dest1 could hold the mean value and dest2 the one-sigma error,
/// or dest1 could hold the X-profile, and dest2 the Y-profile.
///
/// @param param parameter string in a key1=value1;key2=value2;... list.
/// the parameter string is passed by reference.
///
/// all region parameters are relative to the image size (0...1).
/// @arg Lcrop size of the lower cropping region
/// @arg Hcrop size of the upper cropping region
/// @arg Lsize size of the lower background integration region
/// @arg Hsize size of the upper background integration region
/// @arg Cpos center position of the of the peak integration region
/// @arg Csize size of the peak integration region
///
/// typical values (peak centered on detector, FWHM ~ 20 % of image)
/// Lcrop=0.11;Hcrop=0.11;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.2
///
/// @return zero if successful, non-zero if an error occurs.
///
threadsafe function redim_linbg_reduction(source, dest1, dest2, param)
wave source
wave dest1, dest2
string &param
variable nx = dimsize(source, 0)
variable ny = dimsize(source, 1)
duplicate /free source, source_redim
redimension /n=(nx * ny) source_redim
nx += 1
redimension /n=(nx, ny) source_redim
return int_linbg_reduction(source_redim, dest1, dest2, param)
end