class CameraTestCase(unittest.TestCase): def setUp(self): self.cam = Camera() def tearDown(self): del self.cam def test_img(self): img = self.cam.grab(0) self.assertEqual((self.cam.height, self.cam.width), np.shape(img)) def test_radiance(self): img1 = self.cam.grab(0) img2 = self.cam.grab(self.cam.get_radiance_for(mean=250)) self.assertLess(img1.mean(), img2.mean())
def test_bayer_layer(self): # Init the parameters h, w = [480, 640] wavelength = np.linspace(400, 800, 100) transmission_red = 670 transmission_blue = 450 transmission_green = 550 b_layer = routines.get_bayer_filter(transmission_green, transmission_red, transmission_blue, transmission_green, w, h, wavelength) qe = routines.Qe(filter=b_layer) cam = Camera(width=w, height=h, qe=qe) # Initiata a cam without a bayer filter cam_d = Camera(width=w, height=h) # Set the camera for testing the layer target = cam.img_max / 2 # Get the radiance to grab from the second cam. The output radiance # is affected by qe, so the bayer_filter as well radiance = cam_d.get_radiance_for(mean=target) img = cam.grab(radiance) green_filter = np.tile([[0, 1], [1, 0]], (int(h/2), int(w/2))) blue_filter = np.tile([[1, 0], [1, 1]], (int(h/2), int(w/2))) red_filter = np.tile([[1, 1], [0, 1]], (int(h/2), int(w/2))) gf = b_layer[0, 0, :].mean() rf = b_layer[0, 1, :].mean() bf = b_layer[1, 0, :].mean() # Test if the mean of the green it's 100% of the target +/- 5 self.assertAlmostEqual(np.ma.masked_array( img, mask=green_filter).mean(), target * gf, delta=10, msg="green not in range") # Test if the mean of the red it's 15% of the target +/- 5 self.assertAlmostEqual(np.ma.masked_array( img, mask=red_filter).mean(), target * rf, delta=10, msg="red not in range") # Test if the mean of the blue it's 2% of the target +/- 5 self.assertAlmostEqual(np.ma.masked_array( img, mask=blue_filter).mean(), target * bf, delta=10, msg="blue not in range")
def test_prnu(self): # Init the parameters h, w = [480, 640] rep = 200 value8 = 3 # create the pattern of the prnu prnu_array = np.ones((8)) prnu_array[-1] = value8 prnu = routines.get_tile(prnu_array, h, w) # Set the camera for testing the prnu cam = Camera(width=w, height=h, prnu=prnu) var = np.sqrt(cam._sigma2_dark_0) target = cam.img_max / 2 # The target (top_target) is the multiplication of the target # (what we expect without prnu) and the value8(prnu). We can do it # because if we look at the _u_e function in emva1288.camera.camera # the prnu affect the QE with a multiplication. So if we just # multiplied the target by the prnu it's the same thing. # But this value can go over the maximal value for one pixel, this # is why we use the min() function to take the maximal value than the # camera can take. top_target = min(target * value8, cam.img_max) radiance = cam.get_radiance_for(mean=target) img = cam.grab(radiance) # create the mask prnu_mask = np.zeros((8)) prnu_mask[-1] = 1 prnu_mask_resize = routines.get_tile(prnu_mask, h, w) prnu_non_mask = np.ones((8)) prnu_non_mask[-1] = 0 prnu_non_mask_resize = routines.get_tile(prnu_non_mask, h, w) # Test if the mean it's 100% of the target +/- variance self.assertAlmostEqual(np.ma.masked_array( img, mask=prnu_mask_resize).mean(), target, delta=var, msg="values are not in range") # Test if the mean of the 8th value it's value8 # multiplied be the target +/- variance self.assertAlmostEqual(np.ma.masked_array( img, mask=prnu_non_mask_resize).mean(), top_target, delta=var, msg="8th value it's not in range")
def test_dsnu(self): # Init the parameters h, w = [480, 640] value8 = 5 rep = 200 # create the pattern of the dsnu dsnu_array = np.ones((8)) dsnu_array[-1] = value8 dsnu = routines.get_tile(dsnu_array, h, w) # Set the camera for testing the dsnu cam = Camera(width=w, height=h, dsnu=dsnu) var = np.sqrt(cam._sigma2_dark_0) # The target is the number of electrons who are not affected # by the dsnu. To resume, we suppose to observe is a combinaison of # electrons from the dark signal and the temperature. The total need # to be multiplied by the gain of the system (K). # for more eplination see the grab function in emva1288.camera.camera target = cam.K * (cam._dark_signal_0 + cam._u_therm()) # Here the target (top_target) is the part who is affected by # the dsnu. Physicaly, the same phenomen append but this time the # dark signal is NonUniform so thw value who represent the dsnu is # added to the dark signal befor the multiplication of the gain. top_target = cam.K * (cam._dark_signal_0 + cam._u_therm() + value8) img = cam.grab(0) # create the mask dsnu_mask = np.zeros((8)) dsnu_mask[-1] = 1 dsnu_mask_resize = routines.get_tile(dsnu_mask, h, w) dsnu_non_mask = np.ones((8)) dsnu_non_mask[-1] = 0 dsnu_non_mask_resize = routines.get_tile(dsnu_non_mask, h, w) # Test if the mean it's 100% of the target +/- variance self.assertAlmostEqual(np.ma.masked_array( img, mask=dsnu_mask_resize).mean(), target, delta=var, msg="values are not in range") # Test if the mean of the 8th value it's value8 # multiplied be the target +/- variance self.assertAlmostEqual(np.ma.masked_array( img, mask=dsnu_non_mask_resize).mean(), top_target, delta=var, msg="8th value it's not in range")
def test_bayer_layer(self): # Init the parameters h = 480 w = 640 transmition_red = 0.15 transmition_blue = 0.02 transmition_green = 1. b_layer = routines.get_bayer_filter(transmition_green, transmition_red, transmition_blue, transmition_green, w, h) # Test if the b_layer have the same shape than what we give it self.assertEqual((h, w), b_layer.shape) # Set the camera for testing the layer cam = Camera(width=w, height=h, radiance_factor=b_layer) target = cam.img_max / 2 radiance = cam.get_radiance_for(mean=target) img = cam.grab(radiance) green_filter = routines.get_bayer_filter(0, 1, 1, 0, w, h) blue_filter = routines.get_bayer_filter(1, 1, 0, 1, w, h) red_filter = routines.get_bayer_filter(1, 0, 1, 1, w, h) # Test if the mean of the green it's 100% of the target +/- 5% self.assertAlmostEqual(np.ma.masked_array( img, mask=green_filter).mean(), target, delta=5.0, msg="green not in range") # Test if the mean of the red it's 15% of the target +/- 5% self.assertAlmostEqual(np.ma.masked_array( img, mask=red_filter).mean(), target*transmition_red, delta=5.0, msg="red not in range") # Test if the mean of the green it's 2% of the target +/- 5% self.assertAlmostEqual(np.ma.masked_array( img, mask=blue_filter).mean(), target*transmition_blue, delta=5.0, msg="blue not in range")
class DatasetGenerator: """Dataset generator. Creates a descriptor file and the corresponding linked images for a a exposure variant test example according to the emva1288 standart. The images are created using the implemented camera in the emva module. """ def __init__( self, steps=100, L=50, version='3.0', image_format='png', # best memory consumption outdir=None, # directory where to save the dataset radiance_min=None, radiance_max=None, exposure_fixed=None, **kwargs): """Dataset generator init method. The generator uses a :class:`~emva1288.camera.points_generator.PointsGenerator` object to create the operation points. It then grabs the images for these points using a :class:`~emva1288.camera.camera.Camera` simulator object. The camera is intialized according to the given kwargs. Then, after getting the test points, it :meth:`makes <run_test>` the images with it by changing its exposure time, or the radiation and :meth:`saves <save_images>` the images and the descriptor file. Parameters ---------- L : int, optional The number of image taken during a spatial test point. version : str, optional Data version to add in descriptor file. image_format : str, optional The image's format when they are saved. outdir : str, optional The output directory where the descriptor file and the images will be saved. If None, it will create a tempory directory that will be deleted (and its contents) when the dataset generator object is deleted. radiance_min : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. radiance_max : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. exposure_fixed : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. kwargs : All other kwargs are passed to the camera. """ self._steps = steps # number of points to take self.cam = Cam(**kwargs) # set the camera parameters for the test self.cam.exposure = self.cam.exposure_min # If no blackoffset/gain are specified find them according to standard if 'blackoffset' not in kwargs: self.cam.blackoffset = _get_emva_blackoffset(self.cam) if 'K' not in kwargs: self.cam.K = _get_emva_gain(self.cam) # create test points points = PointsGenerator(self.cam, radiance_min=radiance_min, radiance_max=radiance_max, exposure_fixed=exposure_fixed, steps=self._steps) self._points = points.points self._L = L # number of images to take for a spatial test self._version = version # data version # store image format self._image_format = image_format # images will be saved one at a time during the generation into outdir self.outdir = outdir # create temporary directory to store the dataset if outdir is None: self.tempdir = tempfile.TemporaryDirectory() self.outdir = self.tempdir.name # create dir where images will be saved os.makedirs(os.path.join(self.outdir, 'images')) # run test self._descriptor_path = self.run_test() @property def points(self): """The test points suite.""" return self._points @property def descriptor_path(self): """The absolute path to the descriptor file.""" return self._descriptor_path def _is_point_spatial_test(self, i): """Check if a point index should be a spatial test. Spatial points are done at midpoint of bright and dark series. """ v = self._steps // 2 if i in (v, self._steps + v): return True return False def _get_descriptor_line(self, exposure, radiance): """Create the line introducing a test point images in descriptor.""" if np.mean(radiance) == 0.0: # dark image return "d %.1f" % exposure # bright image # round photons count to three decimals return "b %.1f %.3f" % ( exposure, np.round(np.sum(self.cam.get_photons(radiance), axis=2).mean(), 3)) def _get_image_names(self, number, L): """Create an image filename.""" names = [] for l in range(L): names.append("img_%04d.%s" % (number, self._image_format)) number += 1 return names, number def _get_imgs(self, radiance, L): """Create a list of image from the given radiances. """ # computes an array of dict whose keys are the name of the file # and the data of the image to save imgs = [] for l in range(L): imgs.append(self.cam.grab(radiance)) return imgs def run_test(self): """Run the test points, save the images and generate descriptor.""" descriptor_text = OrderedDict() image_number = 0 # descriptor file path path = os.path.join(self.outdir, "EMVA1288descriptor.txt") # open descriptor file to write images in it with open(path, "w") as f: # write version f.write("v %s\n" % self._version) # wtite camera's properties f.write("n %i %i %i\n" % (self.cam.bit_depth, self.cam.width, self.cam.height)) for kind in ('temporal', 'spatial'): # number of image to take L = 2 if kind == 'spatial': L = self._L for texp, radiances in self.points[kind].items(): # set camera self.cam.exposure = texp # Grab all images for these radiances for radiance in radiances: # Get descriptor line introducting the images f.write("%s\n" % self._get_descriptor_line(texp, radiance)) for l in range(L): # grab img = self.cam.grab(radiance) # write the name in descriptor name = "image%i.%s" % (image_number, self._image_format) f.write("i images\\%s\n" % name) image_number += 1 # save image self.save_image(img, name) # return descriptor path return path def save_image(self, img, name): """Save the image. """ # save the images contained in d dtype = np.uint32 mode = 'I' if self.cam.bit_depth <= 8: # 8 bit images have special format for PIL dtype = np.uint8 mode = 'L' im = Image.fromarray(img.astype(dtype), mode) path = os.path.join(self.outdir, 'images', name) # (filename already contains image format) im.save(path)
class DatasetGenerator: """Dataset generator. Creates a descriptor file and the corresponding linked images for a a exposure variant test example according to the emva1288 standart. The images are created using the implemented camera in the emva module. """ def __init__(self, steps=100, L=50, version='3.0', image_format='png', # best memory consumption outdir=None, # directory where to save the dataset radiance_min=None, radiance_max=None, exposure_fixed=None, **kwargs ): """Dataset generator init method. The generator uses a :class:`~emva1288.camera.points_generator.PointsGenerator` object to create the operation points. It then grabs the images for these points using a :class:`~emva1288.camera.camera.Camera` simulator object. The camera is intialized according to the given kwargs. Then, after getting the test points, it :meth:`makes <run_test>` the images with it by changing its exposure time, or the radiation and :meth:`saves <save_images>` the images and the descriptor file. Parameters ---------- L : int, optional The number of image taken during a spatial test point. version : str, optional Data version to add in descriptor file. image_format : str, optional The image's format when they are saved. outdir : str, optional The output directory where the descriptor file and the images will be saved. If None, it will create a tempory directory that will be deleted (and its contents) when the dataset generator object is deleted. radiance_min : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. radiance_max : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. exposure_fixed : float, optional Same as in :class:`~emva1288.camera.points_generator.PointsGenerator`. kwargs : All other kwargs are passed to the camera. """ self._steps = steps # number of points to take self.cam = Cam(**kwargs) # set the camera parameters for the test self.cam.exposure = self.cam.exposure_min # If no blackoffset/gain are specified find them according to standard if 'blackoffset' not in kwargs: self.cam.blackoffset = _get_emva_blackoffset(self.cam) if 'K' not in kwargs: self.cam.K = _get_emva_gain(self.cam) # create test points points = PointsGenerator(self.cam, radiance_min=radiance_min, radiance_max=radiance_max, exposure_fixed=exposure_fixed, steps=self._steps) self._points = points.points self._L = L # number of images to take for a spatial test self._version = version # data version # store image format self._image_format = image_format # images will be saved one at a time during the generation into outdir self.outdir = outdir # create temporary directory to store the dataset if outdir is None: self.tempdir = tempfile.TemporaryDirectory() self.outdir = self.tempdir.name # create dir where images will be saved os.makedirs(os.path.join(self.outdir, 'images')) # run test self._descriptor_path = self.run_test() @property def points(self): """The test points suite.""" return self._points @property def descriptor_path(self): """The absolute path to the descriptor file.""" return self._descriptor_path def _is_point_spatial_test(self, i): """Check if a point index should be a spatial test. Spatial points are done at midpoint of bright and dark series. """ v = self._steps // 2 if i in (v, self._steps + v): return True return False def _get_descriptor_line(self, exposure, radiance): """Create the line introducing a test point images in descriptor.""" if radiance == 0.0: # dark image return "d %.1f" % exposure # bright image # round photons count to three decimals return "b %.1f %.3f" % (exposure, round(self.cam.get_photons(radiance), 3)) def _get_image_names(self, number, L): """Create an image filename.""" names = [] for l in range(L): names.append("img_%04d.%s" % (number, self._image_format)) number += 1 return names, number def _get_imgs(self, radiance, L): """Create a list of image from the given radiances. """ # computes an array of dict whose keys are the name of the file # and the data of the image to save imgs = [] for l in range(L): imgs.append(self.cam.grab(radiance)) return imgs def run_test(self): """Run the test points, save the images and generate descriptor.""" descriptor_text = OrderedDict() image_number = 0 # descriptor file path path = os.path.join(self.outdir, "EMVA1288descriptor.txt") # open descriptor file to write images in it with open(path, "w") as f: # write version f.write("v %s\n" % self._version) # wtite camera's properties f.write("n %i %i %i\n" % (self.cam.bit_depth, self.cam.width, self.cam.height)) for kind in ('temporal', 'spatial'): # number of image to take L = 2 if kind == 'spatial': L = self._L for texp, radiances in self.points[kind].items(): # set camera self.cam.exposure = texp # Grab all images for these radiances for radiance in radiances: # Get descriptor line introducting the images f.write("%s\n" % self._get_descriptor_line(texp, radiance)) for l in range(L): # grab img = self.cam.grab(radiance) # write the name in descriptor name = "image%i.%s" % (image_number, self._image_format) f.write("i images\\%s\n" % name) image_number += 1 # save image self.save_image(img, name) # return descriptor path return path def save_image(self, img, name): """Save the image. """ # save the images contained in d dtype = np.uint32 mode = 'I' if self.cam.bit_depth <= 8: # 8 bit images have special format for PIL dtype = np.uint8 mode = 'L' im = Image.fromarray(img.astype(dtype), mode) path = os.path.join(self.outdir, 'images', name) # (filename already contains image format) im.save(path)