Source code for emva1288.camera.dataset_generator

# This module contains a generator that generates a descriptor file and the
# corresponding images using the implemented camera.

from emva1288.camera.camera import Camera as Cam
from emva1288.camera.points_generator import PointsGenerator
from collections import OrderedDict
import numpy as np
import tempfile
import os
from PIL import Image


def _get_emva_gain(cam):
    """Find the gain to satisfy EMVA1288 requirements"""
    gini = cam.K
    # Find gain with a minum temporal noise of 0.5DN
    g = cam.Ks[0]
    for gain in cam.Ks:
        cam.K = gain
        g = gain
        img1 = cam.grab(0).astype(np.int64)
        img2 = cam.grab(0).astype(np.int64)
        if (img1 - img2).std() > 0.5:
            break
    cam.K = gini
    return g


def _get_emva_blackoffset(cam):
    """Find the blackoffset to satifsfy EMVA1288 requirements"""
    bini = cam.blackoffset
    # Find black offset with a maximum of 0.5% of values at Zero
    bo = cam.blackoffsets[0]
    pixels = cam.width * cam.height
    for i in cam.blackoffsets:
        cam.blackoffset = i
        img = cam.grab(0)
        bo = i
        if np.count_nonzero(img) > pixels * .995:
            break
    cam.blackoffset = bini
    return bo


[docs]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. """
[docs] def __init__(self, steps=100, L=50, version='4.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
[docs] 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
[docs] 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)
# erase image from dict to reduce memory consuption