Example #1
0
File: ptc.py Project: nhaddad/pydtk
def ron_adu(b1, b2, *coor, **kargs):
    """
    Take two bias images, subtract one from another and
    then compute the std for many windows. Compute the median of the RMS

    Syntax:
    ron_adu(b1, b2, 200,400, 300, 600)
    ron_adu(b1, b2, 200,400, 300, 600, PRINT=True)  Print individual values
    ron_adu(b1, b2, 200,400, 300, 600, HIST=True)   Plot histogram

    TODO: return mean instead of median
    DONE: make histogram plot
    DONE: provide RETURN option

    """

    nwx = kargs.get('NWX', 10)  # set number of windows in x direction
    nwy = kargs.get('NWY', 10)  # set number of windows in y direction

    x1, x2, y1, y2 = b1.get_windowcoor(*coor)

    # compute difference of images
    biasdiff = b1 - b2
    # prepare array to receive std values
    std_biasdiff = np.zeros((nwx, nwy))

    # TODO modify to use subwindows to generate them
    # compute stddev and mean for every subwindow
    windows = subwindowcoor(x1, x2, y1, y2, **kargs)
    for i, j, xi, xf, yi, yf in windows:
        std_biasdiff[i, j] = biasdiff[xi:xf, yi:yf].std() / np.sqrt(2)
        if kargs.get('PRINT', False):
            print(f'[{xi:5}:{xf:5},{yi:5}:{yf:5}] => {std_biasdiff[i, j]:.3}')

    diff_median = np.median(std_biasdiff, axis=None)

    if kargs.get('RETURN', False):
        return np.median(std_biasdiff, axis=None)  # /np.sqrt(2.0)
    else:
        print(
            f'RON @ [{x1}:{x2},{y1}:{y2}] = {np.median(std_biasdiff, axis=None):2.2} ADUs'
        )
    if kargs.get('HIST', False):
        std_biasdiff.shape = (nwx * nwy, )
        diff_std = np.std(std_biasdiff, axis=None)
        print(f'StdDev = {diff_std:.2}')
        plt.clf()
        plt.hist(std_biasdiff,
                 range=(diff_median - 3 * diff_std,
                        diff_median + 3 * diff_std),
                 bins=30)
        plt.show()
Example #2
0
File: ptc.py Project: nhaddad/pydtk
def ptc_ffpairs_mw(imagelist, *coor, **kargs):
    """
    Perform ptc plot for pairs of ff at same level in multiple windows.
    The pairs of ff should have the same light level.
    The first 2 images in the list must be bias
    To eliminate the FPN, the 'shotnoise' image is computed as the subtraction
    of two debiased flat field images
    optional kargs arguments:
    FACTOR (default = 2.0)
    MAXSIGNAL (default 65535)  => compute PTC only for signal values less than MAXSIGNAL
    VERBOSE (default=False)  ==> print out table with signal and variance
    CLIP (default=True) ==> Use clipped statistic to on images before computing CF

    """

    order = kargs.get('ORDER', 2)  # order of polynomial regression
    if order > 2:
        order = 2

    MAXSIGNAL = kargs.get('MAXSIGNAL', 65535.0)
    sigma = kargs.get('SIGMA', 3)
    ext = kargs.get('ext', 0)

    x1 = coor[0]
    x2 = coor[1]
    y1 = coor[2]
    y2 = coor[3]

    oddimageindex = list(range(3, len(imagelist), 2))
    evenimageindex = list(range(2, len(imagelist), 2))

    # Read in bias1 and bias2
    b1 = Image(imagelist[0], ext).crop(x1, x2, y1, y2).get_data()
    b2 = Image(imagelist[1], ext).crop(x1, x2, y1, y2).get_data()

    biasRON = sigma_clip(b1 - b2).std() / np.sqrt(2.0)

    # Separate images in even and odd (crop the images..)
    ff1list = [
        Image(imagelist[i], ext).crop(x1, x2, y1, y2).get_data()
        for i in oddimageindex
    ]
    ff2list = [
        Image(imagelist[i], ext).crop(x1, x2, y1, y2).get_data()
        for i in evenimageindex
    ]

    # NHA new implementation with multi windows START
    # generate auxiliary arrays of nwx * nwy elements and initialize to zero

    meanff = []
    signalvar = []

    for ff1, ff2 in zip(ff1list, ff2list):
        # windows is a generator of subwindows
        windows = subwindowcoor(0, ff1.shape[0], 0, ff2.shape[0], **kargs)
        for i, j, xi, xf, yi, yf in windows:
            win = slice(xi, xf), slice(yi, yf)
            # compute mean value on each window for normalized ff
            meanff.append(
                sigma_clip((ff1[win] + ff2[win]) / 2.0, sigma=sigma).mean() -
                sigma_clip((b1[win] + b2[win]) / 2.0, sigma=sigma).mean())
            # compute standard deviation on each window for normalized ff
            if kargs.get('NORMFF2', True):
                ff_diff = ff1[win] - ff2[win] * (
                    sigma_clip(ff1[win], sigma=sigma).mean() /
                    sigma_clip(ff2[win]).mean())
            else:
                ff_diff = ff1[win] - ff2[win]
            var_ff_diff = sigma_clip(ff_diff, sigma=sigma).std()**2
            # Measure RON from difference of two bias
            var_ron = (sigma_clip(b1[win] - b2[win], sigma=sigma).std()**
                       2) / 2.0
            signalvar.append(0.5 * (var_ff_diff - 2 * var_ron))

    # compute polynomial coeficients
    meanff = np.array(meanff)
    signalvar = np.array(signalvar)
    coefts = np.polyfit(meanff, signalvar, order)
    polyts = np.poly1d(coefts)
    variance_fitted = np.polyval(polyts, meanff)

    fig = plt.figure()  # create a figure object
    ax = fig.add_subplot(1, 1, 1)  # create an axes object in the figure

    ax.set_ylabel('Variance')
    ax.set_xlabel('Signal')
    ax.grid(True)
    ax.set_title('Photon Transfer Curve')

    # plot variance v/s signal
    # figure()
    ax.plot(meanff, signalvar, 'b.')
    ax.plot(meanff, variance_fitted, 'r-')
    plt.show()

    if order == 1:
        cf = 1 / coefts[0]
        print(
            f'Extension: {ext}   CF = {cf:2.3f} -e/ADU   RON = {cf * biasRON:2.3f}'
        )
    elif order == 2:
        cf = 1 / coefts[1]
        print(
            f'Extension: {ext}   CF = {cf:2.3f} -e/ADU   RON = {cf * biasRON:2.3f} -e'
        )

    if kargs.get('RETURN', True):
        return meanff, signalvar
Example #3
0
File: ptc.py Project: nhaddad/pydtk
def gain2(imagelist, *coor, **kargs):
    """
    Variation of CCD gain computation
    Compute the gain of the system using 2 Bias and 2 FF. The procedure devides the window
    in NWX*NWY subwindows and computes the Gain for each one of them and then computes the mean
    value and display an histogram. If the windows coordinates are not given, it will use the full
    CCD.

    Syntax:
    gain(imagelist[,xi,xf,yi,yf][,NWX=10][,NWY=10][,VERBOSE=True/False][,SAVE=True][,TITLE='Graph Title'][,RETURN=True/False][, MEDIAN=True/False])

    Note: the image list must contain 2 bias and 2 ff in that order!
    imagelist can be a list of names, a list of Path or list of images
    b1,b2= bias images
    f1,f2= ff images
    *coor=[xi,xf,yi,yf] = coordinates of the window to analize (should be a flat region)
    kargs
    -------
    VERBOSE=True => print intermediate values
    TITLE='Graph Title' => put a title in the graph
    SAVE=True/False => if True, it saves the graph in pnp format
    RETURN=True/False => if True, return only ConFAc without plots
    MEDIAN=True/False => default True, computes median instead of mean
    RATIO=True/FALSE => defaul True. just change the way the FPN is elliminated. Both
    methods give almost the same rusults
    NWX= number of windows in X direction (default 10)
    NWY= number of windows in Y direction (default 10)
    EXT=image extension to load, default 0


    """
    ext = kargs.get('ext', 0)

    if len(imagelist) != 4:
        print('imagelist len different from 4')
        return None

    #  if imagelist contains only image names, load them
    if all([isinstance(i, str) for i in imagelist]):
        images = [Image(i, ext) for i in imagelist]
        print(f'Extension={ext}')
        b1 = images[0]
        b2 = images[1]
        ff1 = images[2]
        ff2 = images[3]
    elif all([isinstance(i, Path) for i in imagelist]):
        images = [Image(i, ext) for i in imagelist]
        print(f'Extension={ext}')
        b1 = images[0]
        b2 = images[1]
        ff1 = images[2]
        ff2 = images[3]
    # elif all are images, just assign them
    elif all([isinstance(i, Image) for i in imagelist]):
        b1 = imagelist[0]
        b2 = imagelist[1]
        ff1 = imagelist[2]
        ff2 = imagelist[3]
    else:
        print("Not all objects in image list are Images or filenames")
        return None

    #Check if bias have EXPTIME = 0.0 and FF EXPTIME > 0
    testlist = []
    testlist.append(b1.header['EXPTIME'] == 0.0)
    testlist.append(b2.header['EXPTIME'] == 0.0)
    testlist.append(ff1.header['EXPTIME'] > 0.0)
    testlist.append(ff2.header['EXPTIME'] > 0.0)
    print(testlist)

    if not all(testlist):
        print('Exposure times for at least one file are not correct')
        return None

    nwx = kargs.get('NWX', 10)  # set number of windows in x direction
    nwy = kargs.get('NWY', 10)  # set number of windows in y direction

    x1, x2, y1, y2 = b1.get_windowcoor(*coor)
    # print(x1,x2,y1,y2)

    # now work with cropped images, where the signal is more or less flat....
    b1 = b1.crop(x1, x2, y1, y2)
    b2 = b2.crop(x1, x2, y1, y2)
    ff1 = ff1.crop(x1, x2, y1, y2)
    ff2 = ff2.crop(x1, x2, y1, y2)

    dbiasff1 = ff1 - b1  # debiased FF1
    dbiasff2 = ff2 - b2  # debiased FF2
    meanff2 = dbiasff2.mean()  # mean signal on FF2 debiased
    meanff1 = dbiasff1.mean()  # mean signal on FF1 debiased
    #ratio = meanff1/meanff2
    # print(ratio)

    if kargs.get('VERBOSE', False):
        print(f'format images X={b1.shape[0]} pix Y={b1.shape[1]} pix')
        print(
            f'Nx:{nwx} Ny:{nwy} X1:{x1} X2:{x2} Y1:{y1} Y2:{y2} WX:{(x2-x1)//nwx} WY:{(y2-y1)//nwy}'
        )
        print('')
        print(f'meanff2 ={meanff2}')

    #dbiasff2 = dbiasff2*ratio
    #dbias_ff_diff = dbiasff1 - dbiasff2
    #dbias_ff_sig = (dbiasff1 + dbiasff2)/2.0

    # from Fabrice Chisthen
    dbias_ff_sig = (dbiasff1 / dbiasff2) * meanff2

    # compute difference of 2 bias to get the RON
    dbias = b1 - b2

    # generate auxiliary arrays of nwx * nwy elements and initialize to zero
    meansig = np.zeros((nwx, nwy))
    stdff = np.zeros((nwx, nwy))
    stdbias = np.zeros((nwx, nwy))
    cf = np.zeros((nwx, nwy))
    signal = (dbiasff1 / dbiasff2)
    stdsig = np.zeros((nwx, nwy))

    # windows is a generator of subwindows
    windows = subwindowcoor(0, b1.shape[0], 0, b1.shape[1], **kargs)
    for i, j, xi, xf, yi, yf in windows:
        # compute mean value on each window for normalized ff
        meansig[i, j] = np.mean(dbias_ff_sig[xi:xf, yi:yf])
        # compute standard deviation on each window for normalized ff
        stdsig[i, j] = np.std(dbias_ff_sig[xi:xf, yi:yf]) / np.sqrt(2.0)
        cf[i,
           j] = meansig[i, j] / (stdsig[i, j]**2)  # compute CF for each window
        # compute standard deviation for each window of bias difference
        stdbias[i, j] = np.std(dbias[xi:xf, yi:yf]) / np.sqrt(2.0)

        if kargs.get('VERBOSE', False):
            print(
                f"X({xi+x1},{xf+x1}) Y({yi+y1},{yf+y2}) Mean:{meansig[i, j]:.2f} stdff:{stdsig[i, j]:.2f}  CF:{cf[i, j]:.2f}"
            )

    if kargs.get('MEDIAN', True):
        ConFac = np.median(cf, axis=None)
        RON = np.median(stdbias, axis=None)

    else:
        ConFac = np.mean(cf, axis=None)
        RON = np.mean(stdbias, axis=None)  # RON in ADUs

    # RON =  RMS / sqrt(2)    #RON in ADUs
    RONe = RON * ConFac  # RON in electrons

    # Error in CF estimation is the std/sqrt(number of windows)
    CFstd = np.std(cf, axis=None) / np.sqrt(nwx * nwy)

    # Check if run as ROUTINE, in that case return only the Conversion Factor and don't continue with plotting
    if kargs.get('RETURN', False):
        return x1, x2, y1, y2, ConFac, RONe, meanff2
    else:
        plt.figure()

    print("*******************************************")
    print(f"*CF  ={ConFac:.2f} +/-{CFstd:.2f} e/ADU")
    print(f"*RON ={RONe:.3f} -e")
    print(f"*RON ={RON:.3f} ADUs")
    print("*******************************************")

    # change shape of cf array to later compute the standard deviation and also make the histogram

    cf.shape = (nwx * nwy, )
    cfstd = np.std(cf, axis=None)
    plt.clf()
    plt.hist(cf, range=(ConFac - 3 * cfstd, ConFac + 3 * cfstd), bins=20)
    plt.figtext(0.15,
                0.8, ("CF mean=%5.3f +/-%5.3f e/ADU") % (ConFac, CFstd),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))
    plt.figtext(0.15,
                0.75, ("RON =%6.3f -e") % (RONe),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))
    plt.figtext(0.15,
                0.70, ("Computed @ %6.3f ADUs") % (np.mean(meansig)),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))

    Title = kargs.get('TITLE', '')
    plt.title(Title)
    filetitle = Title.replace(' ', '_')
    plt.show()

    if kargs.get('SAVE', False):
        plt.savefig('ConFac_' + filetitle + '.png')
Example #4
0
File: ptc.py Project: nhaddad/pydtk
def gain(imagelist, *coor, **kargs):
    """
    Compute the gain of the system using 2 Bias and 2 FF. The procedure divides the window
    in NWX*NWY subwindows and computes the Gain for each one of them and then computes the mean
    value and display an histogram. If the windows coordinates are not given, it will use the full
    CCD.

    Syntax:
    gain(imagelist[,xi,xf,yi,yf][,NWX=10][,NWY=10][,VERBOSE=True/False][,SAVE=True][,TITLE='Graph Title'][,RETURN=True/False][, MEDIAN=True/False])

    Note: the image list must contain 2 bias and 2 ff in that order!
    imagelist can be a list of names, a list of Paths or list of images
    b1,b2= bias images
    f1,f2= ff images
    *coor=[xi,xf,yi,yf] = coordinates of the window to analize (should be a flat region)
    kargs
    -------
    VERBOSE=True => print intermediate values
    TITLE='Graph Title' => put a title in the graph
    SAVE=True/False => if True, it saves the graph in pnp format
    RETURN=True/False => if True, return only ConFAc without plots
    MEDIAN=True/False => default True, computes median instead of mean
    NORMFF2=True/False => default False, normalize FF2 level to set its mean level equal to FF1
    RATIO=True/FALSE => defaul True. just change the way the FPN is elliminated. Both
    methods give almost the same rusults
    NWX= number of windows in X direction (default 10)
    NWY= number of windows in Y direction (default 10)
    EXT=image extension to load, default 0
    SIGMA = std deviation used by sigma_clip, default=3


    """
    ext = kargs.get('ext', 0)
    sigma = kargs.get('SIGMA', 3)

    if len(imagelist) != 4:
        print('imagelist len different from 4')
        return None

    #  if imagelist contains only image names, load them
    if all([isinstance(i, str) for i in imagelist]):
        images = [Image(i, ext) for i in imagelist]
        print(f'Extension={ext}')
        b1 = images[0]
        b2 = images[1]
        ff1 = images[2]
        ff2 = images[3]
    # elif all are images, just assign them
    elif all([isinstance(i, Path) for i in imagelist]):
        images = [Image(i, ext) for i in imagelist]
        b1 = images[0]
        b2 = images[1]
        ff1 = images[2]
        ff2 = images[3]
    # elif all are images, just assign them
    elif all([isinstance(i, Image) for i in imagelist]):
        b1 = imagelist[0]
        b2 = imagelist[1]
        ff1 = imagelist[2]
        ff2 = imagelist[3]
    else:
        print("Not all objects in image list are Images or filenames")
        return None

    x1 = coor[0]
    x2 = coor[1]
    y1 = coor[2]
    y2 = coor[3]

    b1 = b1.get_data()
    b2 = b2.get_data()
    ff1 = ff1.get_data()
    ff2 = ff2.get_data()

    nwx = kargs.get('NWX', 10)  # set number of windows in x direction
    nwy = kargs.get('NWY', 10)  # set number of windows in y direction

    if kargs.get('VERBOSE', False):
        print(f'format images X={b1.shape[0]} pix Y={b1.shape[1]} pix')
        print(
            f'Nx:{nwx} Ny:{nwy} X1:{x1} X2:{x2} Y1:{y1} Y2:{y2} WX:{(x2-x1)//nwx} WY:{(y2-y1)//nwy}'
        )
        print('')

    # generate auxiliary arrays of nwx * nwy elements and initialize to zero
    meansig = np.zeros((nwx, nwy))

    stdbias = np.zeros((nwx, nwy))
    cf = np.zeros((nwx, nwy))

    stdsig = np.zeros((nwx, nwy))

    # windows is a generator of subwindows
    windows = subwindowcoor(x1, x2, y1, y2, **kargs)
    for i, j, xi, xf, yi, yf in windows:
        win = slice(xi, xf), slice(yi, yf)
        # compute mean value on each window for normalized ff
        meansig[i, j] = sigma_clip(
            (ff1[win] + ff2[win]) / 2.0).mean() - sigma_clip(
                (b1[win] + b2[win]) / 2.0).mean()

        # compute standard deviation on each window for normalized ff
        if kargs.get('NORMFF2', False):
            ff_diff = ff1[win] - ff2[win] * (sigma_clip(ff1[win]).mean() /
                                             sigma_clip(ff2[win]).mean())
        else:
            ff_diff = ff1[win] - ff2[win]
        var_ff_diff = sigma_clip(ff_diff).std()**2
        # Measure RON from difference of two bias
        var_ron = (sigma_clip(b1[win] - b2[win]).std()**2) / 2.0
        stdsig[i, j] = (var_ff_diff - 2 * var_ron)
        cf[i,
           j] = 2 * meansig[i, j] / stdsig[i, j]  # compute CF for each window
        # compute standard deviation for each window of bias difference
        stdbias[i, j] = np.sqrt(var_ron)

        if kargs.get('VERBOSE', False):
            print(
                f"X({xi+x1},{xf+x1}) Y({yi+y1},{yf+y2}) Mean:{meansig[i, j]:.2f} stdff:{stdsig[i, j]:.2f}  CF:{cf[i, j]:.2f}"
            )

    if kargs.get('MEDIAN', False):
        ConFac = np.median(cf, axis=None)
        RON = np.median(stdbias, axis=None)

    else:
        ConFac = sigma_clip(cf, sigma=sigma).mean()
        RON = sigma_clip(stdbias, sigma=sigma).mean()

    # RON =  RMS / sqrt(2)    #RON in ADUs
    RONe = RON * ConFac  # RON in electrons

    # Error in CF estimation is the std/sqrt(number of windows)
    #CFstd = np.std(cf, axis=None)/np.sqrt(nwx*nwy)
    CFstd = sigma_clip(cf).std()  #np.std(cf, axis=None)/np.sqrt(nwx*nwy)

    # Check if run as ROUTINE, in that case return only the Conversion Factor and don't continue with plotting
    if kargs.get('RETURN', False):
        return ConFac, RONe, np.mean(meansig, axis=None),np.median(meansig, axis=None), \
            np.mean(stdsig, axis=None)**2,np.median(stdsig, axis=None)**2,x1, x2, y1, y2
    else:
        plt.figure()

    print("*******************************************")
    print(f"*CF  ={ConFac:.2f} +/-{CFstd:.2f} e/ADU")
    print(f"*RON ={RONe:.3f} -e")
    print(f"*RON ={RON:.3f} ADUs")
    print("*******************************************")

    # change shape of cf array to later compute the standard deviation and also make the histogram

    cf.shape = (nwx * nwy, )
    cfstd = np.std(cf, axis=None)
    plt.clf()
    plt.hist(cf, range=(ConFac - 3 * cfstd, ConFac + 3 * cfstd), bins=20)
    plt.figtext(0.15,
                0.8, ("CF mean=%5.3f +/-%5.3f e/ADU") % (ConFac, CFstd),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))
    plt.figtext(0.15,
                0.75, ("RON =%6.3f -e") % (RONe),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))
    plt.figtext(0.15,
                0.70, ("Computed @ %6.3f ADUs") % (np.mean(meansig)),
                fontsize=11,
                bbox=dict(facecolor='yellow', alpha=0.5))

    Title = kargs.get('TITLE', '')
    plt.title(Title)
    filetitle = Title.replace(' ', '_')
    plt.show()

    if kargs.get('SAVE', False):
        plt.savefig('ConFac_' + filetitle + '.png')
Example #5
0
File: ptc.py Project: nhaddad/pydtk
def ptc_pixels(biaslist, fflist, ext=0, *coor, **kargs):
    """
    Perform ptc computation to get gain and RON from a list of bias names and a
    list of ff images names.
    The ff images should be the same scene with all possible light levels.
    An example would be a grism ff on FORS or MUSE
    To eliminate the FPN, the analysis is done pixel by pixel.

    optional kargs:
    LOW: low level in ADUs to compute ptc
    HIGH: high level in ADU to compute ptc
    STEP: step in ADUs to compute variance (small steps will slow down the computation)
    ORDER (default = 1) polynomial order to fit the ptc
    RETURN (default=FALSE) returns CF and RON

    Ex:
    ptc_pixels(biaslist, fflst, 0,2000,300,600, LOW=100, HIGH=50000, STEP=10)

    signal, var, var_fitted, cf = ptc_pixels(
        b1, ff1, ff2, 100,200,10, 2000, LOW=100, HIGH=50000, STEP=100, OLAYERS=0.4, RETURN=True)
    Compute the PTC in the window [100:200,10:2000] from 100ADUs up to 50000 ADUs each 100 ADUs,

    """

    low = kargs.get('LOW', 0)  # minimum signal level to explore
    high = kargs.get('HIGH', 60000)  # maximum signal level to explore
    step = kargs.get('STEP', 100)  # step size, minimum is 1
    nwx = kargs.get('NWX', 10)  # size of windows in X to compute RON
    nwy = kargs.get('NWY', 10)  # size of windows in Y to compute RON

    # print("Low = {}".format(low))
    # print("High = {}".format(high))
    # print("Step = {}".format(step))

    order = kargs.get('ORDER', 1)  # order of polynomial regression
    if order > 2:
        order = 2

    # print("Order = {}".format(order))

    # read biaslist
    biasimages = [Image(i, ext) for i in biaslist]

    # read fflist
    ffimages = [Image(i, ext) for i in fflist]

    x1, x2, y1, y2 = biasimages[0].get_windowcoor(*coor)

    # print("{},{},{},{}".format(x1,x2,y1,y2))

    # crop bias images
    biascroped = [i.crop(*coor) for i in biasimages]

    # compute bias mean
    biasmean = meanstack(biascroped)
    # print(biasmean.shape)

    # compute
    stdsig = np.zeros((nwx, nwy))
    windows = subwindowcoor((x2 - x1) // 2 - 5 * nwx, (x2 - x1) // 2 + 5 * nwx,
                            (y2 - y1) // 2 - 5 * nwy, (y2 - y1) // 2 + 5 * nwy,
                            **kargs)
    for i, j, xi, xf, yi, yf in windows:
        stdsig[i, j] = biasmean[xi:xf, yi:yf].std() * np.sqrt(len(biascroped))

    # crop ff images
    ffcroped = [i.crop(*coor) for i in ffimages]
    # print(ffcroped[0].shape)

    # debiased ff
    ffcropdb = [(i - biasmean) for i in ffcroped]

    ffcropdb_data = [i.get_data() for i in ffcropdb]

    # compute signal
    ffsignal = meanstack(ffcropdb)

    # use only data from images
    ffsignal_data = ffsignal.get_data()

    # compute variance of all ffcropdb images along axis 0
    ffcropdb_stacked = np.stack(ffcropdb_data)
    ffvar = ffcropdb_stacked.var(ddof=1, axis=0)

    # flatten resulting arrays
    ffsignal_flatten = ffsignal_data.flatten()
    ffvar_flatten = ffvar.flatten()
    # print("ffsignal_flatten, ffvar_flatten : {}, {}".format(len(ffsignal_flatten), len(ffvar_flatten)))

    # convert ffsignal_flatten in integer
    ffsignal_flatten = ffsignal_flatten.astype(
        int)  # [int(i) for i in ffsignal_flatten]
    ffvar_flatten = ffvar_flatten.astype(int)

    # sort ffsignal_flatten and  ffvar_flatten
    # indx = np.argsort(ffsignal_flatten)
    # ffsignal_flatten = ffsignal_flatten[indx]
    # ffvar_flatten =ffvar_flatten[indx]

    # get unique values in ff
    ffsignal_unique = np.unique(ffsignal_flatten)
    # print("ffsignal_unique : {}".format(len(ffsignal_unique)))

    # filter out  values lower than LOW and higher than HIGH
    ffsig_unique = [i for i in ffsignal_unique if i >= low and i <= high]
    # print("ffsig_unique: {}".format(len(ffsig_unique)))

    # generate sampling values
    sampling = list(range(low, len(ffsig_unique), step))
    # print("sampling ={}".format(len(sampling)))

    # create subset of unique values using sampling
    ffsampled = [ffsig_unique[i] for i in sampling]
    # print("ffsampled : {}".format(len(ffsampled)))

    # by default use mean computation for variance
    if kargs.get('MEDIAN', False):
        # Compute variance mean for values in ffsignalflatten that are in ffsampled
        variance = [
            np.median(ffvar_flatten[ffsignal_flatten == i]) for i in ffsampled
        ]
    else:
        variance = [
            np.mean(ffvar_flatten[ffsignal_flatten == i]) for i in ffsampled
        ]


# filter out the pixels with variance==0 or greater than 200000
    variance = np.array(variance)
    ffsampled = np.array(ffsampled)
    vfiltered_index = np.where((variance > 0) & (variance < 200000))
    ffsampled = ffsampled[vfiltered_index]
    variance = variance[vfiltered_index]

    # print(len(ffsignal_unique), len(variance))

    plt.scatter(ffsampled[::], variance[::])
    plt.grid()
    plt.show()

    # compute polynomial without filtering outlayers
    coefts_nf = np.polyfit(ffsampled, variance, order)
    polyts_nf = np.poly1d(coefts_nf)
    # var_fitted = polyts_nf(ffsampled)

    if order == 2:
        gain = 1 / coefts_nf[1]
        print(
            f"GAIN = {1/coefts_nf[1]} -e/ADU  RON = {gain*np.median(stdsig)} -e"
        )
    else:
        gain = 1 / coefts_nf[0]
        print(
            f"GAIN = {1/coefts_nf[0]} -e/ADU  RON = {gain*np.median(stdsig)} -e"
        )