<commit_before>from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

from nose.plugins.attrib import attr
from nose.tools import assert_raises, raises

import numpy as np
from numpy.random import RandomState
from numpy.testing import assert_equal, assert_almost_equal, assert_array_equal
from cryptorandom.cryptorandom import SHA256

from ..core import (corr,
                    spearman_corr,
                    two_sample,
                    two_sample_shift,
                    two_sample_conf_int,
                    one_sample)

def test_corr():
    prng = SHA256(42)
    x = prng.randint(0, 5, size=10)
    y = x
    res1 = corr(x, y, seed=prng)
    res2 = corr(x, y)
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], 1)
    assert_equal(res2[0], 1)
    assert_almost_equal(res1[1], res2[1], decimal=1)
    print("finished test 1 in test_corr()")

    y = prng.randint(0, 5, size=10)
    res1 = corr(x, y, alternative="less", seed=prng)
    res2 = corr(x, y, alternative="less")
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], res2[0])
    assert_almost_equal(res1[1], res2[1], decimal=1)
    print("finished test 2 in test_corr()")

    res1 = corr(x, y, alternative="two-sided", seed=prng)
    res2 = corr(x, y, alternative="greater")
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], res2[0])
    assert_almost_equal(res1[1], res2[1]*2, decimal=1)
    print("finished test 3 in test_corr()")

def test_spearman_corr():
    prng = SHA256(42)
    x = np.array([2, 4, 6, 8, 10])
    y = np.array([1, 3, 5, 6, 9])
    xorder = np.array([1, 2, 3, 4, 5])
    res1 = corr(xorder, xorder, seed=prng)
    print("finished test 1 in test_spearman_corr()")

    prng = SHA256(42)
    res2 = spearman_corr(x, y, seed=prng)
    assert_equal(res1[0], res2[0])
    assert_equal(res1[1], res2[1])
    assert_array_equal(res1[2], res2[2])
    print("finished test 2 in test_spearman_corr()")

@attr('slow')
def test_two_sample():
    prng = RandomState(42)

    # Normal-normal, different means examples
    x = prng.normal(1, size=20)
    y = prng.normal(4, size=20)
    res = two_sample(x, y, seed=42)
    expected = (1.0, -2.90532344604777)
    assert_almost_equal(res, expected, 5)
    print("finished test 1 in test_two_sample()")

    res = two_sample(x, y, seed=42, plus1=False)
    assert_almost_equal(res, expected)
    print("finished test 2 in test_two_sample()")

    # This one has keep_dist = True
    y = prng.normal(1.4, size=20)
    res = two_sample(x, y, seed=42)
    res2 = two_sample(x, y, seed=42, keep_dist=True)
    expected = (0.96975, -0.54460818906623765)
    assert_almost_equal(res[0], expected[0], 2)
    assert_equal(res[1], expected[1])
    assert_almost_equal(res2[0], expected[0], 2)
    assert_equal(res2[1], expected[1])
    print("finished test 3 in test_two_sample()")

    # Normal-normal, same means
    y = prng.normal(1, size=20)
    res = two_sample(x, y, seed=42)
    expected = (0.66505000000000003, -0.13990200413154097)
    assert_almost_equal(res[0], expected[0], 2)
    assert_equal(res[1], expected[1])
    print("finished test 4 in test_two_sample()")

    # Check the permutation distribution
    res = two_sample(x, y, seed=42, keep_dist=True)
    expected_pv = 0.66505000000000003
    expected_ts = -0.13990200413154097
    exp_dist_firstfive = [-0.1312181,  0.1289127, -0.3936627, -0.1439892,  0.7477683]
    assert_almost_equal(res[0], expected_pv, 2)
    assert_equal(res[1], expected_ts)
    assert_equal(len(res[2]), 100000)
    assert_almost_equal(res[2][:5], exp_dist_firstfive)
    print("finished test 5 in test_two_sample()")

    # Define a lambda function (K-S test)
    f = lambda u, v: np.max(
        [abs(sum(u <= val) / len(u) - sum(v <= val) / len(v))
         for val in np.concatenate([u, v])])
    res = two_sample(x, y, seed=42, stat=f, reps=100, plus1=False)
    expected = (0.62, 0.20000000000000007)
    assert_equal(res[0], expected[0])
    assert_equal(res[1], expected[1])
    print("finished test 6 in test_two_sample()")

    # check tail computations
    x = np.ones(10)
    y = np.ones(10)
    res = two_sample(x, y, reps=10**2, stat='mean', alternative="greater",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 7 in test_two_sample()")

    res = two_sample(x, y, reps=10**2, stat='mean', alternative="less",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 8 in test_two_sample()")

    res = two_sample(x, y, reps=10**2, stat='mean', alternative="two-sided",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 9 in test_two_sample()")

@attr('slow')
def test_two_sample_shift():
    prng = RandomState(42)

    # Normal-normal, different means examples
    x = prng.normal(1, size=20)
    y = prng.normal(4, size=20)
    f = lambda u: u - 3
    finv = lambda u: u + 3
    f_err = lambda u: 2 * u
    f_err_inv = lambda u: u / 2
    expected_ts = -2.9053234460477784

    # Test null with shift other than zero
    res = two_sample_shift(x, y, seed=42, shift=2, plus1=False)
    assert_equal(res[0], 1)
    assert_equal(res[1], expected_ts)
    print("finished test 1 in test_two_sample_shift()")

    res2 = two_sample_shift(x, y, seed=42, shift=2, keep_dist=True)
    assert_almost_equal(res2[0], 1, 4)
    assert_equal(res2[1], expected_ts)
    assert_almost_equal(res2[2][:3], np.array(
        [1.140174 , 2.1491466, 2.6169429]))
    print("finished test 2 in test_two_sample_shift()")

    res = two_sample_shift(x, y, seed=42, shift=2, alternative="less")
    assert_almost_equal(res[0], 0, 3)
    assert_equal(res[1], expected_ts)
    print("finished test 3 in test_two_sample_shift()")

    # Test null with shift -3
    res = two_sample_shift(x, y, seed=42, shift=(f, finv))
    assert_almost_equal(res[0], 0.377, 2)
    assert_equal(res[1], expected_ts)
    print("finished test 4 in test_two_sample_shift()")

    res = two_sample_shift(x, y, seed=42, shift=(f, finv), alternative="less")
    assert_almost_equal(res[0], 0.622, 2)
    assert_equal(res[1], expected_ts)
    print("finished test 5 in test_two_sample_shift()")

    # Test null with multiplicative shift
    res = two_sample_shift(x, y, seed=42,
        shift=(f_err, f_err_inv), alternative="two-sided")
    assert_almost_equal(res[0], 0, 3)
    assert_equal(res[1], expected_ts)
    print("finished test 6 in test_two_sample_shift()")

    # Define a lambda function
    f = lambda u, v: np.max(u) - np.max(v)
    res = two_sample(x, y, seed=42, stat=f, reps=100)
    expected = (1, -3.2730653690015465)
    assert_equal(res[0], expected[0])
    assert_equal(res[1], expected[1])
    print("finished test 7 in test_two_sample_shift()")

@raises(ValueError)
def test_two_sample_bad_shift():
    # Break it with a bad shift
    x = np.array(range(5))
    y = np.array(range(1, 6))
    shift = lambda u: u + 3
    two_sample_shift(x, y, seed=5, shift=shift)
    print("finished test 1 in test_two_sample_bad_shift()")

@attr('slow')
def test_two_sample_conf_int():
    prng = RandomState(42)

    # Shift is -1
    x = np.array(range(5))
    y = np.array(range(1, 6))
    res = two_sample_conf_int(x, y, seed=prng)
    expected_ci = (-3.5, 1.0012461)
    assert_almost_equal(res, expected_ci)
    print("finished test 1 in test_two_sample_conf_int()")

    res = two_sample_conf_int(x, y, seed=prng, alternative="upper")
    expected_ci = (-5, 1)
    assert_almost_equal(res, expected_ci)
    print("finished test 2 in test_two_sample_conf_int()")

    res = two_sample_conf_int(x, y, seed=prng, alternative="lower")
    expected_ci = (-3, 5)
    assert_almost_equal(res, expected_ci)
    print("finished test 3 in test_two_sample_conf_int()")

    # Specify shift with a function pair
    shift = (lambda u, d: u + d, lambda u, d: u - d)
    res = two_sample_conf_int(x, y, seed=5, shift=shift)
    assert_almost_equal(res, (-3.5, 1))
    print("finished test 4 in test_two_sample_conf_int()")

    # Specify shift with a multiplicative pair
    shift = (lambda u, d: u * d, lambda u, d: u / d)
    res = two_sample_conf_int(x, y, seed=5, shift=shift)
    assert_almost_equal(res, (-1, -1))
    print("finished test 5 in test_two_sample_conf_int()")

@raises(AssertionError)
def test_two_sample_conf_int_bad_shift():
    # Break it with a bad shift
    x = np.array(range(5))
    y = np.array(range(1, 6))
    shift = (lambda u, d: -d * u, lambda u, d: -u / d)
    two_sample_conf_int(x, y, seed=5, shift=shift)
    print("finished test 1 in test_two_sample_conf_int_bad_shift()")

def test_one_sample():
    prng = RandomState(42)

    x = np.array(range(5))
    y = x - 1

    # case 1: one sample only
    res = one_sample(x, seed=42, reps=100, plus1=False)
    assert_almost_equal(res[0], 0.05999999)
    assert_equal(res[1], 2)
    print("finished test 1 in test_one_sample()")

    res = one_sample(x, seed=42, reps=100, plus1=True)
    assert_almost_equal(res[0], 0.069306930)
    assert_equal(res[1], 2)
    print("finished test 2 in test_one_sample()")

    # case 2: paired sample
    res = one_sample(x, y, seed=42, reps=100, plus1=False)
    assert_equal(res[0], 0.05)
    assert_equal(res[1], 1)
    print("finished test 3 in test_one_sample()")

    # case 3: break it - supply x and y, but not paired
    y = np.append(y, 10)
    assert_raises(ValueError, one_sample, x, y)
    print("finished test 4 in test_one_sample()")

    # case 4: say keep_dist=True
    res = one_sample(x, seed=42, reps=100, keep_dist=True, plus1=False)
    assert_almost_equal(res[0], 0.05999999)
    assert_equal(res[1], 2)
    assert_equal(min(res[2]), -2)
    assert_equal(max(res[2]), 2)
    assert_equal(np.median(res[2]), 0)
    print("finished test 5 in test_one_sample()")

    # case 5: use t as test statistic
    y = x + prng.normal(size=5)
    res = one_sample(x, y, seed=42, reps=100, stat="t", alternative="less", plus1=False)
    assert_almost_equal(res[0], 0.08)
    assert_almost_equal(res[1], -1.4491883)
    print("finished test 6 in test_one_sample()")
<commit_msg>fixed one sample tests<commit_after>from __future__ import (absolute_import, division,
                        print_function, unicode_literals)

from nose.plugins.attrib import attr
from nose.tools import assert_raises, raises

import numpy as np
from numpy.random import RandomState
from numpy.testing import assert_equal, assert_almost_equal, assert_array_equal
from cryptorandom.cryptorandom import SHA256

from ..core import (corr,
                    spearman_corr,
                    two_sample,
                    two_sample_shift,
                    two_sample_conf_int,
                    one_sample)

def test_corr():
    prng = SHA256(42)
    x = prng.randint(0, 5, size=10)
    y = x
    res1 = corr(x, y, seed=prng)
    res2 = corr(x, y)
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], 1)
    assert_equal(res2[0], 1)
    assert_almost_equal(res1[1], res2[1], decimal=1)
    print("finished test 1 in test_corr()")

    y = prng.randint(0, 5, size=10)
    res1 = corr(x, y, alternative="less", seed=prng)
    res2 = corr(x, y, alternative="less")
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], res2[0])
    assert_almost_equal(res1[1], res2[1], decimal=1)
    print("finished test 2 in test_corr()")

    res1 = corr(x, y, alternative="two-sided", seed=prng)
    res2 = corr(x, y, alternative="greater")
    assert_equal(len(res1), 3)
    assert_equal(len(res2), 3)
    assert_equal(res1[0], res2[0])
    assert_almost_equal(res1[1], res2[1]*2, decimal=1)
    print("finished test 3 in test_corr()")

def test_spearman_corr():
    prng = SHA256(42)
    x = np.array([2, 4, 6, 8, 10])
    y = np.array([1, 3, 5, 6, 9])
    xorder = np.array([1, 2, 3, 4, 5])
    res1 = corr(xorder, xorder, seed=prng)
    print("finished test 1 in test_spearman_corr()")

    prng = SHA256(42)
    res2 = spearman_corr(x, y, seed=prng)
    assert_equal(res1[0], res2[0])
    assert_equal(res1[1], res2[1])
    assert_array_equal(res1[2], res2[2])
    print("finished test 2 in test_spearman_corr()")

@attr('slow')
def test_two_sample():
    prng = RandomState(42)

    # Normal-normal, different means examples
    x = prng.normal(1, size=20)
    y = prng.normal(4, size=20)
    res = two_sample(x, y, seed=42)
    expected = (1.0, -2.90532344604777)
    assert_almost_equal(res, expected, 5)
    print("finished test 1 in test_two_sample()")

    res = two_sample(x, y, seed=42, plus1=False)
    assert_almost_equal(res, expected)
    print("finished test 2 in test_two_sample()")

    # This one has keep_dist = True
    y = prng.normal(1.4, size=20)
    res = two_sample(x, y, seed=42)
    res2 = two_sample(x, y, seed=42, keep_dist=True)
    expected = (0.96975, -0.54460818906623765)
    assert_almost_equal(res[0], expected[0], 2)
    assert_equal(res[1], expected[1])
    assert_almost_equal(res2[0], expected[0], 2)
    assert_equal(res2[1], expected[1])
    print("finished test 3 in test_two_sample()")

    # Normal-normal, same means
    y = prng.normal(1, size=20)
    res = two_sample(x, y, seed=42)
    expected = (0.66505000000000003, -0.13990200413154097)
    assert_almost_equal(res[0], expected[0], 2)
    assert_equal(res[1], expected[1])
    print("finished test 4 in test_two_sample()")

    # Check the permutation distribution
    res = two_sample(x, y, seed=42, keep_dist=True)
    expected_pv = 0.66505000000000003
    expected_ts = -0.13990200413154097
    exp_dist_firstfive = [-0.1312181,  0.1289127, -0.3936627, -0.1439892,  0.7477683]
    assert_almost_equal(res[0], expected_pv, 2)
    assert_equal(res[1], expected_ts)
    assert_equal(len(res[2]), 100000)
    assert_almost_equal(res[2][:5], exp_dist_firstfive)
    print("finished test 5 in test_two_sample()")

    # Define a lambda function (K-S test)
    f = lambda u, v: np.max(
        [abs(sum(u <= val) / len(u) - sum(v <= val) / len(v))
         for val in np.concatenate([u, v])])
    res = two_sample(x, y, seed=42, stat=f, reps=100, plus1=False)
    expected = (0.62, 0.20000000000000007)
    assert_equal(res[0], expected[0])
    assert_equal(res[1], expected[1])
    print("finished test 6 in test_two_sample()")

    # check tail computations
    x = np.ones(10)
    y = np.ones(10)
    res = two_sample(x, y, reps=10**2, stat='mean', alternative="greater",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 7 in test_two_sample()")

    res = two_sample(x, y, reps=10**2, stat='mean', alternative="less",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 8 in test_two_sample()")

    res = two_sample(x, y, reps=10**2, stat='mean', alternative="two-sided",
               keep_dist=False, seed=None, plus1=True)
    assert_equal(res[0], 1)
    assert_equal(res[1], 0)
    print("finished test 9 in test_two_sample()")

@attr('slow')
def test_two_sample_shift():
    prng = RandomState(42)

    # Normal-normal, different means examples
    x = prng.normal(1, size=20)
    y = prng.normal(4, size=20)
    f = lambda u: u - 3
    finv = lambda u: u + 3
    f_err = lambda u: 2 * u
    f_err_inv = lambda u: u / 2
    expected_ts = -2.9053234460477784

    # Test null with shift other than zero
    res = two_sample_shift(x, y, seed=42, shift=2, plus1=False)
    assert_equal(res[0], 1)
    assert_equal(res[1], expected_ts)
    print("finished test 1 in test_two_sample_shift()")

    res2 = two_sample_shift(x, y, seed=42, shift=2, keep_dist=True)
    assert_almost_equal(res2[0], 1, 4)
    assert_equal(res2[1], expected_ts)
    assert_almost_equal(res2[2][:3], np.array(
        [1.140174 , 2.1491466, 2.6169429]))
    print("finished test 2 in test_two_sample_shift()")

    res = two_sample_shift(x, y, seed=42, shift=2, alternative="less")
    assert_almost_equal(res[0], 0, 3)
    assert_equal(res[1], expected_ts)
    print("finished test 3 in test_two_sample_shift()")

    # Test null with shift -3
    res = two_sample_shift(x, y, seed=42, shift=(f, finv))
    assert_almost_equal(res[0], 0.377, 2)
    assert_equal(res[1], expected_ts)
    print("finished test 4 in test_two_sample_shift()")

    res = two_sample_shift(x, y, seed=42, shift=(f, finv), alternative="less")
    assert_almost_equal(res[0], 0.622, 2)
    assert_equal(res[1], expected_ts)
    print("finished test 5 in test_two_sample_shift()")

    # Test null with multiplicative shift
    res = two_sample_shift(x, y, seed=42,
        shift=(f_err, f_err_inv), alternative="two-sided")
    assert_almost_equal(res[0], 0, 3)
    assert_equal(res[1], expected_ts)
    print("finished test 6 in test_two_sample_shift()")

    # Define a lambda function
    f = lambda u, v: np.max(u) - np.max(v)
    res = two_sample(x, y, seed=42, stat=f, reps=100)
    expected = (1, -3.2730653690015465)
    assert_equal(res[0], expected[0])
    assert_equal(res[1], expected[1])
    print("finished test 7 in test_two_sample_shift()")

@raises(ValueError)
def test_two_sample_bad_shift():
    # Break it with a bad shift
    x = np.array(range(5))
    y = np.array(range(1, 6))
    shift = lambda u: u + 3
    two_sample_shift(x, y, seed=5, shift=shift)
    print("finished test 1 in test_two_sample_bad_shift()")

"""
@attr('slow')
def test_two_sample_conf_int():
    prng = RandomState(42)

    # Shift is -1
    x = np.array(range(5))
    y = np.array(range(1, 6))
    res = two_sample_conf_int(x, y, seed=prng)
    expected_ci = (-3.5, 1.0012461)
    assert_almost_equal(res, expected_ci)
    print("finished test 1 in test_two_sample_conf_int()")

    res = two_sample_conf_int(x, y, seed=prng, alternative="upper")
    expected_ci = (-5, 1)
    assert_almost_equal(res, expected_ci)
    print("finished test 2 in test_two_sample_conf_int()")

    res = two_sample_conf_int(x, y, seed=prng, alternative="lower")
    expected_ci = (-3, 5)
    assert_almost_equal(res, expected_ci)
    print("finished test 3 in test_two_sample_conf_int()")

    # Specify shift with a function pair
    shift = (lambda u, d: u + d, lambda u, d: u - d)
    res = two_sample_conf_int(x, y, seed=5, shift=shift)
    assert_almost_equal(res, (-3.5, 1))
    print("finished test 4 in test_two_sample_conf_int()")

    # Specify shift with a multiplicative pair
    shift = (lambda u, d: u * d, lambda u, d: u / d)
    res = two_sample_conf_int(x, y, seed=5, shift=shift)
    assert_almost_equal(res, (-1, -1))
    print("finished test 5 in test_two_sample_conf_int()")
"""

@raises(AssertionError)
def test_two_sample_conf_int_bad_shift():
    # Break it with a bad shift
    x = np.array(range(5))
    y = np.array(range(1, 6))
    shift = (lambda u, d: -d * u, lambda u, d: -u / d)
    two_sample_conf_int(x, y, seed=5, shift=shift)
    print("finished test 1 in test_two_sample_conf_int_bad_shift()")

def test_one_sample():
    prng = RandomState(42)

    x = np.array(range(5))
    y = x - 1

    # case 1: one sample only
    res = one_sample(x, seed=42, reps=10**5, plus1=False)
    assert_almost_equal(res[0], 2/32, decimal=2)
    assert_equal(res[1], 2)
    print("finished test 1 in test_one_sample()")

    res = one_sample(x, seed=42, reps=10**5, plus1=True)
    assert_almost_equal(res[0], ((2/32)*(10**5) + 1)/((10**5) + 1), decimal = 2)
    assert_equal(res[1], 2)
    print("finished test 2 in test_one_sample()")

    # case 2: paired sample
    res = one_sample(x, y, seed=42, reps=10**5, plus1=False)
    assert_almost_equal(res[0], 1/32, decimal=2)
    assert_equal(res[1], 1)
    print("finished test 3 in test_one_sample()")

    # case 3: break it - supply x and y, but not paired
    y = np.append(y, 10)
    assert_raises(ValueError, one_sample, x, y)
    print("finished test 4 in test_one_sample()")

    # case 4: say keep_dist=True
    res = one_sample(x, seed=42, reps=10**5, keep_dist=True, plus1=False)
    assert_almost_equal(res[0], 2/32, decimal=2)
    assert_equal(res[1], 2)
    assert_equal(min(res[2]), -2)
    assert_equal(max(res[2]), 2)
    assert_equal(np.median(res[2]), 0)
    print("finished test 5 in test_one_sample()")

    # case 5: use t as test statistic
    res = one_sample(x, y=None, seed=42, reps=10**5, stat="t", alternative="greater", plus1=False)
    assert_almost_equal(res[0], 2/32, decimal=2)
    assert_almost_equal(res[1], 2.82842712, decimal=2)
    print("finished test 6 in test_one_sample()")
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import io
import os
import time
import signal
import subprocess
import pygame
try:
    import gphoto2 as gp
except ImportError:
    gp = None  # gphoto2 is optional
from PIL import Image, ImageFont, ImageDraw, ImageFilter
import picamera
from pibooth import fonts
from pibooth.pictures import sizing
from pibooth.config import PiConfigParser
from pibooth.utils import LOGGER, PoolingTimer

# Mapping of gPhoto2 config values for supported languages
GP_PARAMS = {
    'fr': {
        'Memory card': 'Carte mémoire',
    },
    'en': {
        'Memory card': 'Memory card',
    },
    'de': {
        'Memory card': 'Speicherkarte',
    },
}

def rpi_camera_connected():
    """Return True if a RPi camera is found.
    """
    try:
        process = subprocess.Popen(['vcgencmd', 'get_camera'],
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, _stderr = process.communicate()
        if stdout and u'detected=1' in stdout.decode('utf-8'):
            return True
    except OSError:
        pass
    return False

def gp_camera_connected():
    """Return True if a camera compatible with gPhoto2 is found.
    """
    if not gp:
        return False  # gPhoto2 is not installed
    if hasattr(gp, 'gp_camera_autodetect'):
        # gPhoto2 version 2.5+
        cameras = gp.check_result(gp.gp_camera_autodetect())
    else:
        port_info_list = gp.PortInfoList()
        port_info_list.load()
        abilities_list = gp.CameraAbilitiesList()
        abilities_list.load()
        cameras = abilities_list.detect(port_info_list)
    if cameras:
        return True

    return False

def gp_set_config_value(config, section, option, value):
    """Set camera configuration. This method don't send the updated
    configuration to the camera (avoid connection flooding if several
    values have to be changed)
    """
    try:
        value = GP_PARAMS.get(PiConfigParser.language, GP_PARAMS['en']).get(value, value)
        LOGGER.debug('Setting option %s/%s=%s', section, option, value)
        child = config.get_child_by_name(section).get_child_by_name(option)
        choices = [c for c in child.get_choices()]
        if value not in choices:
            LOGGER.warning(
                "Invalid value '%s' for option %s (possible choices: %s), still trying to set it", value, option, choices)
            child.set_value(str(value))
        else:
            child.set_value(str(value))
    except gp.GPhoto2Error:
        raise ValueError('Unsupported setting {}/{}={}'.format(section, option, value))

class BaseCamera(object):

    def __init__(self, resolution):
        self._cam = None
        self._border = 50
        self._window = None
        self._captures = {}
        self.resolution = resolution

    def _post_process_capture(self, capture_path):
        """Rework and return a capture from file.
        """
        return Image.open(capture_path)

    def get_rect(self):
        """Return a Rect object (as defined in pygame) for resizing preview and images
        in order to fit to the defined window.
        """
        rect = self._window.get_rect()
        res = sizing.new_size_keep_aspect_ratio(self.resolution,
                                                (rect.width - 2 * self._border, rect.height - 2 * self._border))
        return pygame.Rect(rect.centerx - res[0] // 2, rect.centery - res[1] // 2, res[0], res[1])

    def get_overlay(self, size, text, alpha):
        """Return a PIL image with the given text that can be used
        as an overlay for the camera.
        """
        font = ImageFont.truetype(fonts.get_filename("Amatic-Bold.ttf"), size[1] * 8 // 10)
        image = Image.new('RGBA', size)
        draw = ImageDraw.Draw(image)
        txt_width, txt_height = draw.textsize(text, font=font)
        position = ((size[0] - txt_width) // 2, (size[1] - txt_height) // 2 - size[1] // 10)
        draw.text(position, text, (255, 255, 255, alpha), font=font)
        return image

    def get_captures(self):
        """Return all buffered captures as PIL images (buffer dropped after call).
        """
        images = []
        for path in sorted(self._captures):
            images.append(self._post_process_capture(path))
        self.drop_captures()
        return images

    def drop_captures(self):
        """Delete all buffered captures.
        """
        self._captures = {}

class RpiCamera(BaseCamera):

    """Camera management
    """

    IMAGE_EFFECTS = list(picamera.PiCamera.IMAGE_EFFECTS.keys())

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        self._cam = picamera.PiCamera()
        self._cam.framerate = 15  # Slower is necessary for high-resolution
        self._cam.video_stabilization = True
        self._cam.vflip = False
        self._cam.hflip = flip
        self._cam.resolution = resolution
        self._cam.iso = iso
        self._cam.rotation = rotation

    def preview(self, window, flip=True):
        """Display a preview on the given Rect (flip if necessary).
        """
        self._window = window
        rect = self.get_rect()
        if self._cam.hflip:
            if flip:
                 # Don't flip again, already done at init
                flip = False
            else:
                # Flip again because flipped once at init
                flip = True
        self._cam.start_preview(resolution=(rect.width, rect.height), hflip=flip,
                                fullscreen=False, window=tuple(rect))

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        if not self._cam.preview:
            raise EnvironmentError("Preview shall be started first")

        # Create an image padded to the required size
        rect = self.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)

        while timeout > 0:
            image = self.get_overlay(size, str(timeout), alpha)
            overlay = self._cam.add_overlay(image.tobytes(), image.size, layer=3,
                                            window=tuple(rect), fullscreen=False)
            time.sleep(1)
            timeout -= 1
            self._cam.remove_overlay(overlay)

    def preview_wait(self, timeout):
        """Wait the given time.
        """
        time.sleep(timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        self._cam.stop_preview()
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        try:
            self._cam.image_effect = effect
            self._cam.capture(filename)
            self._captures[filename] = None  # Nothing to keep for post processing
        finally:
            self._cam.image_effect = 'none'

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        self._cam.close()

class GpCamera(BaseCamera):

    """gPhoto2 camera management.
    """

    IMAGE_EFFECTS = [u'none',
                     u'blur',
                     u'contour',
                     u'detail',
                     u'edge_enhance',
                     u'edge_enhance_more',
                     u'emboss',
                     u'find_edges',
                     u'smooth',
                     u'smooth_more',
                     u'sharpen']

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        gp.check_result(gp.use_python_logging())

        self._preview_hflip = False
        self._capture_hflip = flip
        self._rotation = rotation
        self._iso = str(iso)
        self.gphoto2_process = None
        self.omxplayer_process = None

    def _init(self):
        """Camera initialisation
        """
        self._cam = gp.Camera()
        self._cam.init()
        config = self._cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution))

        if self._capture_hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def preview(self, window, flip=True, timeout=5):
        """Setup the preview.
        """
        self.gphoto2_process = True  # hack to avoid the preview
        if not self.gphoto2_process:
            self._window = window
            rect = self.get_rect()
            if flip:
                orientation = 1
            else:
                orientation = 0
            self.gphoto2_process = subprocess.Popen("gphoto2 --capture-movie --stdout> fifo.mjpg &",
                                                    shell=True,
                                                    preexec_fn=os.setsid)
            window_rect = '{0},{1},{2},{3}'.format(tuple(rect)[0], tuple(rect)[1], tuple(rect)[0] + tuple(rect)[2],
                                                   tuple(rect)[1] + tuple(rect)[3])
            command = "omxplayer fifo.mjpg --live --crop 252,0,804,704 --win {0} --orientation {1}".format(
                window_rect, orientation)
            self.omxplayer_process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
        else:
            self._window = window

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        rect = self._window.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)
        image = Image.new('RGB', size, color=(0, 0, 0))
        overlay = None
        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window)
            remaining = int(timer.remaining() + 1)
            if not overlay or remaining != timeout:
                # Rebluid overlay only if remaining number has changed
                image = Image.new('RGB', size, color=(0, 0, 0))
                overlay = self.get_overlay(image.size, str(remaining), alpha)
                timeout = remaining
                image.paste(overlay, (0, 0), overlay)
                self._window.show_image(image)

    def preview_wait(self, timeout):
        """Wait the given time and refresh the preview.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")

        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window, timeout=timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        # if self.gphoto2_process:
        #     os.killpg(os.getpgid(self.gphoto2_process.pid), signal.SIGTERM)
        #     self.gphoto2_process = None
        if self.omxplayer_process:
            os.killpg(os.getpgid(self.omxplayer_process.pid), signal.SIGTERM)
            self.omxplayer_process = None
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._init()
        self._captures[filename] = (self._cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image
        self.quit()

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        if self._cam:
            self._cam.exit()

class HybridCamera(RpiCamera):

    """Camera management using the Raspberry Pi camera for the preview (better
    video rendering) and a gPhoto2 compatible camera for the capture (higher
    resolution)
    """

    IMAGE_EFFECTS = GpCamera.IMAGE_EFFECTS

    def __init__(self, *args, **kwargs):
        RpiCamera.__init__(self, *args, **kwargs)
        gp.check_result(gp.use_python_logging())
        self._gp_cam = gp.Camera()
        self._gp_cam.init()

        config = self._gp_cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._cam.iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._gp_cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._gp_cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self._cam.resolution))

        if self._cam.hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._captures[filename] = (self._gp_cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        RpiCamera.quit(self)
        self._gp_cam.exit()
<commit_msg>Add an overlay in hybrid mode to tell the users to smile<commit_after># -*- coding: utf-8 -*-

import io
import os
import time
import signal
import subprocess
import pygame
try:
    import gphoto2 as gp
except ImportError:
    gp = None  # gphoto2 is optional
from PIL import Image, ImageFont, ImageDraw, ImageFilter
import picamera
from pibooth import fonts
from pibooth.pictures import sizing
from pibooth.config import PiConfigParser
from pibooth.utils import LOGGER, PoolingTimer

# Mapping of gPhoto2 config values for supported languages
GP_PARAMS = {
    'fr': {
        'Memory card': 'Carte mémoire',
    },
    'en': {
        'Memory card': 'Memory card',
    },
    'de': {
        'Memory card': 'Speicherkarte',
    },
}

LANGUAGES = {
    'fr': {
        'smile_message': "Souriez !"
    },
    'en': {
        'smile_message': "Smile!"
    },
    'de': {
        'smile_message': "Bitte lächeln!"
    }
}

def rpi_camera_connected():
    """Return True if a RPi camera is found.
    """
    try:
        process = subprocess.Popen(['vcgencmd', 'get_camera'],
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, _stderr = process.communicate()
        if stdout and u'detected=1' in stdout.decode('utf-8'):
            return True
    except OSError:
        pass
    return False

def gp_camera_connected():
    """Return True if a camera compatible with gPhoto2 is found.
    """
    if not gp:
        return False  # gPhoto2 is not installed
    if hasattr(gp, 'gp_camera_autodetect'):
        # gPhoto2 version 2.5+
        cameras = gp.check_result(gp.gp_camera_autodetect())
    else:
        port_info_list = gp.PortInfoList()
        port_info_list.load()
        abilities_list = gp.CameraAbilitiesList()
        abilities_list.load()
        cameras = abilities_list.detect(port_info_list)
    if cameras:
        return True

    return False

def gp_set_config_value(config, section, option, value):
    """Set camera configuration. This method don't send the updated
    configuration to the camera (avoid connection flooding if several
    values have to be changed)
    """
    try:
        value = GP_PARAMS.get(PiConfigParser.language, GP_PARAMS['en']).get(value, value)
        LOGGER.debug('Setting option %s/%s=%s', section, option, value)
        child = config.get_child_by_name(section).get_child_by_name(option)
        choices = [c for c in child.get_choices()]
        if value not in choices:
            LOGGER.warning(
                "Invalid value '%s' for option %s (possible choices: %s), still trying to set it", value, option, choices)
            child.set_value(str(value))
        else:
            child.set_value(str(value))
    except gp.GPhoto2Error:
        raise ValueError('Unsupported setting {}/{}={}'.format(section, option, value))

class BaseCamera(object):

    def __init__(self, resolution):
        self._cam = None
        self._border = 50
        self._window = None
        self._captures = {}
        self.resolution = resolution

    def _post_process_capture(self, capture_path):
        """Rework and return a capture from file.
        """
        return Image.open(capture_path)

    def get_rect(self):
        """Return a Rect object (as defined in pygame) for resizing preview and images
        in order to fit to the defined window.
        """
        rect = self._window.get_rect()
        res = sizing.new_size_keep_aspect_ratio(self.resolution,
                                                (rect.width - 2 * self._border, rect.height - 2 * self._border))
        return pygame.Rect(rect.centerx - res[0] // 2, rect.centery - res[1] // 2, res[0], res[1])

    def get_overlay(self, size, text, alpha):
        """Return a PIL image with the given text that can be used
        as an overlay for the camera.
        """
        image = Image.new('RGBA', size)
        draw = ImageDraw.Draw(image)
        txt_width = size[0] + 1
        i = 10
        while txt_width > size[0]:
            font = ImageFont.truetype(fonts.get_filename("Amatic-Bold.ttf"), size[1] * i // 10)
            txt_width, txt_height = draw.textsize(text, font=font)
            i -= 1

        position = ((size[0] - txt_width) // 2, (size[1] - txt_height) // 2 - size[1] // 10)
        draw.text(position, text, (255, 255, 255, alpha), font=font)
        return image

    def get_captures(self):
        """Return all buffered captures as PIL images (buffer dropped after call).
        """
        images = []
        for path in sorted(self._captures):
            images.append(self._post_process_capture(path))
        self.drop_captures()
        return images

    def drop_captures(self):
        """Delete all buffered captures.
        """
        self._captures = {}

class RpiCamera(BaseCamera):

    """Camera management
    """

    IMAGE_EFFECTS = list(picamera.PiCamera.IMAGE_EFFECTS.keys())

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        self._cam = picamera.PiCamera()
        self._cam.framerate = 15  # Slower is necessary for high-resolution
        self._cam.video_stabilization = True
        self._cam.vflip = False
        self._cam.hflip = flip
        self._cam.resolution = resolution
        self._cam.iso = iso
        self._cam.rotation = rotation

    def preview(self, window, flip=True):
        """Display a preview on the given Rect (flip if necessary).
        """
        self._window = window
        rect = self.get_rect()
        if self._cam.hflip:
            if flip:
                 # Don't flip again, already done at init
                flip = False
            else:
                # Flip again because flipped once at init
                flip = True
        self._cam.start_preview(resolution=(rect.width, rect.height), hflip=flip,
                                fullscreen=False, window=tuple(rect))

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        if not self._cam.preview:
            raise EnvironmentError("Preview shall be started first")

        # Create an image padded to the required size
        rect = self.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)

        while timeout > 0:
            image = self.get_overlay(size, str(timeout), alpha)
            overlay = self._cam.add_overlay(image.tobytes(), image.size, layer=3,
                                            window=tuple(rect), fullscreen=False)
            time.sleep(1)
            timeout -= 1
            self._cam.remove_overlay(overlay)

    def preview_wait(self, timeout):
        """Wait the given time.
        """
        time.sleep(timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        self._cam.stop_preview()
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        try:
            self._cam.image_effect = effect
            self._cam.capture(filename)
            self._captures[filename] = None  # Nothing to keep for post processing
        finally:
            self._cam.image_effect = 'none'

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        self._cam.close()

class GpCamera(BaseCamera):

    """gPhoto2 camera management.
    """

    IMAGE_EFFECTS = [u'none',
                     u'blur',
                     u'contour',
                     u'detail',
                     u'edge_enhance',
                     u'edge_enhance_more',
                     u'emboss',
                     u'find_edges',
                     u'smooth',
                     u'smooth_more',
                     u'sharpen']

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        gp.check_result(gp.use_python_logging())

        self._preview_hflip = False
        self._capture_hflip = flip
        self._rotation = rotation
        self._iso = str(iso)
        self.gphoto2_process = None
        self.omxplayer_process = None

    def _init(self):
        """Camera initialisation
        """
        self._cam = gp.Camera()
        self._cam.init()
        config = self._cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution))

        if self._capture_hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def preview(self, window, flip=True, timeout=5):
        """Setup the preview.
        """
        self.gphoto2_process = True  # hack to avoid the preview
        if not self.gphoto2_process:
            self._window = window
            rect = self.get_rect()
            if flip:
                orientation = 1
            else:
                orientation = 0
            self.gphoto2_process = subprocess.Popen("gphoto2 --capture-movie --stdout> fifo.mjpg &",
                                                    shell=True,
                                                    preexec_fn=os.setsid)
            window_rect = '{0},{1},{2},{3}'.format(tuple(rect)[0], tuple(rect)[1], tuple(rect)[0] + tuple(rect)[2],
                                                   tuple(rect)[1] + tuple(rect)[3])
            command = "omxplayer fifo.mjpg --live --crop 252,0,804,704 --win {0} --orientation {1}".format(
                window_rect, orientation)
            self.omxplayer_process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
        else:
            self._window = window

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        rect = self._window.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)
        image = Image.new('RGB', size, color=(0, 0, 0))
        overlay = None
        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window)
            remaining = int(timer.remaining() + 1)
            if not overlay or remaining != timeout:
                # Rebluid overlay only if remaining number has changed
                image = Image.new('RGB', size, color=(0, 0, 0))
                overlay = self.get_overlay(image.size, str(remaining), alpha)
                timeout = remaining
                image.paste(overlay, (0, 0), overlay)
                self._window.show_image(image)

    def preview_wait(self, timeout):
        """Wait the given time and refresh the preview.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")

        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window, timeout=timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        # if self.gphoto2_process:
        #     os.killpg(os.getpgid(self.gphoto2_process.pid), signal.SIGTERM)
        #     self.gphoto2_process = None
        if self.omxplayer_process:
            os.killpg(os.getpgid(self.omxplayer_process.pid), signal.SIGTERM)
            self.omxplayer_process = None
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._init()
        self._captures[filename] = (self._cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image
        self.quit()

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        if self._cam:
            self._cam.exit()

class HybridCamera(RpiCamera):

    """Camera management using the Raspberry Pi camera for the preview (better
    video rendering) and a gPhoto2 compatible camera for the capture (higher
    resolution)
    """

    IMAGE_EFFECTS = GpCamera.IMAGE_EFFECTS

    def __init__(self, *args, **kwargs):
        RpiCamera.__init__(self, *args, **kwargs)
        gp.check_result(gp.use_python_logging())
        self._gp_cam = gp.Camera()
        self._gp_cam.init()

        config = self._gp_cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._cam.iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._gp_cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._gp_cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self._cam.resolution))

        if self._cam.hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        # Create an image padded to the required size
        rect = self.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)
        image = self.get_overlay(size, LANGUAGES.get(PiConfigParser.language, LANGUAGES['en']).get('smile_message'), 60)
        overlay = self._cam.add_overlay(image.tobytes(), image.size, layer=3,
                                        window=tuple(rect), fullscreen=False)
        self._captures[filename] = (self._gp_cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image
        self._cam.remove_overlay(overlay)

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        RpiCamera.quit(self)
        self._gp_cam.exit()
<|endoftext|>


<commit_before>
# Created from dockerfile: /space/git/dockerfiles_repos/SvenDowideit/dockerfiles/minimal-linux/Dockerfile
from shutit_module import ShutItModule

class minimal_linux(ShutItModule):

    def is_installed(self, shutit):
        return False

    def build(self, shutit):
        shutit.send('export KERNEL_VERSION=3.14.2')
        shutit.install('wget')
        shutit.install('fakeroot kernel-package xz-utils bc xorriso syslinux git vim-tiny lib32ncurses5-dev')
        shutit.send('wget -O /linux-3.14.2.tar.xz https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.2.tar.xz')
        shutit.send_host_file('/kernel_config.patch', 'context/kernel_config.patch')
        shutit.send('xz -d linux-3.14.2.tar.xz')
        shutit.send('tar -xvf linux-3.14.2.tar')
        shutit.send('pushd /linux-3.14.2')
        shutit.send('make defconfig')
        shutit.send('make')
        shutit.send('popd')
        shutit.send('pushd /')
        shutit.send('wget -O /busybox-1.22.1.tar.bz2 http://busybox.net/downloads/busybox-1.22.1.tar.bz2')
        shutit.send('bunzip2 /busybox-1.22.1.tar.bz2')
        shutit.send('tar -xvf /busybox-1.22.1.tar')
        shutit.send('popd')
        shutit.send('pushd /busybox-1.22.1')
        shutit.send('make defconfig')
        shutit.send('echo "STATIC=y >> .config" >> .config')
        shutit.send('make'
        shutit.send('make install')
        shutit.send('cp -r _install /rootfs')
        shutit.send('ls -la /rootfs/bin/busybox')
        shutit.send('popd')
        shutit.send('pushd /rootfs')
        shutit.send('mkdir dev proc sys tmp')
        shutit.send('mknod dev/console c 5 1')
        shutit.send_host_file('/rootfs/sbin/init', 'context/init')
        shutit.send_host_file('/isolinux.cfg', 'context/isolinux.cfg')
        shutit.send('cp /busybox-1.22.1/_install/bin/busybox /rootfs/linuxrc')
        shutit.send('mkdir -p /tmp/iso/boot')
        shutit.send('find | cpio -o -H newc | gzip > /tmp/iso/boot/initrd.gz')
        shutit.send('cp -v /linux-3.14.2/arch/x86_64/boot/bzImage /tmp/iso/boot/vmlinuz64')
        shutit.send('cp /usr/lib/syslinux/isolinux.bin /tmp/iso/boot/')
        shutit.send('cp /isolinux.cfg /tmp/iso/boot/')
        shutit.send('cp /busybox-1.22.1/_install/bin/busybox /tmp/iso/linuxrc')
        shutit.send('echo "SVEN" >> /tmp/iso/version')
        shutit.send('xorriso -as mkisofs -l -J -R -V sven -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux.bin -c boot/boot.cat -isohybrid-mbr /usr/lib/syslinux/isohdpfx.bin -o /sven.iso /tmp/iso')
        shutit.send('popd')
        return True

    def finalize(self, shutit):
        return True

    def test(self, shutit):
        return True

    def is_installed(self, shutit):
        return False

    def get_config(self, shutit):
        return True

def module():
        return minimal_linux(
                'shutit.tk.minimal_linux.minimal_linux', 0.1561245235,
                depends=['shutit.tk.setup']
        )
<commit_msg>close<commit_after>
# Created from dockerfile: /space/git/dockerfiles_repos/SvenDowideit/dockerfiles/minimal-linux/Dockerfile
from shutit_module import ShutItModule

class minimal_linux(ShutItModule):

    def is_installed(self, shutit):
        return False

    def build(self, shutit):
        shutit.send('export KERNEL_VERSION=3.14.2')
        shutit.install('wget')
        shutit.install('fakeroot kernel-package xz-utils bc xorriso syslinux git vim-tiny lib32ncurses5-dev')
        shutit.send('wget -O /linux-3.14.2.tar.xz https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.14.2.tar.xz')
        shutit.send_host_file('/kernel_config.patch', 'context/kernel_config.patch')
        shutit.send('xz -d linux-3.14.2.tar.xz')
        shutit.send('tar -xvf linux-3.14.2.tar')
        shutit.send('pushd /linux-3.14.2')
        shutit.send('make defconfig')
        shutit.send('make')
        shutit.send('popd')
        shutit.send('pushd /')
        shutit.send('wget -O /busybox-1.22.1.tar.bz2 http://busybox.net/downloads/busybox-1.22.1.tar.bz2')
        shutit.send('bunzip2 /busybox-1.22.1.tar.bz2')
        shutit.send('tar -xvf /busybox-1.22.1.tar')
        shutit.send('popd')
        shutit.send('pushd /busybox-1.22.1')
        shutit.send('make defconfig')
        shutit.send('echo "STATIC=y >> .config" >> .config')
        shutit.send('make')
        shutit.send('make install')
        shutit.send('cp -r _install /rootfs')
        shutit.send('ls -la /rootfs/bin/busybox')
        shutit.send('popd')
        shutit.send('pushd /rootfs')
        shutit.send('mkdir dev proc sys tmp')
        shutit.send('mknod dev/console c 5 1')
        shutit.send_host_file('/rootfs/sbin/init', 'context/init')
        shutit.send_host_file('/isolinux.cfg', 'context/isolinux.cfg')
        shutit.send('cp /busybox-1.22.1/_install/bin/busybox /rootfs/linuxrc')
        shutit.send('mkdir -p /tmp/iso/boot')
        shutit.send('find | cpio -o -H newc | gzip > /tmp/iso/boot/initrd.gz')
        shutit.send('cp -v /linux-3.14.2/arch/x86_64/boot/bzImage /tmp/iso/boot/vmlinuz64')
        shutit.send('cp /usr/lib/syslinux/isolinux.bin /tmp/iso/boot/')
        shutit.send('cp /isolinux.cfg /tmp/iso/boot/')
        shutit.send('cp /busybox-1.22.1/_install/bin/busybox /tmp/iso/linuxrc')
        shutit.send('echo "SVEN" >> /tmp/iso/version')
        shutit.send('xorriso -as mkisofs -l -J -R -V sven -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux.bin -c boot/boot.cat -isohybrid-mbr /usr/lib/syslinux/isohdpfx.bin -o /sven.iso /tmp/iso')
        shutit.send('popd')
        return True

    def finalize(self, shutit):
        return True

    def test(self, shutit):
        return True

    def is_installed(self, shutit):
        return False

    def get_config(self, shutit):
        return True

def module():
        return minimal_linux(
                'shutit.tk.minimal_linux.minimal_linux', 0.1561245235,
                depends=['shutit.tk.setup']
        )
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import io
import os
import time
import signal
import subprocess
import pygame
try:
    import gphoto2 as gp
except ImportError:
    gp = None  # gphoto2 is optional
from PIL import Image, ImageFont, ImageDraw, ImageFilter
import picamera
from pibooth import fonts
from pibooth.pictures import sizing
from pibooth.config import PiConfigParser
from pibooth.utils import LOGGER, PoolingTimer

# Mapping of gPhoto2 config values for supported languages
GP_PARAMS = {
    'fr': {
        'Memory card': 'Carte mémoire',
    },
    'en': {
        'Memory card': 'Memory card',
    },
    'de': {
        'Memory card': 'Speicherkarte',
    },
}

def rpi_camera_connected():
    """Return True if a RPi camera is found.
    """
    try:
        process = subprocess.Popen(['vcgencmd', 'get_camera'],
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, _stderr = process.communicate()
        if stdout and u'detected=1' in stdout.decode('utf-8'):
            return True
    except OSError:
        pass
    return False

def gp_camera_connected():
    """Return True if a camera compatible with gPhoto2 is found.
    """
    if not gp:
        return False  # gPhoto2 is not installed
    if hasattr(gp, 'gp_camera_autodetect'):
        # gPhoto2 version 2.5+
        cameras = gp.check_result(gp.gp_camera_autodetect())
    else:
        port_info_list = gp.PortInfoList()
        port_info_list.load()
        abilities_list = gp.CameraAbilitiesList()
        abilities_list.load()
        cameras = abilities_list.detect(port_info_list)
    if cameras:
        return True

    return False

def gp_set_config_value(config, section, option, value):
    """Set camera configuration. This method don't send the updated
    configuration to the camera (avoid connection flooding if several
    values have to be changed)
    """
    try:
        value = GP_PARAMS.get(PiConfigParser.language, GP_PARAMS['en']).get(value, value)
        LOGGER.debug('Setting option %s/%s=%s', section, option, value)
        child = config.get_child_by_name(section).get_child_by_name(option)
        choices = [c for c in child.get_choices()]
        if value not in choices:
            LOGGER.warning(
                "Invalid value '%s' for option %s (possible choices: %s), still trying to set it", value, option, choices)
            child.set_value(str(value))
        else:
            child.set_value(str(value))
    except gp.GPhoto2Error:
        raise ValueError('Unsupported setting {}/{}={}'.format(section, option, value))

class BaseCamera(object):

    def __init__(self, resolution):
        self._cam = None
        self._border = 50
        self._window = None
        self._captures = {}
        self.resolution = resolution

    def _post_process_capture(self, capture_path):
        """Rework and return a capture from file.
        """
        return Image.open(capture_path)

    def get_rect(self):
        """Return a Rect object (as defined in pygame) for resizing preview and images
        in order to fit to the defined window.
        """
        rect = self._window.get_rect()
        res = sizing.new_size_keep_aspect_ratio(self.resolution,
                                                (rect.width - 2 * self._border, rect.height - 2 * self._border))
        return pygame.Rect(rect.centerx - res[0] // 2, rect.centery - res[1] // 2, res[0], res[1])

    def get_overlay(self, size, text, alpha):
        """Return a PIL image with the given text that can be used
        as an overlay for the camera.
        """
        font = ImageFont.truetype(fonts.get_filename("Amatic-Bold.ttf"), size[1] * 8 // 10)
        image = Image.new('RGBA', size)
        draw = ImageDraw.Draw(image)
        txt_width, txt_height = draw.textsize(text, font=font)
        position = ((size[0] - txt_width) // 2, (size[1] - txt_height) // 2 - size[1] // 10)
        draw.text(position, text, (255, 255, 255, alpha), font=font)
        return image

    def get_captures(self):
        """Return all buffered captures as PIL images (buffer dropped after call).
        """
        images = []
        for path in sorted(self._captures):
            images.append(self._post_process_capture(path))
        self.drop_captures()
        return images

    def drop_captures(self):
        """Delete all buffered captures.
        """
        self._captures = {}

class RpiCamera(BaseCamera):

    """Camera management
    """

    IMAGE_EFFECTS = list(picamera.PiCamera.IMAGE_EFFECTS.keys())

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        self._cam = picamera.PiCamera()
        self._cam.framerate = 15  # Slower is necessary for high-resolution
        self._cam.video_stabilization = True
        self._cam.vflip = False
        self._cam.hflip = flip
        self._cam.resolution = resolution
        self._cam.iso = iso
        self._cam.rotation = rotation

    def preview(self, window, flip=True):
        """Display a preview on the given Rect (flip if necessary).
        """
        self._window = window
        rect = self.get_rect()
        if self._cam.hflip:
            if flip:
                 # Don't flip again, already done at init
                flip = False
            else:
                # Flip again because flipped once at init
                flip = True
        self._cam.start_preview(resolution=(rect.width, rect.height), hflip=flip,
                                fullscreen=False, window=tuple(rect))

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        if not self._cam.preview:
            raise EnvironmentError("Preview shall be started first")

        # Create an image padded to the required size
        rect = self.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)

        while timeout > 0:
            image = self.get_overlay(size, str(timeout), alpha)
            overlay = self._cam.add_overlay(image.tobytes(), image.size, layer=3,
                                            window=tuple(rect), fullscreen=False)
            time.sleep(1)
            timeout -= 1
            self._cam.remove_overlay(overlay)

    def preview_wait(self, timeout):
        """Wait the given time.
        """
        time.sleep(timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        self._cam.stop_preview()
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        try:
            self._cam.image_effect = effect
            self._cam.capture(filename)
            self._captures[filename] = None  # Nothing to keep for post processing
        finally:
            self._cam.image_effect = 'none'

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        self._cam.close()

class GpCamera(BaseCamera):

    """gPhoto2 camera management.
    """

    IMAGE_EFFECTS = [u'none',
                     u'blur',
                     u'contour',
                     u'detail',
                     u'edge_enhance',
                     u'edge_enhance_more',
                     u'emboss',
                     u'find_edges',
                     u'smooth',
                     u'smooth_more',
                     u'sharpen']

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        gp.check_result(gp.use_python_logging())

        self._preview_hflip = False
        self._capture_hflip = flip
        self._rotation = rotation
        self._iso = str(iso)
        self.gphoto2_process = None
        self.omxplayer_process = None

    def _init(self):
        """Camera initialisation
        """
        self._cam = gp.Camera()
        self._cam.init()
        config = self._cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        # image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer'), Image.ANTIALIAS)
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution))

        if self._capture_hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def preview(self, window, flip=True, timeout=5):
        """Setup the preview.
        """
        self.gphoto2_process = True  # hack to avoid the preview
        if not self.gphoto2_process:
            self._window = window
            rect = self.get_rect()
            if flip:
                orientation = 1
            else:
                orientation = 0
            self.gphoto2_process = subprocess.Popen("gphoto2 --capture-movie --stdout> fifo.mjpg &",
                                                    shell=True,
                                                    preexec_fn=os.setsid)
            window_rect = '{0},{1},{2},{3}'.format(tuple(rect)[0], tuple(rect)[1], tuple(rect)[0] + tuple(rect)[2],
                                                   tuple(rect)[1] + tuple(rect)[3])
            command = "omxplayer fifo.mjpg --live --crop 252,0,804,704 --win {0} --orientation {1}".format(
                window_rect, orientation)
            self.omxplayer_process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
        else:
            self._window = window

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        rect = self._window.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)
        image = Image.new('RGB', size, color=(0, 0, 0))
        overlay = None
        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window)
            remaining = int(timer.remaining() + 1)
            if not overlay or remaining != timeout:
                # Rebluid overlay only if remaining number has changed
                image = Image.new('RGB', size, color=(0, 0, 0))
                overlay = self.get_overlay(image.size, str(remaining), alpha)
                timeout = remaining
                image.paste(overlay, (0, 0), overlay)
                self._window.show_image(image)

    def preview_wait(self, timeout):
        """Wait the given time and refresh the preview.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")

        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window, timeout=timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        # if self.gphoto2_process:
        #     os.killpg(os.getpgid(self.gphoto2_process.pid), signal.SIGTERM)
        #     self.gphoto2_process = None
        if self.omxplayer_process:
            os.killpg(os.getpgid(self.omxplayer_process.pid), signal.SIGTERM)
            self.omxplayer_process = None
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._init()
        self._captures[filename] = (self._cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image
        self.quit()

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        if self._cam:
            self._cam.exit()

class HybridCamera(RpiCamera):

    """Camera management using the Raspberry Pi camera for the preview (better
    video rendering) and a gPhoto2 compatible camera for the capture (higher
    resolution)
    """

    IMAGE_EFFECTS = GpCamera.IMAGE_EFFECTS

    def __init__(self, *args, **kwargs):
        RpiCamera.__init__(self, *args, **kwargs)
        gp.check_result(gp.use_python_logging())
        self._gp_cam = gp.Camera()
        self._gp_cam.init()

        config = self._gp_cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._cam.iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._gp_cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._gp_cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        # image = image.resize(sizing.new_size_keep_aspect_ratio(
        #     image.size, self._cam.resolution, 'outer'), Image.ANTIALIAS)
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self._cam.resolution))

        if self._cam.hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        RpiCamera.capture(self, filename)  # Just to show a captured image at screen
        self._captures[filename] = (self._gp_cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        RpiCamera.quit(self)
        self._gp_cam.exit()
<commit_msg>Remove capture through rpi camera in hybrid mode as it was misleading<commit_after># -*- coding: utf-8 -*-

import io
import os
import time
import signal
import subprocess
import pygame
try:
    import gphoto2 as gp
except ImportError:
    gp = None  # gphoto2 is optional
from PIL import Image, ImageFont, ImageDraw, ImageFilter
import picamera
from pibooth import fonts
from pibooth.pictures import sizing
from pibooth.config import PiConfigParser
from pibooth.utils import LOGGER, PoolingTimer

# Mapping of gPhoto2 config values for supported languages
GP_PARAMS = {
    'fr': {
        'Memory card': 'Carte mémoire',
    },
    'en': {
        'Memory card': 'Memory card',
    },
    'de': {
        'Memory card': 'Speicherkarte',
    },
}

def rpi_camera_connected():
    """Return True if a RPi camera is found.
    """
    try:
        process = subprocess.Popen(['vcgencmd', 'get_camera'],
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, _stderr = process.communicate()
        if stdout and u'detected=1' in stdout.decode('utf-8'):
            return True
    except OSError:
        pass
    return False

def gp_camera_connected():
    """Return True if a camera compatible with gPhoto2 is found.
    """
    if not gp:
        return False  # gPhoto2 is not installed
    if hasattr(gp, 'gp_camera_autodetect'):
        # gPhoto2 version 2.5+
        cameras = gp.check_result(gp.gp_camera_autodetect())
    else:
        port_info_list = gp.PortInfoList()
        port_info_list.load()
        abilities_list = gp.CameraAbilitiesList()
        abilities_list.load()
        cameras = abilities_list.detect(port_info_list)
    if cameras:
        return True

    return False

def gp_set_config_value(config, section, option, value):
    """Set camera configuration. This method don't send the updated
    configuration to the camera (avoid connection flooding if several
    values have to be changed)
    """
    try:
        value = GP_PARAMS.get(PiConfigParser.language, GP_PARAMS['en']).get(value, value)
        LOGGER.debug('Setting option %s/%s=%s', section, option, value)
        child = config.get_child_by_name(section).get_child_by_name(option)
        choices = [c for c in child.get_choices()]
        if value not in choices:
            LOGGER.warning(
                "Invalid value '%s' for option %s (possible choices: %s), still trying to set it", value, option, choices)
            child.set_value(str(value))
        else:
            child.set_value(str(value))
    except gp.GPhoto2Error:
        raise ValueError('Unsupported setting {}/{}={}'.format(section, option, value))

class BaseCamera(object):

    def __init__(self, resolution):
        self._cam = None
        self._border = 50
        self._window = None
        self._captures = {}
        self.resolution = resolution

    def _post_process_capture(self, capture_path):
        """Rework and return a capture from file.
        """
        return Image.open(capture_path)

    def get_rect(self):
        """Return a Rect object (as defined in pygame) for resizing preview and images
        in order to fit to the defined window.
        """
        rect = self._window.get_rect()
        res = sizing.new_size_keep_aspect_ratio(self.resolution,
                                                (rect.width - 2 * self._border, rect.height - 2 * self._border))
        return pygame.Rect(rect.centerx - res[0] // 2, rect.centery - res[1] // 2, res[0], res[1])

    def get_overlay(self, size, text, alpha):
        """Return a PIL image with the given text that can be used
        as an overlay for the camera.
        """
        font = ImageFont.truetype(fonts.get_filename("Amatic-Bold.ttf"), size[1] * 8 // 10)
        image = Image.new('RGBA', size)
        draw = ImageDraw.Draw(image)
        txt_width, txt_height = draw.textsize(text, font=font)
        position = ((size[0] - txt_width) // 2, (size[1] - txt_height) // 2 - size[1] // 10)
        draw.text(position, text, (255, 255, 255, alpha), font=font)
        return image

    def get_captures(self):
        """Return all buffered captures as PIL images (buffer dropped after call).
        """
        images = []
        for path in sorted(self._captures):
            images.append(self._post_process_capture(path))
        self.drop_captures()
        return images

    def drop_captures(self):
        """Delete all buffered captures.
        """
        self._captures = {}

class RpiCamera(BaseCamera):

    """Camera management
    """

    IMAGE_EFFECTS = list(picamera.PiCamera.IMAGE_EFFECTS.keys())

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        self._cam = picamera.PiCamera()
        self._cam.framerate = 15  # Slower is necessary for high-resolution
        self._cam.video_stabilization = True
        self._cam.vflip = False
        self._cam.hflip = flip
        self._cam.resolution = resolution
        self._cam.iso = iso
        self._cam.rotation = rotation

    def preview(self, window, flip=True):
        """Display a preview on the given Rect (flip if necessary).
        """
        self._window = window
        rect = self.get_rect()
        if self._cam.hflip:
            if flip:
                 # Don't flip again, already done at init
                flip = False
            else:
                # Flip again because flipped once at init
                flip = True
        self._cam.start_preview(resolution=(rect.width, rect.height), hflip=flip,
                                fullscreen=False, window=tuple(rect))

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        if not self._cam.preview:
            raise EnvironmentError("Preview shall be started first")

        # Create an image padded to the required size
        rect = self.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)

        while timeout > 0:
            image = self.get_overlay(size, str(timeout), alpha)
            overlay = self._cam.add_overlay(image.tobytes(), image.size, layer=3,
                                            window=tuple(rect), fullscreen=False)
            time.sleep(1)
            timeout -= 1
            self._cam.remove_overlay(overlay)

    def preview_wait(self, timeout):
        """Wait the given time.
        """
        time.sleep(timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        self._cam.stop_preview()
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        try:
            self._cam.image_effect = effect
            self._cam.capture(filename)
            self._captures[filename] = None  # Nothing to keep for post processing
        finally:
            self._cam.image_effect = 'none'

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        self._cam.close()

class GpCamera(BaseCamera):

    """gPhoto2 camera management.
    """

    IMAGE_EFFECTS = [u'none',
                     u'blur',
                     u'contour',
                     u'detail',
                     u'edge_enhance',
                     u'edge_enhance_more',
                     u'emboss',
                     u'find_edges',
                     u'smooth',
                     u'smooth_more',
                     u'sharpen']

    def __init__(self, iso=200, resolution=(1920, 1080), rotation=0, flip=False):
        BaseCamera.__init__(self, resolution)
        gp.check_result(gp.use_python_logging())

        self._preview_hflip = False
        self._capture_hflip = flip
        self._rotation = rotation
        self._iso = str(iso)
        self.gphoto2_process = None
        self.omxplayer_process = None

    def _init(self):
        """Camera initialisation
        """
        self._cam = gp.Camera()
        self._cam.init()
        config = self._cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        # image = image.resize(sizing.new_size_keep_aspect_ratio(image.size, self.resolution, 'outer'), Image.ANTIALIAS)
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self.resolution))

        if self._capture_hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def preview(self, window, flip=True, timeout=5):
        """Setup the preview.
        """
        self.gphoto2_process = True  # hack to avoid the preview
        if not self.gphoto2_process:
            self._window = window
            rect = self.get_rect()
            if flip:
                orientation = 1
            else:
                orientation = 0
            self.gphoto2_process = subprocess.Popen("gphoto2 --capture-movie --stdout> fifo.mjpg &",
                                                    shell=True,
                                                    preexec_fn=os.setsid)
            window_rect = '{0},{1},{2},{3}'.format(tuple(rect)[0], tuple(rect)[1], tuple(rect)[0] + tuple(rect)[2],
                                                   tuple(rect)[1] + tuple(rect)[3])
            command = "omxplayer fifo.mjpg --live --crop 252,0,804,704 --win {0} --orientation {1}".format(
                window_rect, orientation)
            self.omxplayer_process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
        else:
            self._window = window

    def preview_countdown(self, timeout, alpha=60):
        """Show a countdown of `timeout` seconds on the preview.
        Returns when the countdown is finished.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")
        rect = self._window.get_rect()
        size = (((rect.width + 31) // 32) * 32, ((rect.height + 15) // 16) * 16)
        image = Image.new('RGB', size, color=(0, 0, 0))
        overlay = None
        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window)
            remaining = int(timer.remaining() + 1)
            if not overlay or remaining != timeout:
                # Rebluid overlay only if remaining number has changed
                image = Image.new('RGB', size, color=(0, 0, 0))
                overlay = self.get_overlay(image.size, str(remaining), alpha)
                timeout = remaining
                image.paste(overlay, (0, 0), overlay)
                self._window.show_image(image)

    def preview_wait(self, timeout):
        """Wait the given time and refresh the preview.
        """
        timeout = int(timeout)
        if timeout < 1:
            raise ValueError("Start time shall be greater than 0")

        timer = PoolingTimer(timeout)
        while not timer.is_timeout():
            self.preview(self._window, timeout=timeout)

    def stop_preview(self):
        """Stop the preview.
        """
        # if self.gphoto2_process:
        #     os.killpg(os.getpgid(self.gphoto2_process.pid), signal.SIGTERM)
        #     self.gphoto2_process = None
        if self.omxplayer_process:
            os.killpg(os.getpgid(self.omxplayer_process.pid), signal.SIGTERM)
            self.omxplayer_process = None
        self._window = None

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._init()
        self._captures[filename] = (self._cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image
        self.quit()

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        if self._cam:
            self._cam.exit()

class HybridCamera(RpiCamera):

    """Camera management using the Raspberry Pi camera for the preview (better
    video rendering) and a gPhoto2 compatible camera for the capture (higher
    resolution)
    """

    IMAGE_EFFECTS = GpCamera.IMAGE_EFFECTS

    def __init__(self, *args, **kwargs):
        RpiCamera.__init__(self, *args, **kwargs)
        gp.check_result(gp.use_python_logging())
        self._gp_cam = gp.Camera()
        self._gp_cam.init()

        config = self._gp_cam.get_config()
        gp_set_config_value(config, 'imgsettings', 'iso', self._cam.iso)
        gp_set_config_value(config, 'settings', 'capturetarget', 'Memory card')
        self._gp_cam.set_config(config)

    def _post_process_capture(self, capture_path):
        gp_path, effect = self._captures[capture_path]
        camera_file = gp.check_result(gp.gp_camera_file_get(
            self._gp_cam, gp_path.folder, gp_path.name, gp.GP_FILE_TYPE_NORMAL))

        image = Image.open(io.BytesIO(memoryview(camera_file.get_data_and_size())))
        # image = image.resize(sizing.new_size_keep_aspect_ratio(
        #     image.size, self._cam.resolution, 'outer'), Image.ANTIALIAS)
        image = image.crop(sizing.new_size_by_croping_ratio(image.size, self._cam.resolution))

        if self._cam.hflip:
            image = image.transpose(Image.FLIP_LEFT_RIGHT)

        if effect != 'none':
            image = image.filter(getattr(ImageFilter, effect.upper()))

        image.save(capture_path)
        return image

    def capture(self, filename, effect=None):
        """Capture a picture in a file.
        """
        effect = str(effect).lower()
        if effect not in self.IMAGE_EFFECTS:
            raise ValueError("Invalid capture effect '{}' (choose among {})".format(effect, self.IMAGE_EFFECTS))

        self._captures[filename] = (self._gp_cam.capture(gp.GP_CAPTURE_IMAGE), effect)
        time.sleep(1)  # Necessary to let the time for the camera to save the image

    def quit(self):
        """Close the camera driver, it's definitive.
        """
        RpiCamera.quit(self)
        self._gp_cam.exit()
<|endoftext|>