Example #1
0
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())
Example #2
0
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())
Example #3
0
 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")
Example #4
0
 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")
Example #5
0
    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")
Example #6
0
    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")
Example #7
0
 def setUp(self):
     self.cam = Camera()
Example #8
0
 def setUp(self):
     self.cam = Camera()
Example #9
0
    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()
Example #10
0
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)
Example #11
0
    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()
Example #12
0
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)