from subprocess import Popen
from datetime import datetime
import logging
import sys
from dmf_device_ui.options import DebugView
from pygtkhelpers.delegates import SlaveView
from pygtkhelpers.ui.views.cairo_view import GtkCairoView
from pygtkhelpers.ui.views import composite_surface
from pygtkhelpers.utils import gsignal, refresh_gui
import cairo
import cv2
import gobject
import gtk
import numpy as np
import pandas as pd
import zmq
from .mode import VideoModeSelector
from . import np_to_cairo
logger = logging.getLogger(__name__)
class Transform(SlaveView):
gsignal('transform-reset')
gsignal('transform-rotate-left')
gsignal('transform-rotate-right')
gsignal('transform-modify-toggled', bool)
def __init__(self, transform=None):
self.transform = (np.eye(3, dtype=float) if transform is None
else transform)
super(Transform, self).__init__()
def create_ui(self):
super(Transform, self).create_ui()
self.widget.set_orientation(gtk.ORIENTATION_HORIZONTAL)
self.label_tag_transform = gtk.Label('Transform: ')
self.button_rotate_left = gtk.Button('Rotate left')
self.button_rotate_right = gtk.Button('Rotate right')
self.button_reset = gtk.Button('Reset')
self.button_modify = gtk.CheckButton('Modify')
for widget in (self.label_tag_transform, self.button_rotate_left,
self.button_rotate_right, self.button_reset,
self.button_modify):
self.widget.pack_start(widget, False, False, 0)
def on_button_reset__clicked(self, button):
self.emit('transform-reset')
def on_button_rotate_left__clicked(self, button):
self.emit('transform-rotate-left')
def on_button_rotate_right__clicked(self, button):
self.emit('transform-rotate-right')
def on_button_modify__toggled(self, button):
self.emit('transform-modify-toggled', self.modify)
@property
def modify(self):
return self.button_modify.get_active()
class VideoInfo(SlaveView):
def create_ui(self):
super(VideoInfo, self).create_ui()
self.widget.set_orientation(gtk.ORIENTATION_HORIZONTAL)
self.label_tag_fps = gtk.Label('Frames per second:')
self.label_fps = gtk.Label()
self.label_tag_dropped_rate = gtk.Label(' Dropped frames/s:')
self.label_dropped_rate = gtk.Label()
self.widget.pack_start(self.label_tag_fps, False, False)
self.widget.pack_start(self.label_fps, False, False)
self.widget.pack_start(self.label_tag_dropped_rate, False, False)
self.widget.pack_start(self.label_dropped_rate, False, False)
self.frames_per_second = 0
self.dropped_rate = 0
@property
def frames_per_second(self):
return float(self.label_fps.get_text())
@frames_per_second.setter
def frames_per_second(self, value):
self.label_fps.set_text('%.1f' % float(value))
@property
def dropped_rate(self):
return float(self.label_dropped_rate.get_text())
@dropped_rate.setter
def dropped_rate(self, value):
self.label_dropped_rate.set_text('%.1f' % float(value))
class VideoSink(SlaveView):
gsignal('frame-rate-update', float, float)
gsignal('frame-update', object)
gsignal('frame-shape-changed', object)
gsignal('transform-changed', object)
def __init__(self, transport, target_host, port=None):
self.status = {}
super(VideoSink, self).__init__()
self.socket_info = {'host': target_host,
'port': port,
'transport': transport}
self.video_timeout_id = None
self.frame_shape = None
self._transform = np.identity(3, dtype='float32')
self.shape = None
@property
def transform(self):
return self._transform
@transform.setter
def transform(self, value):
self._transform = value
self.emit('transform-changed', value)
def reset(self):
if self.video_timeout_id is not None:
gobject.remove_source(self.video_timeout_id)
self.ctx = zmq.Context.instance()
self.target_socket = zmq.Socket(self.ctx, zmq.PULL)
base_uri = '%s://%s' % (self.socket_info['transport'],
self.socket_info['host'])
port = self.socket_info['port']
if port is None:
port = self.target_socket.bind_to_random_port(base_uri)
uri = '%s:%s' % (base_uri, port)
else:
uri = '%s:%s' % (base_uri, port)
self.target_socket.bind(uri)
self.socket_info['port'] = port
logger.info('Listening on: %s', uri)
status = {'frame_count': 0, 'dropped_count': 0, 'start_time':
datetime.now()}
self.status = status
self.video_timeout_id = gtk.timeout_add(50, self.check_sockets, status)
def check_sockets(self, status):
buf_str = None
frame_count = 0
while True:
try:
shape_str, buf_str = (self.target_socket
.recv_multipart(zmq.NOBLOCK))
frame_count += 1
except zmq.Again:
break
status['dropped_count'] += frame_count - 1 if frame_count > 0 else 0
status['frame_count'] += frame_count
status['buf_str'] = buf_str
status['duration'] = (datetime.now() -
status['start_time']).total_seconds()
if buf_str is not None:
self.prepare_np_frame(shape_str, buf_str)
if status['duration'] > 2:
status['fps'] = status['frame_count'] / status['duration']
#print '\r%5.1f frames/second (%2d dropped)' % (status['fps'],
#status
#['dropped_count']),
self.emit('frame-rate-update', status['fps'],
status['dropped_count'] / status['duration'])
status['frame_count'] = 0
status['dropped_count'] = 0
status['start_time'] = datetime.now()
return True
def prepare_np_frame(self, shape_str, buf_str):
'''
Convert raw frame buffer to numpy array and apply warp perspective
transformation.
Emits:
frame-update : New numpy video frame available with perspective
transform applied.
'''
height, width, channels = np.frombuffer(shape_str, count=3,
dtype='uint32')
im_buf = np.frombuffer(buf_str, dtype='uint8',
count=len(buf_str)).reshape(height, width, -1)
# Cairo surface seems to use BGR ordering, so convert frame color.
np_bgr = cv2.cvtColor(im_buf, cv2.COLOR_RGB2BGR)
# Warp and scale
if self.frame_shape != (width, height):
# Frame shape has changed.
self.frame_shape = width, height
self.emit('frame-shape-changed', self.frame_shape)
if self.shape is None:
self.shape = width, height
np_warped = cv2.warpPerspective(np_bgr, self.transform, self.shape)
self.emit('frame-update', np_warped)
class VideoView(GtkCairoView):
gsignal('point-pair-selected', object)
gsignal('video-enabled')
gsignal('video-disabled')
def __init__(self, transport, target_host, port=None):
self.socket_info = {'transport': transport,
'host': target_host,
'port': port}
self.callback_id = None
self._enabled = False