Source code for emva1288.camera.routines

import numpy as np


[docs]def nearest_value(value, array): """Returns the nearest value in vals. Parameters ---------- value : float The value we want to get as near as possible. array : array_like The array containing the available values to get near the value. Returns ------- The nearest element of `array` to `value`. """ # http://stackoverflow.com/questions/2566412/find-nearest-value-in-numpy-array idx = (np.abs(array - value)).argmin() return array[idx]
[docs]def get_photons(exposure, radiance, pixel_area, wavelength, f_number): """Get the number of photons hitting one pixel. Parameters ---------- exposure : float The pixel exposure time (in ns) to the light. radiance : float or array_like The radiance hitting the sensor (in W/sr/cm^2). pixel_area : float The pixel area in um ^ 2 f_number : float The f number of the setup. Returns ------- float : The number of photons that hit the pixel. """ h = 6.63e-34 c = 3.00e8 w = wavelength * 1e-9 t = exposure * 1e-9 a = pixel_area * 1e-12 # Actual mathematics:- # irradiance = radiance * ((np.pi / (1 + ((2 * f_number) ** 2))) * w) # photons = irradiance * a * t * w / (h * c) # To optimize the calculation time first all the scalars are seperately # calculated # The matrix (w and radiance) multiplication order is the same. scalar = ((a * t) * np.pi / ((h * c) * (1 + ((2 * f_number) ** 2)))) return radiance * (scalar * (w * w))
[docs]def get_radiance(exposure, photons, pixel_area, f_number, wavelength): """From the number of photons, get the radiance hitting a pixel. Parameters ---------- exposure : float The pixel exposure time to the light (in ns). wavelength : float The photons' wavelength (in nm). photons : float The number of photons that hit the pixel. pixel_area : float The pixel area in um^2. f_number : float The f number of the setup. Returns ------- float : The radiance that hit the pixel and gave the number of photons. """ h = 6.63e-34 c = 3.00e8 w = wavelength * 1e-9 t = exposure * 1e-9 a = pixel_area * 1e-12 # p = j * a * t * w / (h * c) # j = np.pi * radiance / (1 + ((2 * f) ** 2)) * delta_w # Actual mathematics:- # irradiance = photons * h * c / (a * t * w) # radiance = irradiance * (1 + ((2 * f_number) ** 2)) / np.pi / w # To optimize the calculation time first all the scalars are seperately # calculated # The matrix (w and photon) multiplication order is the same. scalar = ((h * c) * (1 + ((2 * f_number) ** 2)) / (np.pi * a * t)) return photons * (scalar / (w * w))
[docs]def get_tile(arr, height, width): """From an array with a pattern, repeat the array to fill an 2D array with the given dimention. Parameters ---------- arr : array The pattern wanted to be replecate in a bigger shape. height : int The height of the array wanted in return. width : int The width of the array wanted in return. Returns ------- array : The pattern given(arr) replecate in the size given. """ # To reduce the execution time, we will reduce the width an height of # arguments given in the np.tile function. We take the width and height # wanted, dived by size of the array dimention. By careful, for an 2D # array, the shape[1] is the width and the shape[0] is the height, but # for an 1D array the height is 1 so the shape[0] is the width. # The size need to be a int, so we use np.floor to arroud the division # and add one to be sure than the size is not to short. To be sure to # return the array with the good shape we use [:height, :width]. # if the array given is an 1D array. if len(arr.shape) == 1: # the height suppose to be 1 h_r = height # only the width change w_r = int(np.floor(width / arr.shape[0]) + 1) # if the array given is a 2D array. if len(arr.shape) == 2: # number of repetition needed h_r = int(np.floor(height / arr.shape[0]) + 1) w_r = int(np.floor(width / arr.shape[1]) + 1) # the case if the array given is an multidimentinal array. # else: # TODO: make a error message for to many dimention. # create a array with the dimension given and the array given tile = np.tile(arr, (h_r, w_r))[:height, :width] # if we want an 1D array so with a shape of (width,) and not (1, width) if height == 1: # tile = array([[0,0,0,...]]) so we only want the first line. return tile[0] return tile
[docs]def get_bayer_filter(t00, t01, t10, t11, width, height, w): """From different values of transmition and the size, get a bayer filter. +-----------+-----------+-----------+-----------+-----------+-----------+ | Pattern | +===========+===========+===========+===========+===========+===========+ | This case | Example | Suggested values | +-----------+-----------+-----------+-----------+-----------+-----------+ | t00 | t01 | G1 | R | 1 | 0.15 | +-----------+-----------+-----------+-----------+-----------+-----------+ | t10 | t11 | B | G2 | 0.02 | 1 | +-----------+-----------+-----------+-----------+-----------+-----------+ Where *G1* and *G2* are transmition values for a green filter, *R* for a red filter and *B* for a blue filter. The suggested values are values for a standard bayer filter like the example. Parameters ---------- t00 : int The wavelength of the first pixel. t01 : int The wavelength of the second pixel. t10 : int The wavelength of the third pixel. t11 : int The wavelength of the fourth pixel. width : int The number of columns in the the image. height : int The number of rows in the image. w : array_like Array of the wavelengths to be returned Returns ------- array : The array with the bayer filter of the size gived. """ t00 = poisson(w, int(t00)) t01 = poisson(w, int(t01)) t10 = poisson(w, int(t10)) t11 = poisson(w, int(t11)) pattern_rep = np.array([[t00/np.max(t00), t01/np.max(t01)], [t10/np.max(t10), t11/np.max(t11)]]) b_filter = array_tile(pattern_rep, height, width) return b_filter
[docs]def array_tile(array, height, width): h, w = ((array.shape[:2]) if array.ndim == 3 else (1, array.shape[-1]) if array.ndim == 2 else (1, 1)) return np.tile(array, (int(height/h), int(width/w), 1))
[docs]def poisson(x, loc, mu=1000): x = x.astype(int) + (mu - loc) ln = [v * np.log(mu) - mu - np.sum([np.log(i) for i in range(1, v)]) for v in x] return np.exp(ln)
[docs]class Qe(object): def __init__(self, qe=None, wavelength=np.linspace(400, 800, 100), width=640, height=480, filter=None): self._width = width self._height = height self._qe = qe self._w = wavelength if self._qe is None: self._qe = self.gen_qe() if self._qe.shape != (self._height, self._width, len(self._w)): raise ValueError('qe dimensions mismatch %s != %s' % self._qe.shape, (self._height, self._width, len(self._w))) if filter is not None: self._qe *= filter @property def w(self): return self._w @property def qe(self): return self._qe
[docs] def gen_qe(self): """Simulate quantum efficiency for a specific wavelengths. Returns ------- float : The simulated quantum efficiency. """ # For the time being we just simulate a simple gaussian s = 0.5 u = 0.0 min_ = 350 max_ = 800 def gauss(w): w = -1 + (w) / (max_ - min_) qe = -.1 + np.exp((-(w - u) ** 2) / (2 * s ** 2)) / (np.sqrt(np.pi * 2 * s ** 2)) return np.max([qe, 0]) qe_ = np.array([(gauss(w)) for w in self._w]) qe = np.zeros((self._height, self._width, len(self._w))) qe[:, :] = qe_ return qe