from bokeh.plotting import figure
from bokeh.events import PointEvent
from bokeh.models import Panel, Button, Div, ColumnDataSource
from bokeh.events import ButtonClick
from bokeh.layouts import layout, column, row, gridplot, Spacer
from bokeh.document import without_document_lock
from sensors.GUIclasses.taxonomy_autocomplete_widget import AutocompleteTaxonomy
from sensors.GUIclasses.camera_settings_widget import CameraSettings
from sensors.Utilities.frame_decoder import FrameDecoder
from sensors.Utilities.write_meta_data import WriteMetaData
import picamerax as picamera
from tornado import gen
import numpy as np
from time import sleep, time
from datetime import datetime
import os
import RPi.GPIO as GPIO
from colorama import Fore
import uuid
import json
import cv2
[docs]class CamStreamTab:
"""Class to create a Bokeh tab that displays a life stream from a picamera. Pictures can be taken
manually or are triggered with a light barrier.
:param config: configuration including :ref:`config.yaml` dictionary and current Bokeh document
:type config: object
:param camera: picamera object initialized in :mod:`sensors.app_hooks` with \
:class:`sensors.Utilities.initdevices.InitDevices`
:type camera: object
"""
def __init__(self, config):
self.config = config
self.curdoc = config.curdoc
self.autocomplete_tax = AutocompleteTaxonomy(config)
self.camera_settings = CameraSettings(config)
[self.CAMERA_WIDTH, self.CAMERA_HEIGHT] = config.camera_res_preview
self.stream_callback = None
self.update_in_progress = False
self.save_in_progress = False
self.camera = config.camera
self.template = """<div class='content' style="width: {w}; background-color: {background};color: {colour};">
<div class='info'> {status_text} </div> </div>"""
self.template_png = """<div class='image_preview'>"""
# call tab method to create tab
self._tab = self.make_tab()
self.image_div_list = (self.image_div_1, self.image_div_2, self.image_div_3, self.image_div_4)
self.plot_counter = 0
self.executor = config.executor
self.GPIO_Switch = self.config.GPIO_Switch
if len(self.config.connected_GPIO_IR_pins) == 0:
self.btn_barrier.visible = False
self.barrier = False
self.trigger = False
self.trigger_timeout = 5
self.trigger_time = 0
self.trigger_start = 0
self.png_to_plot_queue = []
self.decode_jobs = 0
self.block_video_save = False
self.decode_callback = None
self.show_histogram = False
# 5 % margin for start histogram statistic
self.pan = False
self.pan_start = [self.CAMERA_WIDTH // 20, self.CAMERA_WIDTH // 20]
self.pan_end = [self.CAMERA_WIDTH - self.CAMERA_WIDTH // 20, self.CAMERA_HEIGHT - self.CAMERA_WIDTH // 20]
self.calculate_image_ROI()
@property
def tab(self):
"""Calls method :meth:`make_tab`.
"""
return self._tab
[docs] def make_tab(self):
""" Creates and arranges the elements of the corresponding Bokeh tab.
:return: Bokeh panel for picamera stream visualization
:rtype: Bokeh object
"""
# define buttons
self.btn_preview = Button(label='Start Stream', width=310)
self.btn_preview.on_event(ButtonClick, self.do_preview)
self.btn_save = Button(label='Save Image Sequence (%d Images)' % self.config.number_frames, width=310)
self.btn_save.disabled = True
self.btn_save.on_event(ButtonClick, self.manual_image_save)
self.btn_barrier = Button(label='Activate Light Barrier', width=200)
self.btn_barrier.disabled = True
self.btn_barrier.on_event(ButtonClick, self.barrier_on_off)
self.btn_LED = Button(label='Activate LED flash', width=200)
self.btn_LED.disabled = True
self.btn_LED.on_event(ButtonClick, self.LED_on_off)
self.text_status = Div(text=self.template.format(status_text='Stream inactive', background='#DC524C',
colour='#FFFFFF', w='200px'), render_as_text=False)
# define figure for histogram
self.hist_y_range = [0, 0.25]
hist_low = 145
hist_high = 155
hist_center = (hist_high + hist_low) // 2
self.histogram = figure(plot_width=208, plot_height=160, x_range=(0, 255), y_range=tuple(self.hist_y_range),
y_axis_type=None, tools="", toolbar_location=None, name='hist')
self.histogram.background_fill_color = "#eaeaea"
self.histogram.outline_line_color = '#595959'
self.histogram.xgrid.grid_line_color = None
self.histogram.xaxis.major_tick_line_color = None
self.histogram.xaxis.ticker = [0, hist_center, 255]
self.histogram.xaxis.major_label_overrides = {0: '0'.ljust(2, ' '), hist_center: str(hist_center),
255: '255'.ljust(8, ' ')}
self.binning = 2**2
self.edges = np.linspace(0, 256, 256//self.binning + 1).astype(np.uint16)
hist = np.zeros(256//self.binning)
self.hist_source_red = ColumnDataSource(data=dict(top=hist, left=self.edges[:-1], right=self.edges[1:]))
self.hist_source_green = ColumnDataSource(data=dict(top=hist, left=self.edges[:-1], right=self.edges[1:]))
self.hist_source_blue = ColumnDataSource(data=dict(top=hist, left=self.edges[:-1], right=self.edges[1:]))
self.histogram.quad(bottom=0, top=self.hist_y_range[1], left=hist_low, right=hist_high,
fill_color='navy', line_color=None, alpha=0.10)
self.histogram.quad(bottom=0, fill_color='#DC524C', line_color=None, alpha=0.25, source=self.hist_source_red)
self.histogram.quad(bottom=0, fill_color='#3EA639', line_color=None, alpha=0.25, source=self.hist_source_green)
self.histogram.quad(bottom=0, fill_color='#21618C', line_color=None, alpha=0.25, source=self.hist_source_blue)
self.mean_red_source = ColumnDataSource(data=dict(x=[0, 0], y=self.hist_y_range))
self.mean_green_source = ColumnDataSource(data=dict(x=[0, 0], y=self.hist_y_range))
self.mean_blue_source = ColumnDataSource(data=dict(x=[0, 0], y=self.hist_y_range))
self.histogram.line(x="x", y="y", line_color='#DC524C', source=self.mean_red_source)
self.histogram.line(x="x", y="y", line_color='#3EA639', source=self.mean_green_source)
self.histogram.line(x="x", y="y", line_color='#21618C', source=self.mean_blue_source)
self.hist_button = Button(label='Activate histogram', width=200)
self.hist_button.on_event(ButtonClick, self.update_hist)
# define figure for image data for stream preview
self.image = np.empty((self.CAMERA_WIDTH, self.CAMERA_HEIGHT, 4), dtype=np.uint8)
self.image_source = ColumnDataSource(dict(image=[]))
self.imagefig = figure(plot_width=self.CAMERA_WIDTH, plot_height=self.CAMERA_HEIGHT,
x_range=(0, self.CAMERA_WIDTH), y_range=(0, self.CAMERA_HEIGHT),
x_axis_type=None, y_axis_type=None, tools="", toolbar_location=None, name='image')
self.imagefig.background_fill_color = "#eaeaea"
self.imagefig.outline_line_color = '#595959'
# fill imagefig with rgba image data source
self.imagefig.image_rgba('image', x=0, y=0, dw=self.CAMERA_WIDTH, dh=self.CAMERA_HEIGHT,
source=self.image_source)
self.hist_ROI_source = ColumnDataSource(data=dict(bottom=[0], top=[0], left=[0], right=[0]))
self.imagefig.quad(fill_color='#ddac4f', line_color='#ddac4f', line_alpha=1, fill_alpha=0.25,
source=self.hist_ROI_source)
self.imagefig.on_event('panstart', self.on_PanStart)
self.imagefig.on_event('panend', self.on_PanEnd)
self.imagefig.on_event('mousemove', self.MouseMove)
self.imagefig.on_event('mouseleave', self.MouseLeave)
# define div containers for preview of last 4 images taken/triggered
self.image_div_1 = Div(text=self.template_png)
self.image_div_2 = Div(text=self.template_png)
self.image_div_3 = Div(text=self.template_png)
self.image_div_4 = Div(text=self.template_png)
self.camera_settings.white_balance_1.on_change('value', self.change_camera_settings)
self.camera_settings.white_balance_2.on_change('value', self.change_camera_settings)
self.camera_settings.analog_gain.on_change('value', self.change_camera_settings)
self.camera_settings.digital_gain.on_change('value', self.change_camera_settings)
self.camera_settings.sharpness.on_change('value', self.change_camera_settings)
self.camera_settings.contrast.on_change('value', self.change_camera_settings)
self.camera_settings.video_denoise.on_change('active', self.change_camera_settings)
# align buttons, image preview and taxonomy widget
start_preview = column(self.btn_preview, self.imagefig, self.autocomplete_tax.widget)
# grid of last 4 saved images
grid = gridplot([self.image_div_1, self.image_div_2, self.image_div_3, self.image_div_4],
ncols=2, merge_tools=False)
save_grid = column(self.btn_save, grid)
status_LED_barrier = column(self.text_status, self.btn_LED, self.btn_barrier, self.hist_button, self.histogram)
# put layout together
_layout = layout(column(row(start_preview, status_LED_barrier, save_grid), self.camera_settings.widget))
return Panel(child=_layout, title='PiCamera')
[docs] def on_PanStart(self, event: PointEvent):
"""Returns relative coordinates of mouse position in image figure of picamera preview on start of panning. \
Updates ROI.
"""
if self.hist_button.label == 'Deactivate histogram':
self.pan_start = [int(event.x), int(event.y)]
self.pan = True
[docs] def on_PanEnd(self, event: PointEvent):
"""Returns relative coordinates of mouse position in image figure of picamera preview on end of panning. \
Updates ROI.
"""
if self.hist_button.label == 'Deactivate histogram':
self.pan_end = [int(event.x), int(event.y)]
self.hist_ROI_source.data.update({'bottom': [self.pan_start[1]], 'top': [event.y],
'left': [self.pan_start[0]], 'right': [event.x]})
self.calculate_image_ROI()
self.pan = False
[docs] def MouseMove(self, event: PointEvent):
"""Returns relative coordinates of mouse position in image figure of picamera preview. Updates ROI.
"""
if self.hist_button.label == 'Deactivate histogram':
if self.pan:
self.hist_ROI_source.data.update({'bottom': [self.pan_start[1]], 'top': [event.y],
'left': [self.pan_start[0]], 'right': [event.x]})
self.pan_end = [int(event.x), int(event.y)]
self.calculate_image_ROI()
[docs] def MouseLeave(self, event: PointEvent):
"""Returns relative coordinates of position where mouse left image figure of picamera preview. Updates ROI.
"""
if self.hist_button.label == 'Deactivate histogram':
if self.pan:
self.pan_end = [int(event.x), int(event.y)]
self.hist_ROI_source.data.update({'bottom': [self.pan_start[1]], 'top': [event.y],
'left': [self.pan_start[0]], 'right': [event.x]})
self.calculate_image_ROI()
[docs] def calculate_image_ROI(self):
"""Calculate coordinates of ROI for histogram data from mouse positions and handle possible cases.
"""
if self.pan_start[0] < 0:
self.pan_start[0] = 0
if self.pan_start[0] > self.CAMERA_WIDTH:
self.pan_start[0] = self.CAMERA_WIDTH
if self.pan_start[1] > self.CAMERA_HEIGHT:
self.pan_start[1] = self.CAMERA_HEIGHT
if self.pan_end[0] < 0:
self.pan_end[0] = 0
if self.pan_end[0] > self.CAMERA_WIDTH:
self.pan_end[0] = self.CAMERA_WIDTH
if self.pan_end[1] > self.CAMERA_HEIGHT:
self.pan_end[1] = self.CAMERA_HEIGHT
if self.pan_start[0] < self.pan_end[0]:
self.left, self.right = self.pan_start[0], self.pan_end[0]
if self.pan_start[0] > self.pan_end[0]:
self.left, self.right = self.pan_end[0], self.pan_start[0]
if self.pan_start[1] < self.pan_end[1]:
self.bottom, self.top = self.CAMERA_HEIGHT - self.pan_end[1], self.CAMERA_HEIGHT - self.pan_start[1]
if self.pan_start[1] > self.pan_end[1]:
self.bottom, self.top = self.CAMERA_HEIGHT - self.pan_start[1], self.CAMERA_HEIGHT - self.pan_end[1]
self.count = (self.right - self.left) * (self.top - self.bottom)
[docs] def check_light_barrier_interruption(self):
""" The method checks if any GPIO pin that is connected to a light barrier is HIGH which
corresponds to an interruption.
:return: Status flag of light barrier interruption
:rtype: boolean
"""
for pin in self.config.connected_GPIO_IR_pins:
if GPIO.input(pin) == GPIO.HIGH:
return True
return False
[docs] def init_circular_stream(self):
""" Initializes a circular buffer for recording a h264 video. Parameters can be tuned via Picamera widget.
"""
self.camera.framerate = self.config.camera_fps
self.camera.resolution = self.config.video_resolution
self.camera.shutter_speed = self.camera_settings.shutter_speed.value
self.camera.exposure_mode = 'off'
self.camera.analog_gain = self.camera_settings.analog_gain.value
self.camera.digital_gain = self.camera_settings.digital_gain.value
self.camera.awb_mode = 'off'
self.camera.awb_gains = (self.camera_settings.white_balance_1.value, self.camera_settings.white_balance_2.value)
self.circular_stream = picamera.PiCameraCircularIO(self.camera, seconds=self.config.video_length,
bitrate=25000000)
self.camera.start_recording(self.circular_stream, format='h264', intra_period=1, quality=20)
[docs] @gen.coroutine
@without_document_lock
def camera_future(self):
"""Targeted by next tick callback from :meth:`periodic_callback`. The method creates a future picamera image
capture in a parallel thread that is not blocking the main Bokeh visualization thread.
:return: future
:rtype: object
"""
# get future object of picamera capture from video port
self.update_in_progress = yield self.executor.submit(self.camera_capture)
if self.stream_callback is not None:
self.curdoc.add_next_tick_callback(self.update_Bokeh)
[docs] def update_hist(self):
"""Activate respectively deactivates histogram in camera preview.
"""
if self.hist_button.label == 'Activate histogram':
self.hist_button.label = 'Deactivate histogram'
self.show_histogram = True
self.hist_ROI_source.data.update({'bottom': [self.pan_start[1]], 'top': [self.pan_end[1]],
'left': [self.pan_start[0]], 'right': [self.pan_end[0]]})
else:
self.hist_button.label = 'Activate histogram'
self.show_histogram = False
self.hist_source_red.data['top'] = np.zeros(256 // self.binning)
self.hist_source_green.data['top'] = np.zeros(256 // self.binning)
self.hist_source_blue.data['top'] = np.zeros(256 // self.binning)
self.mean_red_source.data['x'] = [0, 0]
self.mean_green_source.data['x'] = [0, 0]
self.mean_blue_source.data['x'] = [0, 0]
self.hist_ROI_source.data.update({'bottom': [0], 'top': [0],
'left': [0], 'right': [0]})
[docs] def camera_capture(self):
""" Targeted by ThreadPoolExecutor from :meth:`camera_future`. Method to capture a single image with picamera
video port.
:return: False flag for image capture in progress (capture done)
:rtype: bool
"""
self.camera.capture(self.image, 'rgba', use_video_port=True, resize=self.config.camera_res_preview)
if self.show_histogram:
image_rgba = self.image.reshape((self.CAMERA_HEIGHT, self.CAMERA_WIDTH, 4))
image_rgb_ROI = image_rgba[self.bottom:self.top, self.left:self.right, :3]
# cv2.imwrite('/home/pi/roi.png', cv2.cvtColor(image_rgb_ROI, cv2.COLOR_RGB2BGR))
self.hist_red, _ = np.histogram(image_rgb_ROI[:, :, 0], bins=self.edges)
self.hist_green, _ = np.histogram(image_rgb_ROI[:, :, 1], bins=self.edges)
self.hist_blue, _ = np.histogram(image_rgb_ROI[:, :, 2], bins=self.edges)
self.mean_red = np.mean(image_rgb_ROI[:, :, 0])
self.mean_green = np.mean(image_rgb_ROI[:, :, 1])
self.mean_blue = np.mean(image_rgb_ROI[:, :, 2])
return False
[docs] @gen.coroutine
def update_Bokeh(self):
""" Targeted by next tick callback from :meth:`camera_future`. Updates the picamera preview.
"""
view = self.image.view(dtype=np.uint32).reshape(self.CAMERA_HEIGHT, self.CAMERA_WIDTH)
view = np.flip(view, axis=0)
self.image_source.data['image'] = [view]
if self.show_histogram:
self.hist_source_red.data['top'] = self.hist_red / self.count
self.hist_source_green.data['top'] = self.hist_green / self.count
self.hist_source_blue.data['top'] = self.hist_blue / self.count
self.mean_red_source.data['x'] = [self.mean_red, self.mean_red]
self.mean_green_source.data['x'] = [self.mean_green, self.mean_green]
self.mean_blue_source.data['x'] = [self.mean_blue, self.mean_blue]
[docs] def periodic_callback(self):
"""Targeted by a Bokeh periodic callback from :meth:`do_preview`. Checks if the light barrier got interrupted
and sets the corresponding flag. If the current Bokeh document update is done a new camera capture for the
preview with :meth:`camera_future` gets initiated. If one of the save sequence flags is raised the
:meth:`save_h264_video` gets called before a new preview image is taken.
"""
# check if barrier is activated and triggered
if self.barrier:
if self.check_light_barrier_interruption():
if self.trigger_time >= self.trigger_timeout:
print('Light barrier triggered!')
self.trigger = True
self.trigger_time = 0
self.trigger_start = time()
elif self.trigger_time < self.trigger_timeout/2:
self.trigger_time = time() - self.trigger_start
else:
self.trigger_time = time() - self.trigger_start
self.trigger = False
if self.png_to_plot_queue:
datetime_path = self.png_to_plot_queue.pop(0)
self.plot_decoded_frame(datetime_path)
# let periodic callbacks from bokeh pass if blocking computations are performed to
# not let callback ticks pile up
if self.update_in_progress:
# Bokeh updating in progress, do nothing so js callbacks aren't blocked
pass
# save image sequence OR create camera future that is used to update the bokeh visuals
else:
# check if image sequence should be captured before new camera future gets initiated
# initiating a new image capture while the old capture in progress is not possible with picamera
# both flags are set to False after capture
if self.save_in_progress or self.trigger:
# set flags to false after video capture
self.trigger, self.save_in_progress = False, False
if not self.block_video_save:
self.executor.submit(self.save_h264_video)
else:
self.update_in_progress = True
self.curdoc.add_next_tick_callback(self.camera_future)
[docs] def do_preview(self):
""" Gets called on button click. Starts respectively ends the preview and changes the text status corresponding
to button label.
"""
if self.btn_preview.label == 'Start Stream':
self.btn_preview.label = 'Stop Stream'
self.text_status.text = self.template.format(status_text='Stream active', background='#3EA639',
colour='#FFFFFF', w='200px')
# initialize circular buffer
self.init_circular_stream()
# callback preview set to 10 ms to check light barrier
self.stream_callback = self.curdoc.add_periodic_callback(self.periodic_callback, 10)
# activate save button
self.btn_save.disabled = False
self.btn_barrier.disabled = False
self.btn_LED.disabled = False
if self.decode_callback is not None:
self.curdoc.remove_periodic_callback(self.decode_callback)
self.decode_callback = None
else:
self.btn_preview.label = 'Start Stream'
self.text_status.text = self.template.format(status_text='Stream inactive', background='#DC524C',
colour='#FFFFFF', w='200px')
self.camera.stop_recording()
self.btn_save.disabled = True
if self.btn_barrier.label == 'Deactivate Light Barrier':
self.barrier_on_off()
self.btn_barrier.disabled = True
if self.btn_LED.label == 'Deactivate LED flash':
self.LED_on_off()
self.btn_LED.disabled = True
self.barrier = False
self.btn_barrier.label = 'Activate Light Barrier'
self.btn_barrier.button_type = 'default'
self.curdoc.remove_periodic_callback(self.stream_callback)
self.stream_callback = None
if self.decode_jobs != 0:
self.decode_callback = self.curdoc.add_periodic_callback(self.finish_decodes, 500)
[docs] def manual_image_save(self):
""" Gets called on button click. Raises flag to capture an image sequence in :meth:`periodic_callback` to
call :meth:`save_h264_video`.
"""
self.save_in_progress = True
[docs] def save_h264_video(self):
"""Gets called on button click or when triggered by an interruption of the light barrier. Saves a h264 video
and decodes `n` images at the relative path `p`, where `n` is defined by `number_frames` and `p` by
`img_path` in :ref:`config.yaml`.
"""
self.block_video_save = True
# prepare data json with taxonomy information and construct directory
now = datetime.now()
self.data_dict_temp = self.autocomplete_tax.write_tax_to_data()
self.data_dict_temp["date_time"] = now.strftime("%Y-%m-%dT%H:%M:%SZ")
timestamp = now.strftime("%Y-%m-%d_%H-%M-%S")
folder_name = '{}_{}'.format(self.config.sensor_id, timestamp)
self.datetime_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), self.config.img_path, folder_name)
os.makedirs(self.datetime_path)
GPIO.output(self.GPIO_Switch, GPIO.HIGH)
self.camera.wait_recording(0.5 * self.config.video_length)
self.camera.stop_recording()
# Write the stream to disk
UUID_vid = uuid.uuid1()
self.data_dict_temp["video"]["filename"] = '{}.{}'.format(UUID_vid, 'mp4')
video_filename = os.path.join(self.datetime_path, '{}.{}'.format(UUID_vid, 'h264'))
rel_path = os.path.dirname(video_filename).split(os.path.dirname(
os.path.dirname(os.path.dirname(__file__))))[-1]
print(Fore.RED + 'Write h264 video to ' + rel_path + Fore.WHITE)
self.circular_stream.copy_to(video_filename, seconds=self.config.video_length)
# deactivate LED again if not activated before video capture
if self.btn_LED.label == 'Activate LED flash':
GPIO.output(self.GPIO_Switch, GPIO.LOW)
self.camera.start_recording(self.circular_stream, format='h264', intra_period=1, quality=20)
# converts video to mp4 and decodes frames in parallel thread
h264_decoder = FrameDecoder(self.config, self.datetime_path, self.data_dict_temp)
h264_decoder.start()
self.block_video_save = False
self.decode_frames_write_data(h264_decoder)
[docs] def decode_frames_write_data(self, h264_decoder):
"""Gets called by :meth:`save_h264_video`. :class:`FrameDecoder` converts h264 video to mp4 and \
decodes frames from h264 video and
:class:`WriteMetaData` writes meta data measured with sensors into `data.json` dictionary.
:parameter h264_decoder: object to decode frames from mp4 video
:type h264_decoder: object
"""
self.decode_jobs += 1
# decode frames from h264 video
h264_decoder.frames()
data_dict_temp = h264_decoder.data_json
# measure meta data and write into json
Metadata = WriteMetaData(self.config, data_dict_temp)
Metadata.write_all_data()
data_dict_temp = Metadata.data_json
# save json in datetime path
with open(os.path.join(h264_decoder.video_path, 'data.json'), 'w') as outfile:
json.dump(data_dict_temp, outfile, indent=4, ensure_ascii=False)
self.png_to_plot_queue.append(h264_decoder.plot_png_filename)
[docs] def plot_decoded_frame(self, plot_png_filename):
"""Gets called by :meth:`periodic_callback`. Plots a decoded frame.
"""
div_text_img = '<div class="image_preview"><img src="' + plot_png_filename + \
'" width="100%" height="100%"></div>'
self.image_div_list[self.plot_counter].text = div_text_img
# set counter to cycle through preview plots
self.plot_counter += 1
if self.plot_counter == 4:
self.plot_counter = 0
# decoded frame has been plotted
self.decode_jobs -= 1
if self.decode_jobs == 0:
print('All plotting jobs done!')
else:
print('%d more plotting jobs awaited!' % self.decode_jobs)
[docs] def finish_decodes(self):
"""Periodic callback targeted from :meth:`do_preview`. Finishes plots of decoded frames in preview if picamera
stream gets stopped before all plots were done.
"""
if self.decode_jobs != 0:
if self.png_to_plot_queue:
datetime_path = self.png_to_plot_queue.pop(0)
self.plot_decoded_frame(datetime_path)
else:
self.curdoc.remove_periodic_callback(self.decode_callback)
self.decode_callback = None
[docs] def barrier_on_off(self):
"""Gets called on button click. Activates respectively deactivates light barrier corresponding to button label.
The barrier status gets checked in :meth:`periodic_callback`.
"""
if self.btn_barrier.label == 'Activate Light Barrier':
self.btn_barrier.label = 'Deactivate Light Barrier'
self.btn_barrier.button_type = 'warning'
self.barrier = True
else:
self.btn_barrier.label = 'Activate Light Barrier'
self.btn_barrier.button_type = 'default'
self.barrier = False
[docs] def LED_on_off(self):
"""Gets called on button click. Activates respectively deactivates LED corresponding to button label.
"""
if self.btn_LED.label == 'Activate LED flash':
self.btn_LED.label = 'Deactivate LED flash'
self.btn_LED.button_type = 'warning'
GPIO.output(self.GPIO_Switch, GPIO.HIGH)
else:
self.btn_LED.label = 'Activate LED flash'
self.btn_LED.button_type = 'default'
GPIO.output(self.GPIO_Switch, GPIO.LOW)
[docs] def change_camera_settings(self, attr, old, new):
"""Gets called on value change of gains respectively white balance. Changes camera settings accordingly
"""
self.camera.awb_gains = (self.camera_settings.white_balance_1.value, self.camera_settings.white_balance_2.value)
self.camera.analog_gain = self.camera_settings.analog_gain.value
self.camera.digital_gain = self.camera_settings.digital_gain.value
self.camera.sharpness = self.camera_settings.sharpness.value
self.camera.contrast = self.camera_settings.contrast.value
self.camera.video_denoise = bool(int(self.camera_settings.video_denoise.active - 1))