Instruction:
How would you code a function in Python 3 to
return an overview of all calibrations of all calibrations of all the calendars in the calendar.

Response:
def overview(self):
        '''
        Returns:
            str: an overview covering all calibrations
            infos and shapes
        '''
        c = self.coeffs
        out = 'camera name: %s' % c['name']
        out += '\nmax value: %s' % c['depth']
        out += '\nlight spectra: %s' % c['light spectra']

        out += '\ndark current:'
        for (date, info, (slope, intercept), error) in c['dark current']:
            out += '\n\t date: %s' % self._toDateStr(date)
            out += '\n\t\t info: %s; slope:%s, intercept:%s' % (
                info, slope.shape, intercept.shape)

        out += '\nflat field:'
        for light, vals in c['flat field'].items():
            out += '\n\t light: %s' % light
            for (date, info, arr, error) in vals:
                out += '\n\t\t date: %s' % self._toDateStr(date)
                out += '\n\t\t\t info: %s; array:%s' % (info, arr.shape)

        out += '\nlens:'
        for light, vals in c['lens'].items():
            out += '\n\t light: %s' % light
            for (date, info, coeffs) in vals:
                out += '\n\t\t date: %s' % self._toDateStr(date)
                out += '\n\t\t\t info: %s; coeffs:%s' % (info, coeffs)

        out += '\nnoise:'
        for (date, info, nlf_coeff, error) in c['noise']:
            out += '\n\t date: %s' % self._toDateStr(date)
            out += '\n\t\t info: %s; coeffs:%s' % (info, nlf_coeff)

        out += '\nPoint spread function:'
        for light, vals in c['psf'].items():
            out += '\n\t light: %s' % light
            for (date, info, psf) in vals:
                out += '\n\t\t date: %s' % self._toDateStr(date)
                out += '\n\t\t\t info: %s; shape:%s' % (info, psf.shape)

        return out


Instruction:
How would you explain what the following Python 3 function does
def setCamera(self, camera_name, bit_depth=16):
        '''
        Args:
            camera_name (str): Name of the camera
            bit_depth (int): depth (bit) of the camera sensor
        '''
        self.coeffs['name'] = camera_name
        self.coeffs['depth'] = bit_depth

Response: Sets the name and depth of the camera sensor with the name camera_name and bit_depth.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def addDarkCurrent(self, slope, intercept=None, date=None, info='', error=None):
        '''
        Args:
            slope (np.array)
            intercept (np.array)
            error (numpy.array)
            slope (float): dPx/dExposureTime[sec]
            error (float): absolute
            date (str): "DD Mon YY" e.g. "30 Nov 16"
        '''
        date = _toDate(date)

        self._checkShape(slope)
        self._checkShape(intercept)

        d = self.coeffs['dark current']
        if intercept is None:
            data = slope
        else:
            data = (slope, intercept)
        d.insert(_insertDateIndex(date, d), [date, info, data, error])

Response: Adds a dark current to the calendar.


Instruction:
Explain what the following Python 3 code does
def addNoise(self, nlf_coeff, date=None, info='', error=None):
        '''
        Args:
            nlf_coeff (list)
            error (float): absolute
            info (str): additional information
            date (str): "DD Mon YY" e.g. "30 Nov 16"
        '''
        date = _toDate(date)
        d = self.coeffs['noise']
        d.insert(_insertDateIndex(date, d), [date, info, nlf_coeff, error])

Response: Adds noise to the entry in the dictionary.


Instruction:
Can you tell what is the following Python 3 function doing
def addFlatField(self, arr, date=None, info='', error=None,
                     light_spectrum='visible'):
        '''
        light_spectrum = light, IR ...
        '''
        self._registerLight(light_spectrum)
        self._checkShape(arr)
        date = _toDate(date)
        f = self.coeffs['flat field']
        if light_spectrum not in f:
            f[light_spectrum] = []
        f[light_spectrum].insert(_insertDateIndex(date, f[light_spectrum]),
                                 [date, info, arr, error])

Response: Add a flat field to the log.


Instruction:
Create a Python 3 function to
add lens to the list of available lights

Response:
def addLens(self, lens, date=None, info='', light_spectrum='visible'):
        '''
        lens -> instance of LensDistortion or saved file
        '''
        self._registerLight(light_spectrum)
        date = _toDate(date)

        if not isinstance(lens, LensDistortion):
            l = LensDistortion()
            l.readFromFile(lens)
            lens = l

        f = self.coeffs['lens']
        if light_spectrum not in f:
            f[light_spectrum] = []
        f[light_spectrum].insert(_insertDateIndex(date, f[light_spectrum]),
                                 [date, info, lens.coeffs])


Instruction:
Implement a function in Python 3 to
remove all old calibration entries except the youngest one.

Response:
def clearOldCalibrations(self, date=None):
        '''
        if not only a specific date than remove all except of the youngest calibration
        '''
        self.coeffs['dark current'] = [self.coeffs['dark current'][-1]]
        self.coeffs['noise'] = [self.coeffs['noise'][-1]]

        for light in self.coeffs['flat field']:
            self.coeffs['flat field'][light] = [
                self.coeffs['flat field'][light][-1]]
        for light in self.coeffs['lens']:
            self.coeffs['lens'][light] = [self.coeffs['lens'][light][-1]]


Instruction:
Write a Python 3 script for
transposing all calibration arrays in case different array shape orders were used

Response:
def transpose(self):
        '''
        transpose all calibration arrays
        in case different array shape orders were used (x,y) vs. (y,x)
        '''
        def _t(item):
            if type(item) == list:
                for n, it in enumerate(item):
                    if type(it) == tuple:
                        it = list(it)
                        item[n] = it
                    if type(it) == list:
                        _t(it)
                    if isinstance(it, np.ndarray) and it.shape == s:
                        item[n] = it.T

        s = self.coeffs['shape']

        for item in self.coeffs.values():
            if type(item) == dict:
                for item2 in item.values():
                    _t(item2)
            else:
                _t(item)

        self.coeffs['shape'] = s[::-1]


Instruction:
Can you generate the documentation for the following Python 3 function
def _correctNoise(self, image):
        '''
        denoise using non-local-means
        with guessing best parameters
        '''
        from skimage.restoration import denoise_nl_means  # save startup time
        image[np.isnan(image)] = 0  # otherwise result =nan
        out = denoise_nl_means(image,
                               patch_size=7,
                               patch_distance=11,
                               #h=signalStd(image) * 0.1
                               )

        return out

Response:
Corrects the image using non - local - means
        with guessing best parameters


Instruction:
Create a Python 3 function to
correct dark current image

Response:
def _correctDarkCurrent(self, image, exposuretime, bgImages, date):
        '''
        open OR calculate a background image: f(t)=m*t+n
        '''
        # either exposureTime or bgImages has to be given
#         if exposuretime is not None or bgImages is not None:
        print('... remove dark current')

        if bgImages is not None:

            if (type(bgImages) in (list, tuple) or
                    (isinstance(bgImages, np.ndarray) and
                     bgImages.ndim == 3)):
                if len(bgImages) > 1:
                    # if multiple images are given: do STE removal:
                    nlf = self.noise_level_function
                    bg = SingleTimeEffectDetection(
                        bgImages, nStd=4,
                        noise_level_function=nlf).noSTE
                else:
                    bg = imread(bgImages[0])
            else:
                bg = imread(bgImages)
        else:
            bg = self.calcDarkCurrent(exposuretime, date)
        self.temp['bg'] = bg
        image -= bg


Instruction:
How would you explain what the following Python 3 function does
def _correctArtefacts(self, image, threshold):
        '''
        Apply a thresholded median replacing high gradients
        and values beyond the boundaries
        '''
        image = np.nan_to_num(image)
        medianThreshold(image, threshold, copy=False)
        return image

Response:
Apply a thresholded median replacing high gradients
        and values beyond the boundaries


Instruction:
Can you tell what is the following Python 3 function doing
def getCoeff(self, name, light=None, date=None):
        '''
        try to get calibration for right light source, but
        use another if they is none existent
        '''
        d = self.coeffs[name]

        try:
            c = d[light]
        except KeyError:
            try:
                k, i = next(iter(d.items()))
                if light is not None:
                    print(
                        'no calibration found for [%s] - using [%s] instead' % (light, k))
            except StopIteration:
                return None
            c = i
        except TypeError:
            # coeff not dependent on light source
            c = d
        return _getFromDate(c, date)

Response: get the most recent calibration for a given name light and date


Instruction:
Write a Python 3 function that can
return a tuple of the smoothed and mask of the objects in the objectVignettingSeparation objects.

Response:
def vignettingFromRandomSteps(imgs, bg, inPlane_scale_factor=None,
                              debugFolder=None, **kwargs):
    '''
    important: first image should shown most iof the device
    because it is used as reference
    '''
    # TODO: inPlane_scale_factor
    if debugFolder:
        debugFolder = PathStr(debugFolder)

    s = ObjectVignettingSeparation(imgs[0], bg,  **kwargs)
    for img in imgs[1:]:
        fit = s.addImg(img)

        if debugFolder and fit is not False:
            imwrite(debugFolder.join('fit_%s.tiff' % len(s.fits)), fit)

    if debugFolder:
        imwrite(debugFolder.join('init.tiff'), s.flatField)

    smoothed_ff, mask, flatField, obj = s.separate()

    if debugFolder:
        imwrite(debugFolder.join('object.tiff'), obj)
        imwrite(debugFolder.join('flatfield.tiff'), flatField, dtype=float)
        imwrite(debugFolder.join('flatfield_smoothed.tiff'), smoothed_ff,
                dtype=float)

    return smoothed_ff, mask


Instruction:
Make a summary of the following Python 3 code
def addImg(self, img, maxShear=0.015, maxRot=100, minMatches=12,
               borderWidth=3):  # borderWidth=100
        """
        Args:
            img (path or array): image containing the same object as in the reference image
        Kwargs:
            maxShear (float): In order to define a good fit, refect higher shear values between
                              this and the reference image
            maxRot (float): Same for rotation
            minMatches (int): Minimum of mating points found in both, this and the reference image
        """
        try:
            fit, img, H, H_inv, nmatched = self._fitImg(img)
        except Exception as e:
            print(e)
            return

        # CHECK WHETHER FIT IS GOOD ENOUGH:
        (translation, rotation, scale, shear) = decompHomography(H)
        print('Homography ...\n\ttranslation: %s\n\trotation: %s\n\tscale: %s\n\tshear: %s'
              % (translation, rotation, scale, shear))
        if (nmatched > minMatches
                and abs(shear) < maxShear
                and abs(rotation) < maxRot):
            print('==> img added')
            # HOMOGRAPHY:
            self.Hs.append(H)
            # INVERSE HOMOGRSAPHY
            self.Hinvs.append(H_inv)
            # IMAGES WARPED TO THE BASE IMAGE
            self.fits.append(fit)
            # ADD IMAGE TO THE INITIAL flatField ARRAY:
            i = img > self.signal_ranges[-1][0]

            # remove borders (that might have erroneous light):
            i = minimum_filter(i, borderWidth)

            self._ff_mma.update(img, i)

            # create fit img mask:
            mask = fit < self.signal_ranges[-1][0]
            mask = maximum_filter(mask, borderWidth)
            # IGNORE BORDER
            r = self.remove_border_size
            if r:
                mask[:r, :] = 1
                mask[-r:, :] = 1
                mask[:, -r:] = 1
                mask[:, :r] = 1
            self._fit_masks.append(mask)

            # image added
            return fit
        return False

Response: Add an image to the list of related objects.


Instruction:
How would you explain what the following Python 3 function does
def error(self, nCells=15):
        '''
        calculate the standard deviation of all fitted images,
        averaged to a grid
        '''
        s0, s1 = self.fits[0].shape
        aR = s0 / s1
        if aR > 1:
            ss0 = int(nCells)
            ss1 = int(ss0 / aR)
        else:
            ss1 = int(nCells)
            ss0 = int(ss1 * aR)
        L = len(self.fits)

        arr = np.array(self.fits)
        arr[np.array(self._fit_masks)] = np.nan
        avg = np.tile(np.nanmean(arr, axis=0), (L, 1, 1))
        arr = (arr - avg) / avg

        out = np.empty(shape=(L, ss0, ss1))

        with warnings.catch_warnings():
            warnings.simplefilter("ignore", category=RuntimeWarning)

            for n, f in enumerate(arr):
                out[n] = subCell2DFnArray(f, np.nanmean, (ss0, ss1))

        return np.nanmean(out**2)**0.5

Response: calculate the standard deviation of all fitted images averaged to a grid


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _fitImg(self, img):
        '''
        fit perspective and size of the input image to the reference image
        '''
        img = imread(img, 'gray')
        if self.bg is not None:
            img = cv2.subtract(img, self.bg)

        if self.lens is not None:
            img = self.lens.correct(img, keepSize=True)

        (H, _, _, _, _, _, _, n_matches) = self.findHomography(img)
        H_inv = self.invertHomography(H)

        s = self.obj_shape
        fit = cv2.warpPerspective(img, H_inv, (s[1], s[0]))
        return fit, img, H, H_inv, n_matches

Response: fit the image to the reference image


Instruction:
Can you tell what is the following Python 3 function doing
def _findObject(self, img):
        '''
        Create a bounding box around the object within an image
        '''
        from imgProcessor.imgSignal import signalMinimum
        # img is scaled already
        i = img > signalMinimum(img)  # img.max()/2.5
        # filter noise, single-time-effects etc. from mask:
        i = minimum_filter(i, 4)
        return boundingBox(i)

Response: Find the object within an image


Instruction:
Can you generate the documentation for the following Python 3 function
def filterVerticalLines(arr, min_line_length=4):
    gy = arr.shape[0]
    gx = arr.shape[1]
    mn = min_line_length-1
    for i in range(gy):
        for j in range(gx):
            if arr[i,j]:
                for d in range(min_line_length):
                    if not arr[i+d,j]:
                        break
                if d == mn:
                    d = 0
                    while True:
                        if not arr[i+d,j]:
                            break
                        arr[i+d,j] = 0
                        d +=1

Response: Remove vertical lines in boolean array if linelength < min_line_length


Instruction:
Implement a Python 3 function for
returning the vignetting of a single image

Response:
def vignetting(xy, f=100, alpha=0, rot=0, tilt=0, cx=50, cy=50):
    '''
    Vignetting equation using the KANG-WEISS-MODEL
    see http://research.microsoft.com/en-us/um/people/sbkang/publications/eccv00.pdf

    f - focal length
    alpha - coefficient in the geometric vignetting factor
    tilt - tilt angle of a planar scene
    rot - rotation angle of a planar scene
    cx - image center, x
    cy - image center, y
    '''
    x, y = xy
    # distance to image center:
    dist = ((x - cx)**2 + (y - cy)**2)**0.5

    # OFF_AXIS ILLUMINATION FACTOR:
    A = 1.0 / (1 + (dist / f)**2)**2
    # GEOMETRIC FACTOR:
    if alpha != 0:
        G = (1 - alpha * dist)
    else:
        G = 1
    # TILT FACTOR:
    if tilt != 0:
        T = tiltFactor((x, y), f, tilt, rot, (cy, cx))
    else:
        T = 1
    return A * G * T


Instruction:
Can you write a function in Python 3 where it
returns the tilt factor of a single image

Response:
def tiltFactor(xy, f, tilt, rot, center=None):
    '''
    this function is extra to only cover vignetting through perspective distortion

    f - focal length [px]
    tau - tilt angle of a planar scene [radian]
    rot - rotation angle of a planar scene [radian]
    '''
    x, y = xy
    arr = np.cos(tilt) * (
        1 + (np.tan(tilt) / f) * (
            x * np.sin(rot) - y * np.cos(rot)))**3
    return arr


Instruction:
Can you tell what is the following Python 3 function doing
def imgAverage(images, copy=True):
    '''
    returns an image average

    works on many, also unloaded images
    minimises RAM usage
    '''
    i0 = images[0]
    out = imread(i0, dtype='float')
    if copy and id(i0) == id(out):
        out = out.copy()

    for i in images[1:]:
        out += imread(i, dtype='float')
    out /= len(images)
    return out

Response: Returns an image average of the images in the order they appear.


Instruction:
Implement a Python 3 function for
returning a meshgrid that is offset within the image

Response:
def offsetMeshgrid(offset, grid, shape):
    '''
    Imagine you have cell averages [grid] on an image.
    the top-left position of [grid] within the image
    can be variable [offset]

    offset(x,y)
        e.g.(0,0) if no offset
    grid(nx,ny) resolution of smaller grid
    shape(x,y) -> output shape

    returns meshgrid to be used to upscale [grid] to [shape] resolution
    '''
    g0,g1 = grid
    s0,s1 = shape
    o0, o1 = offset
    #rescale to small grid:
    o0 = - o0/ s0 * (g0-1)
    o1 = - o1/ s1 * (g1-1)

    xx,yy = np.meshgrid(np.linspace(o1, o1+g1-1, s1),
                        np.linspace(o0,o0+g0-1,  s0))
    return yy,xx


Instruction:
Here you have a function in Python 3, explain what it does
def poisson(x, a, b, c, d=0):
    '''
    Poisson function
    a -> height of the curve's peak
    b -> position of the center of the peak
    c ->  standard deviation
    d -> offset
    '''
    from scipy.misc import factorial #save startup time
    lamb = 1
    X = (x/(2*c)).astype(int)
    return a * (( lamb**X/factorial(X)) * np.exp(-lamb) ) +d

Response: Returns the maximal Poisson distance of a peak with respect to a center of the peak b and c.


Instruction:
Given the following Python 3 function, write the documentation
def rotate(image, angle, interpolation=cv2.INTER_CUBIC,
           borderMode=cv2.BORDER_REFLECT, borderValue=0):
    '''
    angle [deg]
    '''
    s0, s1 = image.shape
    image_center = (s0 - 1) / 2., (s1 - 1) / 2.
    rot_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)
    result = cv2.warpAffine(image, rot_mat, image.shape,
                            flags=interpolation, borderMode=borderMode,
                            borderValue=borderValue)
    return result

Response: rotate an image by a given angle


Instruction:
Make a summary of the following Python 3 code
def adjustUncertToExposureTime(facExpTime, uncertMap, evtLenMap):
    '''
    Adjust image uncertainty (measured at exposure time t0)
    to new exposure time

    facExpTime --> new exp.time / reference exp.time =(t/t0)
    uncertMap --> 2d array mapping image uncertainty

    evtLen --> 2d array mapping event duration within image [sec]
                event duration is relative to exposure time
                e.g. duration = 2 means event is 2x longer than
                exposure time

    More information can be found at ...
    ----
    K.Bedrich: Quantitative Electroluminescence Imaging, PhD Thesis, 2017
    Subsection 5.1.4.3: Exposure Time Dependency
    ----
    '''

    #fit parameters, obtained from ####[simulateUncertDependencyOnExpTime]
    params =  np.array(
        #a                 facExpTime        f_0             f_inf
     [[  2.63017121e+00,   3.05873627e-01,   1.00000000e+01, 2.78233309e-01],
      [  2.26467931e+00,   2.86206621e-01,   8.01396977e+00, 2.04089232e-01],
      [  1.27361168e+00,   5.18377189e-01,   3.04180084e+00, 2.61396338e-01],
      [  7.34546040e-01,   7.34549823e-01,   1.86507345e+00, 2.77563156e-01],
      [  3.82715618e-01,   9.32410141e-01,   1.34510254e+00, 2.91228149e-01],
      [  1.71166071e-01,   1.14092885e+00,   1.11243702e+00, 3.07947386e-01],
      [  6.13455410e-02,   1.43802520e+00,   1.02995065e+00, 3.93920802e-01],
      [  1.65383071e-02,   1.75605076e+00,   1.00859395e+00, 5.02132321e-01],
      [  4.55800114e-03,   1.99855711e+00,   9.98819118e-01, 5.99572776e-01]])

    #event duration relative to exposure time:(1/16...16)
    dur = np.array([  0.0625,   0.125 ,   0.25  ,
                      0.5   ,   1.    ,   2.    ,
                      4.    ,   8.    ,   16.    ])
    #get factors from interpolation:
    a = UnivariateSpline(dur, params[:, 0], k=3, s=0)
    b = UnivariateSpline(dur, params[:, 1], k=3, s=0)
    start = UnivariateSpline(dur, params[:, 2], k=3, s=0)
    end = UnivariateSpline(dur, params[:, 3], k=3, s=0)
    p0 = a(evtLenMap), b(evtLenMap), start(evtLenMap), end(evtLenMap)
    #uncertainty for new exposure time:
    out = uncertMap * _fitfn(facExpTime, *p0)
    # everywhere where there ARE NO EVENTS --> scale uncert. as if would
    # be normal distributed:
    i = evtLenMap == 0
    out[i] = uncertMap[i] * (1 / facExpTime)**0.5
    return out

Response: This function adjusts the image uncertainty to the exposure time of the given event.


Instruction:
Implement a Python 3 function for
writing images to a video file.

Response:
def videoWrite(path, imgs, levels=None, shape=None, frames=15,
               annotate_names=None,
               lut=None, updateFn=None):
    '''
    TODO
    '''
    frames = int(frames)
    if annotate_names is not None:
        assert len(annotate_names) == len(imgs)

    if levels is None:
        if imgs[0].dtype == np.uint8:
            levels = 0, 255
        elif imgs[0].dtype == np.uint16:
            levels = 0, 2**16 - 1
        else:
            levels = np.min(imgs), np.max(imgs)

    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    h, w = imgs.shape[1:3]

    if shape and shape != (h, w):
        h, w = shape
        imgs = [cv2.resize(i, (w, h)) for i in imgs]

    assert path[-3:] in ('avi',
                         'png'), 'video export only supports *.avi or *.png'
    isVideo = path[-3:] == 'avi'
    if isVideo:
        cap = cv2.VideoCapture(0)
        # im.ndim==4)
        out = cv2.VideoWriter(path, fourcc, frames, (w, h), isColor=1)

    times = np.linspace(0, len(imgs) - 1, len(imgs) * frames)
    interpolator = LinearInterpolateImageStack(imgs)

    if lut is not None:
        lut = lut(imgs[0])

    for n, time in enumerate(times):
        if updateFn:
            # update progress:
            updateFn.emit(100 * n / len(times))
        image = interpolator(time)

        cimg = makeRGBA(image, lut=lut,
                        levels=levels)[0]
        cimg = cv2.cvtColor(cimg, cv2.COLOR_RGBA2BGR)

        if annotate_names:
            text = annotate_names[n // frames]
            alpha = 0.5
            org = (0, cimg.shape[0])
            fontFace = cv2.FONT_HERSHEY_PLAIN
            fontScale = 2
            thickness = 3
            putTextAlpha(cimg, text, alpha, org, fontFace, fontScale,
                         (0, 255, 0), thickness
                         )

        if isVideo:
            out.write(cimg)
        else:
            cv2.imwrite('%s_%i_%.3f.png' % (path[:-4], n, time), cimg)

    if isVideo:
        cap.release()
        out.release()


Instruction:
Can you tell what is the following Python 3 function doing
def imread(img, color=None, dtype=None):
    '''
    dtype = 'noUint', uint8, float, 'float', ...
    '''
    COLOR2CV = {'gray': cv2.IMREAD_GRAYSCALE,
                'all': cv2.IMREAD_COLOR,
                None: cv2.IMREAD_ANYCOLOR
                }
    c = COLOR2CV[color]
    if callable(img):
        img = img()
    elif isinstance(img, string_types):
        #         from_file = True
        #         try:
        #             ftype = img[img.find('.'):]
        #             img = READERS[ftype](img)[0]
        #         except KeyError:
        # open with openCV
        # grey - 8 bit
        if dtype in (None, "noUint") or np.dtype(dtype) != np.uint8:
            c |= cv2.IMREAD_ANYDEPTH
        img2 = cv2.imread(img, c)
        if img2 is None:
            raise IOError("image '%s' is not existing" % img)
        img = img2

    elif color == 'gray' and img.ndim == 3:  # multi channel img like rgb
        # cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #cannot handle float64
        img = toGray(img)
    # transform array to uint8 array due to openCV restriction
    if dtype is not None:
        if isinstance(img, np.ndarray):
            img = _changeArrayDType(img, dtype, cutHigh=False)

    return img

Response: Returns the image with the specified color and dtype.


Instruction:
How would you code a function in Python 3 to
add an image to the image list and roi - upper left corner of the image

Response:
def addImg(self, img, roi=None):
        '''
        img - background, flat field, ste corrected image
        roi - [(x1,y1),...,(x4,y4)] -  boundaries where points are
        '''
        self.img = imread(img, 'gray')
        s0, s1 = self.img.shape

        if roi is None:
            roi = ((0, 0), (s0, 0), (s0, s1), (0, s1))

        k = self.kernel_size
        hk = k // 2

        # mask image
        img2 = self.img.copy()  # .astype(int)

        mask = np.zeros(self.img.shape)
        cv2.fillConvexPoly(mask, np.asarray(roi, dtype=np.int32), color=1)
        mask = mask.astype(bool)
        im = img2[mask]

        bg = im.mean()  # assume image average with in roi == background
        mask = ~mask
        img2[mask] = -1

        # find points from local maxima:
        self.points = np.zeros(shape=(self.max_points, 2), dtype=int)
        thresh = 0.8 * bg + 0.2 * im.max()

        _findPoints(img2, thresh, self.min_dist, self.points)
        self.points = self.points[:np.argmin(self.points, axis=0)[0]]

        # correct point position, to that every point is over max value:
        for n, p in enumerate(self.points):
            sub = self.img[p[1] - hk:p[1] + hk + 1, p[0] - hk:p[0] + hk + 1]
            i, j = np.unravel_index(np.nanargmax(sub), sub.shape)
            self.points[n] += [j - hk, i - hk]

        # remove points that are too close to their neighbour or the border
        mask = maximum_filter(mask, hk)
        i = np.ones(self.points.shape[0], dtype=bool)
        for n, p in enumerate(self.points):
            if mask[p[1], p[0]]:  # too close to border
                i[n] = False
            else:
                # too close to other points
                for pp in self.points[n + 1:]:
                    if norm(p - pp) < hk + 1:
                        i[n] = False
        isum = i.sum()
        ll = len(i) - isum
        print('found %s points' % isum)
        if ll:
            print(
                'removed %s points (too close to border or other points)' %
                ll)
            self.points = self.points[i]

#         self.n_points += len(self.points)

        # for finding best peak position:
#         def fn(xy,cx,cy):#par
#             (x,y) = xy
#             return 1-(((x-cx)**2 + (y-cy)**2)*(1/8)).flatten()

#         x,y = np.mgrid[-2:3,-2:3]
#         x = x.flatten()
#         y = y.flatten()
        # for shifting peak:
        xx, yy = np.mgrid[0:k, 0:k]
        xx = xx.astype(float)
        yy = yy.astype(float)

        self.subs = []

#         import pylab as plt
#         plt.figure(20)
#         img = self.drawPoints()
#         plt.imshow(img, interpolation='none')
# #                 plt.figure(21)
# #                 plt.imshow(sub2, interpolation='none')
#         plt.show()

        #thresh = 0.8*bg + 0.1*im.max()
        for i, p in enumerate(self.points):
            sub = self.img[p[1] - hk:p[1] + hk + 1,
                           p[0] - hk:p[0] + hk + 1].astype(float)
            sub2 = sub.copy()

            mean = sub2.mean()
            mx = sub2.max()
            sub2[sub2 < 0.5 * (mean + mx)] = 0  # only select peak
            try:
                # SHIFT SUB ARRAY to align peak maximum exactly in middle:
                    # only eval a 5x5 array in middle of sub:
                # peak = sub[hk-3:hk+4,hk-3:hk+4]#.copy()

                #                 peak -= peak.min()
                #                 peak/=peak.max()
                #                 peak = peak.flatten()
                    # fit paraboloid to get shift in x,y:
                #                 p, _ = curve_fit(fn, (x,y), peak, (0,0))
                c0, c1 = center_of_mass(sub2)

#                 print (p,c0,c1,hk)

                #coords = np.array([xx+p[0],yy+p[1]])
                coords = np.array([xx + (c0 - hk), yy + (c1 - hk)])

                #print (c0,c1)

                #import pylab as plt
                #plt.imshow(sub2, interpolation='none')

                # shift array:
                sub = map_coordinates(sub, coords,
                                      mode='nearest').reshape(k, k)
                # plt.figure(2)
                #plt.imshow(sub, interpolation='none')
                # plt.show()

                #normalize:
                bg = 0.25* (  sub[0].mean()   + sub[-1].mean()
                            + sub[:,0].mean() + sub[:,-1].mean())

                sub-=bg
                sub /= sub.max()

#                 import pylab as plt
#                 plt.figure(20)
#                 plt.imshow(sub, interpolation='none')
# #                 plt.figure(21)
# #                 plt.imshow(sub2, interpolation='none')
#                 plt.show()

                self._psf += sub

                if self.calc_std:
                    self.subs.append(sub)
            except ValueError:
                pass


Instruction:
Write a Python 3 function that can
stitch 2 images vertically together. Smooth the overlap area of both images with a linear fade from img1 to img2 @param img1: numpy.2dArray @param img2: numpy.2dArray of the same shape[1,2] as img1 @param overlap: number of pixels both images overlap @returns: stitched-image

Response:
def linearBlend(img1, img2, overlap, backgroundColor=None):
    '''
    Stitch 2 images vertically together.
    Smooth the overlap area of both images with a linear fade from img1 to img2

    @param img1: numpy.2dArray
    @param img2: numpy.2dArray of the same shape[1,2] as img1
    @param overlap: number of pixels both images overlap
    @returns: stitched-image
    '''
    (sizex, sizey) = img1.shape[:2]

    overlapping = True
    if overlap < 0:
        overlapping = False
        overlap = -overlap

    # linear transparency change:
    alpha = np.tile(np.expand_dims(np.linspace(1, 0, overlap), 1), sizey)
    if len(img2.shape) == 3:  # multi channel img like rgb
        # make alpha 3d with n channels
        alpha = np.dstack(([alpha for _ in range(img2.shape[2])]))
    if overlapping:
        img1_cut = img1[sizex - overlap:sizex, :]
        img2_cut = img2[0:overlap, :]
    else:
        # take average of last 5 rows:
        img1_cut = np.tile(img1[-min(sizex, 5):, :].mean(
            axis=0), (overlap, 1)).reshape(alpha.shape)
        img2_cut = np.tile(img2[:min(img2.shape[0], 5), :].mean(
            axis=0), (overlap, 1)).reshape(alpha.shape)

    # fill intermediate area as mixture of both images
    #################bg transparent############
    inter = (img1_cut * alpha + img2_cut * (1 - alpha)).astype(img1.dtype)
    # set background areas to value of respective other img:
    if backgroundColor is not None:
        mask = np.logical_and(img1_cut == backgroundColor,
                              img2_cut != backgroundColor)
        inter[mask] = img2_cut[mask]
        mask = np.logical_and(img2_cut == backgroundColor,
                              img1_cut != backgroundColor)

        inter[mask] = img1_cut[mask]
    if not overlapping:
        overlap = 0
    return np.vstack((img1[0:sizex - overlap, :],
                      inter,
                      img2[overlap:, :]))


Instruction:
Can you tell what is the following Python 3 function doing
def interpolate2dStructuredPointSpreadIDW(grid, mask, kernel=15, power=2,
                                          maxIter=1e5, copy=True):
    '''
    same as interpolate2dStructuredIDW but using the point spread method
    this is faster if there are bigger connected masked areas and the border
    length is smaller

    replace all values in [grid] indicated by [mask]
    with the inverse distance weighted interpolation of all values within
    px+-kernel

    [power] -> distance weighting factor: 1/distance**[power]
    [copy] -> False: a bit faster, but modifies 'grid' and 'mask'
    '''
    assert grid.shape == mask.shape, 'grid and mask shape are different'

    border = np.zeros(shape=mask.shape, dtype=np.bool)
    if copy:
        # copy mask as well because if will be modified later:
        mask = mask.copy()
        grid = grid.copy()
    return _calc(grid, mask, border, kernel, power, maxIter)

Response: same as interpolate2dStructuredPointSpreadIDW but using the point spread method


Instruction:
How would you explain what the following Python 3 function does
def SNR(img1, img2=None, bg=None,
        noise_level_function=None,
        constant_noise_level=False,
        imgs_to_be_averaged=False):
    '''
    Returns a signal-to-noise-map
    uses algorithm as described in BEDRICH 2016 JPV (not jet published)

    :param constant_noise_level: True, to assume noise to be constant
    :param imgs_to_be_averaged: True, if SNR is for average(img1, img2)
    '''
    # dark current subtraction:
    img1 = np.asfarray(img1)
    if bg is not None:
        img1 = img1 - bg

    # SIGNAL:
    if img2 is not None:
        img2_exists = True
        img2 = np.asfarray(img2) - bg
        # signal as average on both images
        signal = 0.5 * (img1 + img2)
    else:
        img2_exists = False
        signal = img1
    # denoise:
    signal = median_filter(signal, 3)

    # NOISE
    if constant_noise_level:
        # CONSTANT NOISE
        if img2_exists:
            d = img1 - img2
            # 0.5**0.5 because of sum of variances
            noise = 0.5**0.5 * np.mean(np.abs((d))) * F_RMS2AAD
        else:
            d = (img1 - signal) * F_NOISE_WITH_MEDIAN
            noise = np.mean(np.abs(d)) * F_RMS2AAD
    else:
        # NOISE LEVEL FUNCTION
        if noise_level_function is None:
            noise_level_function, _ = oneImageNLF(img1, img2, signal)
        noise = noise_level_function(signal)
        noise[noise < 1] = 1  # otherwise SNR could be higher than image value

    if imgs_to_be_averaged:
        # SNR will be higher if both given images are supposed to be averaged:
        # factor of noise reduction if SNR if for average(img1, img2):
        noise *= 0.5**0.5

    # BACKGROUND estimation and removal if background not given:
    if bg is None:
        bg = getBackgroundLevel(img1)
        signal -= bg
    snr = signal / noise

    # limit to 1, saying at these points signal=noise:
    snr[snr < 1] = 1
    return snr

Response: Returns a signal - to - noise - map for the two images img1 and img2.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def sortCorners(corners):
    '''
    sort the corners of a given quadrilateral of the type
    corners : [ [xi,yi],... ]

    to an anti-clockwise order starting with the bottom left corner

    or (if plotted as image where y increases to the bottom):
    clockwise, starting top left
    '''
    corners = np.asarray(corners)
    # bring edges in order:
    corners2 = corners[ConvexHull(corners).vertices]

    if len(corners2) == 3:
        # sometimes ConvexHull one point is missing because it is
        # within the hull triangle
        # find the right position of set corner as the minimum perimeter
        # built with that point as different indices
        for c in corners:
            if c not in corners2:
                break
        perimeter = []
        for n in range(0, 4):
            corners3 = np.insert(corners2, n, c, axis=0)
            perimeter.append(
                np.linalg.norm(
                    np.diff(
                        corners3,
                        axis=0),
                    axis=1).sum())
        n = np.argmin(perimeter)
        corners2 = np.insert(corners2, n, c, axis=0)

    # find the edge with the right angle to the quad middle:
    mn = corners2.mean(axis=0)
    d = (corners2 - mn)
    ascent = np.arctan2(d[:, 1], d[:, 0])
    bl = np.abs(BL_ANGLE + ascent).argmin()
    # build a index list starting with bl:
    i = list(range(bl, 4))
    i.extend(list(range(0, bl)))
    return corners2[i]

Response: sort the corners of a given quadrilateral of the type


Instruction:
How would you implement a function in Python 3 that
returns an array with contains the closest distance to the next positive value given in arr within a given kernel size

Response:
def closestDirectDistance(arr, ksize=30, dtype=np.uint16):
    '''
    return an array with contains the closest distance to the next positive
    value given in arr  within a given kernel size
    '''

    out = np.zeros_like(arr, dtype=dtype)
    _calc(out, arr, ksize)
    return out


Instruction:
Here you have a function in Python 3, explain what it does
def closestConnectedDistance(target, walls=None,
                             max_len_border_line=500,
                             max_n_path=100,
                             concentrate_every_n_pixel=1):
    '''
    returns an array with contains the closest distance from every pixel
    the next position where target == 1

    [walls] binary 2darray - e.g. walls in a labyrinth that have to be surrounded in order to get to the target

    [target] binary 2darray - positions given by 1

    [concentrate_every_n_pixel] often the distance of neighbour pixels is similar
            to speed up calculation set this value to e.g. 3 to calculate only
            the distance for every 3. pixel and interpolate in between

        recommended are values up to 3-5

    [max_len_border_line]
    this function calculates distances travelled using region growth
    e.g.

    0123
    1123
    2223
    3333

    the last steps (e.g. for all steps 3 border_line=7) are stored in an array of limited
    length defined in 'max_len_border_line'

    [max_n_path]
    how many paths are possible between every pixel and the target
    only needed if fast==False
    '''
    c = concentrate_every_n_pixel
    assert c >= 1
    if walls is None:
        walls = np.zeros_like(target, dtype=bool)
    s = target.shape
    dt = np.uint16
    if max(target.shape) < 200:
        dt = np.uint8
    out = np.zeros((s[0] // c, s[1] // c), dtype=dt)
    # temporary arrays:
    growth = np.zeros_like(target, dtype=dt)
    res = np.empty(shape=3, dtype=dt)

    steps = np.empty(shape=(max_len_border_line, 2), dtype=dt)
    new_steps = np.empty(shape=(max_len_border_line, 2), dtype=dt)

    # run calculation:
    _calc(growth, out, walls, target, steps, new_steps,
          res, concentrate_every_n_pixel)

    if c > 1:
        # if concentrate_every_n_pixel > 1
        # the resized output array
        # will have wrong values close to the wall
        # therefore substitute all wall value (-1)
        # with an average of their closest neighbours
        interpolate2dStructuredIDW(out, out == 0)
        out = cv2.resize(out, s[::-1])
        out[walls] = 0
    return out

Response: Returns a binary 2darray with the closest distance from every pixel to the target.


Instruction:
Can you implement a function in Python 3 that
fills [res] with [distance to next position where target == 1, x coord., y coord. of that position in target] using region growth i,j -> pixel position growth -> a work array, needed to measure the distance steps, new_steps -> current and last positions of the region growth steps using this instead of looking for the right step position in [growth] should speed up the process

Response:
def _grow(growth, walls, target, i, j, steps, new_steps, res):
    '''
    fills [res] with [distance to next position where target == 1,
                      x coord.,
                      y coord. of that position in target]
     using region growth

    i,j -> pixel position
     growth -> a work array, needed to measure the distance
     steps, new_steps -> current and last positions of the region growth steps
        using this instead of looking for the right step position in [growth]
        should speed up the process
    '''

    # clean array:
    growth[:] = 0

    if target[i, j]:
        # pixel is in target
        res[0] = 1
        res[1] = i
        res[2] = j
        return

    step = 1
    s0, s1 = growth.shape
    step_len = 1
    new_step_ind = 0

    steps[new_step_ind, 0] = i
    steps[new_step_ind, 1] = j
    growth[i, j] = 1

    while True:
        for n in range(step_len):
            i, j = steps[n]
            for ii, jj in DIRECT_NEIGHBOURS:
                pi = i + ii
                pj = j + jj

                # if in image:
                if 0 <= pi < s0 and 0 <= pj < s1:
                    # is growth array is empty and there are no walls:
                        # fill growth with current step
                    if growth[pi, pj] == 0 and not walls[pi, pj]:
                        growth[pi, pj] = step
                        if target[pi, pj]:
                            # found destination
                            res[0] = 1
                            res[1] = pi
                            res[2] = pj
                            return

                        new_steps[new_step_ind, 0] = pi
                        new_steps[new_step_ind, 1] = pj
                        new_step_ind += 1

        if new_step_ind == 0:
            # couldn't populate any more because growth is full
                # and all possible steps are gone
            res[0] = 0
            return

        step += 1
        steps, new_steps = new_steps, steps
        step_len = new_step_ind
        new_step_ind = 0


Instruction:
Here you have a function in Python 3, explain what it does
def polylinesFromBinImage(img, minimum_cluster_size=6,
                          remove_small_obj_size=3,
                          reconnect_size=3,
                          max_n_contours=None, max_len_contour=None,
                          copy=True):
    '''
    return a list of arrays of un-branching contours

    img -> (boolean) array

    optional:
    ---------
    minimum_cluster_size -> minimum number of pixels connected together to build a contour

    ##search_kernel_size -> TODO
    ##min_search_kernel_moment -> TODO

    numeric:
    -------------
    max_n_contours -> maximum number of possible contours in img
    max_len_contour -> maximum contour length

    '''
    assert minimum_cluster_size > 1
    assert reconnect_size % 2, 'ksize needs to be odd'

    # assert search_kernel_size == 0 or search_kernel_size > 2 and search_kernel_size%2, 'kernel size needs to be odd'
    # assume array size parameters, is not given:
    if max_n_contours is None:
        max_n_contours = max(img.shape)
    if max_len_contour is None:
        max_len_contour = sum(img.shape[:2])
    # array containing coord. of all contours:
    contours = np.zeros(shape=(max_n_contours, max_len_contour, 2),
                        dtype=np.uint16)  # if not search_kernel_size else np.float32)

    if img.dtype != np.bool:
        img = img.astype(bool)
    elif copy:
        img = img.copy()

    if remove_small_obj_size:
        remove_small_objects(img, remove_small_obj_size,
                             connectivity=2, in_place=True)
    if reconnect_size:
        # remove gaps
        maximum_filter(img, reconnect_size, output=img)
        # reduce contour width to 1
        img = skeletonize(img)

    n_contours = _populateContoursArray(img, contours, minimum_cluster_size)
    contours = contours[:n_contours]

    l = []
    for c in contours:
        ind = np.zeros(shape=len(c), dtype=bool)
        _getValidInd(c, ind)
        # remove all empty spaces:
        l.append(c[ind])
    return l

Response: Returns a list of arrays of contour arrays from a binary image.


Instruction:
Can you implement a function in Python 3 that
returns the cumulative density function of a given array or a given position

Response:
def cdf(arr, pos=None):
    '''
    Return the cumulative density function of a given array or
    its intensity at a given position (0-1)
    '''

    r = (arr.min(), arr.max())
    hist, bin_edges = np.histogram(arr, bins=2 * int(r[1] - r[0]), range=r)
    hist = np.asfarray(hist) / hist.sum()
    cdf = np.cumsum(hist)
    if pos is None:
        return cdf
    i = np.argmax(cdf > pos)
    return bin_edges[i]


Instruction:
Can you generate a brief explanation for the following Python 3 code
def subCell2DGenerator(arr, shape, d01=None, p01=None):
    '''Generator to access evenly sized sub-cells in a 2d array

    Args:
       shape (tuple): number of sub-cells in y,x e.g. (10,15)
       d01 (tuple, optional): cell size in y and x
       p01 (tuple, optional): position of top left edge

    Returns:
        int: 1st index
        int: 2nd index
        array: sub array

    Example:

    >>> a = np.array([[[0,1],[1,2]],[[2,3],[3,4]]])
    >>> gen = subCell2DGenerator(a,(2,2))
    >>> for i,j, sub in gen: print( i,j, sub )
    0 0 [[[0 1]]]
    0 1 [[[1 2]]]
    1 0 [[[2 3]]]
    1 1 [[[3 4]]]
    '''
    for i, j, s0, s1 in subCell2DSlices(arr, shape, d01, p01):
        yield i, j, arr[s0, s1]

Response: Generator to access evenly sized sub - cells in a 2d array


Instruction:
Explain what the following Python 3 code does
def subCell2DSlices(arr, shape, d01=None, p01=None):
    '''Generator to access evenly sized sub-cells in a 2d array

    Args:
       shape (tuple): number of sub-cells in y,x e.g. (10,15)
       d01 (tuple, optional): cell size in y and x
       p01 (tuple, optional): position of top left edge

    Returns:
        int: 1st index
        int: 2nd index
        slice: first dimension
        slice: 1st dimension
    '''
    if p01 is not None:
        yinit, xinit = p01
    else:
        xinit, yinit = 0, 0

    x, y = xinit, yinit
    g0, g1 = shape
    s0, s1 = arr.shape[:2]

    if d01 is not None:
        d0, d1 = d01
    else:
        d0, d1 = s0 / g0, s1 / g1

    y1 = d0 + yinit
    for i in range(g0):
        for j in range(g1):
            x1 = x + d1
            yield (i, j, slice(max(0, _rint(y)),
                               max(0, _rint(y1))),
                   slice(max(0, _rint(x)),
                         max(0, _rint(x1))))
            x = x1
        y = y1
        y1 = y + d0
        x = xinit

Response: Generator to access evenly sized sub - cells in a 2d array


Instruction:
Given the following Python 3 function, write the documentation
def subCell2DCoords(*args, **kwargs):
    '''Same as subCell2DSlices but returning coordinates

    Example:
        g = subCell2DCoords(arr, shape)
        for x, y in g:
                plt.plot(x, y)
    '''
    for _, _, s0, s1 in subCell2DSlices(*args, **kwargs):
        yield ((s1.start, s1.start, s1.stop),
               (s0.start, s0.stop,  s0.stop))

Response: Same as subCell2DSlices but returning coordinates


Instruction:
Can you implement a function in Python 3 that
returns array where every cell equals result of fn on all sub - arrays

Response:
def subCell2DFnArray(arr, fn, shape, dtype=None, **kwargs):
    '''
    Return array where every cell is the output of a given cell function

    Args:
       fn (function): ...to be executed on all sub-arrays

    Returns:
        array: value of every cell equals result of fn(sub-array)

    Example:
        mx = subCell2DFnArray(myArray, np.max, (10,6) )
        - -> here mx is a 2d array containing all cell maxima
    '''

    sh = list(arr.shape)
    sh[:2] = shape
    out = np.empty(sh, dtype=dtype)
    for i, j, c in subCell2DGenerator(arr, shape, **kwargs):
        out[i, j] = fn(c)
    return out


Instruction:
Can you generate the documentation for the following Python 3 function
def defocusThroughDepth(u, uf, f, fn, k=2.355):
    '''
    return the defocus (mm std) through DOF

    u -> scene point (depth value)
    uf -> in-focus position (the distance at which the scene point should be placed in order to be focused)
    f -> focal length
    k -> camera dependent constant (transferring blur circle to PSF), 2.335 would be FHWD of 2dgaussian
    fn --> f-number (relative aperture)

    equation (3) taken from http://linkinghub.elsevier.com/retrieve/pii/S0031320312004736
        Pertuz et.al. "Analysis of focus measure operators for shape-from-focus"

    all parameter should be in same physical unit [mm]

    !! assumes spatial invariant blur
    '''
    # A = f/fn
    return (k/fn) * (f**2*abs(u-uf)) / (u*(uf-f))

Response: Returns the defocus through a scene point uf f and fn


Instruction:
Implement a Python 3 function for
extending array for convolution

Response:
def extendArrayForConvolution(arr, kernelXY,
                          modex='reflect',
                          modey='reflect'):
    '''
    extends a given array right right border handling
    for convolution
    -->in opposite to skimage and skipy this function
    allows to chose different mode = ('reflect', 'wrap')
    in x and y direction

    only supports 'warp' and 'reflect' at the moment
    '''
    (kx, ky) = kernelXY
    kx//=2
    ky//=2

    #indexing 0:-0 leads to empty arrays and not the whole thing
    #make it easy with assuming ksize=1 and removing extra size later:
    nokx = kx == 0
    noky = ky == 0
    if nokx:
        kx = 1
    if noky:
        ky = 1

    s0,s1 = arr.shape

    assert ky < s0
    assert kx < s1

    arr2 = np.zeros((s0+2*ky, s1+2*kx), dtype=arr.dtype)
    if kx == 0:
        kx = None
    arr2[ky:-ky,kx:-kx]=arr

    #original array:
    t =  arr[:ky] #TOP
    rb = arr[-1:-ky-1:-1] #reverse bottom
    rt = arr[ky-1::-1] #reverse top
    rr = arr[:,-1:-kx-1:-1] #reverse right
    l = arr[:,:kx] #left
#     rtl = arr[ky-1::-1,kx-1::-1]

    #filter array:
    tm2 = arr2[:ky ,  kx:-kx] #TOP-MIDDLE
    bm2 = arr2[-ky:,  kx:-kx]  #BOTTOM-MIDDLE
    tl2 = arr2[:ky , :kx] #TOP-LEFT
    bl2 = arr2[-ky:, :kx] #BOTTOM-LEFT
    tr2 = arr2[:ky:, -kx:]#TOP-RIGHT
    br2 = arr2[-ky:, -kx:]#TOP-RIGHT

    #fill edges:
    if modey == 'warp':
        tm2[:] = t
        bm2[:] = rb

        tl2[:] = arr2[2*ky:ky:-1,:kx]
        bl2[:] = arr2[-ky-1:-2*ky-1:-1,:kx]
    #TODO: do other options!!!
    elif modey == 'reflect':
        tm2[:] = rt
        bm2[:] = rb
        if modex =='reflect':
            tl2[:] = arr[ky-1::-1,kx-1::-1]
            bl2[:] = arr[-1:-ky-1:-1,kx-1::-1]

            tr2[:] = arr[:ky,-kx:][::-1,::-1]
            br2[:] = arr[-ky:,-kx:][::-1,::-1]

        else:#warp
            tl2[:] = arr[ky-1::-1    , -kx:]
            bl2[:] = arr[-1:-ky-1:-1 , -kx:]
            tr2[:] = arr[ky-1::-1    , :kx]
            br2[:] = arr[-1:-ky-1:-1 , :kx]

    else:
        raise Exception('modey not supported')

    if modex == 'wrap':
        arr2[ky:-ky,kx-1::-1] = rr
        arr2[ky:-ky,-kx:] = l
    elif modex == 'reflect':
        arr2[ky:-ky,:kx] = l[:,::-1]
        arr2[ky:-ky,-kx:] = rr
    else:
        raise Exception('modex not supported')

    if nokx:
        arr2 = arr2[:,1:-1]
    if noky:
        arr2 = arr2[1:-1]

    return arr2


Instruction:
Can you generate a brief explanation for the following Python 3 code
def calibrate(self, board_size=(8, 6), method='Chessboard', images=[],
                  max_images=100, sensorSize_mm=None,
                  detect_sensible=True):
        '''
        sensorSize_mm - (width, height) [mm] Physical size of the sensor
        '''
        self._coeffs = {}
        self.opts = {'foundPattern': [],  # whether pattern could be found for image
                     'size': board_size,
                     'imgs': [],  # list of either npArrsays or img paths
                     # list or 2d coords. of found pattern features (e.g.
                     # chessboard corners)
                     'imgPoints': []
                     }

        self._detect_sensible = detect_sensible

        self.method = {'Chessboard': self._findChessboard,
                       'Symmetric circles': self._findSymmetricCircles,
                       'Asymmetric circles': self._findAsymmetricCircles,
                       'Manual': None
                       # TODO: 'Image grid':FindGridInImage
                       }[method]

        self.max_images = max_images
        self.findCount = 0
        self.apertureSize = sensorSize_mm

        self.objp = self._mkObjPoints(board_size)

        if method == 'Asymmetric circles':
            # this pattern have its points (every 2. row) displaced, so:
            i = self.objp[:, 1] % 2 == 1
            self.objp[:, 0] *= 2
            self.objp[i, 0] += 1

        # Arrays to store object points and image points from all the images.
        self.objpoints = []  # 3d point in real world space
        # self.imgpoints = [] # 2d points in image plane.
        self.mapx, self.mapy = None, None

        # from matplotlib import pyplot as plt
        for n, i in enumerate(images):
            print('working on image %s' % n)
            if self.addImg(i):
                print('OK')

Response: Create a new instance of the appropriate class based on the parameters of the specified method.


Instruction:
Given the following Python 3 function, write the documentation
def addPoints(self, points, board_size=None):
        '''
        add corner points directly instead of extracting them from
        image
        points = ( (0,1), (...),... ) [x,y]
        '''
        self.opts['foundPattern'].append(True)
        self.findCount += 1
        if board_size is not None:
            self.objpoints.append(self._mkObjPoints(board_size))
        else:
            self.objpoints.append(self.objp)
        s0 = points.shape[0]

        self.opts['imgPoints'].append(np.asarray(points).reshape(
            s0, 1, 2).astype(np.float32))

Response: add corner points directly to extract them from image


Instruction:
Given the following Python 3 function, write the documentation
def setImgShape(self, shape):
        '''
        image shape must be known for calculating camera matrix
        if method==Manual and addPoints is used instead of addImg
        this method must be called before .coeffs are obtained
        '''
        self.img = type('Dummy', (object,), {})
#         if imgProcessor.ARRAYS_ORDER_IS_XY:
#             self.img.shape = shape[::-1]
#         else:
        self.img.shape = shape

Response:
image shape must be known for calculating camera matrix
        if method==Manual and addPoints is used instead of addImg
        this method must be called before .coeffs are obtained


Instruction:
Write a Python 3 function that can
add a new image using a continous stream

Response:
def addImgStream(self, img):
        '''
        add images using a continous stream
        - stop when max number of images is reached
        '''
        if self.findCount > self.max_images:
            raise EnoughImages('have enough images')
        return self.addImg(img)


Instruction:
Can you generate the documentation for the following Python 3 function
def addImg(self, img):
        '''
        add one chessboard image for detection lens distortion
        '''
        # self.opts['imgs'].append(img)

        self.img = imread(img, 'gray', 'uint8')

        didFindCorners, corners = self.method()
        self.opts['foundPattern'].append(didFindCorners)

        if didFindCorners:
            self.findCount += 1
            self.objpoints.append(self.objp)
            self.opts['imgPoints'].append(corners)
        return didFindCorners

Response: add one chessboard image for detection lens distortion


Instruction:
Implement a function in Python 3 to
get the distortion coeffs in a formated string

Response:
def getCoeffStr(self):
        '''
        get the distortion coeffs in a formated string
        '''
        txt = ''
        for key, val in self.coeffs.items():
            txt += '%s = %s\n' % (key, val)
        return txt


Instruction:
Make a summary of the following Python 3 code
def drawChessboard(self, img=None):
        '''
        draw a grid fitting to the last added image
        on this one or an extra image
        img == None
            ==False -> draw chessbord on empty image
            ==img
        '''
        assert self.findCount > 0, 'cannot draw chessboard if nothing found'
        if img is None:
            img = self.img
        elif isinstance(img, bool) and not img:
            img = np.zeros(shape=(self.img.shape), dtype=self.img.dtype)
        else:
            img = imread(img, dtype='uint8')
        gray = False
        if img.ndim == 2:
            gray = True
            # need a color 8 bit image
            img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
        # Draw and display the corners
        cv2.drawChessboardCorners(img, self.opts['size'],
                                  self.opts['imgPoints'][-1],
                                  self.opts['foundPattern'][-1])
        if gray:
            img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        return img

Response:
draw a chessboard on the last added image
            - > image with the most recent found pattern


Instruction:
Can you write a function in Python 3 where it
writes the distortion coeffs to file

Response:
def writeToFile(self, filename, saveOpts=False):
        '''
        write the distortion coeffs to file
        saveOpts --> Whether so save calibration options (and not just results)
        '''
        try:
            if not filename.endswith('.%s' % self.ftype):
                filename += '.%s' % self.ftype
            s = {'coeffs': self.coeffs}
            if saveOpts:
                s['opts'] = self.opts
#             else:
#                 s['opts':{}]
            np.savez(filename, **s)
            return filename
        except AttributeError:
            raise Exception(
                'need to calibrate camera before calibration can be saved to file')


Instruction:
Implement a Python 3 function for
reading the distortion coeffs from file

Response:
def readFromFile(self, filename):
        '''
        read the distortion coeffs from file
        '''
        s = dict(np.load(filename))
        try:
            self.coeffs = s['coeffs'][()]
        except KeyError:
            #LEGENCY - remove
            self.coeffs = s
        try:
            self.opts = s['opts'][()]
        except KeyError:
            pass
        return self.coeffs


Instruction:
Can you generate a brief explanation for the following Python 3 code
def undistortPoints(self, points, keepSize=False):
        '''
        points --> list of (x,y) coordinates
        '''
        s = self.img.shape
        cam = self.coeffs['cameraMatrix']
        d = self.coeffs['distortionCoeffs']

        pts = np.asarray(points, dtype=np.float32)
        if pts.ndim == 2:
            pts = np.expand_dims(pts, axis=0)

        (newCameraMatrix, roi) = cv2.getOptimalNewCameraMatrix(cam,
                                                               d, s[::-1], 1,
                                                               s[::-1])
        if not keepSize:
            xx, yy = roi[:2]
            pts[0, 0] -= xx
            pts[0, 1] -= yy

        return cv2.undistortPoints(pts,
                                   cam, d, P=newCameraMatrix)

Response: Returns a list of undistorted points.


Instruction:
Write a Python 3 script for
removing lens distortion from given image keepSize = True keepSize = False borderValue = 0

Response:
def correct(self, image, keepSize=False, borderValue=0):
        '''
        remove lens distortion from given image
        '''
        image = imread(image)
        (h, w) = image.shape[:2]
        mapx, mapy = self.getUndistortRectifyMap(w, h)
        self.img = cv2.remap(image, mapx, mapy, cv2.INTER_LINEAR,
                             borderMode=cv2.BORDER_CONSTANT,
                             borderValue=borderValue
                             )
        if not keepSize:
            xx, yy, ww, hh = self.roi
            self.img = self.img[yy: yy + hh, xx: xx + ww]
        return self.img


Instruction:
Can you generate a brief explanation for the following Python 3 code
def getCameraParams(self):
        '''
        value positions based on
        http://docs.opencv.org/modules/imgproc/doc/geometric_transformations.html#cv.InitUndistortRectifyMap
        '''
        c = self.coeffs['cameraMatrix']
        fx = c[0][0]
        fy = c[1][1]
        cx = c[0][2]
        cy = c[1][2]
        k1, k2, p1, p2, k3 = tuple(self.coeffs['distortionCoeffs'].tolist()[0])
        return fx, fy, cx, cy, k1, k2, k3, p1, p2

Response: Returns a tuple of the two elements of the camera matrix and the two elements of the camera matrix.


Instruction:
Create a Python 3 function for
returning a list of standard uncertainties for the image and the image sharpness.

Response:
def standardUncertainties(self, sharpness=0.5):
        '''
        sharpness -> image sharpness // std of Gaussian PSF [px]

        returns a list of standard uncertainties for the x and y component:
        (1x,2x), (1y, 2y), (intensity:None)
        1. px-size-changes(due to deflection)
        2. reprojection error
        '''
        height, width = self.coeffs['shape']
        fx, fy = self.getDeflection(width, height)
        # is RMSE of imgPoint-projectedPoints
        r = self.coeffs['reprojectionError']
        t = (sharpness**2 + r**2)**0.5
        return fx * t, fy * t


Instruction:
Can you create a Python 3 function that
takes a binary image and returns the edges of the object inside .

Response:
def edgesFromBoolImg(arr, dtype=None):
    '''
    takes a binary image (usually a mask)
    and returns the edges of the object inside
    '''
    out = np.zeros_like(arr, dtype=dtype)
    _calc(arr, out)
    _calc(arr.T, out.T)
    return out


Instruction:
Given the following Python 3 function, write the documentation
def draw_matches(img1, kp1, img2, kp2, matches, color=None, thickness=2, r=15):
    # We're drawing them side by side.  Get dimensions accordingly.
    # Handle both color and grayscale images.
    if len(img1.shape) == 3:
        new_shape = (max(img1.shape[0], img2.shape[0]), img1.shape[
                     1] + img2.shape[1], img1.shape[2])
    elif len(img1.shape) == 2:
        new_shape = (
            max(img1.shape[0], img2.shape[0]), img1.shape[1] + img2.shape[1])
    new_img = np.zeros(new_shape, type(img1.flat[0]))
    # Place images onto the new image.
    new_img[0:img1.shape[0], 0:img1.shape[1]] = img1
    new_img[0:img2.shape[0], img1.shape[1]
        :img1.shape[1] + img2.shape[1]] = img2

    # Draw lines between matches.  Make sure to offset kp coords in second
    # image appropriately.
    if color:
        c = color
    for m in matches:
        # Generate random color for RGB/BGR and grayscale images as needed.
        if not color:
            c = np.random.randint(0, 256, 3) if len(
                img1.shape) == 3 else np.random.randint(0, 256)
        # So the keypoint locs are stored as a tuple of floats.  cv2.line(), like most other things,
        # wants locs as a tuple of ints.
        end1 = tuple(np.round(kp1[m.trainIdx].pt).astype(int))
        end2 = tuple(np.round(kp2[m.queryIdx].pt).astype(
            int) + np.array([img1.shape[1], 0]))
        cv2.line(new_img, end1, end2, c, thickness)
        cv2.circle(new_img, end1, r, c, thickness)
        cv2.circle(new_img, end2, r, c, thickness)
    return new_img

Response: Draws lines between two matching keypoints.


Instruction:
Given the following Python 3 function, write the documentation
def findHomography(self, img, drawMatches=False):
        '''
        Find homography of the image through pattern
        comparison with the base image
        '''
        print("\t Finding points...")
        # Find points in the next frame
        img = self._prepareImage(img)
        features, descs = self.detector.detectAndCompute(img, None)

        ######################
        # TODO: CURRENTLY BROKEN IN OPENCV3.1 - WAITNG FOR NEW RELEASE 3.2
#         matches = self.matcher.knnMatch(descs,#.astype(np.float32),
#                                         self.base_descs,
#                                         k=3)
#         print("\t Match Count: ", len(matches))
#         matches_subset = self._filterMatches(matches)

        # its working alternative (for now):
        bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
        matches_subset = bf.match(descs, self.base_descs)

        ######################
#         matches = bf.knnMatch(descs,self.base_descs, k=2)
#         # Apply ratio test
#         matches_subset = []
#         medDist = np.median([m.distance for m in matches])
#         matches_subset = [m for m in matches if m.distance < medDist]
#         for m in matches:
#             print(m.distance)
#         for m,n in matches:
#             if m.distance < 0.75*n.distance:
#                 matches_subset.append([m])

        if not len(matches_subset):
            raise Exception('no matches found')
        print("\t Filtered Match Count: ", len(matches_subset))

        distance = sum([m.distance for m in matches_subset])
        print("\t Distance from Key Image: ", distance)

        averagePointDistance = distance / (len(matches_subset))
        print("\t Average Distance: ", averagePointDistance)

        kp1 = []
        kp2 = []

        for match in matches_subset:
            kp1.append(self.base_features[match.trainIdx])
            kp2.append(features[match.queryIdx])

        # /self._fH #scale with _fH, if image was resized

        p1 = np.array([k.pt for k in kp1])
        p2 = np.array([k.pt for k in kp2])  # /self._fH

        H, status = cv2.findHomography(p1, p2,
                                       cv2.RANSAC,  # method
                                       5.0  # max reprojection error (1...10)
                                       )
        if status is None:
            raise Exception('no homography found')
        else:
            inliers = np.sum(status)
            print('%d / %d  inliers/matched' % (inliers, len(status)))
            inlierRatio = inliers / len(status)
            if self.minInlierRatio > inlierRatio or inliers < self.minInliers:
                raise Exception('bad fit!')

        # scale with _fH, if image was resized
        # see
        # http://answers.opencv.org/question/26173/the-relationship-between-homography-matrix-and-scaling-images/
        s = np.eye(3, 3)
        s[0, 0] = 1 / self._fH
        s[1, 1] = 1 / self._fH
        H = s.dot(H).dot(np.linalg.inv(s))

        if drawMatches:
            #             s0,s1 = img.shape
            #             out = np.empty(shape=(s0,s1,3), dtype=np.uint8)
            img = draw_matches(self.base8bit, self.base_features, img, features,
                               matches_subset[:20],  # None,#out,
                               # flags=2
                               thickness=5
                               )

        return (H, inliers, inlierRatio, averagePointDistance,
                img, features,
                descs, len(matches_subset))

Response: Find homography of the image through pattern


Instruction:
Here you have a function in Python 3, explain what it does
def patCircles(s0):
    '''make circle array'''
    arr = np.zeros((s0,s0), dtype=np.uint8)
    col = 255
    for rad in np.linspace(s0,s0/7.,10):
        cv2.circle(arr, (0,0), int(round(rad)), color=col,
                   thickness=-1, lineType=cv2.LINE_AA )
        if col:
            col = 0
        else:
            col = 255

    return arr.astype(float)

Response: make a circle array


Instruction:
Implement a function in Python 3 to
make line pattern for the given number of times

Response:
def patCrossLines(s0):
    '''make line pattern'''
    arr = np.zeros((s0,s0), dtype=np.uint8)
    col = 255
    t = int(s0/100.)
    for pos in np.logspace(0.01,1,10):
        pos = int(round((pos-0.5)*s0/10.))
        cv2.line(arr, (0,pos), (s0,pos), color=col,
                   thickness=t, lineType=cv2.LINE_AA )
        cv2.line(arr, (pos,0), (pos,s0), color=col,
                   thickness=t, lineType=cv2.LINE_AA )

    return arr.astype(float)


Instruction:
Here you have a function in Python 3, explain what it does
def removeSinglePixels(img):
    '''
    img - boolean array
    remove all pixels that have no neighbour
    '''

    gx = img.shape[0]
    gy = img.shape[1]

    for i in range(gx):
        for j in range(gy):

            if img[i, j]:

                found_neighbour = False
                for ii in range(max(0, i - 1), min(gx, i + 2)):
                    for jj in range(max(0, j - 1), min(gy, j + 2)):

                        if ii == i and jj == j:
                            continue

                        if img[ii, jj]:
                            found_neighbour = True
                            break
                    if found_neighbour:
                        break

                if not found_neighbour:
                    img[i, j] = 0

Response: remove all pixels that have no neighbour


Instruction:
Create a Python 3 function for
interpolating a 2D array into a 2D array with a cross average.

Response:
def interpolate2dStructuredCrossAvg(grid, mask, kernel=15, power=2):
    '''
    #######
    usefull if large empty areas need to be filled

    '''

    vals = np.empty(shape=4, dtype=grid.dtype)
    dist = np.empty(shape=4, dtype=np.uint16)
    weights = np.empty(shape=4, dtype=np.float32)
    valid = np.empty(shape=4, dtype=bool)

    return _calc(grid, mask, power, kernel, vals, dist, weights, valid)


Instruction:
Explain what the following Python 3 code does
def growPositions(ksize):
    '''
    return all positions around central point (0,0)
    for a given kernel size
    positions grow from smallest to biggest distances

    returns [positions] and [distances] from central cell

    '''
    i = ksize*2+1
    kk = np.ones( (i, i), dtype=bool)
    x,y = np.where(kk)
    pos = np.empty(shape=(i,i,2), dtype=int)
    pos[:,:,0]=x.reshape(i,i)-ksize
    pos[:,:,1]=y.reshape(i,i)-ksize

    dist = np.fromfunction(lambda x,y: ((x-ksize)**2
                                        +(y-ksize)**2)**0.5, (i,i))

    pos = np.dstack(
        np.unravel_index(
            np.argsort(dist.ravel()), (i, i)))[0,1:]

    pos0 = pos[:,0]
    pos1 = pos[:,1]

    return pos-ksize, dist[pos0, pos1]

Response: grows all positions around central point


Instruction:
Create a Python 3 function for
converting a QImage to a numpy array.

Response:
def qImageToArray(qimage, dtype = 'array'):
    """Convert QImage to numpy.ndarray.  The dtype defaults to uint8
    for QImage.Format_Indexed8 or `bgra_dtype` (i.e. a record array)
    for 32bit color images.  You can pass a different dtype to use, or
    'array' to get a 3D uint8 array for color images."""

    result_shape = (qimage.height(), qimage.width())
    temp_shape = (qimage.height(),
                  qimage.bytesPerLine() * 8 // qimage.depth())
    if qimage.format() in (QtGui.QImage.Format_ARGB32_Premultiplied,
                           QtGui.QImage.Format_ARGB32,
                           QtGui.QImage.Format_RGB32):
        if dtype == 'rec':
            dtype = np.dtype({'b': (np.uint8, 0),
                          'g': (np.uint8, 1),
                          'r': (np.uint8, 2),
                          'a': (np.uint8, 3)})
        elif dtype == 'array':
            dtype = np.uint8
            result_shape += (4, )
            temp_shape += (4, )
    elif qimage.format() == QtGui.QImage.Format_Indexed8:
        dtype = np.uint8
    else:
        raise ValueError("qimage2numpy only supports 32bit and 8bit images")
    # FIXME: raise error if alignment does not match
    buf = qimage.bits().asstring(qimage.byteCount())
    result = np.frombuffer(buf, dtype).reshape(temp_shape)
    if result_shape != temp_shape:
        result = result[:,:result_shape[1]]
    if qimage.format() == QtGui.QImage.Format_RGB32 and dtype == np.uint8:
        #case byteorder == 'little'
        result = result[...,:3]
        #byteorder == 'big' -> get ARGB
        result = result[...,::-1]
    return result


Instruction:
Write a Python 3 function that can
create a new array with variable ksize in y

Response:
def varYSizeGaussianFilter(arr, stdyrange, stdx=0,
                           modex='wrap', modey='reflect'):
    '''
    applies gaussian_filter on input array
    but allowing variable ksize in y

    stdyrange(int) -> maximum ksize - ksizes will increase from 0 to given value
    stdyrange(tuple,list) -> minimum and maximum size as (mn,mx)
    stdyrange(np.array) -> all different ksizes in y
    '''
    assert arr.ndim == 2, 'only works on 2d arrays at the moment'

    s0 = arr.shape[0]

    #create stdys:
    if isinstance(stdyrange, np.ndarray):
        assert len(stdyrange)==s0, '[stdyrange] needs to have same length as [arr]'
        stdys = stdyrange
    else:
        if type(stdyrange) not in (list, tuple):
            stdyrange = (0,stdyrange)
        mn,mx = stdyrange
        stdys  = np.linspace(mn,mx,s0)

    #prepare array for convolution:
    kx = int(stdx*2.5)
    kx += 1-kx%2
    ky = int(mx*2.5)
    ky += 1-ky%2
    arr2 = extendArrayForConvolution(arr, (kx, ky), modex, modey)

    #create convolution kernels:
    inp = np.zeros((ky,kx))
    inp[ky//2, kx//2] = 1
    kernels = np.empty((s0,ky,kx))
    for i in range(s0):
        stdy = stdys[i]
        kernels[i] = gaussian_filter(inp, (stdy,stdx))

    out = np.empty_like(arr)
    _2dConvolutionYdependentKernel(arr2, out, kernels)
    return out


Instruction:
Given the following Python 3 function, write the documentation
def numbaGaussian2d(psf, sy, sx):
    '''
    2d Gaussian to be used in numba code
    '''
    ps0, ps1 = psf.shape
    c0,c1 = ps0//2, ps1//2
    ssx = 2*sx**2
    ssy = 2*sy**2
    for i in range(ps0):
        for j in range(ps1):
            psf[i,j]=exp( -( (i-c0)**2/ssy
                            +(j-c1)**2/ssx) )
    psf/=psf.sum()

Response: Returns a 2d Gaussian that is used in numba code


Instruction:
Given the following Python 3 function, write the documentation
def estimateBackgroundLevel(img, image_is_artefact_free=False,
                            min_rel_size=0.05, max_abs_size=11):
    '''
    estimate background level through finding the most homogeneous area
    and take its average

    min_size - relative size of the examined area
    '''

    s0,s1 = img.shape[:2]
    s = min(max_abs_size, int(max(s0,s1)*min_rel_size))
    arr = np.zeros(shape=(s0-2*s, s1-2*s), dtype=img.dtype)

    #fill arr:
    _spatialStd(img, arr, s)
    #most homogeneous area:
    i,j = np.unravel_index(arr.argmin(), arr.shape)
    sub = img[int(i+0.5*s):int(i+s*1.5),
              int(j+s*0.5):int(j+s*1.5)]

    return np.median(sub)

Response: estimate background level through finding the most homogeneous area and take its average


Instruction:
Write a Python 3 script for
returning the angular dependent EL emissivity of a PV module

Response:
def EL_Si_module():
    '''
    returns angular dependent EL emissivity of a PV module

    calculated of nanmedian(persp-corrected EL module/reference module)

    published in K. Bedrich: Quantitative Electroluminescence Measurement on PV devices
                 PhD Thesis, 2017
    '''
    arr = np.array([
                    [2.5, 1.00281 ],
                    [7.5, 1.00238 ],
                    [12.5, 1.00174],
                    [17.5, 1.00204 ],
                    [22.5, 1.00054 ],
                    [27.5, 0.998255],
                    [32.5, 0.995351],
                    [37.5, 0.991246],
                    [42.5, 0.985304],
                    [47.5, 0.975338],
                    [52.5, 0.960455],
                    [57.5, 0.937544],
                    [62.5, 0.900607],
                    [67.5, 0.844636],
                    [72.5, 0.735028],
                    [77.5, 0.57492 ],
                    [82.5, 0.263214],
                    [87.5, 0.123062]
                    ])

    angles = arr[:,0]
    vals = arr[:,1]

    vals[vals>1]=1
    return angles, vals


Instruction:
Write a Python 3 function that can
reflect temperature for 250DEG Glass published in IEC 62446-3 TS: Photovoltaic (PV) systems - Requirements for testing, documentation and maintenance - Part 3: Outdoor infrared thermography of photovoltaic modules and plants p Page 12

Response:
def TG_glass():
    '''
    reflected temperature for 250DEG Glass
    published in IEC 62446-3 TS: Photovoltaic (PV) systems
    - Requirements for testing, documentation and maintenance
    - Part 3: Outdoor infrared thermography of photovoltaic modules
      and plants p Page 12
    '''
    vals = np.array([(80,0.88),
                     (75,0.88),
                     (70,0.88),
                     (65,0.88),
                     (60,0.88),
                     (55,0.88),
                     (50,0.87),
                     (45,0.86),
                     (40,0.85),
                     (35,0.83),
                     (30,0.80),
                     (25,0.76),
                     (20,0.7),
                     (15,0.60),
                     (10,0.44)])
    #invert angle reference:
    vals[:,0]=90-vals[:,0]
    #make emissivity relative:
    vals[:,1]/=vals[0,1]
    return vals[:,0], vals[:,1]


Instruction:
How would you implement a function in Python 3 that
returns the sensitivity of the homogeneously illuminated images.

Response:
def sensitivity(imgs, bg=None):
    '''
    Extract pixel sensitivity from a set of homogeneously illuminated images

    This method is detailed in Section 5 of:
    ---
    K.Bedrich, M.Bokalic et al.:
    ELECTROLUMINESCENCE IMAGING OF PV DEVICES:
    ADVANCED FLAT FIELD CALIBRATION,2017
    ---

    '''
    bg = getBackground(bg)
    for n, i in enumerate(imgs):
        i = imread(i, dtype=float)
        i -= bg
        smooth = fastMean(median_filter(i, 3))
        i /= smooth
        if n == 0:
            out = i
        else:
            out += i
    out /= (n + 1)
    return out


Instruction:
How would you explain what the following Python 3 function does
def navierStokes2d(u, v, p, dt, nt, rho, nu,
                boundaryConditionUV,
                boundardConditionP, nit=100):
    '''
    solves the Navier-Stokes equation for incompressible flow
    one a regular 2d grid

    u,v,p --> initial velocity(u,v) and pressure(p) maps

    dt --> time step
    nt --> number of time steps to caluclate

    rho, nu --> material constants

    nit --> number of iteration to solve the pressure field
    '''
    #next u, v, p maps:
    un = np.empty_like(u)
    vn = np.empty_like(v)
    pn = np.empty_like(p)
    #poisson equation ==> laplace term = b[source term]
    b = np.zeros_like(p)

    ny,nx = p.shape
    #cell size:
    dx = 2 / (nx - 1)
    dy = 2 / (ny - 1)

    #next time step:
    for _ in range(nt):
        un[:] = u
        vn[:] = v
        #pressure
        _buildB(b, rho, dt, u, v, dx, dy)
        for _ in range(nit):
            _pressurePoisson(p, pn, dx, dy, b)
            boundardConditionP(p)
        #UV
        _calcUV(u, v, un, p,vn,  dt, dx, dy, rho, nu)
        boundaryConditionUV(u,v)

    return u, v, p

Response: Returns a new 2d grid with the same initial velocity and pressure.


Instruction:
Implement a function in Python 3 to
add noise to the image and return the new image

Response:
def addNoise(img, snr=25, rShot=0.5):
    '''
    adds Gaussian (thermal) and shot noise to [img]

    [img] is assumed to be noise free

    [rShot] - shot noise ratio relative to all noise
    '''
    s0, s1 = img.shape[:2]

    m = img.mean()
    if np.isnan(m):
        m = np.nanmean(img)
    assert m != 0, 'image mean cannot be zero'

    img = img / m
    noise = np.random.normal(size=s0 * s1).reshape(s0, s1)
    if rShot > 0:
        noise *= (rShot * img**0.5 + 1)
    noise /= np.nanstd(noise)
    noise[np.isnan(noise)] = 0
    return m * (img + noise / snr)


Instruction:
Create a Python 3 function for
returning an array of the given shape coarse - maximum of the given array.

Response:
def coarseMaximum(arr, shape):
    '''
    return an array of [shape]
    where every cell equals the localised maximum of the given array [arr]
    at the same (scalled) position
    '''
    ss0, ss1 = shape
    s0, s1 = arr.shape

    pos0 = linspace2(0, s0, ss0, dtype=int)
    pos1 = linspace2(0, s1, ss1, dtype=int)

    k0 = pos0[0]
    k1 = pos1[0]

    out = np.empty(shape, dtype=arr.dtype)
    _calc(arr, out, pos0, pos1, k0, k1, ss0, ss1)
    return out


Instruction:
Can you implement a function in Python 3 that
returns the angle of view of the aperture given the specified parameters

Response:
def angleOfView(XY, shape=None, a=None, f=None, D=None, center=None):
    '''
    Another vignetting equation from:
    M. Koentges, M. Siebert, and D. Hinken, "Quantitative analysis of PV-modules by electroluminescence images for quality control"
        2009
    f --> Focal length
    D --> Diameter of the aperture
        BOTH, D AND f NEED TO HAVE SAME UNIT [PX, mm ...]
    a --> Angular aperture

    center -> optical center [y,x]
    '''
    if a is None:
        assert f is not None and D is not None
        #https://en.wikipedia.org/wiki/Angular_aperture
        a = 2*np.arctan2(D/2,f)

    x,y = XY

    try:
        c0,c1 = center
    except:
        s0,s1 = shape
        c0,c1 = s0/2, s1/2

    rx = (x-c0)**2
    ry = (y-c1)**2

    return  1 / (1+np.tan(a)*((rx+ry)/c0))**0.5


Instruction:
Create a Python 3 function to
return the angle of view between two camera modules.

Response:
def angleOfView2(x,y, b, x0=None,y0=None):
    '''
    Corrected AngleOfView equation by Koentges (via mail from 14/02/2017)
    b --> distance between the camera and the module in m
    x0 --> viewable with in the module plane of the camera in m
    y0 --> viewable height in the module plane of the camera in m
    x,y --> pixel position [m] from top left
    '''
    if x0 is None:
        x0 = x[-1,-1]
    if y0 is None:
        y0 = y[-1,-1]
    return np.cos( np.arctan( np.sqrt(
                    ( (x-x0/2)**2+(y-y0/2)**2 ) ) /b  ) )


Instruction:
Can you implement a function in Python 3 that
returns a 2d grid of nCells lines from a list of edges.

Response:
def gridLinesFromVertices(edges, nCells, subgrid=None, dtype=float):
    """
    ###TODO  REDO TXT

    OPTIONAL:
    subgrid = ([x],[y]) --> relative positions
        e.g. subgrid = ( (0.3,0.7), () )
             --> two subgrid lines in x - nothing in y

    Returns:
        horiz,vert -> arrays of (x,y) poly-lines

    if subgrid != None, Returns:
            horiz,vert, subhoriz, subvert

    #######
    creates a regular 2d grid from given edge points (4*(x0,y0))
    and number of cells in x and y

    Returns:
        tuple(4lists): horizontal and vertical lines as (x0,y0,x1,y1)
    """

    nx, ny = nCells

    y, x = np.mgrid[0.:ny + 1, 0.:nx + 1]

    src = np.float32([[0, 0], [nx, 0], [nx, ny], [0, ny]])
    dst = sortCorners(edges).astype(np.float32)

    homography = cv2.getPerspectiveTransform(src, dst)

    pts = np.float32((x.flatten(), y.flatten())).T
    pts = pts.reshape(1, *pts.shape)

    pts2 = cv2.perspectiveTransform(pts, homography)[0]

    horiz = pts2.reshape(ny + 1, nx + 1, 2)
    vert = np.swapaxes(horiz, 0, 1)

    subh, subv = [], []
    if subgrid is not None:
        sh, sv = subgrid

        if len(sh):
            subh = np.empty(shape=(ny * len(sh), nx + 1, 2), dtype=np.float32)
            last_si = 0
            for n, si in enumerate(sh):
                spts = pts[:, :-(nx + 1)]
                spts[..., 1] += si - last_si
                last_si = si
                spts2 = cv2.perspectiveTransform(spts, homography)[0]
                subh[n::len(sh)] = spts2.reshape(ny, nx + 1, 2)
        if len(sv):
            subv = np.empty(shape=(ny + 1, nx * len(sv), 2), dtype=np.float32)
            last_si = 0
            sspts = pts.reshape(1, ny + 1, nx + 1, 2)
            sspts = sspts[:, :, :-1]

            sspts = sspts.reshape(1, (ny + 1) * nx, 2)
            for n, si in enumerate(sv):
                sspts[..., 0] += si - last_si
                last_si = si
                spts2 = cv2.perspectiveTransform(sspts, homography)[0]
                subv[:, n::len(sv)] = spts2.reshape(ny + 1, nx, 2)
            subv = np.swapaxes(subv, 0, 1)
    return [horiz, vert, subh, subv]


Instruction:
Write a Python 3 script for
returning the most recent object resolution as [ line pairs / mm ] where MTF = 50% of the given time frame.

Response:
def MTF50(self, MTFx,MTFy):
        '''
        return object resolution as [line pairs/mm]
               where MTF=50%
               see http://www.imatest.com/docs/sharpness/
        '''
        if self.mtf_x is None:
            self.MTF()
        f = UnivariateSpline(self.mtf_x, self.mtf_y-0.5)
        return f.roots()[0]


Instruction:
How would you explain what the following Python 3 function does
def MTF(self, px_per_mm):
        '''
        px_per_mm = cam_resolution / image_size
        '''
        res = 100 #numeric resolution
        r = 4 #range +-r*std

        #size of 1 px:
        px_size = 1 / px_per_mm

        #standard deviation of the point-spread-function (PSF) as normal distributed:
        std = self.std*px_size #transform standard deviation from [px] to [mm]

        x = np.linspace(-r*std,r*std, res)
        #line spread function:
        lsf = self.gaussian1d(x, 1, 0, std)
        #MTF defined as Fourier transform of the line spread function:
            #abs() because result is complex
        y = abs(np.fft.fft(lsf))
            #normalize fft so that max = 1
        y /= np.max(y)
            #step length between xn and xn+1
        dstep = r*std/res
            # Fourier frequencies - here: line pairs(cycles) per mm
        freq = np.fft.fftfreq(lsf.size, dstep)
        #limit mtf between [0-px_per_mm]:
        i = np.argmax(freq>px_per_mm)
        self.mtf_x = freq[:i]
        self.mtf_y = y[:i]
        return self.mtf_x, self.mtf_y

Response: Returns the MTF and the MTF_y values for the most recent image.


Instruction:
Implement a function in Python 3 to
return the intensity based uncertainty due to the unsharpness of the image as standard deviation

Response:
def uncertaintyMap(self, psf, method='convolve', fitParams=None):
        '''
        return the intensity based uncertainty due to the unsharpness of the image
        as standard deviation

        method = ['convolve' , 'unsupervised_wiener']
                    latter one also returns the reconstructed image (deconvolution)
        '''

        #ignore background:
        #img[img<0]=0
        ###noise should not influence sharpness uncertainty:
        ##img = median_filter(img, 3)

        # decrease noise in order not to overestimate result:
        img = scaleSignal(self.img, fitParams=fitParams)

        if method == 'convolve':
            #print 'convolve'
            blurred = convolve2d(img, psf, 'same')
            m = abs(img-blurred) / abs(img + blurred)
            m = np.nan_to_num(m)
            m*=self.std**2
            m[m>1]=1
            self.blur_distortion = m
            np.save('blurred', blurred)
            return m
        else:
            restored = unsupervised_wiener(img, psf)[0]
            m = abs(img-restored) / abs(img + restored)
            m = np.nan_to_num(m)
            m*=self.std**2
            m[m>1]=1
            self.blur_distortion = m
            return m, restored


Instruction:
Can you generate the documentation for the following Python 3 function
def stdDev(self):
        '''
        get the standard deviation
        from the PSF is evaluated as 2d Gaussian
        '''
        if self._corrPsf is None:
            self.psf()
        p = self._corrPsf.copy()
        mn = p.min()
        p[p<0.05*p.max()] = mn
        p-=mn
        p/=p.sum()

        x,y = self._psfGridCoords()
        x = x.flatten()
        y = y.flatten()

        guess = (1,1,0)

        param, _ = curve_fit(self._fn, (x,y), p.flatten(), guess)

        self._fitParam = param
        stdx,stdy =  param[:2]
        self._std = (stdx+stdy) / 2

        return self._std

Response:
get the standard deviation of the current time - domain entry from the PSF is evaluated as 2d Gaussian
        self. _corrPsf is None


Instruction:
Can you write a function in Python 3 where it
interpolates all values in a 2D structured IDW grid with inverse distance weighted interpolation of all values in a 2D structured IDW grid

Response:
def interpolate2dStructuredIDW(grid, mask, kernel=15, power=2, fx=1, fy=1):
    '''
    replace all values in [grid] indicated by [mask]
    with the inverse distance weighted interpolation of all values within
    px+-kernel
    [power] -> distance weighting factor: 1/distance**[power]

    '''
    weights = np.empty(shape=((2*kernel+1,2*kernel+1)))
    for xi in range(-kernel,kernel+1):
        for yi in range(-kernel,kernel+1):
            dist = ((fx*xi)**2+(fy*yi)**2)
            if dist:
                weights[xi+kernel,yi+kernel] = 1 / dist**(0.5*power)

    return _calc(grid, mask, kernel, weights)


Instruction:
Make a summary of the following Python 3 code
def temporalSignalStability(imgs, times, down_scale_factor=1):
    '''
    (Electroluminescence) signal is not stable over time
        especially next to cracks.
    This function takes a set of images
    and returns parameters, needed to transform uncertainty
        to other exposure times using [adjustUncertToExposureTime]

    return [signal uncertainty] obtained from linear fit to [imgs]
           [average event length]
           [ascent],[offset] of linear fit

    --------
    [imgs] --> corrected EL images captured in sequence

    [times] --> absolute measurement times of all [imgs]
                e.g. every image was taken every 60 sec, then
                times=60,120,180...
    [down_scale_factor] --> down scale [imgs] to speed up process
    -------

    More information can be found at ...
    ----
    K.Bedrich: Quantitative Electroluminescence Imaging, PhD Thesis, 2017
    Subsection 5.1.4.3: Exposure Time Dependency
    ----
    '''
    imgs = np.asarray(imgs)
    s0, s1, s2 = imgs.shape

    #down scale imgs to speed up process:
    if down_scale_factor > 1:
        s1 //= down_scale_factor
        s2 //= down_scale_factor
        imgs2 = np.empty(shape=(s0, s1, s2))
        for n, c in enumerate(imgs):
            imgs2[n] = cv2.resize(c, (s2, s1), interpolation=cv2.INTER_AREA)
        imgs = imgs2

    # linear fit for every point in image set:
    ascent, offset, error = linRegressUsingMasked2dArrays(
                                times, imgs, calcError=True)

    # functionally obtained [imgs]:
    fn_imgs = np.array([offset + t * ascent for t in times])
    #difference between [imgs] for fit result:
    diff = imgs - fn_imgs
    diff = median_filter(diff, 5)

    error_t = np.tile(error, (s0, 1, 1))
    # find events:
    evt = (np.abs(diff) > 0.5 * error_t)
    # calc average event length:
    avlen = _calcAvgLen(evt, np.empty(shape=evt.shape[1:]))

    #cannot calc event length smaller exposure time, so:
    i = avlen == 0
    avlen = maskedFilter(avlen, mask=i, fn='mean', ksize=7, fill_mask=False)
    # remove single px:
    i = maximum_filter(i, 3)
    avlen[i] = 0
    avlen = maximum_filter(avlen, 3)

    i = avlen == 0
    avlen = median_filter(avlen, 3)
    avlen[i] = 0

    return error, avlen, ascent, offset

Response: This function takes a set of images and returns a set of time - dependent Electrouminescence signal parameters needed to transform the uncertainty of each image to other exposure times using adjustUncertToExposureTime.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def vignettingFromSpotAverage(
        images, bgImages=None, averageSpot=True, thresh=None):
    '''
    [images] --> list of images containing
                 small bright spots generated by the same
                 device images at different positions within image plane
            depending on the calibrated waveband the device can be
            a LCD display or PV 1-cell mini module

    This method is referred as 'Method B' in
    ---
    K.Bedrich, M.Bokalic et al.:
    ELECTROLUMINESCENCE IMAGING OF PV DEVICES:
    ADVANCED FLAT FIELD CALIBRATION,2017
    ---

    Args:
        averageSpot(bool): True: take only the average intensity of each spot
        thresh(float): marks the minimum spot value
                       (estimated with Otsus method otherwise)

    Returns:
        * array to be post processed
        * image mask containing valid positions
    '''

    fitimg, mask = None, None
    mx = 0
    for c, img in enumerate(images):
        print('%s/%s' % (c + 1, len(images)))

        if c == 0:
            avgBg = getBackground2(bgImages, img)
        img = imread(img, dtype=float)
        img -= avgBg
        # init:
        if fitimg is None:
            fitimg = np.zeros_like(img)
            mask = np.zeros_like(img, dtype=bool)
        # find spot:
        if thresh is None:
            t = threshold_otsu(img)
        else:
            t = thresh

        # take brightest spot
        spots, n = label(minimum_filter(img > t, 3),
                         background=0, return_num=True)
        spot_sizes = [(spots == i).sum() for i in range(1, n + 1)]

        try:
            spot = (spots == np.argmax(spot_sizes) + 1)
        except ValueError:
            print("couldn't find spot in image")
            continue

        if averageSpot:
            spot = np.rint(center_of_mass(spot)).astype(int)
            mx2 = img[spot].max()
        else:
            mx2 = img[spot].mean()
        fitimg[spot] = img[spot]
        mask[spot] = 1

        if mx2 > mx:
            mx = mx2

    # scale [0...1]:
    fitimg /= mx

    return fitimg, mask

Response:
Returns a new image mask containing the small bright spots generated by the same
AttributeNames image mask containing valid positions and background images that are at different positions within the same
AttributeNames image mask containing valid positions.