def parse_config(self, config_file):
        """Summary

    Args:
        config_file (str): Description
    """
        # check if config file exists
        if not os.path.exists(config_file):
            print("Couldn't find config_file '{}'...".format(config_file))
            sys.exit(-1)
        try:
            self.central_config = LocalConfig(config_file, interpolation=True)
        except Exception as e:
            print(e)
            print("Issue with LocalConfig")
            sys.exit(-21)

        try:
            self.central_config._parser = \
              ConfigParser(interpolation=ExtendedInterpolation())
        except Exception as e:
            print(e)
            print("Issue with ConfigParser")
            sys.exit(-22)

        sections = list(self.central_config)
        #Prepare config_dict
        source_dict = {}
        for section in sections:
            # TODO add exception handling configparser.InterpolationMissingOptionError
            source_dict[section] = dict(self.central_config.items(section))

        self.update_dict(self.config_dict, source_dict)

        # Configure sections
        for section in sections:
            if '.' in section:
                self.config_dict[section.split('.')[0]]\
                  [section.split('.')[1]] = self.config_dict[section]
                self.config_dict.pop(section, None)
Example #2
0
from localconfig import LocalConfig
import os

config = None
gain_loss_rules = dict()
market_attributes = dict()
monitor_conf = dict()
debug_cfg = dict()

if os.path.isfile('user_config.ini'):
    config = LocalConfig('user_config.ini')

    gain_loss_rules = dict(list(config.gain_loss_rules))
    market_attributes = dict(list(config.market_attributes))
    monitor_conf = dict(list(config.monitor_conf))
    debug_cfg = dict(list(config.debug_cfg))

else:
    print("There is no user config file.")
    print("I will create an empty config file for you then exit.")

    ok = False
    try:
        with open("user_config.ini", "w+") as cf:
            cf.write('[gain_loss_rules]\n')
            cf.write('goal       = \n')
            cf.write('loss       = \n')
            cf.write('buy        = \n')
            cf.write('sell       = \n')
            cf.write('price      = \n')
            cf.write('i_am_lucky = \n')
Example #3
0
    def run(self):
        if self.minor and self.major:
            log.error('--minor and --major are mutually exclusive, please use only one.')
            return

        repo_check()

        pypirc = LocalConfig('~/.pypirc')
        repository = pypirc.get(self.repo, 'repository')
        username = pypirc.get(self.repo, 'username')
        password = pypirc.get(self.repo, 'password')
        repo_title = 'PyPI' if self.repo == 'pypi' else self.repo.title()

        if not repository:
            log.error('Please add repository / username to [%s] section in ~/.pypirc', self.repo)
            sys.exit(1)

        if not username:
            username = getpass.getuser('{} Username: '******'{} Password: '******'update')

        published_version, changes = self.changes_since_last_publish()

        if not changes:
            click.echo('There are no changes since last publish')
            sys.exit(0)

        silent_run('rm -rf dist/*', shell=True, cwd=repo_path())

        if self.major or self.minor:
            new_version, setup_file = self.bump_version(major=self.major, minor=self.minor)
            major_minor = 'major' if self.major else 'minor'
            self.commander.run('commit', msg=f'Bump {major_minor} version', files=[setup_file], push=2,
                               skip_style_check=True)

        else:
            current_version, setup_file = self.get_version()

            # Previously, we publish the current version in setup.py, so need to bump it first before
            # we can publish a new version.
            if published_version == current_version:
                new_version, setup_file = self.bump_version()
            else:
                new_version = current_version

        changelog_file = self.update_changelog(new_version, changes, self.minor or self.major)

        tox = ToxIni()
        envs = [e for e in tox.envlist if e != 'style']

        if envs:
            env = envs[0]
            if len(envs) > 1:
                log.debug('Found multiple default envs in tox.ini, will use first one to build: %s', env)

        else:
            click.echo('Odd, there are no default envs in tox.ini, so we can not build.')
            sys.exit(1)

        envdir = tox.envdir(env)
        python = os.path.join(envdir, 'bin', 'python')

        click.echo('Building source/built distribution')

        silent_run(f'{python} setup.py sdist bdist_wheel', cwd=repo_path())

        click.echo('Uploading to ' + repo_title)

        try:
            run('twine upload -r "{repo}" -u "{username}" -p "{password}" dist/*'.format(
                repo=self.repo,
                username=username,
                password=password), shell=True, cwd=repo_path(), silent=2)

        except Exception:
            sys.exit(1)

        self.bump_version()
        self.commander.run('commit', msg=PUBLISH_VERSION_PREFIX + new_version, push=2,
                           files=[setup_file, changelog_file],
                           skip_style_check=True)
Example #4
0
    def __init__(self, configfile="pirecorder.conf", logging=True):

        if not isrpi():
            lineprint("PiRecorder only works on a raspberry pi. Exiting..")
            return

        self.system = "auto"
        self.host = gethostname()
        self.home = homedir()
        self.setupdir = self.home + "pirecorder"
        self.logfolder = self.setupdir + "/logs/"
        if not os.path.exists(self.logfolder):
            os.makedirs(self.setupdir)
            os.makedirs(self.logfolder)
            lineprint("Setup folder created (" + self.setupdir + ")..")
        if not os.path.exists(self.logfolder):
            lineprint("Setup folder exists but was not set up properly..")

        if logging:
            self.log = Logger(self.logfolder + "/pirecorder.log")
            self.log.start()
            print("")

        lineprint("pirecorder " + __version__ + " started!", date=True)
        lineprint("=" * 47, False)

        self.brightfile = self.setupdir + "/cusbright.yml"
        self.configfilerel = configfile
        self.configfile = self.setupdir + "/" + configfile

        self.config = LocalConfig(self.configfile, compact_form=True)
        if not os.path.isfile(self.configfile):
            lineprint("Config file " + configfile +
                      " not found, new file created..")
            for section in ["rec", "cam", "cus", "img", "vid"]:
                if section not in list(self.config):
                    self.config.add_section(section)
            self.settings(recdir="pirecorder/recordings",
                          subdirs=False,
                          label="test",
                          rectype="img",
                          rotation=0,
                          brighttune=0,
                          roi=None,
                          gains=(1.0, 2.5),
                          brightness=45,
                          contrast=10,
                          saturation=0,
                          iso=200,
                          sharpness=0,
                          compensation=0,
                          shutterspeed=8000,
                          imgdims=(2592, 1944),
                          maxres=None,
                          viddims=(1640, 1232),
                          imgfps=1,
                          vidfps=24,
                          imgwait=5.0,
                          imgnr=12,
                          imgtime=60,
                          imgquality=50,
                          vidduration=10,
                          viddelay=10,
                          vidquality=11,
                          automode=True,
                          internal="",
                          maxviddur=3600,
                          maxvidsize=0)
            lineprint("Config settings stored..")

        else:
            lineprint("Config file " + configfile + " loaded..")
            lineprint("Recording " + self.config.rec.rectype + " in " +\
                          self.home + self.config.rec.recdir)

        self._imgparams()
        self._shuttertofps()
        if self.config.rec.rectype == "imgseq":
            if self.config.cam.shutterspeed / 1000000. >= (
                    self.config.img.imgwait / 5):
                lineprint("imgwait is not enough for provided shutterspeed" + \
                          ", will be overwritten..")

        if self.config.rec.recdir == "NAS":
            if not os.path.ismount(self.config.rec.recdir):
                self.recdir = self.home
                lineprint("Recdir not mounted, storing in home directory..")
        self.recdir = self.home + self.config.rec.recdir
        if not os.path.exists(self.recdir):
            os.makedirs(self.recdir)

        os.chdir(self.recdir)
Example #5
0
class PiRecorder:
    """
    Sets up the rpi with a pirecorder folder with configuration and log files,
    and initiates a recorder instance for controlled image and video recording

    Parameters
    ----------
    configfile : str, default = "pirecorder.conf"
        The name of the configuration file to be used for recordings. If the
        file does not exist yet, automatically a new file with default
        configuration values will be created.

    Returns
    -------
    self : class
        PiRecorder class instance that can be used to set the configuration,
        start a video stream to calibrate and configure the camera, to set the
        shutterspeed and white balance automatically, to start recordings, and
        to schedule future recordings.
    """
    def __init__(self, configfile="pirecorder.conf", logging=True):

        if not isrpi():
            lineprint("PiRecorder only works on a raspberry pi. Exiting..")
            return

        self.system = "auto"
        self.host = gethostname()
        self.home = homedir()
        self.setupdir = self.home + "pirecorder"
        self.logfolder = self.setupdir + "/logs/"
        if not os.path.exists(self.logfolder):
            os.makedirs(self.setupdir)
            os.makedirs(self.logfolder)
            lineprint("Setup folder created (" + self.setupdir + ")..")
        if not os.path.exists(self.logfolder):
            lineprint("Setup folder exists but was not set up properly..")

        if logging:
            self.log = Logger(self.logfolder + "/pirecorder.log")
            self.log.start()
            print("")

        lineprint("pirecorder " + __version__ + " started!", date=True)
        lineprint("=" * 47, False)

        self.brightfile = self.setupdir + "/cusbright.yml"
        self.configfilerel = configfile
        self.configfile = self.setupdir + "/" + configfile

        self.config = LocalConfig(self.configfile, compact_form=True)
        if not os.path.isfile(self.configfile):
            lineprint("Config file " + configfile +
                      " not found, new file created..")
            for section in ["rec", "cam", "cus", "img", "vid"]:
                if section not in list(self.config):
                    self.config.add_section(section)
            self.settings(recdir="pirecorder/recordings",
                          subdirs=False,
                          label="test",
                          rectype="img",
                          rotation=0,
                          brighttune=0,
                          roi=None,
                          gains=(1.0, 2.5),
                          brightness=45,
                          contrast=10,
                          saturation=0,
                          iso=200,
                          sharpness=0,
                          compensation=0,
                          shutterspeed=8000,
                          imgdims=(2592, 1944),
                          maxres=None,
                          viddims=(1640, 1232),
                          imgfps=1,
                          vidfps=24,
                          imgwait=5.0,
                          imgnr=12,
                          imgtime=60,
                          imgquality=50,
                          vidduration=10,
                          viddelay=10,
                          vidquality=11,
                          automode=True,
                          internal="",
                          maxviddur=3600,
                          maxvidsize=0)
            lineprint("Config settings stored..")

        else:
            lineprint("Config file " + configfile + " loaded..")
            lineprint("Recording " + self.config.rec.rectype + " in " +\
                          self.home + self.config.rec.recdir)

        self._imgparams()
        self._shuttertofps()
        if self.config.rec.rectype == "imgseq":
            if self.config.cam.shutterspeed / 1000000. >= (
                    self.config.img.imgwait / 5):
                lineprint("imgwait is not enough for provided shutterspeed" + \
                          ", will be overwritten..")

        if self.config.rec.recdir == "NAS":
            if not os.path.ismount(self.config.rec.recdir):
                self.recdir = self.home
                lineprint("Recdir not mounted, storing in home directory..")
        self.recdir = self.home + self.config.rec.recdir
        if not os.path.exists(self.recdir):
            os.makedirs(self.recdir)

        os.chdir(self.recdir)

    def _setup_cam(self, auto=False, fps=None):
        """Sets up the raspberry pi camera based on the configuration"""

        import picamera
        import picamera.array

        self.cam = picamera.PiCamera()
        self.cam.rotation = self.config.cus.rotation
        self.cam.exposure_compensation = self.config.cam.compensation

        if self.config.rec.rectype in ["img", "imgseq"]:
            self.cam.resolution = literal_eval(self.config.img.imgdims)
            self.cam.framerate = self.config.img.imgfps
        if self.config.rec.rectype in ["vid", "vidseq"]:
            self.cam.resolution = picamconv(
                literal_eval(self.config.vid.viddims))
            self.cam.framerate = self.config.vid.vidfps
        if fps != None:
            self.cam.framerate = fps

        if self.config.cus.roi is None:
            self.cam.zoom = (0, 0, 1, 1)
            self.resize = self.cam.resolution
        else:
            self.cam.zoom = literal_eval(self.config.cus.roi)
            w = int(self.cam.resolution[0] * self.cam.zoom[2])
            h = int(self.cam.resolution[1] * self.cam.zoom[3])
            if self.config.rec.rectype in ["vid", "vidseq"]:
                self.resize = picamconv((w, h))
            else:
                self.resize = (w, h)

        self.longexpo = False if self.cam.framerate >= 6 else True

        self.cam.exposure_mode = "auto"
        self.cam.awb_mode = "auto"
        lineprint("Camera warming up..")
        if auto or self.config.cam.automode:
            self.cam.shutter_speed = 0
            sleep(2)
        elif self.cam.framerate >= 6:
            sleep(6) if self.cam.framerate > 1.6 else sleep(10)
        else:
            sleep(2)
        if not auto and self.config.cam.automode == False:
            self.cam.shutter_speed = self.config.cam.shutterspeed
            self.cam.exposure_mode = "off"
            self.cam.awb_mode = "off"
            self.cam.awb_gains = eval(self.config.cus.gains)
            sleep(0.1)

        brightness = self.config.cam.brightness + self.config.cus.brighttune
        self.cam.brightness = brightness
        self.cam.contrast = self.config.cam.contrast
        self.cam.saturation = self.config.cam.saturation
        self.cam.iso = self.config.cam.iso
        self.cam.sharpness = self.config.cam.sharpness

        self.rawCapture = picamera.array.PiRGBArray(self.cam,
                                                    size=self.cam.resolution)

        self.maxvidsize = self.config.vid.maxvidsize if self.config.vid.maxvidsize > 0 else 999

    def _imgparams(self, mintime=0.45):
        """
        Calculates minimum possible imgwait and imgnr based on imgtime. The
        minimum time between subsequent images is by default set to 0.45s, the
        time it takes to take an image with max resolution.
        """

        self.config.img.imgwait = max(mintime, self.config.img.imgwait)
        totimg = int(self.config.img.imgtime / self.config.img.imgwait)
        self.config.img.imgnr = min(self.config.img.imgnr, totimg)

    def _shuttertofps(self, minfps=1, maxfps=40):
        """Computes image fps based on shutterspeed within provided range"""

        fps = round(1. / (self.config.cam.shutterspeed / 1000000.), 2)
        self.config.img.imgfps = min(max(fps, minfps), maxfps)

    def _namefile(self):
        """
        Provides a filename for the media recorded. Filenames include label,
        date, rpi name, and time. Images part of image sequence additionally
        contain a sequence number. e.g. test_180708_pi12_S01_100410
        """

        imgtypes = ["img", "imgseq"]
        self.filetype = ".jpg" if self.config.rec.rectype in imgtypes else ".h264"

        if self.config.rec.rectype == "imgseq":
            date = strftime("%y%m%d")
            counter = "im{counter:05d}" if self.config.img.imgnr > 999 else "im{counter:03d}"
            time = "{timestamp:%H%M%S}"
            self.filename = "_".join(
                [self.config.rec.label, date, self.host, counter, time])
            self.filename = self.filename + self.filetype
        else:
            date = strftime("%y%m%d")
            self.filename = "_".join([self.config.rec.label, date, self.host
                                      ]) + "_"

        if self.config.rec.subdirs:
            subdir = name("_".join([self.config.rec.label, date, self.host]))
            os.makedirs(subdir, exist_ok=True)
            self.filename = subdir + "/" + self.filename

    def autoconfig(self):
        """
        Sets the shutterspeed and white balance automatically using the
        framerate provided in the configuration file
        """

        self._setup_cam(auto=True)
        with self.rawCapture as stream:
            for a in range(5):
                self.cam.capture(stream, format="bgr", use_video_port=True)
                image = stream.array
                stream.seek(0)
                stream.truncate()

            self.config.cam.shutterspeed = self.cam.exposure_speed
            self.config.cus.gains = tuple(
                [round(float(i), 2) for i in self.cam.awb_gains])
            self.config.save()
            lineprint("Shutterspeed set to " + str(self.cam.exposure_speed))
            lineprint("White balance gains set to " +
                      str(self.config.cus.gains))

        stream.close()
        self.rawCapture.close()
        self.cam.close()

    def settings(self, **kwargs):
        """
        Configure the camera and recording settings

        Parameters
        ---------------
        recdir : str, default = "pirecorder/recordings"
            The directory where media will be stored. Default is "recordings".
            If different, a folder with name corresponding to location will be
            created inside the home directory. If no name is provided (""), the
            files are stored in the home directory. If "NAS" is provided it will
            additionally check if the folder links to a mounted drive.
        subdirs : bool, default = False
            If files of individual recordings should be stored in subdirectories
            or not, to keep all files of a single recording session together.
        label : str, default = "test"
            Label that will be associated with the specific recording and stored
            in the filenames.
        rectype : ["img", "imgseq", "vid", "vidseq"], default = "img"
            Recording type, either a single image or video or a sequence of
            images or videos.
        automode : bool, default = True
            If the shutterspeed and white balance should be set automatically
            and dynamically for each recording.
        maxres : str or tuple, default = "v2"
            The maximum potential resolution of the camera used. Either provide
            a tuple of the max resolution, or use "v1.5", "v2" (default), or
            "hq" to get the maximum resolution associated with the official
            cameras directly.
        rotation : int, default = 0
            Custom rotation specific to the Raspberry Pi, should be either 0 or
            180.
        brighttune : int, default = 0
            A rpi-specific brightness compensation factor to standardize light
            levels across multiple rpi"s, an integer between -10 and 10.
        roi : tuple, default = None
            Region of interest to be used for recording. Consists of coordinates
            of top left and bottom right coordinate of a rectangular area
            encompassing the region of interest. Can be set with the set_roi()
            method.
        gains : tuple, default = (1.0, 2.5)
            Sets the blue and red gains to acquire the desired white balance.
            Expects a tuple of floating values (e.g. "(1.5, 1.85)"). Can be
            automatically set with the autoconfig() function and interactively
            with the camconfig() function using a live video stream.
        brightness : int, default = 45
            Sets the brightness level of the camera. Expects an integer value
            between 0 and 100. Higher values result in brighter images.
        contrast : int, default = 20
            Sets the contrast for the recording. Expects an integer value
            between 0 and 100. Higher values result in images with higher
            contrast.
        saturation : int, default 0
            Sets the saturation level for the recording. Expects an integer
            value between -100 and 100.
        iso : int, default = 200
            Sets the camera ISO value. Should be one of the following values:
            [100, 200, 320, 400, 500, 640, 800]. Higher values result in
            brighter images but with higher gain.
        sharpness : int, default = 50
            Sets the sharpness of the camera. Expects an integer value between
            -100 and 100. Higher values result in sharper images.
        compensation : int, default = 0
            Adjusts the camera’s exposure compensation level before recording.
            Expects a value between -25 and 25, with each increment representing
            1/6th of a stop and thereby a brighter image.
        shutterspeed : int, detault = 10000
            Sets the shutter speed of the camera in microseconds, i.e. a value
            of 10000 would indicate a shutterspeed of 1/100th of a second. A
            longer shutterspeed will result in a brighter image but more motion
            blur. Important to consider is that the framerate of the camera will
            be adjusted based on the shutterspeed. At low shutterspeeds (i.e.
            above ~ 0.2s) the required waiting time between images increases
            considerably due to the raspberry pi hardware. To control for this,
            automatically a standard `imgwait` time should be chosen that is at
            least 6x the shutterspeed. For example, for a shutterspeed of 300000
            imgwait should be > 1.8s.
        imgdims : tuple, default = (2592, 1944)
            The resolution of the images to be taken in pixels. The default is
            the max resolution for the v1.5 model, the v2 model has a max
            resolution of 3280 x 2464 pixels, and the hq camera 4056 x 3040
            pixels.
        viddims : tuple, default = (1640, 1232)
            The resolution of the videos to be taken in pixels. The default is
            the max resolution that does not return an error for this mode.
        imgfps : int, default = 1
            The framerate for recording images. Will be set automatically based
            on the imgwait setting so should not be set by user.
        vidfps : int, default = 24
            The framerate for recording video.
        imgwait : float, default = 5.0
        	The delay between subsequent images in seconds. When a delay is
            provided that is less than ~x5 the shutterspeed, the camera
            processing time will take more time than the provided imgwait
            parameter and so images are taken immideately one after the other.
            To take a sequence of images at the exact right delay interval the
            imgwait parameter should be at least 5x the shutterspeed (e.g.
            shutterspeed of 400ms needs imgwait of 2s).
        imgnr : int, default = 12
            The number of images that should be taken. When this number is
            reached, the recorder will automatically terminate.
        imgtime : integer, default = 60
            The time in seconds during which images should be taken. The minimum
            of a) imgnr and b) nr of images based on imgwait and imgtime will be
            used.
        imgquality : int, default = 50
            Specifies the quality that the jpeg encoder should attempt to
            maintain. Use values between 1 and 100, where higher values are
            higher quality.
        vidduration : int, default = 10
            Duration of video recording in seconds.
        viddelay : int, default = 0
            Extra recording time in seconds that will be added to vidduration.
            Its use is to add a standard amount of time to the video that can be
            easily cropped or skipped, such as for tracking, but still provides
            useful information, such as behaviour during acclimation.
        vidquality : int, default = 11
            Specifies the quality that the h264 encoder should attempt to
            maintain. Use values between 10 and 40, where 10 is extremely high
            quality, and 40 is extremely low.
        maxviddur : int, default = 3600
            The maximum duration in seconds for single videos, beyond which
            videos will be automatically split. A value of 0 indicates there is
            no maximum file duration.
        maxvidsize : int, default = 0
            The maximum file size in Megabytes for single videos, beyond which
            videos will be automatically split. A value of 0 indicates there is
            no maximum file size.
        """

        if "recdir" in kwargs:
            self.config.rec.recdir = kwargs["recdir"]
        if "subdirs" in kwargs:
            self.config.rec.subdirs = kwargs["subdirs"]
        if "label" in kwargs:
            self.config.rec.label = kwargs["label"]
        if "rectype" in kwargs:
            self.config.rec.rectype = kwargs["rectype"]
        if "maxres" in kwargs:
            self.config.rec.maxres = kwargs["maxres"]
            if isinstance(self.config.rec.maxres, tuple):
                self.config.img.imgdims = self.config.rec.maxres
            elif self.config.rec.maxres == "v2":
                self.config.img.imgdims = (3264, 2464)
            elif self.config.rec.maxres == "hq":
                self.config.img.imgdims = (4056, 3040)
        if "rotation" in kwargs:
            self.config.cus.rotation = kwargs["rotation"]
        if "brighttune" in kwargs:
            self.config.cus.brighttune = kwargs["brighttune"]
        if "roi" in kwargs:
            self.config.cus.roi = kwargs["roi"]
        if "gains" in kwargs:
            self.config.cus.gains = kwargs["gains"]

        if "automode" in kwargs:
            self.config.cam.automode = kwargs["automode"]
        if "brightness" in kwargs:
            self.config.cam.brightness = kwargs["brightness"]
        if "contrast" in kwargs:
            self.config.cam.contrast = kwargs["contrast"]
        if "saturation" in kwargs:
            self.config.cam.saturation = kwargs["saturation"]
        if "iso" in kwargs:
            self.config.cam.iso = kwargs["iso"]
        if "sharpness" in kwargs:
            self.config.cam.sharpness = kwargs["sharpness"]
        if "compensation" in kwargs:
            self.config.cam.compensation = kwargs["compensation"]
        if "shutterspeed" in kwargs:
            self.config.cam.shutterspeed = kwargs["shutterspeed"]

        if "imgdims" in kwargs:
            self.config.img.imgdims = kwargs["imgdims"]
        if "viddims" in kwargs:
            self.config.vid.viddims = kwargs["viddims"]
        if "imgfps" in kwargs:
            self.config.img.imgfps = kwargs["imgfps"]
        if "vidfps" in kwargs:
            self.config.vid.vidfps = kwargs["vidfps"]

        if "imgwait" in kwargs:
            self.config.img.imgwait = kwargs["imgwait"]
        if "imgnr" in kwargs:
            self.config.img.imgnr = kwargs["imgnr"]
        if "imgtime" in kwargs:
            self.config.img.imgtime = kwargs["imgtime"]
        if "imgquality" in kwargs:
            self.config.img.imgquality = kwargs["imgquality"]

        if "vidduration" in kwargs:
            self.config.vid.vidduration = kwargs["vidduration"]
        if "viddelay" in kwargs:
            self.config.vid.viddelay = kwargs["viddelay"]
        if "maxviddur" in kwargs:
            self.config.vid.maxviddur = kwargs["maxviddur"]
        if "maxvidsize" in kwargs:
            self.config.vid.maxvidsize = kwargs["maxvidsize"]

        brightchange = False
        if os.path.exists(self.brightfile):
            with open(self.brightfile) as f:
                brighttune = yaml.load(f, Loader=yaml.FullLoader)
                if brighttune != self.config.cus.brighttune:
                    if "internal" not in kwargs:
                        lineprint("cusbright.yml file found and loaded..")
                    self.config.cus.brighttune = brighttune
                    brightchange = True

        if len(kwargs) > 0 or brightchange:

            self._imgparams()
            self._shuttertofps()
            if self.config.rec.rectype == "imgseq":
                if self.config.cam.shutterspeed / 1000000. >= (
                        self.config.img.imgwait / 5):
                    lineprint("imgwait is not enough for provided shutterspeed" + \
                              ", will be overwritten..")
            self.config.save()

            if "internal" not in kwargs:
                lineprint("Config settings stored and loaded..")

    def stream(self, fps=None):
        """Shows an interactive video stream"""

        lineprint("Opening stream for cam positioning and roi extraction..")
        vidstream = Stream(internal=True,
                           rotation=self.config.cus.rotation,
                           maxres=self.config.rec.maxres)
        if vidstream.roi:
            self.settings(roi=vidstream.roi, internal="")
            lineprint("Roi stored..")
        else:
            lineprint("No roi selected..")

    def camconfig(self, fps=None, vidsize=0.4):

        lineprint("Opening stream for interactive configuration..")
        fps = max(self.config.vid.vidfps, 1) if fps == None else int(fps)
        self._setup_cam(fps=fps)
        configout = Camconfig(self.cam,
                              auto=self.config.cam.automode,
                              vidsize=vidsize)
        if len(configout) > 0:
            self.settings(**configout)

    def schedule(self,
                 jobname=None,
                 timeplan=None,
                 enable=True,
                 showjobs=False,
                 delete=None,
                 test=False):
        """
        Schedule future recordings

        Parameters
        ----------
        jobname : str, default = None
            Name for the scheduled recorder task to create, modify or remove.
        timeplan : string, default = None
            Code string representing the time planning for the recorder to run
            with current configuration set. Build on CRON, the time plan should
            consist of the following parts:
            * * * * *
            - - - - -
            | | | | |
            | | | | +----- day of week (0 - 7) (sunday = 0 or 7)
            | | | +---------- month (1 - 12)
            | | +--------------- day of month (1 - 31)
            | +-------------------- hour (0 - 23)
            +------------------------- min (0 - 59)
            Each of the parts supports wildcards (*), ranges (2-5), and lists
            (2,5,6,11). For example, if you want to schedule a recording at
            22:00, every workday of the week, enter the code '0 22 * * 1-5' If
            uncertain, crontab.guru is a great website for checking your CRON
            code. Note that the minimum time between subsequent scheduled
            recordings is 1 minute. Smaller intervals between recordings is
            possible for images with the imgseq command with the Record method.
        enable : bool, default = None
            If the scheduled job should be enabled or not.
        showjobs : bool, default = False
            If the differently timed tasks should be shown or not.
        delete : [None, "job", "all"], default = None
            If a specific job ('job'), all jobs ('all') or no jobs (None)
            should be cleared from the scheduler.
        test : bool; default = False
            Determine if the timeplan is valid and how often it will run the
            record command.
        configfile : str, default = "pirecorder.conf"
            The name of the configuration file to be used for the scheduled
            recordings. Make sure the file exists, otherwise the default
            configuration settings will be used.

        Note: Make sure Recorder configuration timing settings are within the
        timespan between subsequent scheduled recordings based on the provided
        timeplan. For example, a video duration of 20 min and a scheduled
        recording every 15 min between 13:00-16:00 (*/15 13-16 * * *) will fail.
        This will be checked automatically.
        """

        S = Schedule(jobname,
                     timeplan,
                     enable,
                     showjobs,
                     delete,
                     test,
                     logfolder=self.logfolder,
                     internal=True,
                     configfile=self.configfilerel)

    def record(self):
        """
        Starts a recording as configured and returns either one or multiple
        .h264 or .jpg files that are named automatically according to the label,
        the host name, date, time and potentially session number or count nr.

        Example output files:
        rectype = "img" : test_180312_pi13_101300.jpg
        rectype = "vid" : test_180312_pi13_102352.h264
        rectype = "imgseq" : test_180312_pi13_img00231_101750.jpg
        rectype = "vidseq" : test_180312_pi13_101810_S01.h264
        """

        self._setup_cam()
        self._namefile()

        if self.config.rec.rectype == "img":

            self.filename = self.filename + strftime("%H%M%S") + self.filetype
            self.cam.capture(self.filename,
                             format="jpeg",
                             resize=self.resize,
                             quality=self.config.img.imgquality)
            lineprint("Captured " + self.filename)

        elif self.config.rec.rectype == "imgseq":

            starttime = datetime.now()
            timepoint = starttime
            for i, img in enumerate(
                    self.cam.capture_continuous(
                        self.filename,
                        format="jpeg",
                        resize=self.resize,
                        quality=self.config.img.imgquality)):
                tottimepassed = (datetime.now() - starttime).total_seconds()
                if i < self.config.img.imgnr - 1 and tottimepassed < self.config.img.imgtime:
                    timepassed = (datetime.now() - timepoint).total_seconds()
                    delay = max(0, self.config.img.imgwait - timepassed)
                    lineprint("Captured " + img + ", sleeping " +
                              str(round(delay, 2)) + "s..")
                    sleep(delay)
                    timepoint = datetime.now()
                else:
                    lineprint("Captured " + img)
                    break

        elif self.config.rec.rectype in ["vid", "vidseq"]:

            # Temporary fix for flicker at start of (first) video
            self.cam.start_recording(BytesIO(),
                                     format="h264",
                                     resize=self.resize,
                                     level="4.2")
            self.cam.wait_recording(2)
            self.cam.stop_recording()

            for session in ["_S%02d" % i for i in range(1, 999)]:
                session = "" if self.config.rec.rectype == "vid" else session
                filename = self.filename + strftime("%H%M%S") + session
                timeremaining = self.config.vid.vidduration + self.config.vid.viddelay
                counter = 0
                while timeremaining > 0:
                    counter += 1
                    waittime = timeremaining
                    if self.config.vid.maxviddur > 0:
                        waittime = min(timeremaining,
                                       self.config.vid.maxviddur)
                    if waittime == timeremaining and self.config.vid.maxvidsize == 0:
                        nr = ""
                    else:
                        nr = "_v" + str(counter).zfill(2)
                    finalname = filename + nr + self.filetype
                    video = VidOutput(finalname)
                    self.cam.start_recording(
                        video,
                        resize=self.resize,
                        quality=self.config.vid.vidquality,
                        level="4.2",
                        format=self.filetype[1:])
                    lineprint("Start recording " + filename)
                    rectime = 0
                    while video.size < self.maxvidsize * 1000000 and rectime < waittime:
                        rectime += 0.1
                        self.cam.wait_recording(0.1)
                    timeremaining -= rectime
                    self.cam.stop_recording()
                    vidinfo = " (" + str(round(rectime)) + "s; " + str(
                        round(video.size / 1000000, 2)) + "MB)"
                    lineprint("Finished recording " + finalname + vidinfo)
                if self.config.rec.rectype == "vid":
                    break
                else:
                    msg = "\nPress Enter for new session, or e and Enter to exit: "
                    if input(msg) == "e":
                        break
        self.cam.close()
Example #6
0
from localconfig import LocalConfig
import os

config = None

if os.path.isfile('acts_config.ini'):
    config = LocalConfig('acts_config.ini')

else:
    print("It seems that this is your first time :)")
    print("Welcome. Currently I'm able to trade on Bittrex exchange only.")
    print(
        "I need Bittrex's API key and secret with appropriate permission. Please give me those info."
    )
    api_key = input(
        'Please input your API Key of your Bittrex representative: ')
    api_secret = input(
        'Please input your API Secret of your Bittrex representative: ')
    print("Creating your config file, please wait ...")

    ok = False
    try:
        with open("acts_config.ini", "w+") as cf:
            cf.write('[Bittrex]\n')
            cf.write('API_KEY = ' + api_key + '\n')
            cf.write('API_SECRET = ' + api_secret + '\n')
        ok = True

    except IOError:
        print("Can not save config file.")
Example #7
0
    def run(self):
        if self.minor and self.major:
            log.error('--minor and --major are mutually exclusive, please use only one.')
            return

        repo_check()

        pypirc = LocalConfig('~/.pypirc')
        repository = pypirc.get(self.repo, 'repository')
        username = pypirc.get(self.repo, 'username')
        password = pypirc.get(self.repo, 'password')
        repo_title = 'PyPI' if self.repo == 'pypi' else self.repo.title()

        if not repository:
            log.error('Please add repository / username to [%s] section in ~/.pypirc', self.repo)
            sys.exit(1)

        if not username:
            username = getpass.getuser('{} Username: '******'{} Password: '******'update')

        published_version, changes = self.changes_since_last_publish()

        if not changes:
            click.echo('There are no changes since last publish')
            sys.exit(0)

        silent_run('rm -rf dist/*', shell=True, cwd=repo_path())

        if self.major or self.minor:
            new_version, setup_file = self.bump_version(major=self.major, minor=self.minor)
            major_minor = 'major' if self.major else 'minor'
            self.commander.run('commit', msg=f'Bump {major_minor} version', files=[setup_file], push=2,
                               skip_style_check=True)

        else:
            current_version, setup_file = self.get_version()

            # Previously, we publish the current version in setup.py, so need to bump it first before
            # we can publish a new version.
            if published_version == current_version:
                new_version, setup_file = self.bump_version()
            else:
                new_version = current_version

        changelog_file = self.update_changelog(new_version, changes, self.minor or self.major)

        tox = ToxIni()
        envs = [e for e in tox.envlist if e != 'style']

        if envs:
            env = envs[0]
            if len(envs) > 1:
                log.debug('Found multiple default envs in tox.ini, will use first one to build: %s', env)

        else:
            click.echo('Odd, there are no default envs in tox.ini, so we can not build.')
            sys.exit(1)

        envdir = tox.envdir(env)
        python = os.path.join(envdir, 'bin', 'python')

        click.echo('Building source/built distribution')

        silent_run(f'{python} setup.py sdist bdist_wheel', cwd=repo_path())

        click.echo('Uploading to ' + repo_title)

        try:
            run('twine upload -r "{repo}" -u "{username}" -p "{password}" dist/*'.format(
                repo=self.repo,
                username=username,
                password=password), shell=True, cwd=repo_path(), silent=2)

        except Exception:
            sys.exit(1)

        self.bump_version()
        self.commander.run('commit', msg=PUBLISH_VERSION_PREFIX + new_version, push=2, files=[setup_file, changelog_file],
                           skip_style_check=True)
Example #8
0
    def from_cfg(self, config=None, weight_map=None):
        """
        Loads network parameters from <configparser.ConfigParser> object.
        :param config:      <LocalConfig>
        :param weight_map:  <torch.tensor>
        :return:            <Network>
        """
        if config is None:
            default_path = os.path.dirname(__file__)
            config = LocalConfig(os.path.join(default_path, 'template.ini'))

        config = self._infer_fields(config)

        # Specify sizes
        sizes = {
            'I': config.network.input_neuron_number,
            'O': config.network.output_neuron_number
        }

        # Specify layers
        # {name: <Layer>}
        layers = {
            'I': PoissonLayer(firing_step=torch.zeros(sizes['I']),
                              config=config),
            'O': LIFLayer(sizes['O'], config=config)
        }

        # Specify weights
        # {(source, target): weight}
        # will be converted into
        # {(source, target): <Connection>}
        if weight_map is None:
            if config.synapse.init_weights == 'min':
                weight_map = config.synapse.w_min * torch.ones(
                    sizes['I'], sizes['O'])
            elif config.synapse.init_weights == 'max':
                weight_map = config.synapse.w_max * torch.ones(
                    sizes['I'], sizes['O'])
            elif config.synapse.init_weights == 'random':
                weight_map = config.synapse.w_min + (config.synapse.w_max - config.synapse.w_min) \
                    * torch.rand(sizes['I'], sizes['O'])
            else:
                raise ValueError(
                    'Wrong configuration for synapse.init_weights')

        weights = {('I', 'O'): weight_map}

        # Specify monitors (monitors will be instantiated later during the building process of network)
        # {name: [state_vars]}
        monitors = {
            # 'I': ['o'],
            # 'O': ['o']
        }

        # Config network
        builder = NetworkBuilder(layers=layers,
                                 weights=weights,
                                 monitors=monitors,
                                 config=config)
        network = builder.build()

        return network
Example #9
0
def read_config(config_file_or_string):
    config = LocalConfig(interpolation=EnvInterpolation())
    config.read(config_file_or_string)
    return config
class CGNConfigParser():
    """This class is used to parse the config file

  Attributes:
      central_config (str): Description
      config_dict (dict): Description
      env_configs (dict): Description
      path_to_config_ini (str): Description
  """
    def __init__(self,
                 path_to_config_ini=None,
                 env_configs_check=False,
                 logger=None):
        """Summary

    Args:
        path_to_config_ini (None, optional): Description
        env_configs_check (bool, optional): Description
    """
        #print (path_to_config_ini)
        # self.logger = logger
        # or abzlogger()
        CGN_DS_ROOT = \
          os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                       '..', '..')
        self.path_to_config_ini = path_to_config_ini or \
          os.path.join(CGN_DS_ROOT, 'common',
                       'config_files', 'config.ini')
        self.config_dict = {}
        self.central_config = None

        # Parse central config first
        central_config_file = os.path.join(CGN_DS_ROOT, 'common',
                                           'config_files', 'config.ini')
        self.parse_config(central_config_file)

        # Parse user specified now if provided
        if self.path_to_config_ini != \
            central_config_file and self.path_to_config_ini != None :
            self.parse_config(self.path_to_config_ini)

        # checks whether config_dict should be updated with os environ values,
        # if True (1) retrieves all the os environs who has prefix "dsenv_"
        # (2) creates os environ dict (3) updates main config_dict
        if env_configs_check:
            self.env_configs = {}
            for k, v in dict(os.environ).items():
                if k.startswith('dsenv_'):
                    k = k[6:]
                    config_list = k.split("__")
                    self.build_env_dict(self.env_configs, config_list, v)
            self.update_dict(self.config_dict, self.env_configs)

    def printall(self, out_file=None):
        """Summary

    Args:
        out_file (None, optional): Description
    """
        if out_file is not None:
            if os.path.exists(out_file):
                self.central_config.save(out_file)
            else:
                print("Couldn't find out_file '{}'...".format(out_file))
        else:
            import yaml
            print(yaml.dump(self.config_dict, default_flow_style=False))

    def parse_config(self, config_file):
        """Summary

    Args:
        config_file (str): Description
    """
        # check if config file exists
        if not os.path.exists(config_file):
            print("Couldn't find config_file '{}'...".format(config_file))
            sys.exit(-1)
        try:
            self.central_config = LocalConfig(config_file, interpolation=True)
        except Exception as e:
            print(e)
            print("Issue with LocalConfig")
            sys.exit(-21)

        try:
            self.central_config._parser = \
              ConfigParser(interpolation=ExtendedInterpolation())
        except Exception as e:
            print(e)
            print("Issue with ConfigParser")
            sys.exit(-22)

        sections = list(self.central_config)
        #Prepare config_dict
        source_dict = {}
        for section in sections:
            # TODO add exception handling configparser.InterpolationMissingOptionError
            source_dict[section] = dict(self.central_config.items(section))

        self.update_dict(self.config_dict, source_dict)

        # Configure sections
        for section in sections:
            if '.' in section:
                self.config_dict[section.split('.')[0]]\
                  [section.split('.')[1]] = self.config_dict[section]
                self.config_dict.pop(section, None)

    def update_dict(self, target_dict, source_dict):
        """Summary

    Args:
        target_dict (str): Description
        source_dict (str): Description
    """
        for k, v in source_dict.items():
            if (k in target_dict and isinstance(target_dict[k], dict) \
                and isinstance(source_dict[k], collections.Mapping)):
                self.update_dict(target_dict[k], source_dict[k])
            else:
                target_dict[k] = source_dict[k]

    def build_env_dict(self, env_configs, config_list, env_val):
        """updates os environ dict env_configs

    Args:
        env_configs (dict): os environment dictionary
        config_list (list): contains hierarchy of config in flat form. For ex:
          if os env is dsenv_service_coordinator__sharding_policy__fixed_shard_count
          this list will contain ["service_coordinator",
          "sharding_policy", "fixed_shard_count"]
        env_val (str/int): os environ value
    """
        head = config_list[0]
        tail = config_list[1:]
        if not tail:
            env_configs[head] = env_val
        else:
            if head not in env_configs:
                env_configs[head] = {}
            self.build_env_dict(env_configs[head], config_list[1:], env_val)

    def get_sub_config(self, type_of_config):
        """Summary

    Args:
        type_of_config (str): Description

    Returns:
        str: Description
    """
        if type_of_config == 'all':
            return self.config_dict
        return self.config_dict[type_of_config]

    def get_sub_config_section(self, section):
        """Summary

    Args:
        section (str): Description

    Returns:
        str: Description

    Raises:
        Exception: Description
    """
        #Currently only allow section depth = 2
        try:
            sub_config_name, section_name = section.split('.')
        except:
            raise Exception('input section error')
        sub_config = self.config_dict[sub_config_name]
        section_config = sub_config[section_name]
        #sub section should inherit all configurations from its parent
        for k, v in sub_config.items():
            if (not isinstance(v, dict)) and (k not in section_config):
                section_config[k] = v
        return section_config

    def is_file(self, filename):
        """Summary

    Args:
        filename (str): Description

    Returns:
        str: Description
    """
        if 'checkpoint-299000' in filename:
            return True
        basename = os.path.basename(filename)
        if len(basename.split('.')) > 1:
            return True
        return False

    def sanity_check(self):
        """Sanity check
    """
        check_file_permission = self.check_file_permission(self.config_dict)
        if check_file_permission == False:
            print(
                "\n******* Config file sanity check failed. Please review '{}'"
                .format(self.path_to_config_ini))
            sys.exit(-1)

    def __getitem__(self, section):
        return self.config_dict.__getitem__(section)
print("This code assumes python 3.7.2\n")

print("install localconfig using pip install localconfig - substitue for configparser")
print("configparser reads all values as string. localconfig tries to interpret the value into correct datatype.")
print(" Warning - localconfig does not work for python2\n")

from localconfig import LocalConfig
from configparser import ExtendedInterpolation
import json

config = LocalConfig(interpolation=ExtendedInterpolation())
config._parser.optionxform = str  #Make keys case sensitive. otherwise keys are all interpreted as lowercase strings. so camelcase, uppercase all goes to hell.
config.read("config.ini")
prop = dict(config.items("PROPERTIES"))
print("Iterate over all variables in section PROPERTIES...")
for key, value in prop.items():
    print(key + " " + str(value) + " " + str(type(value)))

print("\nIterate over all variables in section New Section...")
for key, value in config.items('New Section'):
    print(key + " " + str(value) + " " + str(type(value)))

print("\nno special function to read it as list though. have to break a string into a list yourself.")
print("one easy way to do that if you used json-like string as the value for your listKey is to just parse the value as json.")
listKey = json.loads(prop['listkey'])
listKey[4] = "No Longer!!"
listKey[10] = "do"
##### Updating the dict back to reflect a list, not a string ####
prop['listkey'] = listKey
print("listKey " + str(listKey) + " " + str(type(listKey)))
Example #12
0
def load_config():
    return LocalConfig(os.path.join(os.path.dirname(__file__), 'network.ini'))
Example #13
0
def evaluate(training_images,
             training_labels,
             testing_images,
             testing_labels,
             result_folder,
             trial_id=0):
    if notification:
        # send message to my telegram account
        telemessage.notify_me('Evaluation job started.')

    # load final weight after training
    w = torch.load(os.path.join(result_folder, 'final_weight.pt'))
    v_th = torch.load(os.path.join(result_folder, 'v_th.pt'))

    # load config
    cfg = LocalConfig(os.path.join(result_folder, 'training.ini'))

    # build network
    network = snet.NetworkLoader().from_cfg(config=cfg, weight_map=w)
    network.inference_mode()

    # show weight map
    # network.plot_weight_map(('I', 'O'), 3)

    # adjust configs for testing process
    network.layers['O'].v_th = v_th
    # network.layers['O'].winners = 3
    network.export_cfg().save(os.path.join(result_folder, 'testing.ini'))

    # inference
    # get responses on training set
    training_responses = []

    for idx, image in enumerate(training_images):
        logging.info('Inference training: image #%d, label %d' %
                     (idx, training_labels[idx]))

        firing_step = network.poisson_encoding(image)
        network.layers['I'].set_step(firing_step)

        # Run simulation
        network.run(cfg.input.duration_per_testing_image)
        training_responses.append(network.layers['O'].spike_counts.tolist())
        network.after_batch()

    # get responses on testing set
    testing_responses = []

    for idx, image in enumerate(testing_images):
        logging.info('Inference testing: image #%d, label %d' %
                     (idx, testing_labels[idx]))

        firing_step = network.poisson_encoding(image)
        network.layers['I'].set_step(firing_step)

        # Run simulation
        network.run(cfg.input.duration_per_testing_image)
        spike_counts = network.layers['O'].spike_counts.tolist()
        # print(spike_counts)
        testing_responses.append(spike_counts)
        network.after_batch()

    # SVM training
    clf = svm.SVC(gamma='scale', probability=True)
    clf.fit(training_responses, training_labels)

    def get_accuracy(classifier, samples, sample_labels, trial_name):
        result = collections.OrderedDict()

        result['Trial'] = trial_name

        n = len(samples)

        # predict labels
        predict_labels = classifier.predict(samples)

        # predict probabilities
        probas = classifier.predict_proba(samples)

        acc_on_classes = collections.OrderedDict()
        for lbl in range(cfg.input.start_category, cfg.input.end_category + 1):
            indices = np.nonzero(sample_labels.numpy() == lbl)
            h = (predict_labels[indices] == lbl).sum()
            t = indices[0].shape[0]
            proba_mean = probas[indices].mean(axis=0)

            acc_on_classes[lbl] = (h, t, h / t, proba_mean)

        h = (predict_labels == sample_labels.numpy()).sum()
        acc_summary = (h, n, h / n)

        result['Acc. of each class'] = acc_on_classes
        result['Acc. summary'] = acc_summary

        formatted_str = ''
        # serialize result
        for key, value in result.items():
            if isinstance(value, collections.OrderedDict):
                formatted_str += f'{key}:\n'
                for k, v in value.items():
                    formatted_str += f'    {k}: {v}\n'
            else:
                formatted_str += f'{key}: {value}\n'

        return result, formatted_str

    training_result, training_summary = get_accuracy(clf, training_responses,
                                                     training_labels,
                                                     'Training #%d' % trial_id)
    testing_result, testing_summary = get_accuracy(clf, testing_responses,
                                                   testing_labels,
                                                   'Testing #%d' % trial_id)

    logging.info(training_summary)
    logging.info(testing_summary)

    with open(os.path.join(result_folder, 'result-%d.txt' % trial_id),
              'w') as f:
        f.write(training_summary)
        f.write(testing_summary)

    if notification:
        telemessage.notify_me(
            'Evaluation job completed, summary: \n %s \n %s' %
            (training_summary, testing_summary))

    return training_result, testing_result
Example #14
0
class PiRecorder:
    """
    Recorder class for setting up the rpi for controlled image & video recording

    Parameters
    ----------
    recdir : str, default = "pirecorder/recordings"
        The directory where media will be stored. Default is "recordings". If
        different, a folder with name corresponding to location will be created
        inside the home directory. If no name is provided (""), the files are
        stored in the home directory. If "NAS" is provided it will additionally
        check if the folder links to a mounted drive.
    label : str, default = "test"
        Label for associating with the recording and stored in the filenames.
    rectype : ["img", "imgseq", "vid", "vidseq"], default = "img"
        Recording type, either a single image or video or a sequence of images
        or videos.

    Config settings
    ---------------
    rotation : int, default = 0
        Custom rotation specific to the Raspberry Pi, should be either 0 or 180.
    brighttune : int, default = 0
        A rpi-specific brightness compensation factor to standardize light levels
        across multiple rpi's, an integer between -10 and 10.
    roi : tuple, default = None
        Region of interest to be used for recording. Consists of coordinates of
        topleft and bottom right coordinate of a rectangular area encompassing
        the region of interest. Can be set with the set_roi() method.
    gains : tuple, default = (1.0, 2.5)
        Custom gains specific to the Raspberry PI to set the colorspace. The
        gains for an ideal white balance can be automatically set with the
        get_gains() method.

    brightness : int, default = 45
        The brightness level of the camera, an integer value between 0 and 100.
    contrast : int, default = 20
        The image contrast, an integer value between 0 and 100.
    saturation : int, default -100
        The color saturation level of the image, an integer value between -100
        and 100.
    iso : int, default = 200
        The camera ISO value, an integer value in sequence [200,400,800,1600].
        Higher values are more light sensitive but have higher gain.
    sharpness : int, default = 50
        The sharpness of the camera, an integer value between -100 and 100.
    compensation : int, default = 0
        Camera lighting compensation. Ranges between 0 and 20. Compensation
        artificially adds extra light to the image.
    shutterspeed : int, detault = 10000
        Shutter speed of the camera in microseconds, i.e. the default of 10000
        is equivalent to 1/100th of a second. A longer shutterspeed will result
        in a brighter image but more motion blur. Important: the framerate of
        the camera will be adjusted based on the shutterspeed. At shutter-
        speeds above ~ 0.2s this results in increasingly longer waiting times
        between images so a standard imgwait time should be chosen that is 6+
        times more than the shutterspeed. For example, for a shutterspeed of
        300000 imgwait should be > 1.8s.
    imgdims : tuple, default = (2592, 1944)
        The resolution of the images to be taken in pixels. The default is the
        max resolution that does not return an error for this mode for the v1.5
        rpi camera. Note that rpi camera v2 has a much higher maximum resolution
        of 3280 x 2464.
    viddims : tuple, default = (1640,1232)
        The resolution of the videos to be taken in pixels. The default is the
        max resolution that does not return an error for this mode.
    imgfps : int, default = 1
        The framerate for recording images. Will be set automatically based on
        the imgwait setting so should not be set by user.
    vidfps : int, default = 24
        The framerate for recording video.
    imgwait : float, default = 5.0
    	The delay between subsequent images in seconds. When a delay is provided
      	that is less than ~x5 the shutterspeed, the camera processing time will
        take more time than the provided imgwait parameter and so images are
        taken immideately one after the other. To take a sequence of images at
        the exact right delay interval the imgwait parameter should be at least
        5x the shutterspeed (e.g. shutterspeed of 400ms needs imgwait of 2s).
    imgnr : int, default = 12
        The number of images that should be taken. When this number is reached,
        the recorder will automatically terminate.
    imgtime : integer, default = 60
        The time in seconds during which images should be taken. The minimum of
        a) imgnr and b) nr of images based on imgwait and imgtime will be
        selected.
    imgquality : int, default = 50
        Specifies the quality that the jpeg encoder should attempt to maintain.
        Use values between 1 and 100, where higher values are higher quality.
    vidduration : int, default = 10
        Duration of video recording in seconds.
    viddelay : int, default = 0
        Extra recording time in seconds that will be added to vidduration. Its
        use is for filming acclimatisation time that can then easily be cropped
        for tracking.
    vidquality : int, default = 11
        Specifies the quality that the h264 encoder should attempt to maintain.
        Use values between 10 and 40, where 10 is extremely high quality, and
        40 is extremely low.

    Output
    -------
    Either one or multiple .h264 or .jpg files. All files are automatically
    named according to the label, the host name, date, time and potentially
    session number or count nr, e.g.
    - single image: 'pilot_180312_PI13_101300.jpg
    - multiple images: 'pilot_180312_PI13_img00231_101300.jpg
    - video: 'pilot_180312_PI13_S01_101300.h264

    Returns
    -------
    self : class
        Recorder class instance
    """
    def __init__(self, configfile="pirecorder.conf", logging=True):

        lineprint("pirecorder " + __version__ + " started!")
        lineprint("=" * 47, False)

        self.system = "auto"
        self.host = gethostname()
        self.home = homedir()
        self.setupdir = self.home + "pirecorder"
        self.logfolder = self.setupdir + "/logs"
        if not os.path.exists(self.logfolder):
            os.makedirs(self.setupdir)
            os.makedirs(self.logfolder)
            lineprint("Setup folder created (" + self.setupdir + ")..")
        if not os.path.exists(self.logfolder):
            lineprint(
                "Setup folder already exists but was not set up properly..")

        if logging:
            self.log = Logger(self.logfolder + "/pirecorder.log")
            self.log.start()

        self.brightfile = self.setupdir + "/cusbright.yml"
        self.configfile = self.setupdir + "/" + configfile

        self.config = LocalConfig(self.configfile, compact_form=True)
        if not os.path.isfile(self.configfile):
            lineprint("Config file not found, new file created..")
            for section in ["rec", "cam", "cus", "img", "vid"]:
                if section not in list(self.config):
                    self.config.add_section(section)
            self.set_config(recdir="pirecorder/recordings",
                            subdirs=False,
                            label="test",
                            rectype="vid",
                            rotation=0,
                            brighttune=0,
                            roi=None,
                            gains=(1.0, 2.5),
                            brightness=45,
                            contrast=10,
                            saturation=-100,
                            iso=200,
                            sharpness=0,
                            compensation=0,
                            shutterspeed=8000,
                            imgdims=(2592, 1944),
                            viddims=(1640, 1232),
                            imgfps=1,
                            vidfps=24,
                            imgwait=5.0,
                            imgnr=12,
                            imgtime=60,
                            imgquality=50,
                            vidduration=10,
                            viddelay=10,
                            vidquality=11,
                            internal="")
            lineprint("Config settings stored..")

        else:
            lineprint("Config file " + configfile + " loaded..")
            lineprint("Recording " + self.config.rec.rectype + " in " +\
                          self.home + self.config.rec.recdir)

        self._imgparams()
        self._shuttertofps()
        if self.config.rec.rectype == "imgseq":
            if self.config.cam.shutterspeed / 1000000. <= (
                    self.config.img.imgwait / 5):
                lineprint("imgwait is not enough for provided shutterspeed" + \
                          ", will be overwritten..")

        if self.config.rec.recdir == "NAS":
            if not os.path.ismount(self.config.rec.recdir):
                self.recdir = self.home
                lineprint("Recdir not mounted, storing in home directory..")
        self.recdir = self.home + self.config.rec.recdir
        if not os.path.exists(self.recdir):
            os.makedirs(self.recdir)

        os.chdir(self.recdir)

    def _setup_cam(self):
        """Sets up the raspberry pi camera based on configuration"""

        #load picamera module in-function so pirecorder is installable on all OS
        import picamera
        import picamera.array

        self.cam = picamera.PiCamera()
        self.cam.rotation = self.config.cus.rotation
        self.cam.exposure_compensation = self.config.cam.compensation

        if self.config.rec.rectype in ["img", "imgseq"]:
            self.cam.resolution = literal_eval(self.config.img.imgdims)
            self.cam.framerate = self.config.img.imgfps
        if self.config.rec.rectype in ["vid", "vidseq"]:
            self.cam.resolution = literal_eval(self.config.vid.viddims)
            self.cam.framerate = self.config.vid.vidfps
        self.rawCapture = picamera.array.PiRGBArray(self.cam,
                                                    size=self.cam.resolution)

        if self.config.cus.roi is None:
            self.cam.zoom = (0, 0, 1, 1)
            self.resize = self.cam.resolution
        else:
            self.cam.zoom = literal_eval(self.config.cus.roi)
            w = int(self.cam.resolution[0] * self.cam.zoom[2])
            h = int(self.cam.resolution[1] * self.cam.zoom[3])
            self.resize = picamconv((w, h))

        self.longexpo = False if self.cam.framerate >= 6 else True
        if self.longexpo:
            lineprint("Long exposure, warming up camera..")
            sleep(6) if self.cam.framerate > 1.6 else sleep(10)
        else:
            lineprint("Camera warming up..")
            sleep(2)

        self.cam.shutter_speed = self.config.cam.shutterspeed
        self.cam.exposure_mode = "off"
        self.cam.awb_mode = "off"
        self.cam.awb_gains = checkfrac(self.config.cus.gains)
        brightness = self.config.cam.brightness + self.config.cus.brighttune
        self.cam.brightness = brightness

        self.cam.contrast = self.config.cam.contrast
        self.cam.saturation = self.config.cam.saturation
        self.cam.iso = self.config.cam.iso
        self.cam.sharpness = self.config.cam.sharpness

    def _imgparams(self, mintime=0.45):
        """
        Calculates minimum possible imgwait and imgnr based on imgtime. The
        minimum time between subsequent images is by default set to 0.45s, the
        time it takes to take an image with max resolution.
        """

        self.config.img.imgwait = max(mintime, self.config.img.imgwait)
        totimg = int(self.config.img.imgtime / self.config.img.imgwait)
        self.config.img.imgnr = min(self.config.img.imgnr, totimg)

    def _shuttertofps(self, minfps=1, maxfps=40):
        """Computes image fps based on shutterspeed within provided range"""

        fps = 1. / (self.config.cam.shutterspeed / 1000000.)
        fps = max(fps, minfps)
        self.config.img.imgfps = min(fps, maxfps)

    def _namefile(self):
        """
        Provides a filename for the media recorded. Filenames include label,
        date, rpi name, and time. Images part of image sequence additionally
        contain a sequence number. e.g. test_180708_pi12_S01_100410
        """

        imgtypes = ["img", "imgseq"]
        self.filetype = ".jpg" if self.config.rec.rectype in imgtypes else ".h264"

        if self.config.rec.rectype == "imgseq":
            date = strftime("%y%m%d")
            counter = "im{counter:05d}" if self.config.img.imgnr > 999 else "im{counter:03d}"
            time = "{timestamp:%H%M%S}"
            self.filename = "_".join(
                [self.config.rec.label, date, self.host, counter, time])
            self.filename = self.filename + self.filetype
        else:
            date = strftime("%y%m%d")
            self.filename = "_".join([self.config.rec.label, date, self.host
                                      ]) + "_"

        if self.config.rec.subdirs:
            subdir = name("_".join([self.config.rec.label, date, self.host]))
            os.makedirs(subdir, exist_ok=True)
            self.filename = subdir + "/" + self.filename

    def set_config(self, **kwargs):
        """ Dynamically sets the configuration file """

        if "recdir" in kwargs:
            self.config.rec.recdir = kwargs["recdir"]
        if "subdirs" in kwargs:
            self.config.rec.subdirs = kwargs["subdirs"]
        if "label" in kwargs:
            self.config.rec.label = kwargs["label"]
        if "rectype" in kwargs:
            self.config.rec.rectype = kwargs["rectype"]

        if "rotation" in kwargs:
            self.config.cus.rotation = kwargs["rotation"]
        if "brighttune" in kwargs:
            self.config.cus.brighttune = kwargs["brighttune"]
        if "roi" in kwargs:
            self.config.cus.roi = kwargs["roi"]
        if "gains" in kwargs:
            self.config.cus.gains = kwargs["gains"]

        if "brightness" in kwargs:
            self.config.cam.brightness = kwargs["brightness"]
        if "contrast" in kwargs:
            self.config.cam.contrast = kwargs["contrast"]
        if "saturation" in kwargs:
            self.config.cam.saturation = kwargs["saturation"]
        if "iso" in kwargs:
            self.config.cam.iso = kwargs["iso"]
        if "sharpness" in kwargs:
            self.config.cam.sharpness = kwargs["sharpness"]
        if "compensation" in kwargs:
            self.config.cam.compensation = kwargs["compensation"]
        if "shutterspeed" in kwargs:
            self.config.cam.shutterspeed = kwargs["shutterspeed"]

        if "imgdims" in kwargs:
            self.config.img.imgdims = kwargs["imgdims"]
        if "viddims" in kwargs:
            self.config.vid.viddims = kwargs["viddims"]
        if "imgfps" in kwargs:
            self.config.img.imgfps = kwargs["imgfps"]
        if "vidfps" in kwargs:
            self.config.vid.vidfps = kwargs["vidfps"]

        if "imgwait" in kwargs:
            self.config.img.imgwait = kwargs["imgwait"]
        if "imgnr" in kwargs:
            self.config.img.imgnr = kwargs["imgnr"]
        if "imgtime" in kwargs:
            self.config.img.imgtime = kwargs["imgtime"]
        if "imgquality" in kwargs:
            self.config.img.imgquality = kwargs["imgquality"]

        if "vidduration" in kwargs:
            self.config.vid.vidduration = kwargs["vidduration"]
        if "viddelay" in kwargs:
            self.config.vid.viddelay = kwargs["viddelay"]
        if "vidquality" in kwargs:
            self.config.vid.vidquality = kwargs["vidquality"]

        brightchange = False
        if os.path.exists(self.brightfile):
            with open(self.brightfile) as f:
                brighttune = yaml.load(f, Loader=yaml.FullLoader)
                if brighttune != self.config.cus.brighttune:
                    self.config.cus.brighttune = brighttune
                    brightchange = True

        if len(kwargs) > 0 or brightchange:

            self._imgparams()
            self._shuttertofps()
            self.config.save()

            if "internal" not in kwargs:
                lineprint("Config settings stored and loaded..")

    def set_roi(self):
        """
        Dynamically draw a region of interest

        Explanation
        ===========
        This function will open a video stream of the raspberry pi camera. Enter
        'd' to start drawing the region of interest on an image taken from the
        video stream. When happy with the region selected, press 's' to store
        the coordinates, or 'esc' key to exit drawing on the image. To exist the
        video stream enter 'esc' key again.
        """

        C = Calibrate(internal=True, rotation=self.config.cus.rotation)
        if C.roi:
            self.set_config(roi=C.roi, internal="")
            lineprint("Roi stored..")
        else:
            lineprint("No roi selected..")

    def set_gains(self, auto=True):
        """Find the best gains for the raspberry pi camera"""

        if self.config.cus.roi == None:
            zoom = (0, 0, 1, 1)
        else:
            zoom = literal_eval(self.config.cus.roi)
        (rg, bg) = setgains(startgains=checkfrac(self.config.cus.gains),
                            zoom=zoom,
                            auto=auto)
        self.set_config(gains="(%5.2f, %5.2f)" % (rg, bg), internal="")
        lineprint("Gains: " + "(R:%5.2f, B:%5.2f)" % (rg, bg) + " stored..")

    def schedule(self,
                 jobname=None,
                 timeplan=None,
                 enable=True,
                 showjobs=False,
                 clear=None,
                 test=False):

        S = Schedule(jobname,
                     timeplan,
                     enable,
                     showjobs,
                     clear,
                     test,
                     logfolder=self.logfolder,
                     internal=True)

    def record(self):
        """Runs the Recorder instance"""

        self._setup_cam()
        self._namefile()

        if self.config.rec.rectype == "img":

            self.filename = self.filename + strftime("%H%M%S") + self.filetype
            self.cam.capture(self.filename,
                             format="jpeg",
                             resize=self.resize,
                             quality=self.config.img.imgquality)
            lineprint("Captured " + self.filename)

        elif self.config.rec.rectype == "imgseq":

            timepoint = datetime.now()
            for i, img in enumerate(
                    self.cam.capture_continuous(
                        self.filename,
                        format="jpeg",
                        resize=self.resize,
                        quality=self.config.img.imgquality)):
                if i < self.config.img.imgnr - 1:
                    timepassed = (datetime.now() - timepoint).total_seconds()
                    delay = max(0, self.config.img.imgwait - timepassed)
                    lineprint("Captured " + img + ", sleeping " +
                              str(round(delay, 2)) + "s..")
                    sleep(delay)
                    timepoint = datetime.now()
                else:
                    lineprint("Captured " + img)
                    break

        elif self.config.rec.rectype in ["vid", "vidseq"]:

            # Temporary fix for flicker at start of (first) video..
            self.cam.start_recording(BytesIO(),
                                     format="h264",
                                     resize=self.resize)
            self.cam.wait_recording(2)
            self.cam.stop_recording()

            for session in ["_S%02d" % i for i in range(1, 999)]:
                session = "" if self.config.rec.rectype == "vid" else session
                filename = self.filename + strftime(
                    "%H%M%S") + session + self.filetype
                self.cam.start_recording(filename,
                                         resize=self.resize,
                                         quality=self.config.vid.vidquality)
                lineprint("Start recording " + filename)
                self.cam.wait_recording(self.config.vid.vidduration +
                                        self.config.vid.viddelay)
                self.cam.stop_recording()
                lineprint("Finished recording " + filename)
                if self.config.rec.rectype == "vid":
                    break
                else:
                    if input("\nAny key for new session, e to exit: ") == "e":
                        break

        self.cam.close()